Skip to content
BlocksCoding agentMenu

Coding agent

A Claude Code and Cursor style coding agent in one block: the thread with reasoning, tool calls and approvals, a workspace with changed files, diffs and the agent's terminal, todos, a message queue, permission mode and a context meter.

A scripted coding session: plan, tool calls, an approval, diffs and a terminal.

The demo plays a scripted session with no model and no API key. The first edit waits for your approval and allows itself after 3.5 s, so it also runs hands free. A message queued halfway goes out when the run ends. Open /preview/coding-agent-demo to see it full screen, and narrow the window to watch it fold into tabs.

Installation

$ pnpm dlx shadcn@latest add https://chatcn.dev/r/coding-agent.json

The block installs to components/chatcn/coding-agent/coding-agent.tsx with every component it composes. It is your code from here: add a file tree, swap the terminal for a live one, change the header.

Usage

Pass messages in the chatcn contract, a status and the actions you support. The workspace builds itself from the tool calls in the thread: edits and writes become changed files and diffs, shell commands become the terminal session, and the latest todo_write becomes the pinned todo list.

app/agent/page.tsx
"use client"


export default function Page() {
  const chat = useChatcnChat({
    transport: new DefaultChatTransport({ api: "/api/agent" }),
  })
  return (
    <CodingAgent
      fullHeight
      messages={chat.chatcnMessages}
      status={chat.status}
      actions={chat.actions}
      branch="fix/runs-list-query"
      model="claude-sonnet-4-5"
      context={{ limit: 200_000, used: inputTokens }}
    />
  )
}

Tool names are matched loosely (edit_file, str_replace, apply_patch, write_file, bash, run_command, TodoWrite and their spellings). Edits read a unified diff or old and new text; commands read stdout, stderr and exitCode, and a preliminary output streams into the terminal while it runs. Use codingWorkspaceFromMessages on its own to build the same view elsewhere, or pass workspace to replace any part of it.

tsx
import { codingWorkspaceFromMessages } from "@/components/chatcn/coding-agent/coding-agent"

const { files, terminal, todos, pendingPaths } = codingWorkspaceFromMessages(messages)

Queue and permission mode

While the agent works, an empty composer offers Stop; typing turns the button back into Send, and sending adds to the queue above the composer. When the run ends, everything queued goes out as one message through actions.send; the rows show Sending until that message appears in the thread. Shift+Tab in the message field cycles the permission mode. The block only shows the mode: read it with onPermissionModeChange and apply it where your tools ask for approval.

Components it composes

ComponentRole
ConversationThe scrolling thread that sticks to the bottom while you follow
Message and Message partsTurns and their parts in order
MarkdownThe agent's prose and code
ReasoningThinking, folded once done
Trace groupRuns of file reads collapsed into Read 4 files. Repeat todo updates stay out of the thread, since the list is pinned
Tool call and the tool renderersSearch, read, edit, write and shell calls, approvals included
Changed filesThe change set in the workspace
Diff viewerThe selected file's diff, streaming while the agent writes it
TerminalThe agent's shell session with live output
Todo listThe plan, pinned above the composer
Queued messagesMessages sent while the agent works
Prompt inputThe composer
Permission modeAsk before edits, edit automatically, plan only or full access
Context meterHow full the context window is
Run statusRunning, needs approval or done, in the header
ResizableThe split between thread and workspace

Responsive

The block measures its own width, not the window, so it behaves the same in a docs column, a dialog or a full page. From 768 px (splitAt) the thread and workspace sit side by side with a draggable divider. Below it, one pane shows at a time under a Chat, Changes and Terminal tab bar, with 44 px targets on touch screens. Inside Changes the file list moves above the diff when the pane is narrow. While the workspace is on screen, edits and commands stay folded in the thread, since their diff and output are one glance away; in the stacked layout they open as usual.

Props

PropTypeNotes
messagesChatcnMessage[]AI SDK UIMessage works as is
status"ready" | "submitted" | "streaming" | "error"Same values as useChat().status
actionsChatcnActionssend, stop, approve, deny. Missing ones hide their controls
titleReactNodeDefaults to the first line of the first user message
branch, modelstringQuiet meta in the header from 768 px
headerActionsReactNodeExtra controls at the end of the header
workspacePartial<CodingWorkspace>Replace any derived part: files, terminal, todos
toolRenderersToolRendererMapDefaults to every chatcn renderer plus todo_write
workspaceTab, defaultWorkspaceTab, onWorkspaceTabChange"changes" | "terminal"Controlled or uncontrolled
followActivitybooleanMove to what the agent touches. Defaults to true; picking a tab pauses it until you send
permissionModes, permissionMode, defaultPermissionMode, onPermissionModeChangeControlled or uncontrolled
context{ limit, used?, breakdown?, model?, compacted?, onCompact? }No meter without it
queue, defaultQueue, onQueueChangeQueuedMessage[]Controlled or uncontrolled
placeholder, queuePlaceholderstringComposer placeholder while idle and while the agent works
emptyTitle, emptyDescriptionReactNodeShown before the first message
splitAtnumberOwn width in px for the two pane layout. Defaults to 768
fullHeightbooleanFill the viewport. Otherwise give the parent a height

Keyboard

KeyAction
EnterSend, or queue while the agent works
Shift EnterNew line
Shift TabNext permission mode, in the message field
EscapeStop the run, from an empty composer
Ctrl ` ``Open the terminal
ArrowLeft ArrowRightMove between tabs
ArrowLeft ArrowRight on the dividerResize the panes

Accessibility

  • The thread is a labelled log that stays silent while tokens stream; finished replies are announced once.
  • Tabs are a real tablist with roving focus, and each panel is labelled by its tab.
  • Approvals, the queue and the composer are reachable in reading order; the header announces Needs approval as text, not only as an icon.
  • The terminal and the diff are focusable scroll regions with their own names.

Motion

The block adds no motion of its own. Messages, tool rows, the todo fold and queue rows use their components' timings, all of them under 300 ms and reduced to fades with prefers-reduced-motion. Following the agent swaps tabs without animation, so nothing moves on its own except the content being written.