{"slug": "show-hn-bough-a-coding-agent-that-writes-a-program-per-turn-not-tool-calls", "title": "Show HN: Bough – a coding agent that writes a program per turn, not tool calls", "summary": "Bough, a new open-source coding agent from developer Andrey Lukin, lets AI models write one JavaScript program per turn with real loops and branching instead of emitting individual tool calls, executing code directly on the user's machine with no sandbox or isolation. The harness, which runs on macOS or Linux and supports Anthropic, OpenAI, OpenRouter, and Cloudflare Workers AI models, edits the user's real checkout and delivers changes via git diff, commit, and push. Bough is positioned as an alternative harness design rather than a better coding agent, with history stored as a tree and all state managed by a headless server.", "body_md": "**A coding agent that acts by writing programs.**\n\nOne JavaScript program per round, with real loops and branching, run against your real checkout.\n\n**bough** rhymes with *now*, not with *dough*: /baʊ/. It is the word for a branch of a tree, which\nis what a conversation is here: you fork a turn and the old line goes on living as a branch.\n\nMost harnesses let the model emit one tool call and wait. bough gives it a single tool that takes a program: the model writes JavaScript with real control flow, and a harness executes it on your machine. A headless server owns all state and execution; the terminal UI is a view over it.\n\nbough is an alternative harness **design**, not a better coding agent. That distinction is the point\nof the project, and this README tries not to blur it.\n\nWarning\n\n**There is no isolation boundary.** Programs run as you, with your full authority: filesystem,\nnetwork, subprocesses, `npm:`\n\nimports. No sandbox, no egress proxy, no credential gating. Host\nfunctions are convenience and session integration, never a wall.\n\nThis is a deliberate choice, not an unfinished one ([spec §2](/andreylukin/bough/blob/main/docs/spec.md)): the harness edits\nyour real files because reviewing `git diff`\n\nand pushing with your own git is the delivery\nmechanism. Run it only on a machine where you would be comfortable running the code it writes,\nbecause that is exactly what happens.\n\n**One program per round.** The model's only action is`run_steps(code)`\n\n. Control flow lives in the program, not in a chain of round-trips.**In place.** The agent edits your own checkout. No copy, no overlay. The Changes rail is`git diff`\n\nagainst the sha the session started from; you deliver with`git commit`\n\n/`git push`\n\n.**History is a tree.** Fork any turn, compact a span onto a new branch, lift messages into a fresh root. Nothing is destructively rewritten; every operation produces a new branch.**The server is the system.** State, execution, and orchestration are server-side. A client can crash or detach without affecting a running turn.**Delegation is core.** Subagents and workflows are primary capabilities with real persistence, lifecycle control, and observability.\n\nHere is a round. It is one program where another harness would spend five round-trips:\n\n``` js\n// Which crates still pin the old ratatui, and do they still pass?\nconst pinned = (await bash(\"rg -l 'ratatui = \\\"0.29\\\"' crates\", \"repo:scan:ratatui\"))\n  .trim().split(\"\\n\").filter(Boolean);\n\nconst names = pinned.map(p => p.split(\"/\")[1]);\n\n// sh() runs them concurrently; a non-zero exit is data, not an exception.\nconst runs = await sh(names.map(n => ({\n  cmd: `cargo test -p ${n} 2>&1 | tail -3`,\n  tag: `cargo:test:${n}`,\n})));\n\nconst broken = names.filter((_, i) => runs[i].code !== 0);\nconsole.log(broken.length ? `failed: ${broken.join(\", \")}` : \"all green\");\n```\n\nThe loop, the fan-out and the branch are the model's own code. `console.log`\n\nis what streams to\nyou and what comes back as the round's result, so the program decides what is worth your context:\nhere, the names that failed rather than four test logs.\n\nmacOS or Linux. Builds from source, so the first run takes a few minutes.\n\n```\nbrew tap andreylukin/bough https://github.com/andreylukin/bough\nbrew install bough\n$EDITOR ~/.bough/env      # ANTHROPIC_API_KEY=…\nbough start               # background service\nbough                     # the TUI\n```\n\nWithout Homebrew, the same install as a script. It clones into `~/bough`\n\nand builds there:\n\n```\n/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/andreylukin/bough/main/install.sh)\"\n```\n\n**Models.** Four providers, and a model routes to one by the shape of its id alone:\n`claude-opus-5`\n\nis Anthropic, `openai:gpt-5`\n\nis OpenAI's Responses API, `vendor/model`\n\nis\nOpenRouter, `@cf/vendor/model`\n\nis Cloudflare Workers AI. OpenRouter is the wide door: if it\ncarries a model, bough can run a turn on it. Every provider's base URL is overridable, and the\nOpenRouter path speaks `/v1/chat/completions`\n\n, so pointing `OPENROUTER_API_BASE`\n\nat Ollama,\nvLLM, LM Studio or a gateway runs turns against that instead. The picker (`^o`\n\n) lists what your\nkeys actually reach rather than a compiled-in catalog, and any one key is enough to start.\n\nFull instructions, keys, and updating: [docs/install.md](/andreylukin/bough/blob/main/docs/install.md).\n\nPoint a session at a repo and ask in plain language. bough writes a small program, runs it, and\nanswers. Folded reasoning, the code that ran, and live cost and context all sit in one view.\nUnfold a step (`^e`\n\n) and you see the actual program and its output:\n\nEverything that is not the conversation lives in one panel with nine tabs, each on a direct-jump\nchord: the conversation tree (`^f`\n\n), changes (`^d`\n\n), workflows (`^w`\n\n), model (`^o`\n\n), MCP (`^p`\n\n),\nskills (`^k`\n\n), hooks (`^x`\n\n), context (`⌥c`\n\n), theme (`^y`\n\n). Press `?`\n\nfor the full keymap.\n\nReview with `^d`\n\n: the Changes rail is `git diff`\n\nagainst the sha the session started from, per file\nand revertable per path. You commit and push with your own git.\n\nRewind to any turn and send something else, and the old line survives as a branch.\n\n→ [docs/tui.md](/andreylukin/bough/blob/main/docs/tui.md) for panels and every chord · [docs/cli.md](/andreylukin/bough/blob/main/docs/cli.md) for `exec`\n\n,\n`acp`\n\n, `mcp`\n\n, `tags`\n\nand the rest\n\n**Programs.** Eighteen host functions in scope, plus the full JS runtime. One editing idiom:\n`view`\n\ngives numbered lines with a version tag, `patch`\n\nnames lines instead of quoting them, so code\nbeing edited never has to survive the model's own string escaping, and a stale edit reports a\nconflict instead of clobbering. → [docs/programs.md](/andreylukin/bough/blob/main/docs/programs.md)\n\n**Delegation.** `agent`\n\nand `spawn`\n\nrun subagents in the same checkout; a workflow is a detached\norchestration script with `parallel`\n\n/ `pipeline`\n\nprimitives, schema-validated results, and a\njournal that replays unchanged work on rerun instead of paying for it twice.\n→ [docs/delegation.md](/andreylukin/bough/blob/main/docs/delegation.md)\n\n**Memory across sessions.** Every shell command carries tags naming what it is *for*, written at the\nmoment the command is written. A session opens primed with its project's own vocabulary, and\n`bough tags`\n\nanswers what was tried here, what worked, and what it printed.\n→ [docs/tags.md](/andreylukin/bough/blob/main/docs/tags.md)\n\n**Extending it.** Skills, Lua hooks that can start work rather than only veto it, JavaScript\nextensions bound into every program's scope, and MCP as a command rather than a verb. Reads the\n`AGENTS.md`\n\n, `CLAUDE.md`\n\nand `.claude/skills`\n\nyour other harnesses already wrote.\n→ [docs/extending.md](/andreylukin/bough/blob/main/docs/extending.md)\n\n[ docs/](/andreylukin/bough/blob/main/docs) is the map. Start at\n\n[install.md](/andreylukin/bough/blob/main/docs/install.md), then\n\n[tui.md](/andreylukin/bough/blob/main/docs/tui.md).\n\n[how-it-works.md](/andreylukin/bough/blob/main/docs/how-it-works.md)is the architecture in one page;\n\n[spec.md](/andreylukin/bough/blob/main/docs/spec.md)and\n\n[specs/](/andreylukin/bough/blob/main/specs)are authoritative for behavior.\n\nThese are decisions, not gaps:\n\n- No confinement of any kind, and no credential gating.\n- No acceptance gate. The model reports what it did and you verify it. The harness does not re-run a committed command or block a turn from finishing.\n- No local inference in the turn loop; the cheap tier is a hosted model. The one exception is the embedding layer, which runs a small model inside SQLite.\n- No embeddings over transcripts; cross-session transcript search is SQLite FTS. The two vector\nindexes cover the tagged command memory and note sections; both live in a separate\n`embeddings.db`\n\nthat is derived state and can be deleted at any time. - No per-agent worktrees or file leases. One shared checkout.\n- No remote access, no auth layer, no web UI.\n\nThe most useful contributions sharpen or falsify the design. Read\n[CONTRIBUTING.md](/andreylukin/bough/blob/main/.github/CONTRIBUTING.md) for setup, the bar for a pull request, and the\nverification you are expected to have done.\n\nBugs and features go through the [issue templates](https://github.com/andreylukin/bough/issues/new/choose); questions and design debates belong in\n[Discussions](https://github.com/andreylukin/bough/discussions). Security issues go through [SECURITY.md](/andreylukin/bough/blob/main/.github/SECURITY.md), never the\npublic tracker. Participation is governed by the [Code of Conduct](/andreylukin/bough/blob/main/.github/CODE_OF_CONDUCT.md).\n\n[Apache License 2.0](/andreylukin/bough/blob/main/LICENSE). By contributing you agree your contributions are licensed under it;\nthere is no CLA.", "url": "https://wpnews.pro/news/show-hn-bough-a-coding-agent-that-writes-a-program-per-turn-not-tool-calls", "canonical_source": "https://github.com/andreylukin/bough", "published_at": "2026-08-12 14:51:13+00:00", "updated_at": "2026-08-12 15:13:18.411149+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "artificial-intelligence"], "entities": ["Bough", "Andrey Lukin", "Anthropic", "OpenAI", "OpenRouter", "Cloudflare Workers AI"], "alternates": {"html": "https://wpnews.pro/news/show-hn-bough-a-coding-agent-that-writes-a-program-per-turn-not-tool-calls", "markdown": "https://wpnews.pro/news/show-hn-bough-a-coding-agent-that-writes-a-program-per-turn-not-tool-calls.md", "text": "https://wpnews.pro/news/show-hn-bough-a-coding-agent-that-writes-a-program-per-turn-not-tool-calls.txt", "jsonld": "https://wpnews.pro/news/show-hn-bough-a-coding-agent-that-writes-a-program-per-turn-not-tool-calls.jsonld"}}