{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ai-chat",
  "type": "registry:block",
  "title": "AI Chat",
  "description": "AI SDK chat route, model selector, message persistence, and prompt input.",
  "dependencies": [
    "ai",
    "@ai-sdk/react",
    "@ai-sdk/gateway"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://stackfoundry.dev/r/ai-sdk.json"
  ],
  "files": [
    {
      "path": "apps/web/src/app/api/ai/chat/route.ts",
      "type": "registry:file",
      "target": "apps/web/src/app/api/ai/chat/route.ts",
      "content": "import { convertToModelMessages, streamText, type UIMessage } from \"ai\";\n\nimport { getSelectedModel } from \"@/lib/ai/models\";\nimport { chatSystemPrompt } from \"@/lib/ai/prompts\";\n\nexport const maxDuration = 60;\n\ntype ChatRequest = {\n  messages?: UIMessage[];\n  message?: UIMessage;\n  model?: string;\n};\n\nexport async function POST(request: Request) {\n  let body: ChatRequest;\n\n  try {\n    body = (await request.json()) as ChatRequest;\n  } catch {\n    return Response.json({ error: \"Invalid JSON body\" }, { status: 400 });\n  }\n\n  const messages = body.messages ?? (body.message ? [body.message] : []);\n\n  try {\n    const result = streamText({\n      model: getSelectedModel(body.model),\n      system: chatSystemPrompt,\n      messages: convertToModelMessages(messages),\n    });\n\n    return result.toUIMessageStreamResponse();\n  } catch (error) {\n    return Response.json(\n      { error: error instanceof Error ? error.message : \"Unable to start chat stream.\" },\n      { status: 400 },\n    );\n  }\n}\n"
    },
    {
      "path": "apps/web/src/components/ai/chat-box.tsx",
      "type": "registry:component",
      "target": "apps/web/src/components/ai/chat-box.tsx",
      "content": "\"use client\";\n\nimport { useChat } from \"@ai-sdk/react\";\nimport { DefaultChatTransport } from \"ai\";\nimport { ArrowUpIcon, SquareIcon } from \"lucide-react\";\nimport { useMemo, useState } from \"react\";\n\nexport function ChatBox() {\n  const [input, setInput] = useState(\"\");\n  const transport = useMemo(() => new DefaultChatTransport({ api: \"/api/ai/chat\" }), []);\n  const { messages, sendMessage, status, stop } = useChat({ transport });\n  const running = status === \"submitted\" || status === \"streaming\";\n\n  function submit() {\n    const text = input.trim();\n    if (!text || running) return;\n    sendMessage({ text });\n    setInput(\"\");\n  }\n\n  return (\n    <div className=\"flex flex-col gap-4 rounded-lg border p-4\">\n      <div className=\"flex flex-col gap-3\">\n        {messages.map((message) => (\n          <div key={message.id} className=\"rounded-md bg-muted p-3 text-sm\">\n            <div className=\"mb-1 font-medium\">{message.role}</div>\n            {message.parts.map((part, index) =>\n              part.type === \"text\" ? <p key={index}>{part.text}</p> : null,\n            )}\n          </div>\n        ))}\n      </div>\n      <div className=\"flex gap-2\">\n        <textarea\n          className=\"min-h-24 flex-1 rounded-md border bg-background p-3 text-sm\"\n          value={input}\n          onChange={(event) => setInput(event.target.value)}\n          placeholder=\"Ask a question...\"\n        />\n        {running ? (\n          <button className=\"rounded-md border px-3\" type=\"button\" onClick={stop}>\n            <SquareIcon />\n          </button>\n        ) : (\n          <button className=\"rounded-md border px-3\" type=\"button\" onClick={submit}>\n            <ArrowUpIcon />\n          </button>\n        )}\n      </div>\n    </div>\n  );\n}\n"
    },
    {
      "path": "apps/web/src/lib/ai/prompts.ts",
      "type": "registry:file",
      "target": "apps/web/src/lib/ai/prompts.ts",
      "content": "export const chatSystemPrompt = `You are a concise assistant inside a production SaaS application.\n\nAnswer directly, ask for missing critical context only when necessary, and avoid exposing internal implementation details.`;\n"
    },
    {
      "path": "apps/web/src/lib/ai/models.ts",
      "type": "registry:file",
      "target": "apps/web/src/lib/ai/models.ts",
      "content": "export const DEFAULT_AI_MODEL = process.env.AI_GATEWAY_MODEL;\n\nexport function getSelectedModel(model?: string) {\n  const selected = model || DEFAULT_AI_MODEL;\n  if (!selected) {\n    throw new Error(\"AI_GATEWAY_MODEL is not set.\");\n  }\n  return selected;\n}\n"
    }
  ],
  "maintenanceSkills": [
    {
      "name": "ai-chat",
      "target": ".stackfoundry/skills/ai-chat/SKILL.md",
      "content": "---\nname: ai-chat\ndescription: Maintain the AI Chat module installed by StackFoundry.\n---\n\n# AI Chat Maintenance Instructions\n\n- Keep provider/model selection server-controlled.\n- Use `DefaultChatTransport` with manually managed input state.\n- Return UI message streams from server routes.\n- Keep prompt defaults in `lib/ai/prompts.ts`.\n- Do not hardcode a long-lived provider key in source.\n\n## Shared Skills\n\nWhen provider, framework, or database behavior changes, load the installed shared skill before editing implementation details:\n\n- `.stackfoundry/skills/ai-sdk/SKILL.md` (source: `registry/skills/ai-sdk/SKILL.md`)\n- `.stackfoundry/skills/nextjs/SKILL.md` (source: `registry/skills/nextjs/SKILL.md`)\n\nKeep this module skill focused on ownership, installed files, env vars, deployment checks, and module-specific invariants.\n\n"
    },
    {
      "name": "ai-sdk",
      "target": ".stackfoundry/skills/ai-sdk/SKILL.md",
      "content": "---\nname: ai-sdk\ndescription: AI SDK guidance for installed AI modules.\n---\n\n# Ai Sdk Guidance\n\n## Installed Location\n\n- Installed target: `.stackfoundry/skills/ai-sdk/SKILL.md`\n- Registry source: `registry/skills/ai-sdk/SKILL.md`\n\nAgents maintaining an installed module should load this shared skill from the installed target when provider, framework, database, SDK, or platform behavior is involved. Keep provider-specific API details here instead of duplicating them inside module maintenance skills.\n\n- Keep provider keys server-only.\n- Stream responses deliberately and handle tool errors.\n- Validate tool inputs and outputs.\n- Document model ids, fallback behavior, and cost-sensitive paths.\n"
    },
    {
      "name": "nextjs",
      "target": ".stackfoundry/skills/nextjs/SKILL.md",
      "content": "---\nname: nextjs\ndescription: Maintain Next.js App Router code installed by StackFoundry modules.\n---\n\n# Next.js Operating Instructions\n\n## Installed Location\n\n- Installed target: `.stackfoundry/skills/nextjs/SKILL.md`\n- Registry source: `registry/skills/nextjs/SKILL.md`\n\nAgents maintaining an installed module should load this shared skill from the installed target when provider, framework, database, SDK, or platform behavior is involved. Keep provider-specific API details here instead of duplicating them inside module maintenance skills.\n\n- Keep server-only data access out of Client Components.\n- Put route handlers under `app/api` and UI routes under the relevant App Router segment.\n- Prefer Server Components for data loading and add `\"use client\"` only for interactivity.\n- Keep public environment variables prefixed with `NEXT_PUBLIC_`; keep secrets server-only.\n- Re-run typecheck and build after changing route handlers, layouts, or shared app configuration.\n"
    }
  ],
  "envVars": {
    "AI_GATEWAY_API_KEY": "",
    "AI_GATEWAY_MODEL": ""
  },
  "docs": "# AI Chat Module\n\nAdds a minimal Vercel AI SDK chat endpoint and client component using current `useChat` semantics.\n\nThis module credits Vercel AI SDK for the `useChat`, `DefaultChatTransport`, UI message parts, and `toUIMessageStreamResponse()` patterns.\n\n## Owns\n\n- `/api/ai/chat` route\n- chat prompt component\n- model/env helper\n- prompt defaults\n\n## Requirements\n\n- `AI_GATEWAY_API_KEY`\n- `AI_GATEWAY_MODEL`\n\n## Verification\n\n- API route returns a UI message stream.\n- Client component sends messages with `DefaultChatTransport`.\n- Missing model returns a readable error instead of a failed stream.\n",
  "meta": {
    "category": "ai",
    "env": [
      "AI_GATEWAY_API_KEY",
      "AI_GATEWAY_MODEL"
    ],
    "status": "ready",
    "maturity": "ready",
    "recommendedFor": []
  }
}
