This repo is a minimal, educational starter for building a documentation bot that can convert a codebase into structured documentation using:
- Next.js App Router (UI + API route)
- AI SDK (
ai,@ai-sdk/react,@ai-sdk/openai) for streaming UI messages + tool calls - A ToolLoopAgent that can call tools to read and summarize code
- A small tool UI renderer that shows doc generation progress/output inside the chat
It’s intentionally small so you can fork it and create your own custom variant (different models, tools, schemas, UI, and routing).
- Chat UI (Vercel-ish dark, mobile-first) with a fixed composer + suggestion pills (
app/page.tsx,components/chat-input.tsx) - Tool UI that renders tool progress/output inline as “cards” (
components/docs-view.tsx) - Single API endpoint that streams agent UI messages (
app/api/chat/route.ts) - Agent definition with tools and model selection (
agent/docs-agent.tsx) - Documentation tool that can read and summarize repo files into structured docs (
tool/docs-tool.ts)
app/page.tsx uses useChat() from @ai-sdk/react. By default it POSTs to /api/chat with { messages }, and receives a streaming response of UI message “parts” (text, step markers, tool calls).
app/api/chat/route.ts passes the incoming messages to createAgentUIStreamResponse({ agent, uiMessages }), which streams back UI-friendly message parts.
agent/docs-agent.tsx defines a ToolLoopAgent that uses an OpenAI model and a tools map:
- Tool name:
docs - Tool implementation:
docsTool
The agent can emit normal text and invoke tools during its reasoning loop.
tool/docs-tool.ts defines a streaming tool via tool({ inputSchema, execute }):
inputSchema: validates tool inputs withzodexecute: is an async generator (async *) that can yield intermediate states
This tool yields { state: "loading" } first, then { state: "ready", docs: { ... } }.
When the agent calls a tool, useChat() receives a message part like tool-docs. app/page.tsx routes those parts into components/docs-view.tsx, which renders:
- input streaming/available (the tool is about to run)
- output available (loading/ready)
- output error
- Node.js (modern LTS recommended)
- Bun (recommended) or npm/pnpm/yarn
- An OpenAI API key (or swap provider/model; see below)
Using Bun:
bun installUsing npm:
npm installCreate .env.local:
OPENAI_API_KEY=your_key_hereNotes:
- The OpenAI provider reads
OPENAI_API_KEYfrom the environment. - The included docs tool reads your local repo; keep inputs scoped and avoid large paths if you want faster responses.
bun devThen open http://localhost:3000.
Try prompts like:
- “Generate an overview of this repo.”
- “Summarize the
app/andcomponents/folders.” - “Create an API reference for the endpoints in
app/api.”
Edit agent/docs-agent.tsx:
- Swap
openai("gpt-5o")to a different model string - Or switch providers entirely (e.g. another AI SDK provider package)
Keep the rest of the architecture the same.
-
Create a tool file (e.g.
tool/api-docs-tool.ts)- Define
tool({ description, inputSchema, execute }) - Use
zodto validate inputs - Yield intermediate states if you want progressive UI updates
- Define
-
Register the tool on the agent
In agent/docs-agent.tsx, add it to tools:
tools: { docs: docsTool, apiDocs: apiDocsTool }
- Render it in the UI
In app/page.tsx, add a new case "tool-api-docs": ... and create a renderer component similar to components/docs-view.tsx.
tool/docs-tool.ts can be extended with documentation-focused capabilities that:
- Accepts a path or scope (e.g.
app/,components/, or a file) - Reads files and extracts purpose, public APIs, and usage
- Emits structured output (JSON) for sections like Overview, API, and Examples
Key files:
app/page.tsx: chat shell (header/content/footer), message loop + routing message “parts”, builds dynamic suggestion promptscomponents/chat-input.tsx: composer (textarea), Enter-to-send, and optional suggestion pillscomponents/docs-view.tsx: tool output displayapp/globals.css: global styling (Tailwind)
app/
api/chat/route.ts # POST /api/chat → streams agent UI response
globals.css # global theme + markdown tweaks
layout.tsx # app shell + fonts
page.tsx # Chat UI using useChat()
agent/
docs-agent.tsx # ToolLoopAgent definition (model + tools)
tool/
docs-tool.ts # Streaming tool (zod schema + execute generator)
components/
chat-input.tsx # Chat input component
docs-view.tsx # Tool invocation renderer
public/
assets/
chat-conversation.png
logo.png
bun dev # run locally
bun build # production build
bun start # run production server
bun lint # lintThis is a standard Next.js App Router project. On Vercel (or similar), ensure:
OPENAI_API_KEYis set in your deployment environment- Your chosen model/provider is supported in the runtime you deploy to
- Cause:
useChat()disables input unlessstatus === "ready". - Fix:
- Make sure the dev server is running (
bun dev) and the page loads without errors. - If it becomes stuck after an error, refresh the page to reset state.
- Make sure the dev server is running (
- Cause: Missing or invalid
OPENAI_API_KEY. - Fix:
- Add
OPENAI_API_KEY=...to.env.local. - Restart the dev server after changing env vars.
- If deployed, set the env var in your hosting provider’s environment settings.
- Add
- Cause: The tool threw an error (file read failure, bad input, provider response not OK).
- Fix:
- Try a smaller scope like
app/or a single file. - Ensure the path you requested exists and is within the repo.
- Check the server console for the full error message.
- Try a smaller scope like
- Cause: The tool is processing too many files at once.
- Fix:
- Scope requests to a folder or file.
- Add caching or precomputed summaries for large repos.
- Cause: The tool currently returns a JSON string and the view renders it directly.
- Fix:
- Return a structured object from the tool and render a richer UI in
components/market-view.tsx.
- Return a structured object from the tool and render a richer UI in
MIT