@hizliemre/horse-code
v0.5.0
Published
Terminal coding agent: one sentence to reviewed, committed code — in its own git worktree
Downloads
1,216
Maintainers
Readme
horse-code
A terminal coding agent that takes a feature request from one sentence to reviewed, committed code — in its own git worktree, without touching your checkout.
hcode # interactive REPL
hcode "add a supplier routing table" # one prompt, run to completionIt is a single Node CLI (hcode), written in TypeScript, with an Ink terminal UI. Node 20+.
What it actually does
Given a request, it runs a pipeline rather than a single agent:
refine → size → brainstorm → constitution → specify → clarify → plan → tasks
│
┌─────────────────────────────────────────────┘
▼
implement → code review → test suite → acceptance gate → commit
▲ │
└────────────┘ (revision rounds, escalating model tier)Each document stage (spec, plan) is written by one role, then read by a team of lenses — separate agents with one perspective each (spec-clarity, code-security, code-concurrency, …). When the team splits, a five-member council votes. When the council splits, a judge rules. Only the judge may decide that a question belongs to you.
Implementation tasks run in parallel, each in its own worktree, and escalate through model tiers (coder → senior-coder → architect / principal-coder) when a task does not close.
Why it is shaped this way
Three constraints drove most of the design:
One session, one worktree. A run never writes to the checkout you are working in — enforced, not intended: merge, commit, reset, checkout and the rest are refused outright at the project root, so a finished run hands you a branch and the command to bring it in, and the decision stays yours. Every file an agent writes is committed as a wip(…) checkpoint, so a bad change is recoverable and the review always sees the whole diff — an unstaged file is a hole in the evidence.
Every message an agent reads is a decision point. A tool that answers "unknown tool: view_file" costs a full model turn to say nothing. So error messages name what exists, suggest the near miss, and say what to do next. Much of this repository is that: the difference between a syscall name and an answer.
Measure before changing. Almost every non-obvious line here carries the measurement that caused it, in the comment above it — how many calls, how much context, which run. If you are changing behaviour, the comment tells you what breaks.
Install
npm install -g @hizliemre/horse-codeThe package is scoped; the command it installs is hcode.
Or from source:
git clone https://github.com/hizliemre/horse-code && cd horse-code
npm install # `prepare` builds automatically
npm link # puts `hcode` on your PATHThen, in the project you want to work on:
hcode init # writes .horsecode/ and asks for the essentials
hcode # start the REPLConfiguration
~/.horsecode/config.json (global) and .horsecode/config.json (per project) are merged, project last.
{
"apiKey": "…",
"baseUrl": "https://…", // any OpenAI-compatible endpoint
"model": "cc/claude-opus-5",
"mode": "ask", // ask | acceptEdits | auto
"allowlist": ["npm test", "git status"],
"maxParallel": 4, // parallel implementation tasks
"roles": {
"coder": { "models": ["cc/claude-opus-5", "cx/gpt-5.6-terra"], "effort": "high" }
},
"team": { "code": [{ "name": "code-security", "perspective": "…", "models": ["…"] }] },
"council": { "members": [ /* … */ ] },
"mcp": { /* Model Context Protocol servers */ },
"telemetry": true // JSONL run traces under ~/.horsecode/telemetry
}Every role resolves to an ordered model chain: the head is tried first, and a transport failure falls to the next. A model that is overloaded is benched briefly; one that is out of quota is benched for the session, and the roles it was serving are moved and moved back when it recovers.
Claude models are sent over Anthropic's own /v1/messages so that effort actually reaches them — the OpenAI-compatible endpoint accepts the field and drops it.
In the REPL
| | |
|---|---|
| /model, /roles | see and change the model chains, per role |
| /mode | ask / acceptEdits / auto |
| /memories, /remember, /forget | the project's durable facts |
| /skills, /sources | installed skills and where they come from |
| /graph | build the code graph; /graph trace writes per-file traces |
| /mcp | connected MCP servers and their tools |
| /parallel, /next, /resume, /sessions | run control |
| /clean-worktrees | remove finished session worktrees |
| /monitor, /watch | live view of a running job |
Memory
Facts an agent learns — a command that only works from a subdirectory, a file that is not where it looks — are written to .horsecode/memory.jsonl in the session worktree, so they ship with the work rather than sitting in someone's checkout. They are retrieved lexically and injected into later turns, including a slice reserved for operational lessons that only implementers receive.
Each role is asked, at the close of its turn, whether anything cost it more than one attempt. That question is the difference between a run that learns and one that rediscovers.
Skills
Six skills ship with the package. Each is a verbatim copy of its upstream, under skills/, in the same
format a project uses for its own — the only difference is who supplies them, and a project may replace any
of them by defining a skill of the same name in .horsecode/skills/.
| | |
|---|---|
| brainstorming | intent and requirements before implementation — bound to the brainstormer |
| writing-plans | what makes an individual task executable — bound to the task list |
| test-driven-development | inlined into every role that writes code |
| frontend-design | design direction, inlined into the design roles |
| systematic-debugging | fetched on demand, when something is stuck |
| ui-ux-pro-max | 161 palettes, 57 font pairings, 25 chart types across 10 stacks — fetched on demand |
A skill can also be referenced rather than copied, for the ones that are large, script-driven, or
maintained upstream. impeccable ships that way: declared by
default, installed by /skills update into ~/.horsecode/skills/, and updatable from its source. Startup
never waits on the network — installing is an explicit act. An explicitly stated skillSources list, an
empty one included, replaces the shipped default entirely.
Tools an agent has
read_file write_file edit_file grep glob shell git (read-only) git_write web_fetch ask_user remember_fact propose_memory skill find_tool find_unfinished, plus the graph tools (graph_overview, graph_find, graph_context, graph_impact, graph_trace) and every tool exposed by a connected MCP server.
Some boundaries are enforced rather than requested, because an instruction is advice and advice is what a model skips:
shellrefuses to rewrite a file (edit_filereports what changed; a heredoc does not).shellrefuses acdout of the working directory, and the git commands that throw away uncommitted work wholesale (reset --hard,checkout -- .).gitis read-only; the writing verbs live in a separate tool.- A call whose arguments arrive truncated is a stream that stopped, not a call — it is retried, not handed to the model as broken JSON.
Development
npm test # vitest — 3200+ tests
npm run typecheck # tsc --noEmit (tsup does NOT typecheck; run this)
npm run build # tsup → dist/
CI=1 npm test # what CI runs — see belowCI=1 is not decoration: libraries change behaviour under it. Ink stops repainting, so a terminal test
reads an empty frame; colour libraries switch themselves off, so an escape a test asserts is never
emitted. Six failures that only appeared on the runner reproduced locally the moment that variable was
set. Set it before blaming the runner.
Tests carry the measurement that motivated them in the describe block. A test whose comment says "measured live: 27 of 90 calls" is documentation as much as a guard — if you change the behaviour, that number is what you are trading away.
Releases are cut by tag: npm version <patch|minor|major> then push the tag. The workflow refuses to publish
if the tag and package.json disagree, or if that version is already on the registry — npm versions are
permanent, so both are checked before anything is built.
Status
Working software, used daily against a real .NET + Angular monorepo. Pre-1.0 is honest: interfaces move when a measurement says they should.
License
MIT — see LICENSE.
