{"id":50666663,"url":"https://github.com/ahmad-luqman/build-your-own-coding-agent","last_synced_at":"2026-06-08T07:03:47.761Z","repository":{"id":339850755,"uuid":"1163513623","full_name":"ahmad-luqman/build-your-own-coding-agent","owner":"ahmad-luqman","description":"A minimal CLI coding agent built from scratch with Bun, Ink, and AI SDK v6. Educational project demystifying how AI coding agents work.","archived":false,"fork":false,"pushed_at":"2026-03-08T15:22:58.000Z","size":149,"stargazers_count":5,"open_issues_count":8,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-08T16:10:31.031Z","etag":null,"topics":["ai-agent","bun","cli","ink","typescript"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ahmad-luqman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-21T18:42:35.000Z","updated_at":"2026-03-08T15:23:01.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ahmad-luqman/build-your-own-coding-agent","commit_stats":null,"previous_names":["ahmad-luqman/build-your-own-coding-agent"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ahmad-luqman/build-your-own-coding-agent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ahmad-luqman%2Fbuild-your-own-coding-agent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ahmad-luqman%2Fbuild-your-own-coding-agent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ahmad-luqman%2Fbuild-your-own-coding-agent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ahmad-luqman%2Fbuild-your-own-coding-agent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ahmad-luqman","download_url":"https://codeload.github.com/ahmad-luqman/build-your-own-coding-agent/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ahmad-luqman%2Fbuild-your-own-coding-agent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34051772,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-08T02:00:07.615Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agent","bun","cli","ink","typescript"],"created_at":"2026-06-08T07:03:14.213Z","updated_at":"2026-06-08T07:03:47.756Z","avatar_url":"https://github.com/ahmad-luqman.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Build Your Own Coding Agent\n\nA minimal CLI coding agent built from scratch with Bun, Ink, and AI SDK v6. It connects to LLMs via OpenRouter or runs them locally via Ollama, providing an interactive terminal interface where you can chat with an AI that has access to your file system and shell.\n\n```\n┌──────────────────────────────────────────────────┐\n│  Model: anthropic/claude-sonnet-4  Tokens: 1.2k  │\n├──────────────────────────────────────────────────┤\n│                                                  │\n│  You: Read src/index.tsx and explain it          │\n│                                                  │\n│  Assistant: [reads file, explains code]          │\n│                                                  │\n│  \u003e Tool: read_file ✓                             │\n│                                                  │\n├──────────────────────────────────────────────────┤\n│  \u003e _                                             │\n└──────────────────────────────────────────────────┘\n```\n\n## Why\n\nThis project exists to demystify how AI coding agents work. The entire codebase is ~600 lines of TypeScript across 14 files — small enough to read in one sitting, but complete enough to actually use.\n\nIf you've used tools like Claude Code, Cursor, or Copilot Workspace and wondered how they work under the hood, this is a from-scratch implementation of the core patterns: multi-turn agentic loops, tool use, streaming, and a terminal UI.\n\n## Features\n\n- **Multi-turn agent loop** — The AI can chain tool calls across multiple turns to complete complex tasks\n- **6 built-in tools** — `read_file`, `glob`, `grep`, `write_file`, `edit_file`, `bash`\n- **Safety system** — Dangerous tools (write, edit, bash) require user approval before execution\n- **Streaming responses** — Text streams token-by-token as the model generates it\n- **Multi-provider** — OpenRouter for cloud models or Ollama for local models, switchable via env var\n- **Terminal UI** — Built with Ink (React for the terminal) with markdown rendering and syntax highlighting\n\n## Quick Start\n\n**Prerequisites:** [Bun](https://bun.sh) and either an [OpenRouter API key](https://openrouter.ai/keys) or [Ollama](https://ollama.com) for local models.\n\n```bash\n# Clone and install\ngit clone https://github.com/ahmad-luqman/build-your-own-coding-agent.git\ncd build-your-own-coding-agent\nbun install\n\n# Configure\ncp .env.example .env\n# Edit .env and add your OPENROUTER_API_KEY\n\n# Run\nbun run start\n```\n\nTo use a different model:\n\n```bash\nMODEL_ID=openai/gpt-4o bun run start\n```\n\n### Using Ollama (Local Models)\n\nRun models locally with no API key required:\n\n```bash\nollama pull qwen3-coder-next\nPROVIDER=ollama bun run start\n```\n\nSee [docs/ollama-setup.md](docs/ollama-setup.md) for recommended models, configuration, and troubleshooting.\n\n## Project Structure\n\n```\nsrc/\n  index.tsx        Entry point — wires config, model, tools, and renders the app\n  config.ts        Loads env vars and builds the system prompt\n  model.ts         Multi-provider model factory (OpenRouter / Ollama)\n  agent.ts         The core agent loop (async generator)\n  app.tsx           Main React component (state machine)\n  types.ts         All shared type definitions\n  tools/\n    registry.ts    Collects tools into a Map\n    read.ts        read_file — read file contents\n    glob.ts        glob — find files by pattern\n    grep.ts        grep — search file contents\n    write.ts       write_file — create/overwrite files (requires approval)\n    edit.ts        edit_file — surgical string replacements (requires approval)\n    bash.ts        bash — run shell commands (requires approval)\n  hooks/\n    manager.ts     Sequential hook pipeline\n    dangerous-command-guard.ts — approval flow for dangerous operations\n  components/\n    StatusBar.tsx   Model name + token usage\n    MessageList.tsx Conversation display with markdown rendering\n    InputBar.tsx    Text input with loading state\n    ApprovalPrompt.tsx  y/n prompt for dangerous tool calls\n```\n\n## How It Works\n\n1. **Entry** — `index.tsx` loads config, creates the AI model, builds the tool registry, and renders the Ink app\n2. **Input** — User types a message, `App` adds it to the conversation history\n3. **Agent loop** — `runAgent()` calls `streamText()` with the conversation + tools, yielding events as an async generator\n4. **Tool use** — When the model calls a tool, the hook system checks if approval is needed. Safe tools run immediately; dangerous ones show an approval prompt\n5. **Multi-turn** — If the model called tools, the loop continues with the updated history. Otherwise, it stops\n6. **Display** — Events stream into React state, updating the TUI in real-time\n\nSee [docs/architecture.md](docs/architecture.md) for detailed diagrams and [docs/technical-choices.md](docs/technical-choices.md) for rationale behind each technology decision.\n\n## Key Concepts\n\n### The Agent Loop Pattern\n\nThe heart of any coding agent is a loop: call the model, execute any tool calls, feed results back, repeat until done. Here it's implemented as an async generator that yields typed events:\n\n```typescript\nasync function* runAgent(messages, options): AsyncGenerator\u003cAgentEvent\u003e {\n  while (turn \u003c maxTurns) {\n    const result = streamText({ model, messages, tools });\n    for await (const chunk of result.fullStream) {\n      yield { type: \"text-delta\", text: chunk.text };  // stream to UI\n    }\n    if (finishReason !== \"tool-calls\") break;  // done if no more tool calls\n  }\n}\n```\n\n### Dual Output Pattern\n\nTools return both human-readable output (for the terminal) and structured data (for the model). The user sees a clean summary; the model gets data it can reason about:\n\n```typescript\ninterface ToolResult {\n  output: string;     // \"Read 42 lines from src/index.tsx\"\n  data?: object;      // { content: \"...\", lines: 42 }\n}\n```\n\n### Safety via Hooks\n\nInstead of hardcoding safety checks, a hook pipeline intercepts tool calls. The `dangerous-command-guard` checks if a tool is marked `dangerous` and bridges async execution with the React UI through a shared Promise:\n\n```typescript\n// Hook creates a Promise → App renders ApprovalPrompt → user input resolves it\nconst approved = await requestApproval(toolName, input);\n```\n\n## Development\n\n```bash\nbun run dev          # Run with auto-reload on file changes\nbunx tsc --noEmit    # Type-check without emitting\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fahmad-luqman%2Fbuild-your-own-coding-agent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fahmad-luqman%2Fbuild-your-own-coding-agent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fahmad-luqman%2Fbuild-your-own-coding-agent/lists"}