Five open conventions that make any CLI fully agent-first: learnable, drivable, reportable, and self-updating — and finally, measurable. Adopt any subset in 30 minutes per spec.
An agent lands on a fresh machine with only the binary. Six commands, six specs.
Each is independently adoptable. Together they make a CLI fully agent-first.
The output contract. stdout = data only (JSON, parseable, versioned). stderr = context only (progress, logs). Semantic exit codes (80–119). Typed errors with recoverable and suggestions. help-json introspection. No internal retries.
The embedded mental model. <tool> guide bakes the full operator manual into the binary — the model, the loop, the concepts, the gotchas. An agent reads it once and drives the tool with no external docs. Optional HTTP: GET /guide + GET /llms.txt.
The dual-write relay. <tool> feedback sends feedback to the tool's own endpoint AND a central relay — same idempotency key, never fails the caller. Open submission, admin-gated reads. FEEDBACK_RELAY=off to opt out.
Content-hash self-update. <tool> update downloads, verifies sha256[:12], smoke-tests, atomically swaps (current → .bak, new → in place). Passive stderr nudge when stale. No silent auto-update — manual by default. v1.1 adds <tool> install/uninstall — relocate or remove a binary already in hand (default ~/.local/bin, idempotent, never sudo).
An honest usage signal. Downloads, clones and stars all report the same zero whether nobody wants your tool or the install command has been broken since the first release — and an agent-operated CLI has no human who will ever file an issue. One POST, seven non-identifying fields, disclosed on stderr before the first send, and <tool> telemetry prints the real next payload so a reviewer verifies instead of trusting. DO_NOT_TRACK=1 honoured; CI defaults to off.
Daemon lifecycle. <tool> serve --host --port binds loopback by default — never 0.0.0.0. GET /_health + POST /_shutdown, token-gated the instant the bind isn't loopback. <tool> daemon start|stop|status polls health instead of a fixed sleep, fully idempotent. Formalizes what chatsnip and machin-cms had already reinvented independently.
Same contract, two languages, five tools. Each is a worked example.
| Tool | Language | output | guide | feedback | update | |
|---|---|---|---|---|---|---|
| grepapi | machin + shell | ✓ | ✓ | ✓ | ✓ | |
| mago | Go | ✓ | ✓ | — | #10003;✓ | |
| automaintainer | Go | ✓ | ✓ | — | #10003;✓ | |
| remotecmd | Go | ✓ | ✓ | ✓ | ✓ | |
| chatsnip | machin | ✓ | ✓ | ✓ | — | #10003;
The old Unix philosophy, tightened for LLMs.
One command, one endpoint, one JSON shape. Addable in 30 minutes.
The contract is the JSON shape and the behavior, not the implementation.
Each spec is adoptable alone. No cross-dependencies.
JSON by default, stderr for context, semantic exit codes, self-describing.
stdout is a pipe-friendly API. stderr is ignorable context. Exit codes are actionable.
Copy them, fork them, adopt them. No attribution required.