import { createRoot } from "react-dom/client"; import { useAgent } from "agents/react"; import { useAgentChat } from "@cloudflare/ai-chat/react"; import { isToolUIPart } from "ai"; import "./styles.css"; import { useState, useEffect, useRef, useCallback } from "react"; import { Streamdown } from "streamdown"; import { Button, Surface, Text, InputArea, Empty, Badge } from "@cloudflare/kumo"; import { PaperPlaneRightIcon, TrashIcon, GearIcon, LightningIcon, CaretRightIcon, XIcon, CodeIcon, TerminalIcon, WarningCircleIcon, CheckCircleIcon, CircleNotchIcon, BrainIcon, CaretDownIcon, PlugsConnectedIcon, PlusIcon, CubeIcon, SpinnerGapIcon } from "@phosphor-icons/react"; import { ModeToggle, PoweredByAgents, ConnectionIndicator, type ConnectionStatus } from "@cloudflare/agents-ui"; import { ThemeProvider } from "@cloudflare/agents-ui/hooks"; import type { ExecutorType } from "./server"; interface McpTool { serverId: string; name: string; description?: string; } interface ToolPart { type: string; toolCallId?: string; state?: string; errorText?: string; input?: { functionDescription?: string; [key: string]: unknown }; output?: { code?: string; result?: string; logs?: string[]; [key: string]: unknown; }; } const EXECUTORS: { value: ExecutorType; label: string; description: string }[] = [ { value: "dynamic-worker", label: "Dynamic Worker", description: "Sandboxed Cloudflare Worker via WorkerLoader" }, { value: "node-server", label: "Node Server", description: "Node.js VM via external HTTP server" } ]; const TOOLS: { name: string; description: string }[] = [ { name: "createProject", description: "Create a new project" }, { name: "listProjects", description: "List all projects" }, { name: "createTask", description: "Create a task in a project" }, { name: "listTasks", description: "List tasks with optional filters" }, { name: "updateTask", description: "Update a task's fields" }, { name: "deleteTask", description: "Delete a task and its comments" }, { name: "createSprint", description: "Create a sprint for a project" }, { name: "listSprints", description: "List sprints, optionally by project" }, { name: "addComment", description: "Add a comment to a task" }, { name: "listComments", description: "List comments on a task" } ]; function extractFunctionCalls(code?: string): string[] { if (!code) return []; const matches = code.match(/codemode\.(\w+)/g); if (!matches) return []; return [...new Set(matches.map((m) => m.replace("codemode.", "")))]; } function ReasoningBlock({ text, isStreaming }: { text: string; isStreaming: boolean; }) { const [expanded, setExpanded] = useState(false); if (!text?.trim()) return null; return (
{expanded && (
{text}
)}
); } function ToolCard({ toolPart }: { toolPart: ToolPart }) { const [expanded, setExpanded] = useState(false); const hasError = toolPart.state === "output-error" || !!toolPart.errorText; const isComplete = toolPart.state === "output-available"; const isRunning = !isComplete && !hasError; const functionCalls = extractFunctionCalls( toolPart.output?.code || (toolPart.input?.code as string) ); const summary = functionCalls.length > 0 ? functionCalls.join(", ") : "code execution"; return ( {expanded && (
{toolPart.output?.code && (
Code
                {toolPart.output.code}
              
)} {!toolPart.output?.code && toolPart.input && (
Input
                {JSON.stringify(toolPart.input, null, 2)}
              
)} {toolPart.output?.result !== undefined && (
Result
                {JSON.stringify(toolPart.output.result, null, 2)}
              
)} {toolPart.output?.logs && toolPart.output.logs.length > 0 && (
Console
                {toolPart.output.logs.join("\n")}
              
)} {toolPart.errorText && (
Error
                {toolPart.errorText}
              
)}
)}
); } function SettingsPanel({ executor, onExecutorChange, loading, onClose, mcpTools, onAddMcp, onRemoveMcp, onRefreshMcpTools, mcpLoading }: { executor: ExecutorType; onExecutorChange: (e: ExecutorType) => void; loading: boolean; onClose: () => void; mcpTools: McpTool[]; onAddMcp: (url: string, name?: string) => Promise; onRemoveMcp: (serverId: string) => Promise; onRefreshMcpTools: () => void; mcpLoading: boolean; }) { const [mcpUrl, setMcpUrl] = useState(""); const [mcpName, setMcpName] = useState(""); const [addingMcp, setAddingMcp] = useState(false); const handleAddMcp = async () => { if (!mcpUrl.trim()) return; setAddingMcp(true); try { await onAddMcp(mcpUrl.trim(), mcpName.trim() || undefined); setMcpUrl(""); setMcpName(""); } finally { setAddingMcp(false); } }; // Group MCP tools by server const toolsByServer = mcpTools.reduce( (acc, tool) => { if (!acc[tool.serverId]) acc[tool.serverId] = []; acc[tool.serverId].push(tool); return acc; }, {} as Record ); return ( <> {Object.keys(toolsByServer).length > 0 && (
Connected Servers
{Object.entries(toolsByServer).map(([serverId, tools]) => (
{serverId} {tools.length} tools
{tools.map((tool) => (
{tool.name} {tool.description && ( {tool.description} )}
))}
))}
)} {Object.keys(toolsByServer).length === 0 && (
No MCP servers connected
)} {/* Available Functions Section */}
Built-in Functions
{TOOLS.map((tool) => (
{tool.name} {tool.description}
))}
); } function App() { const [input, setInput] = useState(""); const [executor, setExecutor] = useState("dynamic-worker"); const [settingsOpen, setSettingsOpen] = useState(false); const [connectionStatus, setConnectionStatus] = useState("connecting"); const [mcpTools, setMcpTools] = useState([]); const [mcpLoading, setMcpLoading] = useState(false); const messagesEndRef = useRef(null); const agent = useAgent({ agent: "codemode", onOpen: useCallback(() => setConnectionStatus("connected"), []), onClose: useCallback(() => setConnectionStatus("disconnected"), []), onError: useCallback(() => setConnectionStatus("disconnected"), []) }); const { messages, sendMessage, clearHistory, status } = useAgentChat({ agent }); const isStreaming = status === "streaming"; const isConnected = connectionStatus === "connected"; const handleExecutorChange = useCallback( (newExecutor: ExecutorType) => { setExecutor(newExecutor); agent.call("setExecutor", [newExecutor]); }, [agent] ); const refreshMcpTools = useCallback(async () => { setMcpLoading(true); try { const tools = await agent.call("listMcpTools", []); setMcpTools(tools as McpTool[]); } catch (err) { console.error("Failed to list MCP tools:", err); } finally { setMcpLoading(false); } }, [agent]); const handleAddMcp = useCallback( async (url: string, name?: string) => { try { await agent.call("addMcp", [url, name]); // Refresh tools list after adding server await refreshMcpTools(); } catch (err) { console.error("Failed to add MCP server:", err); throw err; } }, [agent, refreshMcpTools] ); const handleRemoveMcp = useCallback( async (serverId: string) => { try { await agent.call("removeMcp", [serverId]); await refreshMcpTools(); } catch (err) { console.error("Failed to remove MCP server:", err); } }, [agent, refreshMcpTools] ); // Load MCP tools when settings panel opens useEffect(() => { if (settingsOpen && isConnected) { refreshMcpTools(); } }, [settingsOpen, isConnected, refreshMcpTools]); const send = useCallback(() => { const text = input.trim(); if (!text || isStreaming) return; setInput(""); sendMessage({ role: "user", parts: [{ type: "text", text }] }); }, [input, isStreaming, sendMessage]); useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }, [messages]); return (

Codemode

{EXECUTORS.find((e) => e.value === executor)?.label}
{messages.length === 0 && ( } title="Welcome to Codemode" description="AI-powered project management. Ask me to create projects, manage tasks, plan sprints, and more." /> )} {messages.map((message, msgIndex) => { const isUser = message.role === "user"; const isLastAssistant = message.role === "assistant" && msgIndex === messages.length - 1; const isAnimating = isStreaming && isLastAssistant; if (isUser) { return (
{message.parts .filter((p) => p.type === "text") .map((p) => (p.type === "text" ? p.text : "")) .join("")}
); } return (
{message.parts.map((part, partIdx) => { if (part.type === "text") { if (!part.text || part.text.trim() === "") return null; return (
{part.text}
); } if (part.type === "step-start") return null; if (part.type === "reasoning") { return ( ); } if (isToolUIPart(part)) { const toolPart = part as unknown as ToolPart; return (
); } return null; })}
); })}
{ e.preventDefault(); send(); }} className="max-w-3xl mx-auto px-5 py-4" >
{ if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); } }} placeholder={ isConnected ? "Ask me to manage your projects..." : "Connecting..." } disabled={!isConnected || isStreaming} rows={2} className="flex-1 !ring-0 focus:!ring-0 !shadow-none !bg-transparent !outline-none" />
{settingsOpen && ( setSettingsOpen(false)} mcpTools={mcpTools} onAddMcp={handleAddMcp} onRemoveMcp={handleRemoveMcp} onRefreshMcpTools={refreshMcpTools} mcpLoading={mcpLoading} /> )}
); } createRoot(document.getElementById("root")!).render( );