A coding agent that acts by writing programs.
One JavaScript program per round, with real loops and branching, run against your real checkout.
bough rhymes with now, not with dough: /baʊ/. It is the word for a branch of a tree, which is what a conversation is here: you fork a turn and the old line goes on living as a branch.
Most 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.
bough is an alternative harness design, not a better coding agent. That distinction is the point of the project, and this README tries not to blur it.
Warning
There is no isolation boundary. Programs run as you, with your full authority: filesystem,
network, subprocesses, npm: imports. No sandbox, no egress proxy, no credential gating. Host
functions are convenience and session integration, never a wall.
This is a deliberate choice, not an unfinished one (spec §2): the harness edits
your real files because reviewing git diff and pushing with your own git is the delivery
mechanism. Run it only on a machine where you would be comfortable running the code it writes,
because that is exactly what happens.
The idea
- One program per round. The model's only action is
run_steps(code). 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 diffagainst the sha the session started from; you deliver withgit commit/git push. - 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.
Here is a round. It is one program where another harness would spend five round-trips:
// Which crates still pin the old ratatui, and do they still pass? const pinned = (await bash("rg -l 'ratatui = \"0.29\"' crates", "repo:scan:ratatui")) .trim().split("\n").filter(Boolean); const names = pinned.map(p => p.split("/")[1]); // sh() runs them concurrently; a non-zero exit is data, not an exception. const runs = await sh(names.map(n => ({ cmd: `cargo test -p ${n} 2>&1 | tail -3`, tag: `cargo:test:${n}`, }))); const broken = names.filter((_, i) => runs[i].code !== 0); console.log(broken.length ? `failed: ${broken.join(", ")}` : "all green");
The loop, the fan-out and the branch are the model's own code. console.log is what streams to
you and what comes back as the round's result, so the program decides what is worth your context:
here, the names that failed rather than four test logs.
Install
macOS or Linux. Builds from source, so the first run takes a few minutes.
brew tap andreylukin/bough https://github.com/andreylukin/bough brew install bough $EDITOR ~/.bough/env # ANTHROPIC_API_KEY=… bough start # background service bough # the TUI
Without Homebrew, the same install as a script. It clones into ~/bough and builds there:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/andreylukin/bough/main/install.sh)"Models. Four providers, and a model routes to one by the shape of its id alone:
claude-opus-5 is Anthropic, openai:gpt-5 is OpenAI's Responses API, vendor/model is
OpenRouter, @cf/vendor/model is Cloudflare Workers AI. OpenRouter is the wide door: if it
carries a model, bough can run a turn on it. Every provider's base URL is overridable, and the
OpenRouter path speaks /v1/chat/completions, so pointing OPENROUTER_API_BASE at Ollama,
vLLM, LM Studio or a gateway runs turns against that instead. The picker (^o) lists what your
keys actually reach rather than a compiled-in catalog, and any one key is enough to start.
Full instructions, keys, and updating: docs/install.md.
Use it
Point a session at a repo and ask in plain language. bough writes a small program, runs it, and
answers. Folded reasoning, the code that ran, and live cost and context all sit in one view.
Unfold a step (^e) and you see the actual program and its output:
Everything that is not the conversation lives in one panel with nine tabs, each on a direct-jump
chord: the conversation tree (^f), changes (^d), workflows (^w), model (^o), MCP (^p),
skills (^k), hooks (^x), context (⌥c), theme (^y). Press ? for the full keymap.
Review with ^d: the Changes rail is git diff against the sha the session started from, per file
and revertable per path. You commit and push with your own git.
Rewind to any turn and send something else, and the old line survives as a branch.
→ docs/tui.md for panels and every chord · docs/cli.md for exec,
acp, mcp, tags and the rest
What it can do
Programs. Nineteen host functions in scope, plus the full JS runtime. One editing idiom:
view gives numbered lines with a version tag, patch names lines instead of quoting them, so code
being edited never has to survive the model's own string escaping, and a stale edit reports a
conflict instead of clobbering. → docs/programs.md
Delegation. agent and spawn run subagents in the same checkout; a workflow is a detached
orchestration script with parallel / pipeline primitives, schema-validated results, and a
journal that replays unchanged work on rerun instead of paying for it twice.
→ docs/delegation.md
Memory across sessions. Every shell command carries tags naming what it is for, written at the
moment the command is written. A session opens primed with its project's own vocabulary, and
bough tags answers what was tried here, what worked, and what it printed.
→ docs/tags.md
Large output is described, not dumped or cut. A command's output is the one thing in a turn
whose size the model does not choose: it asks for a test run and gets 12MB. Past 20,000 characters
bough streams the whole thing to a file and returns head, tail, and a digest of the middle rather
than an ellipsis, so the model is told what the omitted output consists of: the distinct statements
it is made of, with exact counts, typed per-variable statistics and flagged anomalies, problems
ranked first rather than whatever happened last. It is one streaming pass that keeps nothing per
line, so a 200,000-line file and a 2,000-line one cost the same to summarize. Nothing is lost, the
marker names the file and how to grep it, and the same pipeline is available by hand as
bough patterns on any log, offline, even against a stopped server.
→ docs/logs.md
Extending it. Skills, Lua hooks that can start work rather than only veto it, JavaScript
extensions bound into every program's scope, and MCP tools called with real objects
rather than through a shell. Reads the
AGENTS.md, CLAUDE.md and .claude/skills your other harnesses already wrote.
→ docs/extending.md
Documentation
docs/ is the map. Start at install.md, then tui.md. how-it-works.md is the architecture in one page; spec.md and specs/ are authoritative for behavior.
What bough is not
These are decisions, not gaps:
- No confinement of any kind, and no credential gating.
- 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.
- 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.
- No embeddings over transcripts; cross-session transcript search is SQLite FTS. The two vector
indexes cover the tagged command memory and note sections; both live in a separate
embeddings.dbthat is derived state and can be deleted at any time. - No per-agent worktrees or file leases. One shared checkout.
- No remote access, no auth layer, no web UI.
Contributing
The most useful contributions sharpen or falsify the design. Read CONTRIBUTING.md for setup, the bar for a pull request, and the verification you are expected to have done.
Bugs and features go through the issue templates; questions and design debates belong in Discussions. Security issues go through SECURITY.md, never the public tracker. Participation is governed by the Code of Conduct.
License
Apache License 2.0. By contributing you agree your contributions are licensed under it; there is no CLA.



