Skip to main content

agent-cli-scaffold

ci

Build CLIs an agent can actually drive. A runtime you import, a scaffold that generates a working tool, and a checklist that runs.

The command and the module are both agtcli; agent-cli-scaffold is only the name it is published under.

The rules and the incident behind each are in PLAYBOOK.md. This file is how to use the thing.

Why a library and not a template

The same contract copied into five projects is five things that drift. orly and mercadona-cli independently grew the same exit-code taxonomy, the same compact/--raw split, and the same atomic-write helper, in two languages, and the copies already disagree.

So the mechanical half is imported and tested once. agtcli check covers the half that is still yours.

Install

uv tool install git+https://github.com/ivorpad/agent-cli-scaffold
agtcli --help

From a clone instead, which is what you want if you are changing the contract:

uv sync && uv tool install --editable .

Either works. A generated project depends on agent-cli-scaffold, and agtcli new pins that dependency at wherever this copy came from: the local path from a clone, the same commit from a git install. So a project scaffolded from your clone builds on your machine and nowhere else until this is on an index.

Recipes

Scaffold a CLI and confirm it works

agtcli new hansard --description "Search UK Hansard debates." \
                    --triggers "hansard, what did parliament say about X"
cd hansard && uv sync && uv run pytest -q     # 14 passing
uv tool install --editable . && hansard --help

You get a tool that already has typed exit codes, a compact default with a --raw escape hatch, atomic 0600 state, a credential-rotation hook, a blast-radius guard, a thin skill, and contract tests that pass. Then replace src/hansard/api.py with the real service.

Audit one before shipping

agtcli check ./hansard          # exits non-zero while anything fails
agtcli check ./hansard --json   # one object per check
agtcli rules                    # just the checklist

Gate a commit or a CI job on the checklist

agtcli check . --json | jq -e '.failed == 0'

Find which rule a check is about

agtcli rules --json | jq -r '.[] | select(.name=="truncation-marked") | .why'

Fifteen items are checked by reading the project. Nine cannot be, so they are reported as unverified rather than counted as passes.

$ agtcli check ./hansard
  ✓ skill-shipped: skill/SKILL.md
  ✓ exit-codes-typed: inherited from agtcli.build
  ✓ truncation-marked: cuts are marked
  ✓ blast-radius-capped: costly commands consult the guard
  ...
  ? dumb-subagent-test-run: the only step that reliably finds what you missed

9 item(s) cannot be checked mechanically; verify them yourself.

What you import

import agtcli
from agtcli import Session

app = agtcli.build("mytool", help="...", notes=AGENT_NOTES, unit="EUR")

@app.command()
def get(ctx: typer.Context, id: str):
    s = Session.get(ctx)
    s.out.emit(record, compact=lambda r: {"id": r["id"]})

@app.command()
@agtcli.costly                       # cannot return without checking the cap
def submit(ctx: typer.Context, id: str):
    s = Session.get(ctx)
    s.guard.check(total, f"submitting {id}")

main = agtcli.main_for(app)
build / run / main_for a Typer app wired to the contract; one place decides every exit code
ExitCode 0 ok, 1 retryable, 2 usage, 3 not-found, 4 auth, 5 timeout, 6 refused. The --help table is generated from this enum, so it cannot drift
Output compact by default, --raw for everything, and under --json stdout carries JSON and nothing else
trim cut a value yourself; the result says how much is missing. The one to reach for
mark_truncation / elide the halves of trim. mark_truncation alone is for text somebody else cut, where the last character is the only evidence; wrapping your own elide in it marks whole values as truncated
write_atomic / write_json mkstemp plus os.replace, 0600, because agents run in parallel
Guard / @costly a cap by flag, env or config that fails closed, and raises if a costly command never consults it
Config pydantic-settings with the precedence the right way round: flag, then env, then file

Errors are typed and carry what to do next:

$ hansard submit 123 --json ; echo "exit $?"
{
  "error": "refused",
  "message": "submitting 123 is irreversible and --yes was not given",
  "exit_code": 6,
  "retryable": false,
  "remedy": "re-run with --yes once a human has agreed"
}
exit 6

The skill

skill/ is meant to be symlinked, so it versions with the code:

ln -s "$PWD/skill" ~/.claude/skills/agtcli

A CLI is invoked, not found, so the skill body is a one-liner that points at --help. What the agent discovers progressively is the tool: tool --help for the verbs, rules and exit codes; tool <verb> --help for one command, with the global flags carried down so --max-cost is still visible on the command that spends; and a next-step hint in the output itself.

Development

uv run pytest -q

Every test is a rule from the playbook or a bug that actually happened. If you change the contract, a test should fail.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agent_cli_scaffold-0.1.0.tar.gz (77.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agent_cli_scaffold-0.1.0-py3-none-any.whl (45.0 kB view details)

Uploaded Python 3

File details

Details for the file agent_cli_scaffold-0.1.0.tar.gz.

File metadata

  • Download URL: agent_cli_scaffold-0.1.0.tar.gz
  • Upload date:
  • Size: 77.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for agent_cli_scaffold-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b85682b096d4968162c1c93762ab8298915ac99cd7beae5d5888a284b71f4058
MD5 f117459eebf71794ec1d573a55190dda
BLAKE2b-256 18fdc83741577fd67120de1de5d2d632539f99ca151e44fe6a4eb3c48f050787

See more details on using hashes here.

Provenance

The following attestation bundles were made for agent_cli_scaffold-0.1.0.tar.gz:

Publisher: release.yml on ivorpad/agent-cli-scaffold

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file agent_cli_scaffold-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for agent_cli_scaffold-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7e767cec8b79d16528db1f579dfdc00cb43ec240fe47d4c1577e3c34c4a28b9a
MD5 98928d5443d83f99afafc4dbefc7638e
BLAKE2b-256 c8cc8b678983a2843d14e7b354ac8b3eb309309a9619e14e33bc425a6b7f7b55

See more details on using hashes here.

Provenance

The following attestation bundles were made for agent_cli_scaffold-0.1.0-py3-none-any.whl:

Publisher: release.yml on ivorpad/agent-cli-scaffold

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page