{"id":"6545fe7f-5fe2-45b2-930b-6e5a41a383ed","slug":"bonfire","kind":"mcp","name":"bonfire","summary":"Start a bonfire in your terminal - A free, local-first AI coding assistant for the terminal. No API keys, no cloud — just your models and your code.","owner":"Praneethtkonda","repoUrl":"https://github.com/Praneethtkonda/bonfire","license":"MIT","licenseTier":"permissive","trust":"community","stars":1,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-04-20T10:56:04.000Z","addedAt":"2026-10-21T21:02:57.352Z","logoUrl":"https://avatars.githubusercontent.com/u/9162297?u=90fbff3b03014bbac7619eea37a90c0c3548f8c8&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["developer-tools"],"tags":[],"runtime":"node","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-26T04:16:08.613Z","sample":false,"url":"https://conduits.dev/tools/bonfire","language":"TypeScript","transports":["stdio"],"install":{"npm":"@praneethtss/bonfire"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"<div align=\"center\">\n\n# bonfire\n\n<img src=\"./docs/banner.jpg\" alt=\"bonfire — a local-first AI coding assistant for the terminal\" width=\"820\" />\n\n# Your code. Your model. Your machine.\n\n**A terminal coding assistant that runs against any local model.**\n\nNo keys. No meters. No data leaks. Just you and the box you trust.\n\n</div>\n\n---\n\n## What makes bonfire different\n\nThere's no shortage of terminal coding assistants. Here's what bonfire does that the others don't.\n\n### Local-first, by default\n\nThe model lives on a machine **you control** — your laptop, your homelab, a friend's GPU rig. bonfire is just the CLI that talks to it.\n\n- No API keys to leak.\n- No tokens to budget.\n- No \"we may use your data to improve our models.\"\n\n### Bring your own model\n\n[Ollama](https://ollama.com), [llama.cpp](https://github.com/ggml-org/llama.cpp), or **any OpenAI-compatible API** (Google Gemini, NVIDIA NIM, OpenAI, custom endpoints). `qwen3.6:latest`, `llama3.3`, `deepseek-coder-v2`, `gpt-4o-mini` — whatever fits your hardware and your taste.\n\n### Codemap — your repo as a tree of summaries\n\nOther assistants either grep blindly (and choke on a big monorepo) or stand up a vector DB you have to babysit.\n\nbonfire takes a third path: it walks your repo **once**, asks the model for a one-line summary of every file and directory, and stores the result as JSON in `.bonfire/codemap.json`. The model then navigates that tree like a filesystem — reading **summaries**, not source — until it finds what it's looking for.\n\n```\n> /codemap\ncodemap · 247 files · 38 dirs · 285/285 summarized · 142 KB indexed\n\n> where do we handle session deletion?\n[bonfire] navigate(\".\")\n  ↳ src/         — TypeScript source for the bonfire CLI\n[bonfire] navigate(\"src\")\n  ↳ session/     — JSON session persistence in .bonfire/sessions\n[bonfire] read_file(\"src/session/storage.ts\")\n  ↳ deleteSession(root, id) at line 41\n\n  Sessions are deleted via deleteSession() in src/session/storage.ts:41,\n  which unlinks .bonfire/sessions/<id>.json after verifying it exists.\n```\n\n**Three tool calls. Zero greps.** The cache invalidates per-file on `mtime` so it stays current as you edit. Directory summaries auto-invalidate when any descendant file is modified after the summary was written. No embeddings, no vector DB, no daemon, no cloud round-trips.\n\n`.gitignore` is honoured with full glob semantics (`*.log`, `**/generated/**`, `!keep.log`, anchored `/root-only`). Symlinks are skipped to avoid cycles and out-of-repo escapes.\n\nThe first build does read every file — but you only run it once per repo, the result is cached on disk, and `/codemap rebuild` is the only way to redo it. The build checkpoints to disk every 25 summaries, so a crash mid-pass picks up where it left off. A failed summary leaves the node un-summarized so the next build retries it instead of caching a fake. Works comfortably on repos with thousands of files; very large monorepos (tens of thousands) will benefit from running `/codemap build` over a coffee break or bumping `codemap.concurrency` if you're driving a remote API.\n\n### Sessions — pick up tomorrow where you left off\n\nEvery conversation auto-saves to `.bonfire/sessions/<id>.json` after each turn. List them, share them, diff them, resume them.\n\n```\n> /sessions list\nSessions (3):\n▸ a3f1c8d92b4e  2 hours ago   12 turns · ollama    · \"refactor the auth flow\"\n  4d8e2f1a7b9c  yesterday     8 turns  · ollama    · \"fix the failing test\"\n  92c1f0e8a4b7  2 days ago    24 turns · llama.cpp · \"build codemap navigation\"\n\n> /sessions load 4d8e\nLoaded session 4d8e2f1a7b9c · 8 messages · cwd: /work/api\n```\n\nNo SaaS account. No \"your history will be cleared in 30 days.\" Just JSON files on your disk, gitignored by default.\n\n---\n\n## Three ways to run it\n\nbonfire is just a CLI. The model lives wherever you put it. Pick the topology that fits your hardware:\n\n### 1. Self-contained on your laptop\n\n```\n[ laptop ]  bonfire  ──  ollama  ──  qwen3.6:latest (local GPU)\n```\n\nWorks offline. Take it on a plane. Best for M-series Macs, gaming laptops, anything with ≥ 8 GB VRAM.\n\n### 2. Laptop + your homelab\n\n```\n[ laptop ]  bonfire  ───→  homelab.local:11434  ──  qwen3.6:latest (24 GB box)\n```\n\nType on the machine you like. Run the **model** on whichever box has the GPU. Set `OLLAMA_BASE_URL=http://homelab.local:11434/api` and you're done.\n\nBest for: Mac mini cluster, the 4090 in the closet, the old gaming PC you stopped using.\n\n### 3. Remote APIs (cloud / edge)\n\n```\n[ laptop ]  bonfire  ──→  Google Gemini / OpenAI / NVIDIA NIM / any OpenAI-compatible API\n```\n\nConnect to any OpenAI-compatible API endpoint. Great for when you're traveling, don't have a GPU, or want to use a specific cloud model.\n\n### 4. Shared rig with friends\n\n```\nlaptop A   bonfire ─┐\nlaptop B   bonfire ─┼──→  llama-server (shared, API-key gated)\nlaptop C   bonfire ─┘\n```\n\nOne person hosts a serious model. Everyone else points their bonfire at it. Same shape works for any OpenAI-compatible cloud endpoint when you're traveling.\n\nBest for: small teams, study groups, family GPU.\n\n**Cross-platform.** macOS, Linux, Windows (Terminal / PowerShell). Git Bash / MSYS users prefix with `winpty bonfire`.\n\n---\n\n## Quick start\n\n```bash\nnpm install -g @praneethtss/bonfire\nbonfire\n```\n\nOn first run, bonfire will guide you through a quick setup. Choose your provider (ollama, llama.cpp, or remote), and you're ready to go.\n\n### Using Ollama (recommended for Mac/Linux)\n\n```bash\nollama pull qwen3.6:latest\nbonfire\n```\n\n### Using llama.cpp (full control)\n\n```bash\n.\\llama-server.exe --jinja -m .\\qwen3.6.gguf -c 8192 -ngl 99\nbonfire\n```\n\n> The `--jinja` flag is **mandatory** for tool calling. Without it, bonfire's tool calls won't reach the model.\n\n### Using an OpenAI-compatible API (Google Gemini, OpenAI, NVIDIA NIM, etc.)\n\n```bash\nbonfire\n# Select \"remote\" when prompted\n# Enter your API endpoint and key\n```\n\nOr configure manually in your config file (see below).\n\n### Try these first\n\n- `list the files in src` — feel for navigation\n- `/codemap build` — kick off the first summarization pass (slow once; it reads every file)\n- `read README.md and summarize what this project does`\n- `/help` — every slash command at a glance\n\n---\n\n## Configuration\n\n### Config Location\n\nConfiguration is layered — later layers win, and each is optional:\n\n1. **Built-in defaults**\n2. **Global config** — `~/.config/bonfire/config.json` (Linux/macOS) or `%APPDATA%/bonfire/config.json` (Windows)\n3. **Project config** — `bonfire.config.json` in the directory you launched bonfire from\n\nThe global file travels with you; drop a `bonfire.config.json` in a repo when that repo needs a different model, a bigger context window, or its own MCP servers. Merging is deep, so a project file that only sets `context.maxTokens` keeps every other global setting intact.\n\n### Quick Reconfigure\n\nChange your provider anytime with:\n\n```bash\n/reconfigure\n```\n\nThis opens an interactive TUI to change:\n- Provider (ollama, llama.cpp, remote)\n- Base URL\n- Model\n- API key\n- Custom headers (for APIs like Google Gemini)\n\n### View Current Config\n\n```bash\n/config\n```\n\n### Full Config File\n\nEdit `config.json` at the path above directly, or use `/reconfigure` for the interactive flow. Everything is optional.\n\n```json\n{\n  \"provider\": {\n    \"active\": \"remote\",\n    \"ollama\": {\n      \"baseURL\": \"http://127.0.0.1:11434/api\",\n      \"model\": \"qwen3.6:latest\"\n    },\n    \"llama.cpp\": {\n      \"baseURL\": \"http://127.0.0.1:8080/v1\",\n      \"model\": \"unsloth/Qwen3.6-35B-A3B\"\n    },\n    \"remote\": {\n      \"baseURL\": \"https://api.openai.com/v1\",\n      \"model\": \"gpt-4o-mini\",\n      \"apiKey\": \"\",\n      \"headers\": { \"X-Trace\": \"${TRACE_ID}\" }\n    }\n  },\n\n  \"systemPrompt\": \"Always reply in haiku.\",\n  \"systemPromptMode\": \"append\",\n\n  \"security\": {\n    \"shell\": {\n      \"requireApproval\": true,\n      \"allowedCommands\": [\"^git status$\", \"^npm test$\"],\n      \"deniedCommands\": [\"^curl\\\\s\"],\n      \"timeoutMs\": 60000\n    },\n    \"mcpRequireApproval\": false,\n    \"fetch\": {\n      \"allowPrivateNetwork\": false,\n      \"requireApproval\": false\n    }\n  },\n\n  \"codemap\": {\n    \"concurrency\": 3\n  },\n\n  \"context\": {\n    \"maxTokens\": 8192,\n    \"compactThreshold\": 0.75,\n    \"autoCompact\": true\n  },\n\n  \"ui\": {\n    \"theme\": \"default\"\n  },\n\n  \"mcpServers\": {\n    \"filesystem\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@modelcontextprotocol/server-filesystem\", \".\"]\n    }\n  }\n}\n```\n\n`${ENV_VAR}` references inside any provider field, MCP env, or MCP headers are expanded at startup. Bump `codemap.concurrency` if you're driving a fast remote API — a local Ollama is fine at the default 3.\n\n#### Context window\n\n`context.maxTokens` should match what your model server is actually serving — `--ctx-size` for `llama-server`, `num_ctx` for Ollama. bonfire estimates how much of that window the conversation is using, shows it in the status bar, and compacts history automatically once usage crosses `compactThreshold`. Compaction is mechanical, not model-driven: it shortens old tool output first, then drops whole turns from the top, always cutting at a user message so a tool result never loses the call that produced it. No extra model round-trip, no new failure mode. Set `autoCompact: false` if you'd rather run `/compact` by hand.\n\n#### Themes\n\n`ui.theme` picks one of four presets — `default` (blue accent, cyan tools), `ember` (magenta, warmer), `forest` (green), `mono` (grayscale except errors). Switch at any time with `/theme <name>`: it applies immediately and is written back to your global config.\n\nEvery preset uses only the 16 ANSI colour names, never hex, so bonfire inherits your terminal's palette instead of fighting it — a solarized or gruvbox terminal gets a solarized or gruvbox bonfire for free. Semantic colours stay put across presets: green means good and red means destructive in all of them, because an approval prompt is the wrong place to get decorative.\n\n#### Outbound fetches\n\n`fetch_url` refuses private, loopback, link-local and cloud-metadata addresses by default, and re-checks every redirect hop — a public hostname pointing at `127.0.0.1` or `169.254.169.254` is caught because the guard resolves DNS rather than pattern-matching the URL. Set `security.fetch.allowPrivateNetwork: true` if you genuinely need to reach a service on your LAN, and `requireApproval: true` to be prompted before every fetch.\n\n### Environment Variables\n\n| Variable | Default | Purpose |\n| --- | --- | --- |\n| `BONFIRE_PROVIDER` | `ollama` | `ollama`, `llama.cpp`, or `remote` |\n| `BONFIRE_MODEL` | provider-specific | Model id (Ollama tag, or label for llama.cpp) |\n| `OLLAMA_BASE_URL` | `http://127.0.0.1:11434/api` | Ollama host |\n| `LLAMACPP_BASE_URL` | `http://127.0.0.1:8080/v1` | `llama-server` endpoint |\n| `LLAMACPP_API_KEY` | — | Optional bearer token |\n| `REMOTE_BASE_URL` | `https://api.openai.com/v1` | Remote API endpoint |\n| `REMOTE_API_KEY` | — | Remote API key |\n| `BONFIRE_DEBUG` | — | `1` to log every HTTP request (auth headers redacted) |\n| `BONFIRE_DISABLE_BUILTINS` | — | `1` to run with only MCP tools |\n\n---\n\n## Customise it: system prompt + skills\n\nbonfire reads two kinds of markdown from your home directory and your repo, **with no config at all**:\n\n```\n# Global (OS-specific location - see below)\n~/.config/bonfire/system.md         # Linux/macOS\n~/.config/bonfire/skills/<name>.md  # Linux/macOS\n%APPDATA%/bonfire/system.md         # Windows\n%APPDATA%/bonfire/skills/<name>.md # Windows\n\n# Project (in your repo)\n.bonfire/system.md           # project system prompt\n.bonfire/skills/<name>.md    # project skills\n```\n\n**System prompts** are *appended* to bonfire's built-in prompt. Use them to teach the model about conventions that aren't obvious from the code:\n\n```bash\nmkdir -p .bonfire\ncat > .bonfire/system.md <<'EOF'\nThis project uses Bun. Prefer `bun test` over `npm test`.\nTreat src/legacy/ as read-only — never edit those files.\nEOF\n```\n\nRun `/system` inside bonfire to see the assembled prompt.\nSet `\"systemPromptMode\": \"replace\"` in config if you want to fully replace the built-in.\n\n**Skills** are markdown files the model loads *on demand*. Each has YAML frontmatter and a body:\n\n```markdown\n---\nname: api-endpoint\ndescription: Add a new HTTP endpoint following project conventions\n---\n\nTo add a new endpoint:\n1. Add the route to `src/server/routes.ts`.\n2. Add a handler in `src/handlers/`.\n3. Add a contract test in `tests/api/`.\n4. Run `bun test` to confirm.\n```\n\nAt startup bonfire scans both skill directories and tells the model: *\"these skills exist, here's their description, call `load_skill(name)` if a request matches.\"* The body only enters the context when the model asks for it — so you can ship 50 skills without bloating every prompt.\n\nType these inside the bonfire TUI:\n\n```\n/init                          scaffold .bonfire/system.md + .bonfire/skills/example.md\n/skills                        list available skills\n/skills show api-endpoint      print a skill's body\n/skills reload                 re-scan after editing\n```\n\n---\n\n## Slash commands\n\n| Command | Effect |\n| --- | --- |\n| `/help` | Show every command (or type `/` for autocomplete) |\n| `/init` | Scaffold `.bonfire/system.md` + an example skill |\n| `/system` | Show the effective system prompt (base + overrides + skills) |\n| `/skills` | List available skills |\n| `/skills show <name>` | Print a skill body |\n| `/skills reload` | Re-scan skill directories |\n| `/codemap` | Codemap stats — files, dirs, summarized count |\n| `/codemap build` | Summarize every file (slow once, then cached) |\n| `/codemap rebuild` | Wipe cache and re-summarize from scratch |\n| `/sessions` | List saved sessions |\n| `/sessions new` | Start a fresh conversation |\n| `/sessions load <id>` | Resume a session — full ID or any unique prefix |\n| `/sessions save` | Force-save the active session |\n| `/sessions delete <id>` | Remove a session — full ID or any unique prefix |\n| `/context` | Show context-window usage for the live conversation |\n| `/compact` | Reclaim context now — trims old tool output, then drops old turns |\n| `/tools` | List every tool the model can call, built-in and MCP |\n| `/theme` | List colour themes |\n| `/theme <name>` | Switch theme — applies instantly, saved to your config |\n| `/dirs` | Show the filesystem allowlist |\n| `/add-dir <path>` | Grant access to a sibling project |\n| `/config` | Show current configuration |\n| `/reconfigure` | Interactive reconfiguration TUI |\n| `/exit`, `/quit`, `esc` | Leave |\n\nDuring an approval prompt: `y` = yes, `n` = no, `a` = always for the rest of the session. `a` works for shell commands, file edits and MCP tool calls alike; `/sessions new` clears everything you've remembered.\n\nTip: type `/` at any time to see a dropdown of every command — Tab completes, ↑/↓ moves the selection, Enter executes selected command.\n\n---\n\n## Other things bonfire does well\n\n**Diff-preview approvals.** Every `write_file` and `edit_file` shows a coloured unified diff and waits for `y` / `n` before touching disk. Same for `shell` — every command needs your green-light, with `a` for \"always for this session.\" A hardcoded refusal list catches `rm -rf /`, fork bombs, `mkfs`, and friends *even after approval*.\n\n> **On the deny-list.** It is a backstop, not a sandbox. Pattern matching on a command string cannot be complete — the same destructive action can be spelled with variable expansion, a base64 pipe, an alias, or a script file, and the list will not see it. The approval prompt is the real control: read the command before you press `y`, and treat `a` (\"always\") as a decision you are making for the rest of the session. If you need a genuine boundary, run bonfire against a model you trust in a container or VM.\n\n**Streaming everything.** Text and tool arguments stream as they're generated. No 30-second blank stares.\n\n**Multiline prompts.** `Shift+Enter` (modern terminals — kitty, Ghostty, WezTerm, recent iTerm2), `Alt+Enter`, or `Ctrl+J` inserts a newline. Plain Enter submits.\n\n**Smart paste.** Bracketed paste mode is enabled at startup, so multi-line pastes never submit mid-block. A *huge* paste — multi-line, or longer than ~200 chars — is redacted in the input as `[Pasted #1 (35 lines, 1234 chars)]` rather than flooding the prompt with the literal content. The real text is held aside and substituted back in when you hit Enter.\n\n**MCP-native.** Plug in any [Model Context Protocol](https://modelcontextprotocol.io) server — stdio or Streamable HTTP. Each server's tools land namespaced as `<server>__<tool>`.\n\n**Custom system prompt.** Drop a `~/.config/bonfire/system.md` (Linux/macOS) or `%APPDATA%/bonfire/system.md` (Windows) for a global override, or `.bonfire/system.md` for a project-level one. Appended to the built-in prompt by default; flip `systemPromptMode: \"replace\"` for full control.\n\n**`/tools`.** See exactly what the model has access to, built-ins and MCP alike.\n\n**Status line that knows what it's doing.** \"Skimming bytes…\" while it reads. \"Pacing the codemap…\" while it navigates. \"Summoning the shell…\" while it runs a command. Beats staring at \"thinking…\" for the 20th time.\n\n**Context meter.** The status line shows how much of the model's window the conversation is using. Cross the threshold and bonfire compacts history itself, telling you what it dropped — a long session on an 8K local model no longer ends in a wall of provider errors.\n\n**Filesystem allowlist.** All file tools refuse paths outside the allowed set. Symlinks inside an allowed dir can't escape it (realpath-checked). `/add-dir <path>` to extend.\n\n**Lightweight.** A single minified Node CLI bundle (~65 KB). No Docker, no Electron, no daemons. Runtime deps are installed by npm alongside it.\n\n---\n\n## Built-in tools\n\n| Tool | What it does |\n| --- | --- |\n| `navigate` | Walk the codemap with one-line summaries |\n| `search` | Regex search across the repo — gitignore-aware, skips binaries, same on every OS |\n| `read_file` | Read a file from an allowed directory — large files truncate; `offset`/`limit` page by line |\n| `write_file` | Create / overwrite — **diff-approval gated** |\n| `edit_file` | Exact-string replace — **diff-approval gated** |\n| `list_dir` | Directory listing, capped at 1000 entries |\n| `shell` | Run a shell command — **approval gated, deny-list applied** |\n| `fetch_url` | Fetch an http(s) URL — **SSRF-guarded**, HTML stripped to text, 200 KB cap, 30 s timeout |\n| `load_skill` | Load a named skill's full instructions on demand |\n\n---\n\n## MCP servers\n\nbonfire speaks both stdio (local processes) and Streamable HTTP (remote servers, per the [MCP spec](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http)). `${ENV_VAR}` references are expanded at startup.\n\n```json\n{\n  \"mcpServers\": {\n    \"filesystem\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@modelcontextprotocol/server-filesystem\", \".\"]\n    },\n    \"github\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@modelcontextprotocol/server-github\"],\n      \"env\": { \"GITHUB_PERSONAL_ACCESS_TOKEN\": \"${GITHUB_TOKEN}\" }\n    },\n    \"remote-docs\": {\n      \"type\": \"streamable-http\",\n      \"url\": \"https://mcp.company.com/docs\",\n      \"headers\": { \"Authorization\": \"Bearer ${DOCS_MCP_TOKEN}\" }\n    }\n  }\n}\n```\n\nTools land namespaced as `<server>__<tool>`. A failing server doesn't block the others. On Windows, bare commands like `npx` / `uvx` are auto-resolved to their `.cmd` shims.\n\n---\n\n## Models that work\n\nAnything trained for tool calling:\n\n| Model | Tool calling | Notes |\n| --- | --- | --- |\n| `qwen3.6:latest` | yes | **Recommended.** Strong tool calling, good code reasoning. |\n| earlier qwen / `qwen3-coder` | yes | Solid fallback if you can't pull 3.6 |\n| `llama3.3` / `llama3.1` | yes | Good general coding |\n| `hermes3:8b` | yes | Fast, lightweight |\n| `llama3-groq-tool-use:8b` | yes | Specifically tuned for tool calling |\n| `deepseek-coder-v2` | varies | Check the specific revision |\n| `gemma3` / `gemma2` | no | Not tool-tuned |\n| `gpt-4o-mini` (remote) | yes | Cloud option |\n| `gemini-2.0-flash-exp` (remote) | yes | Google Gemini via OpenAI compat |\n\nFor llama.cpp, tool calling requires `llama-server --jinja`.\n\n---\n\n## Roadmap\n\n- [x] Codemap navigation (BFS walk, real `.gitignore` globs, symlink-safe)\n- [x] Session persistence + prefix lookup\n- [x] Slash-command autocomplete\n- [x] Multiline input (Shift+Enter on modern terminals; Alt+Enter / Ctrl+J everywhere) + paste redaction\n- [x] Shell approval + hardcoded deny-list + configurable timeout\n- [x] Skills support — drop-in `~/.config/bonfire/skills/<name>.md` (Linux/macOS) / `%APPDATA%/bonfire/skills/<name>.md` (Windows)\n- [x] Configurable system prompt — `~/.config/bonfire/system.md` / `%APPDATA%/bonfire/system.md` + project override\n- [x] OS config directory (not project-level)\n- [x] Interactive reconfigure TUI (`/reconfigure`) handling first-run + remote provider\n- [x] Remote provider (any OpenAI-compatible API)\n- [x] Custom headers + `${ENV}` expansion across provider config\n- [x] Configurable codemap concurrency + mid-build checkpointing\n- [x] Project-level `bonfire.config.json` layered over the global config\n- [x] Context-window budgeting + automatic compaction (`/context`, `/compact`)\n- [x] Native cross-platform `search` tool (no shelling out to grep/findstr)\n- [x] SSRF guard on `fetch_url`, redirect hops included\n- [x] Retry with backoff on transient provider failures\n- [ ] Editable diffs (`y` / `n` / `e` opens the patch in `$EDITOR`)\n- [ ] Side-by-side model race mode\n- [ ] Local RAG tool via a small embedding model\n- [ ] First-run benchmark (tokens/sec per installed model)\n- [ ] Auto-verify loop — typecheck/test after each edit, feed failures back\n\n---\n\n## Contributing\n\nIssues and small PRs welcome. The codebase is intentionally minimal — favour clarity over cleverness.\n\n```\nsrc/\n  config.ts            # layered loader: defaults + OS config dir + project bonfire.config.json\n  agent/\n    index.ts           # runAgent, initMcp, listTools\n    context.ts         # token budgeting + mechanical history compaction\n    retry.ts           # backoff for transient provider failures\n    provider.ts        # lazy provider, debug fetch with header redaction\n    stream.ts          # typed normalizer for AI SDK fullStream events\n    system-prompt.ts   # 3-layer prompt: built-in + global bonfire dir + .bonfire/system.md\n    error-format.ts    # single source of truth for provider error messages\n  tools/\n    safe-path.ts       # realpath-aware allowlist (closes symlink escapes)\n    approval.ts        # tri-state yes/no/always handler\n    remembered.ts      # session-scoped \"always allow\" store, shared by every gate\n    shell-policy.ts    # hardcoded deny-list + per-config allow/deny patterns\n    net-guard.ts       # SSRF guard: resolves hostnames, blocks private ranges\n    search-tool.ts     # gitignore-aware regex search\n    file-tools.ts, shell-tool.ts, navigate-tool.ts, fetch-tool.ts\n  mcp/\n    index.ts, stdio.ts, http.ts, windows.ts\n  session/\n    index.ts, storage.ts, meta.ts\n  skills/\n    loader.ts          # scans global bonfire dir + .bonfire/skills, parses frontmatter\n    tool.ts            # load_skill tool — model calls this on demand\n  codemap/\n    walk.ts            # iterative BFS, lstat-based, builds skeleton tree\n    summarize.ts       # bottom-up LLM pass, retry-on-fail, per-ext sampling\n    store.ts           # JSON cache + in-process memoization\n    ignore.ts          # gitignore-pkg-backed matcher + curated defaults\n    index.ts, types.ts\n  providers/\n    index.ts, ollama.ts, llamacpp.ts, remote.ts, types.ts\n  cli/\n    bin.tsx            # entry: TTY check, MCP boot, banner, render <App firstRun=…/>, signals\n    App.tsx            # single-column layout + first-run hand-off into <ReconfigurePrompt/>\n    theme.ts           # presets + live theme object — the one place colour is decided\n    components/        # Banner, Transcript, ToolLine, ResultLine, DiffPreview,\n                       # ApprovalPrompt, ModifiedFilesPanel, StatusBar,\n                       # MultilineInput, CommandSuggestions, PromptBar,\n                       # ReconfigurePrompt (handles both first-run + /reconfigure)\n    hooks/             # useAgentStream, useApproval, useProvider,\n                       # useThinkingPhrase, useElapsed\n    commands/          # slash-command registry\n```\n\n---\n\n## License\n\nMIT"}