Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

treaty

PyPI Python CI Dependencies License: MIT

A Python CLI framework for command-line tools that AI agents can call safely.

treaty implements the CLI Agent Spec: an agent reads every command, flag, and exit code in one manifest call, gets a JSON envelope on every exit, and knows from the exit code alone whether a retry is safe. The manifest is the treaty between the CLI author and the agent; exit codes are its clauses.

Why treaty

Agents drive CLIs by trial and error: they parse --help, guess flags, scrape stderr, and retry commands that already half-ran. treaty makes the contract explicit and checks it:

  • One-call discovery: manifest returns every command, flag, example, and exit code as JSON
  • An envelope on every exit: ok, data, error, warnings, meta, even when a handler crashes
  • Typed exit codes: each one says whether it is retryable and what side effects it left; a handler that raises an undeclared code fails loudly
  • Declared danger: every command is safe, mutating, or destructive; destructive ones refuse without --confirm-destructive and show a dry-run preview instead
  • Safe retries: idempotency keys and session deduplication for mutating commands
  • Strict validation: every argument error in one run, exit 2, and the handler never called
  • Secrets stay secret: read only from env or file, redacted in output and logs
  • MCP for free: treaty-mcp module:app serves any treaty app's commands as MCP tools
  • Zero runtime dependencies in the core

Install

uv add treaty                # the library, inside a uv project
uv tool install treaty       # the treaty CLI on PATH

Requires Python 3.14. Add the mcp extra (treaty[mcp]) for the MCP server.

Quick start

Handlers are plain functions over a frozen dataclass of arguments:

from dataclasses import dataclass
from treaty import Affects, App, Arg, Ctx, Exit, Flag

app = App("deployctl", version="1.4.0")
app.exit_code("DEPLOY_CONFLICT", 79, description="Target already has a deployment in progress",
              retryable=False, side_effects="none")

@dataclass(frozen=True, slots=True)
class Rollback:
    service: str = Arg(description="Service name")
    to: str | None = Flag(default=None, description="Release tag to roll back to")
    dry_run: bool = Flag(default=False, description="Plan the rollback, write nothing")

@dataclass(frozen=True, slots=True)
class Plan:
    effect: str
    service: str
    release: str
    would_affect: Affects | None = None

deploy = app.group("deploy", description="Manage deployments")

@deploy.command("rollback", description="Roll a service back to its previous release",
                danger_level="destructive", exit_codes=["DEPLOY_CONFLICT"])
def rollback(args: Rollback, ctx: Ctx) -> Plan:
    if args.to is None:
        raise Exit.DEPLOY_CONFLICT("No previous release recorded", context={"service": args.service})
    if args.dry_run:
        affects = Affects(f"Rolls {args.service} back to {args.to}", (f"service/{args.service}",), 1)
        return Plan("would_update", args.service, args.to, affects)
    return Plan("updated", args.service, args.to)

if __name__ == "__main__":
    app.main()

What the agent sees

The manifest entry for deploy rollback declares its danger level and every exit code it can return:

{
  "danger_level": "destructive",
  "exit_codes": {
    "6":  {"name": "CONFLICT", "retryable": false, "side_effects": "none"},
    "79": {"name": "DEPLOY_CONFLICT", "retryable": false, "side_effects": "none",
           "description": "Target already has a deployment in progress"}
  }
}

deployctl deploy rollback api --to v1.3.0 without confirmation changes nothing. The agent gets exit 2, a preview of what would happen, and the exact fix (meta trimmed):

{
  "ok": false,
  "data": {
    "effect": "would_update",
    "service": "api",
    "release": "v1.3.0",
    "would_affect": {"summary": "Rolls api back to v1.3.0", "resources": ["service/api"], "count": 1}
  },
  "error": {
    "code": "CONFIRMATION_REQUIRED",
    "message": "Command deploy.rollback is destructive and was not applied; it would: Rolls api back to v1.3.0",
    "phase": "validation",
    "retryable": false,
    "suggestion": "rerun with --confirm-destructive to apply (confirm_destructive: true in exec, MCP, or --raw-payload)"
  },
  "warnings": [],
  "meta": {"command": "deploy.rollback", "exit_code": 2, "dry_run": true, "request_id": "66bd15e2696b"}
}

Without --to, the handler raises DEPLOY_CONFLICT: exit 79, retryable: false, and error.context names the service.

Output is JSON automatically when stdout is not a terminal and human-readable text when it is. Off a terminal nothing prompts: an answer a command needs fails with an exit code.

Tooling

The treaty CLI is itself a treaty app:

Command What it does
treaty init Scaffold a new CLI project that passes the audit
treaty scaffold-from Write a treaty module from a typer, click, or argparse CLI
treaty audit Check an app against the spec and print the next fix, in your own names
treaty conformance Generate a conformance profile and run the spec's kit
treaty agents-md Write AGENTS.md from the registry
treaty generate-skills Write agent skill files from the command schemas
treaty-mcp Serve any treaty app's commands as MCP tools over stdio

Every app also gets built-ins: manifest, version, exec, doctor, status, cleanup, completion, and more.

Documentation

  • Tutorial: take a CLI, new or migrated from argparse, click, or typer, through every audit rule to a release
  • Reference: the full API, one section per concern
  • Design guide: naming, error context, exit codes, and when to split a command
  • Public API: the surface frozen at 1.0
  • Spec compliance: the requirement-by-requirement score
  • Changelog

Status

treaty is in release candidates for 1.0 and follows semantic versioning from 1.0; until then a release may break an app, and the changelog's "Breaking" sections say where. treaty 1.0 claims CLI Agent Spec Level 2 conformance.

Contributing

Issues and pull requests are welcome. See CONTRIBUTING.md for the development setup and how changes are released, and SECURITY.md to report a vulnerability. Everyone taking part follows the Code of Conduct.

License

MIT

Metadata

Release files for treaty 1.0.0rc39

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for treaty 1.0.0rc39
File Size Uploaded
treaty-1.0.0rc39.tar.gz 516.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for treaty 1.0.0rc39
File Interpreter ABI Platform
treaty-1.0.0rc39-py3-none-any.whl Python 3 none any Details

Total release size: 1.1 MB

Release files / treaty-1.0.0rc39.tar.gz

Download URL treaty-1.0.0rc39.tar.gz
Size 516.1 kB
Tags Source
SHA-256 checksum
How to use checksums
0d8830d75fdf8e09973fd16191fb134ad3880f7ead06cbbd4f23e147009891e6
BLAKE2b-256 checksum
How to use checksums
b779be87477ce344b4665213767b3545dcb154d69d85f1f392e42f9bd8691baf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release files / treaty-1.0.0rc39-py3-none-any.whl

Download URL treaty-1.0.0rc39-py3-none-any.whl
Size 566.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1efd7948327ad870c63e7e0411cbd025c258d52b44579e630fc9a044066e7b3d
BLAKE2b-256 checksum
How to use checksums
aef256ad55e3cfb3432346d3e364cbaeaca655bfb7a51451a1a9042ff2043ee5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.0rc39 This release

2 release files

0.1.0

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release 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