GitHub - agent-fox-dev/agent-fox: agent-fox is an autonomous spec-first coding agent (golang version).

GitHub

5 min read Original article ↗

agent-fox is an autonomous spec-first coding agent (golang version).

The mono-repo for all agent-fox (golang) code: library modules, CLI tools and services. It depends on

  • coder — the AgentKit SDK (module github.com/agentfox/agentkit-go)
  • spec — the spec format, whose JSON Schemas are copied into afspec/schemas/

and works with hub as its backend service.

The tools

Four programs, one interface: one input, one JSON object out.

spec  [flags] <input>     a product idea      → a validated specification package
issue [flags] <input>     a problem report    → a structured issue on GitHub or GitLab
fix   [flags] <input>     a problem           → a verified change on a branch
impl  [flags] <input>     a specification     → the spec implemented, task by task, on a branch

The input is exactly one of: a GitHub or GitLab issue or pull/merge-request URL, a path to a readable file, - for stdin, or any other text. There is no flag that selects the kind — the argument's shape decides, in Go, before anything else happens.

export ANTHROPIC_API_KEY=sk-ant-...
export GITHUB_TOKEN=ghp_...            # or GITLAB_TOKEN=glpat-...

issue "panic: assignment to entry in nil map in loop.go, after an abort"
fix   https://github.com/acme/widgets/issues/42 --dir ~/src/widgets
spec  ./docs/prds/widget-cache.md --architecture
impl  09 --dir ~/src/widgets --land branch

kubectl logs deploy/api --since 1h | issue - --repo acme/widgets --dry-run

Each writes exactly one JSON object to stdout, on every program-driven path including the failing ones. Progress goes to stderr, so the two never interleave. --version and -h/--help (and a bare invocation with no input) are the human-driven exceptions: they print text and emit no JSON. Exit codes are shared: 0 done, 1 failed, 2 usage, 3 a person has to answer something, 4 work exists but the checks do not pass.

See the tool reference for every flag, every result field and every error category.

Why they are programs

Each tool started as a markdown skill handed to a coding CLI. A skill can describe a workflow; it cannot enforce one. af-issue states its read-only mandate three times and then hands the CLI a write_file tool. af-fix opens a pull request and squash-merges the same branch, posts its analysis before checking whether it can work at all, and writes "✅ all tests pass" from a template rather than from a test run.

So the model does the two or three steps that genuinely need judgment, and Go does the rest:

The skill says, in prose The tool does, in code
"the codebase is read-only to you" the mutating tools are not in the resolved set
the issue-body template a JSON Schema; Go renders the markdown
"cite real files; do not guess" every cited path resolved against the workspace
gh issue create --repo … a net/http call after the run, suppressed by --dry-run
"run the tests" then "✅ all tests pass" a measured before/after pair, and a renderer that takes one
"halt until input is received" exit 2, before a token is spent

None of the right-hand column depends on the model cooperating. That is the whole argument for embedding an agent in a program rather than writing a longer prompt: the parts you cannot afford to have wrong stop being prompt.

ADR 03 records the reasoning and the errors the rewrite found; ADR 04 applies it to the legacy orchestrator that impl replaces.

Modules

Path Package Purpose
afspec/ afspec Spec format library: load, validate, mutate, render and save specification packages.
specgen/ specgen The spec pipeline: the PRD phase, the three generation phases, and the project audit.
issuetriage/ issuetriage The triage pipeline: the diagnosis schema, the citation check, the rendered issue.
codefix/ codefix The fix pipeline: pre-flight, analysis, implementation, verification, landing.
codeimpl/ codeimpl The implementation pipeline: a spec's tasks in order, each verified by the spec's own checks and committed with its state.
issuex/ issuex The forge client: one interface over GitHub and GitLab for reading issues, filing them, commenting, and opening pull or merge requests. Every forge call the tools make goes through it.
internal/toolio/ toolio Input classification, the JSON envelope, exit codes, and the shell the commands share.
internal/agentrun/ agentrun Model and credential resolution, the phase runner, the read-only invariant, the shell guard.
internal/gitx/, internal/checks/ git, and the command that decides whether a change is correct.
internal/project/ project What a repository is written in, and the audit that refuses a plan naming another ecosystem's tooling.
cmd/spec/, cmd/issue/, cmd/fix/, cmd/impl/ main The four tools.

The spec format itself is specified in the spec repository (specification/spec-format-v2.md). The JSON Schemas the library compiles and embeds live in afspec/schemas/ — and they are also what the generation tools declare to the model, so the two cannot drift.

Quick start

Release binaries for darwin and linux, on arm64 and amd64, are installed by the script at the repository root. It fetches all four tools, because they share one release and one interface:

curl -fsSL https://raw.githubusercontent.com/agent-fox-dev/agent-fox/main/install.sh | sh

TOOLS, INSTALL_DIR (default /usr/local/bin) and VERSION (default latest) are read from the environment.

To build from source: the tools run on AgentKit, which lives in the coder repository under the module path github.com/agentfox/agentkit-go. That path does not match its repository URL, so the module proxy cannot serve it and it is consumed through a replace to a sibling checkout named after the module:

git clone https://github.com/agent-fox-dev/coder ../agentkit-go
make check          # gofmt + go vet + all tests
make build          # go install spec, issue, fix and impl; af and nightshift into bin/
make build-all      # static cross-builds of the four tools into dist/

The test suite needs no API key, no GitHub token and no network: the model half runs against AgentKit's scripted provider, GitHub and GitLab against httptest servers, and git against real temporary repositories.

Documentation