{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"coding-agent","type":"registry:block","title":"AI Coding Agent","description":"A coding run with agent status, tool calls, diffs and a human-in-the-loop approval gate.","author":"Scrim UI (https://scrimui.dev)","categories":["pattern"],"docs":"https://scrimui.dev/patterns/coding-agent","dependencies":[],"registryDependencies":["https://scrimui.dev/r/agent-status.json","https://scrimui.dev/r/tool-call.json","https://scrimui.dev/r/approval-request.json"],"files":[{"path":"src/showcase/patterns/coding-agent/coding-agent.tsx","content":"\"use client\";\n\nimport * as React from \"react\";\nimport { AgentStatus } from \"@/components/ui/agent-status\";\nimport { ToolCall } from \"@/components/ui/tool-call\";\nimport { ApprovalRequest } from \"@/components/ui/approval-request\";\n\n/* ------------------------------------------------------------------ */\n/* Icons                                                               */\n/* ------------------------------------------------------------------ */\n\nfunction FileIcon() {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" width=\"14\" height=\"14\">\n      <path d=\"M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2Z\" />\n      <path d=\"M14 2v6h6\" />\n    </svg>\n  );\n}\n\nfunction TerminalIcon() {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" width=\"14\" height=\"14\">\n      <path d=\"m4 17 6-6-6-6M12 19h8\" />\n    </svg>\n  );\n}\n\nfunction CheckIcon() {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" width=\"13\" height=\"13\">\n      <path d=\"M20 6 9 17l-5-5\" />\n    </svg>\n  );\n}\n\n/* ------------------------------------------------------------------ */\n/* Diff block                                                          */\n/* ------------------------------------------------------------------ */\n\nfunction DiffBlock() {\n  const lines = [\n    { type: \"hunk\", text: \"@@ -24,7 +24,8 @@ function refresh() {\" },\n    { type: \"ctx\", text: \"   const token = await readStoredToken();\" },\n    { type: \"del\", text: \"-  const { access, expiresAt } = await refreshToken(token);\" },\n    { type: \"add\", text: \"+  const result = await refreshWithSingleFlight(token);\" },\n    { type: \"ctx\", text: \"   if (isExpired(result.expiresAt)) throw new TokenError();\" },\n    { type: \"add\", text: \"+  // Only the first caller refreshes; the rest wait on the same promise.\" },\n    { type: \"ctx\", text: \"   return result.access;\" },\n  ];\n  return (\n    <div className=\"overflow-hidden rounded-lg border border-zinc-800 bg-zinc-950 dark:border-zinc-700\">\n      <div className=\"flex items-center gap-2 border-b border-zinc-800 px-3 py-2 text-xs text-zinc-400 dark:border-zinc-700\">\n        <FileIcon />\n        src/lib/auth/token.ts\n        <span className=\"ml-auto tabular-nums\">+2 −1</span>\n      </div>\n      <pre className=\"overflow-x-auto p-3 font-mono text-xs leading-5\">\n        {lines.map((l, i) => (\n          <span\n            key={i}\n            className={`block ${\n              l.type === \"add\"\n                ? \"bg-emerald-950/50 text-emerald-300\"\n                : l.type === \"del\"\n                  ? \"bg-red-950/50 text-red-300\"\n                  : l.type === \"hunk\"\n                    ? /* zinc-400: on the zinc-950 code surface, zinc-500 is\n                         4.12:1. -400 is 7.76:1 and still reads dimmer than the\n                         zinc-300 code lines around it. */\n                      \"text-zinc-400\"\n                    : \"text-zinc-300\"\n            }`}\n          >\n            {l.text}\n          </span>\n        ))}\n      </pre>\n    </div>\n  );\n}\n\n/* ------------------------------------------------------------------ */\n/* CodingAgentPattern                                                  */\n/* ------------------------------------------------------------------ */\n\nexport function CodingAgentPattern() {\n  const [approved, setApproved] = React.useState(false);\n\n  return (\n    <div className=\"space-y-3 rounded-2xl border border-zinc-200 bg-white p-5 dark:border-zinc-800 dark:bg-zinc-900\">\n      {/* Header */}\n      <div>\n        <p className=\"text-[11px] font-medium uppercase tracking-wide text-zinc-500 dark:text-zinc-400\">Task</p>\n        <h3 className=\"mt-1 text-lg font-semibold text-zinc-900 dark:text-zinc-100\">\n          Fix the token refresh race condition\n        </h3>\n      </div>\n\n      {/* Agent status */}\n      <AgentStatus\n        name=\"Coding Agent\"\n        status={approved ? \"completed\" : \"running\"}\n        action={\n          approved\n            ? \"Applied fix and re-ran the full auth suite\"\n            : \"Analyzing token lifecycle, then applying patch to 3 files\"\n        }\n        elapsed={approved ? \"42.0s\" : \"18.6s\"}\n        progress={approved ? 100 : 64}\n        onStop={() => {}}\n      />\n\n      {/* Tool calls */}\n      <div className=\"space-y-2\">\n        <ToolCall\n          name=\"Run auth tests (auth.test.ts)\"\n          icon={<TerminalIcon />}\n          status=\"success\"\n          duration=\"4.6s\"\n          defaultOpen\n          input={`{\"command\": \"npm test -- --filter=auth\"}`}\n          output={`{\"passed\": 8, \"failed\": 1, \"skipped\": 0}`}\n        />\n        <ToolCall\n          name=\"Read source (token.ts)\"\n          icon={<FileIcon />}\n          status=\"success\"\n          duration=\"0.3s\"\n          input={`{\"file\": \"src/lib/auth/token.ts\", \"range\": \"1-60\"}`}\n        />\n      </div>\n\n      {/* Diff */}\n      <div>\n        <p className=\"mb-1.5 text-xs font-medium text-zinc-500 dark:text-zinc-400\">Proposed change</p>\n        <DiffBlock />\n      </div>\n\n      {/* Approval */}\n      <ApprovalRequest\n        title=\"Apply patch and run migration?\"\n        requester=\"Coding Agent\"\n        description=\"Applies the fix to 3 files and re-runs the auth suite against the staging database.\"\n        detail={`- src/lib/auth/token.ts\\n- src/lib/auth/refresh.ts\\n- src/test/auth/refresh.test.ts`}\n        status={approved ? \"approved\" : \"pending\"}\n        onAllow={() => setApproved(true)}\n        onDeny={() => setApproved(false)}\n      />\n\n      {/* Completion */}\n      {approved && (\n        <div className=\"flex items-center gap-2 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-800 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-300\">\n          <CheckIcon />\n          <span className=\"font-medium\">Task completed.</span>\n          <span className=\"text-emerald-700/80 dark:text-emerald-400/80\">\n            All 9 tests pass. The race is resolved with single-flight refresh.\n          </span>\n        </div>\n      )}\n    </div>\n  );\n}\n","type":"registry:block","target":"components/blocks/coding-agent.tsx"}]}