Skip to main content

agentcli

Shared conventions for command-line tools whose primary callers are agents. It owns no food domain: it owns predictable errors, JSON output, skills, in-binary guides, and the candidate record used for composition.

Use it in a package

Install the distribution (the import name remains agentcli):

uv add click-agentcli

Register the shared output, guide, and skill commands on a Click CLI:

import click

from agentcli import (
    JsonAwareGroup,
    emit,
    guide_command,
    json_option,
    skill_group,
)

GUIDE = """# acme guide

Use `acme hello` to print a greeting.
"""


@click.group(cls=JsonAwareGroup)
def cli() -> None:
    """Acme's agent-facing CLI."""


@cli.command()
@json_option
def hello(json_output: bool) -> None:
    """Print a greeting."""
    emit(
        {"message": "hello"},
        json_output=json_output,
        human=lambda result: [result["message"]],
    )


cli.add_command(guide_command(GUIDE))
cli.add_command(skill_group(name="acme", package="acme"))

The command has readable output for people and one stable document for agents:

$ acme hello
hello
$ acme hello --json
{"ok":true,"data":{"message":"hello"}}
$ acme guide
# acme guide
...
$ acme skill install
copied  /home/me/.agents/skills/acme

Declare the entry point and ship the skill inside the import package. For Hatchling, a project-root SKILL.md can be mapped into the required wheel location like this:

[project.scripts]
acme = "acme.cli:cli"

[tool.hatch.build.targets.wheel.force-include]
"SKILL.md" = "acme/skills/acme/SKILL.md"

skill_group(name="acme", package="acme") expects an installed wheel to contain acme/skills/acme/SKILL.md. The same file may stay at the repository root for source-checkout use. Its frontmatter name must match the skill name:

---
name: acme
description: Use Acme from an agent.
---

Run `acme guide` for the complete manual.

Add @json_option to each command that supports structured output and call emit once with both the data and its human renderer. JsonAwareGroup then keeps parse failures structured when --json was requested.

Develop this package

uv sync --project .
uv run --project . pytest -q

CLI contract

Every consuming tool uses click, declares --json per command with json_option, and makes its top-level group JsonAwareGroup. The group scans raw arguments so even parse failures that happen before a subcommand exists honour a --json request. Importing agentcli.exits also changes Click's own usage-error code from 2 to 1; consumers must not repeat that correction.

code meaning
0 success
1 usage error or a caller-liftable refusal
2 remote, network, or site failure after allowed retries
3 a caller-stated assertion did not hold
4 a data-quality warning escalated by --strict

An exhausted request budget is code 1, because the caller can lift it. A proportional recipe fit with no solution is code 3.

--json emits exactly one JSON object on stdout and nothing else. Success and failure are symmetric:

{"ok":true,"data":{}}
{"ok":false,"error":{"message":"..."}}

A search with no matches is successful with an empty list. Under --json, errors go to stdout so a caller never has to merge streams to recover the one promised document. Human errors go to stderr.

The stable public surface is:

  • UsageError, RemoteError, AssertionFailure, and StrictFailure.
  • dumps, emit, emit_error, json_option, and limit_option.
  • JsonAwareGroup for every consuming tool's top-level group.
  • skill_group(name=..., package=...) for skill install, uninstall, and status. Installation refuses an unrelated destination, recognises owned broken symlinks, copies by default, and supports --link, --to, and --dry-run. With no options it installs everywhere the skill is wanted and refreshes its own earlier copies, so plain install is the whole job; a directory holding somebody else's skill is still refused.
  • guide_command(text) for a complete manual available without a network.
  • candidate, macro_options, matches, rank, and unverifiable for the shared composition record and filters below.

Candidate contract

Candidate sources answer the same question: filter things someone could eat by per-serving macros, then rank them with provenance. Recipes and restaurant meals therefore emit the same record:

{
  "kind":"recipe",
  "id":"sourdough-pizza",
  "name":"Sourdough Pizza",
  "per_serving":{"kcal":384.2,"protein":31.5,"fat":12.1,"carbs":38.4},
  "complete":true,
  "detail":{}
}

kind is recipe or meal. id is accepted back by the emitting tool; display-only slugs are not identifiers. Source-specific fields live under detail, which shared code never reads.

Sources accept macro_options (--max-kcal, --min-protein) and use rank. The rank key is unrounded protein per 100 kcal, then absolute protein, then name. --max-kcal 0 is valid because zero-calorie records exist.

per_serving contains only macros actually known by the source. Missing is never filled with zero. complete exposes whether the full shape is present; a candidate missing a requested filter macro is excluded and returned in the source's unverifiable or equivalent bucket. Every source emits that bucket, even when its loader makes it structurally empty.

This contract is the reason the tools can be independent packages: an orchestrator can merge and rank results without knowing which source answered.

Download files

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

Source Distribution

click_agentcli-0.3.0.tar.gz (24.1 kB view details)

Uploaded Source

Built Distribution

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

click_agentcli-0.3.0-py3-none-any.whl (24.5 kB view details)

Uploaded Python 3

File details

Details for the file click_agentcli-0.3.0.tar.gz.

File metadata

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

File hashes

Hashes for click_agentcli-0.3.0.tar.gz
Algorithm Hash digest
SHA256 55a4c6d60f93a04c0e25df47df75c1e4169a1224ac2306da2fd544ca272ae579
MD5 e506608c77d68077fb6f1dd69e4fdfe5
BLAKE2b-256 75d5660cd6f973e3cda14aaec0b31306c739069ae6a45e3b5ca86632a0af8561

See more details on using hashes here.

Provenance

The following attestation bundles were made for click_agentcli-0.3.0.tar.gz:

Publisher: release.yml on owahltinez/click-agentcli

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

File details

Details for the file click_agentcli-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: click_agentcli-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 24.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for click_agentcli-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5115cdbde546e8eb8abe60ca1e22eedff9174591431e760e237d7c418d86e8dd
MD5 8cbbb34af35fe4b329ab86684a752c6f
BLAKE2b-256 d4c7fec03b1cae7febb4bb67ab8b18000af29859526b55850b45e0ba2732c16a

See more details on using hashes here.

Provenance

The following attestation bundles were made for click_agentcli-0.3.0-py3-none-any.whl:

Publisher: release.yml on owahltinez/click-agentcli

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

Release history Release notifications | RSS feed

0.4.1

2 files

This release

0.3.0 This release

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page