Skip to main content

A bundled copier conductor skill — the bailiff fills in the paperwork; copier makes the copies.

Project description

bailiff

An agentic conductor for copier. The bailiff fills in the paperwork; copier makes the copies.

Status: active development. Design is captured in docs/decisions/; implementation is spec-driven (SpecKit).

What bailiff is

copier is a superb deterministic scaffolding engine: it renders templates from answers, pins templates to git refs, and reproduces a project byte-for-byte from a committed answers file. What copier has no concept of is the agentic layer — choosing which templates to apply, authoring the answers for you, and wiring up AI-agent tooling (APM packages, MCP servers, SpecKit, steering/ADR docs).

bailiff is exactly that layer, and nothing more:

  • an init-time agent that selects modules from a user-owned catalog and authors the per-module answer files (the inputs);
  • a thin orchestrator for multi-module enablement + dependency ordering;
  • the agentic-ecosystem wiring (APM / MCP / SpecKit / ADR) that has no off-the-shelf analog.

What bailiff is NOT

  • Not a scaffolder. copier renders; bailiff only decides and fills in.
  • Not in the reproduce path. Reproduce is agent-free: bailiff reproduce replays the committed answers at the recorded commit (recopy --vcs-ref=:current: --defaults --overwrite), never a bare recopy that would silently upgrade — run by a human or CI with no agent. Reproduce also works with copier alone (no bailiff): copier recopy --vcs-ref=:current: --defaults --overwrite per answers file.
  • Not a template hub. The catalog is user-owned; bailiff works against any copier templates you point it at. See 0002.

How it works (one line)

The agent (skill) inspects a copier template, collects answers from the user, records trust consent if the template executes code, and hands the frozen answers to the deterministic CLI — bailiff on PyPI (uvx bailiff) — which drives copier to render and initialize the project. Any template that takes actions (its _tasks / migrations / jinja extensions) runs only from a source the user has trusted — copier's settings.yml trust list, recorded by an explicit consent step, never a blanket unsafe=True. See 0003 and 0001.

Install

bailiff is distributed as an APM skill package — installable into any macOS/Linux/WSL project that uses Claude Code or Codex. There is no PyPI bailiff package; the skill bundles its own Python modules.

Install into a Claude Code project

# Add the bailiff marketplace (one-time per machine / project):
apm marketplace add bailiff-io/bailiff

# Install the bailiff skill:
apm install bailiff

# Check that runtime deps (copier, pyyaml, packaging, tomli-w) are present:
uvx bailiff doctor

Install into a Codex project

apm marketplace add --marketplace=codex bailiff-io/bailiff
apm install --marketplace=codex bailiff

Runtime dependencies

bailiff checks its deps at startup. If any are missing or version-incompatible (esp. copier>=9.16,<10), it prints an environment-aware install suggestion and exits cleanly — no traceback. Run bailiff.py doctor for an explicit check.

Quick install (choose one):

uv pip install copier pyyaml packaging tomli-w    # uv (recommended)
pip install copier pyyaml packaging tomli-w        # pip
brew install copier && pip install pyyaml packaging tomli-w   # macOS brew (copier only via brew)

Or use uvx bailiff <verb> — uvx resolves the published wheel and auto-provisions a hermetic ephemeral environment on every invocation.


Invocation

bailiff ships as a bundled script (not an installed CLI or PyPI package):

uvx bailiff discover <source> [--ref REF]   # inspect template (static, safe)
uvx bailiff trust add <prefix>              # record consent
uvx bailiff trust add --from-source <src>   # record org-level consent
uvx bailiff init --run-spec <file> [--check] # generate (or dry-run)
uvx bailiff reproduce [<dest>]              # faithful reproduce (primary)

# Copier-only fallback (no bailiff needed):
cd <project> && copier recopy --vcs-ref=:current: --defaults --overwrite

See specs/010-delivery-reshape/contracts/invocation.md for the full surface, exit codes, and the copier-only fallback.

Catalog

The catalog is user-owned configuration: a plain TOML file listing the source repos you want to scaffold from. bailiff depends on no first-party hub; you bring your own templates.

uvx bailiff catalog init                       # create the catalog file if absent
uvx bailiff catalog add gituser/gitrepo        # add a source (idempotent)
uvx bailiff catalog add gituser/mod@v2.1.0     # with a display-version override
uvx bailiff catalog remove gituser/gitrepo     # remove a source (idempotent)
uvx bailiff catalog list                       # show usable templates + flag unusable sources
uvx bailiff catalog list --json                # machine-readable listing
uvx bailiff catalog validate demo/my-template  # gate: exit 0 if valid, non-zero if unknown/ambiguous

Pass --catalog PATH between catalog and the subverb to target a non-default file (e.g. catalog --catalog PATH list); the default location follows the same platformdirs/BAILIFF_CATALOG_PATH resolution as trust.py. Templates are identified by a full-id <catalog>/<template> (where <catalog> is the pointer name, defaulting to a sanitized source basename). The listing is deterministic — same sources at the same pins produce identical output. A source that is unusable (no PEP 440 tag, bad copier.yml, unreachable) is reported per-source with a reason; the rest of the catalog still lists.

The skill (step 0 of skills/bailiff/SKILL.md) manages the catalog on the user's behalf: ensure → list → pick → validate → init.

The bailiff-io module catalog

The first-party module family (bailiff-mod-*) is authored in the bailiff-io/bailiff monorepo and published as a generated JSON index committed to the (public) monorepo and served directly via raw git at the stable URL:

https://raw.githubusercontent.com/bailiff-io/bailiff/main/catalog.json

The index is derived from released modules — a module appears only once it has a published vX.Y.Z tag on its split repo, so the catalog is empty until the first release fan-out runs. See docs/runbooks/fanout-release.md for the release/fan-out pipeline and the one-time maintainer setup it requires.

Multi-template

Select several templates from the catalog and bailiff applies them in dependency order — no manual sequencing. Each template declares its depends_on/run_after/ run_before edges in its copier.yml; bailiff builds the DAG, validates it (refuses cycles, dangling edges, and basename collisions before writing anything), and runs one copier copy per layer in topological order, threading earlier layers' answers into later ones.

Each layer commits its own .copier-answers.<basename>.yml. At reproduce, bailiff enumerates those files, fetches each template at its pinned _commit, re-reads the edges, and recomputes the same order — no frozen recipe file is committed to the project. Pinned commits → identical edges → identical order, so reproduce is deterministic and agent-free.

The copier-only-by-hand fallback (copier recopy … -a <each answers file> in recomputed order) works for any number of layers — nothing about the project requires bailiff to reproduce.

N=1 is the degenerate case: a single-template project runs the same path with a one-node DAG, no special-casing.

See skills/bailiff/SKILL.md (multi-template section) and specs/003-multi-template/contracts/ordering.md for the run-spec shape, edge semantics, and ordering algorithm.

Upgrade

Move a project from one template version to a newer one:

# Upgrade all layers to the latest PEP 440 tag (single-layer or multi-layer):
uvx bailiff update <project-dir>

# Target a specific version:
uvx bailiff update <project-dir> --vcs-ref v1.2.0

# Dry-run preview:
uvx bailiff update <project-dir> --pretend

Upgrade is the only bailiff path that advances a template version — reproduce always stays pinned. copier's 3-way merge handles local edits; bailiff supplies the version announcement, the cross-template ordering, and the conflict report.

  • Multi-layer: layers are upgraded in dependency order (DAG re-solved at target versions). New dependencies introduced in a newer template version are detected and refused until the user adds the missing layer.
  • Migrations (_migrations in copier.yml): version-crossing migration commands run automatically via copier, trust-gated identically to _tasks. The new format is enforced; the deprecated before/after dict form is refused at discovery.
  • Conflicts: if copier's 3-way merge leaves conflict markers, bailiff reports the conflicted files and exits 4. Resolve and re-run.

Exit codes: 0 ok, 1 error, 3 untrusted source, 4 merge conflicts.

Design decisions

Development

uv sync --dev
uv run pytest                         # run the hermetic test suite
uv run pytest -m network -v           # live smoke test (requires gh auth)
uv run ruff check src/ tests/ scripts/
uv run mypy
bash scripts/try-bailiff.sh             # interactive end-to-end walkthrough

License

Apache-2.0

Project details


Download files

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

Source Distribution

bailiff-0.2.0.tar.gz (1.9 MB view details)

Uploaded Source

Built Distribution

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

bailiff-0.2.0-py3-none-any.whl (53.2 kB view details)

Uploaded Python 3

File details

Details for the file bailiff-0.2.0.tar.gz.

File metadata

  • Download URL: bailiff-0.2.0.tar.gz
  • Upload date:
  • Size: 1.9 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for bailiff-0.2.0.tar.gz
Algorithm Hash digest
SHA256 c483a85cfa8ef9ffe3f7f763f681817edc517b44473333a02ea852d2e10f871c
MD5 7dfccbf1bbaea583858ad227b33dd23a
BLAKE2b-256 ee2347b4d45ef3a143ac7dfb84f844d766b857c604b90340870811d94e46d66f

See more details on using hashes here.

File details

Details for the file bailiff-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: bailiff-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 53.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for bailiff-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c00ae4756c8300fb3c7e3142b3236346cb34ebce1ab813b84a5ef43369105317
MD5 3aa275a80e928d4b82d04b0fcb0ac5f4
BLAKE2b-256 ac7c6f2b45440feba8a9e359961dbd23237777baf42aca9e24253439a00a9070

See more details on using hashes here.

Supported by

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