AGENTS.md
Overview
clnkr is a coding agent CLI that queries LLMs and executes bash commands in a loop. Core library is stdlib only. Talks to the Anthropic Messages API and any OpenAI-compatible endpoint. Ships clnkr (plain CLI) and clnkrd (stdio JSONL adapter). Evals run through the standalone clankerval project.
Commands
make build # Build shipped binaries (default target)
make test # Run all tests
make check # Run the full quality suite
make man # Generate staged man pages from doc/*.1.md
make docs # Build the documentation site
make docs-serve # Run the documentation site locally
make _fmt # Format source
make _hooks # Configure repo-local Git hooks
Run make _fmt then make check before committing. Do not commit if either fails.
Rules
- No external deps in root go.mod. Stdlib only.
- Assume Unix (
bash, process groups,/usr/bin/env). Windows unsupported. - Root
clnkris the only public import surface.internal/is allowed when it clarifies ownership. - Output goes through typed events. Do not add
io.Writerparameters. - Policy logic in
Run(), shared logic inStep(). No policy inStep(). - CLI config resolution stays in
cmd/internal/providerconfig: env/flag precedence,CLNKR_*, API keys, base URL parsing, provider detection, and user-facing config errors. - Provider request semantics stay in
internal/providers/providerconfig: provider/API constants, request options, model capability checks, and provider-specific validation. - Provider adapters serialize validated options; they do not resolve CLI config.
- Wrap errors:
fmt.Errorf("context: %w", err). No bare returns or third-party error packages. - Adapter tests use external packages (
package anthropic_test). Core tests use internal (package clnkr). CLI tests use internal (package main). Match the existing pattern. exhaustivelinter is enabled. Switch on sealed types must cover all cases.defaultcounts as exhaustive.- Do not manually edit
CHANGELOG.md; generated by the release pipeline. - Do not hand-edit generated files in
build/docs/,site/content/docs/clnkr.md,site/content/docs/clnkrd.md, orsite/public/. - Public-facing docs describe current behavior, not implementation history. No
in this pass,for now,currently deferred. - SLOC gates count non-test Go only. Treat them as invariants and honesty constraints: do not shrink counted CLI help, diagnostics, UX, or error clarity to satisfy them, and do not weaken tests or docs to normalize that kind of regression. Agents must not raise SLOC limits unless the user explicitly gives a target number. If raising a gate is the only reasonable path, stop and ask for clarification.
- For changes that touch clnkr's agent design, read
doc/clnkr.7.mdfor current architecture context: act protocol, transcript, provider boundary, command execution, and 12-factor mapping. It is descriptive, not prescriptive. Verify behavior in code and follow this file for rules. - Repo-maintenance helpers live under
scripts/. Readscripts/README.mdbefore adding new ones.
Architecture
Core importable library at module root. Two command adapters. evaluations/ consumed by external clankerval runner.
clnkr/ # core: types, Agent, events (stdlib only)
├── internal/providers/ # Anthropic/OpenAI adapters
├── cmd/clnkr/ # Plain CLI (root go.mod, no external deps)
├── cmd/clnkrd/ # Stdio JSONL adapter
└── evaluations/ # Eval suites for clankerval
Agent API: Step() = one typed-turn cycle, no policy. ExecuteTurn() = run an act turn, update state, emit events. Run() = full-send policy loop (3 consecutive protocol failures = exit).
Act protocol: Three turn types: act, clarify, done. Providers own wire translation. Root ParseTurn validates canonical internal JSON for replay/tests only.
Events: Sealed interface, five types: EventResponse, EventCommandStart, EventCommandDone, EventProtocolFailure, EventDebug. Nil Notify = silent.
Command results (host→model): JSON with stdout, stderr, outcome, and optional feedback. Exit outcomes include exit_code; non-exit outcomes include timeout, cancelled, denied, skipped, and error.
Release
Tag-driven. Push feature to main, wait for CI/evals/site to go green, then push the plain semantic version tag. Release workflow triggers from semver tags, updates debian/main, and generates Debian changelog. Remote main may move. Fetch and rebase before follow-up pushes.
Evals
Optimize for live runs that measure actual agent behavior. Fixture evals exist for harness determinism in CI, not as the primary signal. clankerval requires a clean checkout. Make a temporary commit from a dirty worktree, run, then unroll.
Site
Hugo site under site/. Source of truth for reference docs is doc/*.1.md. Run make docs to regenerate. Do not commit site/public/. Deployment to gh-pages is automatic from main.
Testing
- Provider adapters:
httptest.NewServer, external test packages, no real API calls - Agent loop:
fakeModel/fakeExecutorinagent_test.go - CLI: inject via narrowest local interface, not concrete structs
- Executor: real shell integration tests; handles macOS
/private/tmpsymlink
CI
GitHub Actions on push to main and PRs. Runs lint, tests (-race), build, docs, and evals. make check also enforces architecture edges and core SLOC accounting. Run make _hooks for local pre-commit. golangci-lint required.