{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tool",
  "title": "Tool",
  "description": "Quiet collapsible tool call with input and output.",
  "dependencies": [
    "ai"
  ],
  "registryDependencies": [
    "collapsible"
  ],
  "files": [
    {
      "path": "packages/registry/registry/ui/tool.tsx",
      "content": "\"use client\";\n\nimport type { ToolUIPart } from \"ai\";\nimport { ChevronDownIcon } from \"lucide-react\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { isValidElement } from \"react\";\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from \"@/components/ui/collapsible\";\nimport { cn } from \"@/lib/utils\";\n\nexport type ToolProps = ComponentProps<typeof Collapsible>;\n\nexport function Tool({ className, defaultOpen = false, ...props }: ToolProps) {\n  return (\n    <Collapsible\n      className={cn(\"not-prose my-1 w-full min-w-0\", className)}\n      data-slot=\"tool\"\n      defaultOpen={defaultOpen}\n      {...props}\n    />\n  );\n}\n\nfunction readableToolName(name: string) {\n  return name\n    .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n    .replace(/[_-]+/g, \" \")\n    .trim()\n    .replace(/\\b\\w/g, (char) => char.toUpperCase());\n}\n\nfunction toolInputSummary(\n  toolName: string,\n  input: unknown\n): string | undefined {\n  if (!input || typeof input !== \"object\") {\n    return;\n  }\n  const record = input as Record<string, unknown>;\n  const name = toolName.toLowerCase();\n  if (\n    (name === \"bash\" || name === \"shell\") &&\n    typeof record.command === \"string\"\n  ) {\n    return record.command;\n  }\n  const path = record.file_path ?? record.path ?? record.filePath;\n  if (typeof path === \"string\") {\n    return path;\n  }\n  if (typeof record.pattern === \"string\") {\n    return record.pattern;\n  }\n  if (typeof record.query === \"string\") {\n    return record.query;\n  }\n}\n\nfunction truncateSummary(value: string, max = 72): string {\n  const trimmed = value.replace(/\\s+/g, \" \").trim();\n  if (trimmed.length <= max) {\n    return trimmed;\n  }\n  return `${trimmed.slice(0, max - 1)}…`;\n}\n\nexport function toolTitle(toolName: string, input?: unknown): string {\n  const readable = readableToolName(toolName);\n  const summary =\n    input === undefined ? undefined : toolInputSummary(toolName, input);\n  if (!summary) {\n    return readable;\n  }\n  return `${readable} · ${truncateSummary(summary)}`;\n}\n\nexport interface ToolHeaderProps {\n  title?: string;\n  type: ToolUIPart[\"type\"];\n  state: ToolUIPart[\"state\"];\n  input?: unknown;\n  className?: string;\n}\n\nexport function ToolHeader({\n  className,\n  title,\n  type,\n  state,\n  input,\n  ...props\n}: ToolHeaderProps) {\n  const toolName = title ?? type.split(\"-\").slice(1).join(\"-\");\n  const label = toolTitle(toolName, input);\n  const isRunning =\n    state === \"input-available\" ||\n    state === \"input-streaming\" ||\n    state === \"approval-responded\";\n  const needsApproval = state === \"approval-requested\";\n  const isDenied = state === \"output-denied\";\n  const isError = state === \"output-error\";\n\n  return (\n    <CollapsibleTrigger\n      className={cn(\n        \"group inline-flex max-w-full cursor-pointer items-center gap-1 text-muted-foreground text-sm transition-colors hover:text-foreground\",\n        className\n      )}\n      data-slot=\"tool-header\"\n      {...props}\n    >\n      <span\n        className={cn(\n          \"truncate\",\n          (isRunning || needsApproval) && \"animate-pulse\"\n        )}\n      >\n        {label}\n      </span>\n      {needsApproval ? (\n        <span className=\"shrink-0 text-xs\">Needs approval</span>\n      ) : null}\n      {isDenied ? (\n        <span className=\"shrink-0 text-destructive text-xs\">Denied</span>\n      ) : null}\n      {isError ? (\n        <span className=\"shrink-0 text-destructive text-xs\">Error</span>\n      ) : null}\n      <ChevronDownIcon className=\"size-3.5 shrink-0 -rotate-90 transition-transform group-data-[panel-open]:rotate-0\" />\n    </CollapsibleTrigger>\n  );\n}\n\nexport type ToolContentProps = ComponentProps<typeof CollapsibleContent>;\n\nexport function ToolContent({ className, ...props }: ToolContentProps) {\n  return (\n    <CollapsibleContent\n      className={cn(\n        \"flex flex-col gap-1 py-1 text-muted-foreground text-sm outline-none\",\n        className\n      )}\n      data-slot=\"tool-content\"\n      {...props}\n    />\n  );\n}\n\nfunction JsonPre({ value }: { value: unknown }) {\n  const text =\n    typeof value === \"string\" ? value : JSON.stringify(value, null, 2);\n  return <pre className=\"overflow-x-auto font-mono text-xs\">{text}</pre>;\n}\n\nexport type ToolInputProps = ComponentProps<\"div\"> & {\n  input: ToolUIPart[\"input\"];\n  label?: string;\n};\n\nexport function ToolInput({\n  className,\n  input,\n  label = \"Input\",\n  ...props\n}: ToolInputProps) {\n  if (input == null) {\n    return null;\n  }\n\n  return (\n    <div\n      className={cn(\"flex flex-col gap-0.5 overflow-hidden\", className)}\n      data-slot=\"tool-input\"\n      {...props}\n    >\n      <p className=\"text-muted-foreground text-xs\">{label}</p>\n      <JsonPre value={input} />\n    </div>\n  );\n}\n\nexport type ToolOutputProps = ComponentProps<\"div\"> & {\n  output: ToolUIPart[\"output\"];\n  errorText: ToolUIPart[\"errorText\"];\n  label?: string;\n  errorLabel?: string;\n};\n\nexport function ToolOutput({\n  className,\n  output,\n  errorText,\n  label = \"Output\",\n  errorLabel = \"Error\",\n  ...props\n}: ToolOutputProps) {\n  if (!(output || errorText)) {\n    return null;\n  }\n\n  let body: ReactNode = output as ReactNode;\n  if (typeof output === \"object\" && !isValidElement(output)) {\n    body = <JsonPre value={output} />;\n  } else if (typeof output === \"string\") {\n    body = <JsonPre value={output} />;\n  }\n\n  return (\n    <div\n      className={cn(\n        \"flex flex-col gap-0.5 overflow-x-auto text-xs\",\n        errorText ? \"text-destructive\" : \"text-foreground\",\n        className\n      )}\n      data-slot=\"tool-output\"\n      {...props}\n    >\n      <p className=\"text-muted-foreground text-xs\">\n        {errorText ? errorLabel : label}\n      </p>\n      {errorText ? errorText : body}\n    </div>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}