Model-agnostic decomposition coding harness that orchestrates agent providers (OpenCode, Claude Code) behind deterministic gates.
Project description
director
A model-agnostic decomposition coding harness — a thin orchestrator over agent providers (OpenCode, Claude Code, Codex, …).
director tests one hypothesis:
A strong planner model decomposes a coding task into small, atomic, well-specified units with acceptance tests written first. A cheaper executor model — local or low-cost cloud — implements each unit in an isolated, fresh context. Deterministic gates (user-defined commands; exit codes, never an LLM's opinion) decide what merges. This cuts token cost dramatically with minimal quality loss.
It is model-agnostic by construction: roles (planner, executor, reviewer, …)
bind to provider/model-ref strings in config. The provider is the tool director
drives (opencode, claude-code, or codex); the rest of the tier string is whatever that
tool expects. Switching the executor from a local 27B to a frontier model — or
anything in between — is a one-line config edit, never a code change.
Status: beta. Validated end-to-end (plan → run → bench) under local, cheap-cloud, and all-frontier executor tiers.
Install
director is a pure-standard-library Python CLI (no dependencies), so it installs anywhere Python 3.11+ runs:
uv tool install director-cli # recommended
# or
pipx install director-cli
# or
pip install director-cli
Prerequisites (providers)
director orchestrates other tools rather than replacing them, so it needs:
- Python ≥ 3.11
- git on
PATH(isolation is real git worktrees + branches) - OpenCode on
PATH, if you use anopencode/*tier - Claude Code (
claude) onPATH, if you use aclaude-code/*tier - Codex (
codex) onPATH, if you use acodex/*tier (e.g.codex/gpt-5-codex; runs via the OpenAI Codex CLI) - Pi (
pi) onPATH, if you use api/<provider>/<model>tier (install withnpm install -g --ignore-scripts @earendil-works/pi-coding-agent) - Provider auth: if you use OpenCode tiers, configure auth in OpenCode via
opencode auth; Claude Code tiers use the Claude Code CLI's own auth; Codex tiers use the Codex CLI's own auth; Pi uses its normal BYOK provider environment variables
director never manages model-provider keys itself — each tool handles its own credentials.
Pi tiers use the exact pi/<provider>/<model> form, such as
pi/anthropic/claude-sonnet-4-5, pi/openai/gpt-5.5, or pi/groq/<model>.
Director does not install Pi, store Pi credentials, discover Pi models, manage Pi
extensions, or resume Pi sessions; v1 discovery returns no models, so configure tiers
explicitly. Pricing can be added with [pricing."pi/anthropic/claude-sonnet-4-5"].
Manual Pi smoke test (requires a real provider key; not part of automated tests):
install Pi, export the applicable provider credential such as ANTHROPIC_API_KEY, set
executor = "pi/anthropic/claude-sonnet-4-5" in .director/config.toml, run a small
Director command, and inspect the attempt logs under .director/logs/.
Quickstart
cd your-repo
# 1. Create .director/config.toml interactively — director init asks which model to
# use per role and optionally configures gates for this repository.
director init
# (See director/config.example.toml for the full/advanced schema if you want to
# hand-tune beyond what init prompts for.)
$EDITOR .director/config.toml
# 2. Install director's role agents into .opencode/ (+ gitignore, starter opencode.json)
# — written only when the opencode provider is configured
director sync-agents
# 3. Plan: brainstorm → spec → test-gated task DAG (two approval gates)
director plan "Add a --json flag to the export command"
# …review .director/spec.md, then continue; review the plan, then continue:
director plan --continue # after approving the spec
director plan --continue # after approving the plan + failing tests
# 4. Run the DAG: isolated worktree per node, deterministic gates, auto-merge
director run
# 5. Inspect
director status
# …or watch it live in a browser (read-only dashboard, run from another terminal)
director ui --open
Unattended? Let the planner self-critique at each gate instead of pausing for you:
director plan "…" --auto # planner self-critiques at each gate
director plan "…" --auto --no-critique # gates auto-pass, fully hands-off
director run
Commands
| Command | What it does |
|---|---|
director plan "<task>" [--auto] [--no-critique] [--continue] |
Brainstorm → spec → test-gated task DAG, with two artifact-based approval gates. |
director run [--parallel N] [--max-attempts K] |
Execute the DAG: each node runs in an isolated git worktree behind its acceptance test; configured repository gates run after merge. Escalates a stuck node one tier up. |
director status |
Per-node progress, attempts, cost, and the executor-tier completion rate. |
director ui [--port 8642] [--host 127.0.0.1] [--open] |
Live read-only web dashboard: planning-stage progress (recon → spec → gates → ready, with recon/spec viewers), then the DAG animating as nodes run/pass/fail, with per-node detail, logs, and cost/metrics — all read from .director/. Localhost-only by default; --host 0.0.0.0 exposes unauthenticated read access (specs, logs) to your network. |
director auto "<task>" [--input FILE|-] [--open] [--hold] [--max-cost N] [--force] |
One-shot autonomous mode: plan with self-critiqued gates (no pauses) → run, serving the live dashboard from the same process. Task from a positional string, --input FILE, or --input - (stdin); resumes an interrupted job, erroring on a changed task unless --force replans. --open implies --hold (dashboard stays up after the run until Ctrl-C). |
director bench "<task>" --profiles a,b,c |
Run the same task (same frozen acceptance tests) across profile variants and diff cost / quality / wall-time. |
director init [--repo .] |
Interactively create config — asks which model to use per role and, for repo-local config, optionally asks for gate commands. |
director sync-agents |
(Re)install the role agents into <repo>/.opencode/ (plus gitignore, starter opencode.json) — only when an opencode/* tier is configured. |
director agent … |
Machine-local job supervisor: submit many director jobs (even against the same repo, concurrently), monitor/cancel/retry them, one dashboard for all of them, installable as a user service. See below. |
All state lives under .director/ (resumable, debuggable): plan.json, state.json,
costs.jsonl, metrics.jsonl, per-call logs/, and bench/.
The local agent (director agent)
director auto owns one job in one foreground process. The agent is a durable
local supervisor for many jobs at once — built for coding-agent orchestrators that
want to submit work, yield, and come back later:
director agent install [--port 8642] # systemd --user unit (Linux) / LaunchAgent (macOS)
director agent serve # …or run it in the foreground yourself
director agent submit --repo ~/code/proj --input task.md \
--request-id my-id-42 --label ticket=PROJ-7 --max-cost 5 --json
director agent list --state running # every command supports --json
director agent show <job-id> --json # progress, DAG counts, cost, next action
director agent wait <job-id> --timeout 3600 # exit 0 only if the job succeeded
director agent logs <job-id> --tail 50 [--follow]
director agent events <job-id> --after <cursor> # JSON Lines, monotonic cursor
director agent cancel <job-id> # kills the whole job process tree
director agent retry <job-id> # resume a failed/interrupted job
director agent prune # delete storage of terminal jobs (branches stay)
director agent status --json # daemon, service, capacity, health warnings
- Job-scoped storage. Everything an agent job generates lives under
~/.director/agent/jobs/<job-id>/(override the root withDIRECTOR_AGENT_HOME) — never inside the target repository. Repo-local.director/config.tomlis still read as configuration. - Concurrent jobs per repo. Submission captures the base commit; each job runs
in its own top-level git worktree on a unique
director/job-…branch. The submitted checkout (branch, index, uncommitted files) is never touched, and successful job branches are visible from the source repo. - Durable + recoverable. Runners heartbeat and write results atomically. On
restart the agent re-adopts live runners, reflects finished results, and resumes
interrupted jobs from their persisted state.
--request-idmakes submission retries idempotent. - One read-only dashboard.
http://127.0.0.1:8642/lists every job;/job/<id>/is the familiar per-run DAG view. Mutations require the bearer token in~/.director/agent/token, so the browser stays read-only. - Stable exit codes for scripting:
0ok,1waiton a job that didn't succeed,2usage,3daemon unavailable,4job not found,5conflict,6wait --timeoutelapsed. - Service environment. Services don't inherit your shell env; put
KEY=VALUElines (PATH, provider keys) in~/.director/agent/agent.env— parsed strictly, never shell-evaluated. Manual platform checks live indocs/agent-smoke-tests.md.
Configuration
director init interactively creates config — it asks which model to use for each
role and, for repo-local config, optionally collects deterministic gate commands.
A config contains roles → provider/model-ref strings, per-model pricing, and run
limits; repo-local config may also contain arbitrary user-defined gate commands.
Director has no fixed gate vocabulary. For the full/advanced schema, see
the complete, commented director/config.example.toml:
it shows how to bind the executor tier to a local
model (≈ $0 implementation), a low-cost cloud model (zero local infra), or a frontier
model (the expensive baseline). See director/README.md for the
full architecture (gates, two-stage review, red-green hardening, metrics).
Two-level configuration
director supports two config levels that are combined at runtime:
- User-level defaults live at
~/.director/config.toml. These apply across all repositories on your machine. - Repo-local overrides live at
<repo>/.director/config.toml. These apply only to the current repository and take precedence over user-level settings.
The repo file deep-merges over the user file: nested tables merge recursively, while
scalars and arrays replace wholesale. [gates] is repo-local only: gate commands in
the user-level file are ignored. The repo table is optional and is the complete
authoritative gate set; omitting it configures zero repository gates. For example, a
user file might supply several roles under [tiers]:
# ~/.director/config.toml
[tiers]
planner = "claude-code/opus"
executor = "opencode/openrouter/deepseek/deepseek-v4-pro"
reviewer = "claude-code/sonnet"
And a repo file overrides just one role:
# <repo>/.director/config.toml
[tiers]
executor = "opencode/lmstudio/qwen3.6-27b-mtp"
The resulting effective config uses the repo-local executor but falls back to the user
values for planner and reviewer (each role binds to a single "provider/model-ref"
string — the repo [tiers] overrides only the roles it names).
director init target selection
director init auto-detects its write target: inside a git repo it writes <repo>/.director/config.toml; outside any git repo it writes ~/.director/config.toml. It offers optional gate configuration only for the repo-local target. You can override this behavior with flags (mutually exclusive):
--userforces the user-level target (~/.director/config.toml).--localforces the repo-level target (<repo>/.director/config.toml).--repo PATHstill sets the target directory explicitly.
Comparing setups with bench
director bench plans a task once, then runs the same frozen acceptance tests under
several config variants to compare cost/quality/wall-time. Create the variants as
.director/profiles/<name>.toml (copy your config.toml and change the executor tier in
each), then:
director bench "<task>" --profiles all-frontier,cheap-cloud,local-first
For agents & scripting
director is built to be driven by humans or by another agent:
- Deterministic, non-interactive:
--auto --no-critiqueruns plan→run with no prompts; every merge decision is an exit code, never a chat. - Machine-readable output:
.director/metrics.jsonl(per-node + per-run records) and.director/bench/summary.jsonare stable JSON for downstream tooling. - Resumable: re-running
plan --continue/runpicks up from.director/state.
Development
uv sync # create the dev environment
uv run python -m unittest discover -s tests -q # tests
uvx ruff check . && uvx ruff format --check . # lint + format
uv build # build the wheel/sdist
Releases are automated with python-semantic-release
on merge to main (conventional-commit messages drive the version bump, changelog, and
PyPI publish via Trusted Publishing). See CONTRIBUTING.md.
License
MIT © Christopher Manzi. The ported TDD/review discipline is adapted from obra/superpowers (MIT).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file director_cli-0.11.0.tar.gz.
File metadata
- Download URL: director_cli-0.11.0.tar.gz
- Upload date:
- Size: 119.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
83a55f3a4852aef09999c37595dc977bee9a8ad47268265834d3f261c34e43f3
|
|
| MD5 |
3fbd710a15b9415fb0aef099d5c5f3a7
|
|
| BLAKE2b-256 |
9cd0f2e2b8507fab832d596f6840980d290b7dbfe9a2926c979d7b3cca970e06
|
Provenance
The following attestation bundles were made for director_cli-0.11.0.tar.gz:
Publisher:
release.yml on manziman/director
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
director_cli-0.11.0.tar.gz -
Subject digest:
83a55f3a4852aef09999c37595dc977bee9a8ad47268265834d3f261c34e43f3 - Sigstore transparency entry: 2207255015
- Sigstore integration time:
-
Permalink:
manziman/director@e61d7a33b3f9b247c7e150643718ddd243318048 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/manziman
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e61d7a33b3f9b247c7e150643718ddd243318048 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file director_cli-0.11.0-py3-none-any.whl.
File metadata
- Download URL: director_cli-0.11.0-py3-none-any.whl
- Upload date:
- Size: 133.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8c3db69b26b8006ddfc3d186125514cf46abf113a00db4b11b4f287239b54a2e
|
|
| MD5 |
9846927198bed2c8833ff78f8c83630a
|
|
| BLAKE2b-256 |
e5d441c6d59b296502be568b3c4dca6ae7d57028f38138f3d0d2c191ce7c02a0
|
Provenance
The following attestation bundles were made for director_cli-0.11.0-py3-none-any.whl:
Publisher:
release.yml on manziman/director
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
director_cli-0.11.0-py3-none-any.whl -
Subject digest:
8c3db69b26b8006ddfc3d186125514cf46abf113a00db4b11b4f287239b54a2e - Sigstore transparency entry: 2207255089
- Sigstore integration time:
-
Permalink:
manziman/director@e61d7a33b3f9b247c7e150643718ddd243318048 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/manziman
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e61d7a33b3f9b247c7e150643718ddd243318048 -
Trigger Event:
workflow_dispatch
-
Statement type: