Planning-first, repo-aware AI coding orchestrator CLI
Project description
PLANTOD
Plan, Task, Orchestrate, Deliver — a planning-first, repo-aware AI coding orchestrator CLI.
PLANTOD turns a natural-language request ("add login feature") into a structured
workflow: plan → tasks → scoped execution → test → handoff → escalation → final review.
A strong model plans and reviews; a fast model executes small, scoped tasks. All
decisions and outputs are written to .plantod/ in the repo so the work is auditable.
Install
pip install plantod
# or, isolated global CLI:
pipx install plantod
Requires Python 3.11+. Then configure your providers with plantod login.
From source (development)
git clone https://github.com/wandi1209/plantod && cd plantod
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest
Providers
PLANTOD drives agentic coding CLIs as its backends — one per role. Install the CLI(s) for the providers you pick; each handles its own auth/model config.
| Provider | Binary | Invocation |
|---|---|---|
claude-code |
claude |
claude -p "<prompt>" |
codex |
codex |
codex exec "<prompt>" |
opencode |
opencode |
opencode run "<prompt>" (models: opencode/*) |
opencode-go |
opencode |
same binary, restricted to opencode-go/* models/endpoint |
plantod login
Set the provider + model for each role (planner / executor / reviewer):
plantod login # arrow-key wizard (provider + model per role)
plantod login --role executor --provider codex --model o4 # non-interactive, one role
plantod login --role planner --provider claude-code # model optional -> provider default
plantod login ... --project # save to THIS repo instead of global
The wizard is an inline arrow-key menu (↑/↓ + Enter, type to filter): pick a
provider, then a model. Where the provider CLI can list its models (e.g.
opencode models), PLANTOD fetches the live list so you never type an id by
hand — otherwise it offers a shortlist plus (default) / custom…. plantod mode
with no argument is selectable the same way.
Config precedence (low → high): defaults < global (~/.config/plantod/config.yaml) < project (.plantod/config.yaml).
login writes the global scope by default so settings carry across repos (NFR-02);
--project overrides for one repo. plantod init inherits the global defaults.
Run mode
plantod mode # show current mode
plantod mode auto # unattended: plan, execute, and review to done, no prompts
plantod mode approval # default: gate risky/wide-scope tasks for your approval
plantod mode auto --project # set for this repo only
- approval (default) — small, low-risk, testable tasks auto-run; anything risky or wide-scope waits for your yes/no.
- auto — every task runs unattended through execution and final review. The scope guard and escalation loop still apply, so out-of-scope edits are reverted and blocked tasks are re-planned automatically.
Prerequisites
- Install the CLI for each provider you configure (see table above); each tool manages its own authentication.
.envin the repo root is auto-loaded into the environment (see.env.example) for any provider that reads env vars.- A git repo is required for the scope guard — run
plantod initinside one.
Usage
plantod login # pick provider + model per role (see Providers)
plantod init # detect repo, scaffold .plantod/ (inherits global)
plantod plan "add login feature" # plan + break into tasks + run the default flow
plantod plan "..." --yes --review # approve gated tasks, run final review
plantod tasks # list tasks + status
plantod next # next runnable task
plantod run T001 # run a single task
plantod review R001 # final review for a requirement
plantod status # board summary
plantod resume # where the last session left off
plantod # interactive chat session (see below)
plantod usage # estimated token usage / cost
Interactive session
Run bare plantod for a Claude-Code-style chat: an inline REPL with scrollback,
arrow-key history, ctrl-r search, slash-command autocomplete, and multi-turn
memory — follow-ups keep context, so after "add login" you can say "now add
logout too" and the planner understands the thread.
plantod › add a health-check endpoint
plantod › now add a test for it # remembers the previous turn
plantod › /tasks # slash commands for everything
Slash commands: /help /login /mode /status /tasks /usage /review <RID> /resume /clear /quit.
First run offers to init for you and checks your provider CLIs are installed.
Conversation persists in .plantod/session.json (last 40 turns); /clear resets it.
Why planning-first saves tokens (cost model)
PLANTOD's economics come from routing: a strong, expensive model only plans and reviews (low token volume, high-value reasoning), while a cheap, fast model does the bulk editing (high token volume, low-level work). Most tokens in a coding task are spent reading context and writing edits — so you want those on the cheap model.
Illustrative example — one feature = 1 plan + 4 tasks + 1 review. Token splits below are assumptions to show the shape of the saving, not measurements:
| Stage | Tokens | All-in-one (strong model) | PLANTOD (routed) |
|---|---|---|---|
| Plan + review | ~30K | strong | strong |
| Task execution (×4) | ~200K | strong | cheap executor |
| Total billed as | ~230K | 230K × strong-rate | 30K × strong-rate + 200K × cheap-rate |
If the executor model is ~15× cheaper per token than the planner (a typical fast-model vs frontier-model gap), the routed run costs roughly:
strong_share = 30K
cheap_share = 200K / 15 ≈ 13.3K strong-equivalent tokens
routed_total ≈ 43.3K vs 230K → ~5× cheaper for this mix
The exact multiple depends on your models and the plan/execution token ratio — plug in current per-token prices for your planner and executor to get real numbers.
Honest caveat: the table above is an illustrative model, not a benchmark. PLANTOD does meter usage, but as an estimate — provider CLIs run headless and don't reliably report token counts, so figures are derived from text length (~4 chars/token). Good for relative comparison, not billing. The structural saving (route bulk edits to a cheap model, keep reasoning on a strong one) holds regardless.
Seeing your own numbers
Every run prints an estimated token line, and plantod usage shows the breakdown:
$ plantod usage
Estimated usage (heuristic)
Provider Calls Tokens in Tokens out
opencode 4 12,400 8,900
claude-code 2 3,100 1,200
Total ~ 25,600 tokens ~ $0.42
Add a prices table to your config to get the cost estimate (USD per 1M tokens,
[input, output] per provider):
prices:
claude-code: [15, 75]
opencode: [0.3, 0.9]
Beyond cost, the planning-first flow also reduces wasted tokens: scoped tasks and the scope guard stop the executor from sprawling across the repo and re-generating work, which is where "just let the big model code" runs burn tokens on churn.
How it works
- Adapters (
plantod/adapters/) make backends swappable:claude,opencode,mock. - State machine (
plantod/schemas.py) enforces legal task transitions (pending → ready → in_progress → testing → done → reviewed, plusblocked → needs_planner_review). - Approval gate (
plantod/orchestrator.py) auto-runs only low-risk, small-scope, testable tasks; everything else asks for approval. - Artifacts (
.plantod/):requirements/ plans/ tasks/ handoffs/ reviews/ logs/, markdown + YAML frontmatter;board.json/session.jsonfor state.
Configuration (.plantod/config.yaml)
| Key | Default | Meaning |
|---|---|---|
planner / executor / reviewer |
{provider, model} per role |
backend for each role — set via plantod login |
| default providers | claude-code / opencode / claude-code | planner / executor / reviewer |
mode |
approval | approval (gate risky) or auto (unattended to done) |
auto_run_small_tasks |
true | auto-run low-risk tasks (approval mode) |
require_approval_for_architecture |
true | gate high-risk changes |
test_before_done |
true | run tests before marking done |
enforce_scope |
true | revert executor edits outside files_allowed |
apply_requires_approval |
false | confirm the in-scope diff before keeping it |
exec_timeout_s / test_timeout_s |
900 / 600 | subprocess timeouts |
max_retries |
3 | retry transient backend failures / cap replans |
auto_replan_on_escalation |
true | planner advises + retries an escalated task |
Production notes
- Scope guard — after each executor run, any file changed outside the task's
files_allowed(or matchingfiles_forbidden) is reverted via git and the task is escalated. This is enforced, not just prompted. - State safety —
board.json/session.jsonand all artifacts are written atomically (temp file + rename). A per-project advisory lock (.plantod/.lock) stops concurrent runs from corrupting state. - Escalation loop — a blocked task goes
needs_planner_review; the planner produces guidance and the task retries with narrower scope, capped bymax_retries. - Resilience — backend calls retry with exponential backoff; subprocesses have timeouts; malformed model JSON is parsed defensively.
- Requires a git repo for the scope guard; run
plantod initinside one.
Development
pytest # state machine, artifacts, config, full mock-adapter flow
Layout: src/ package layout, entry point plantod.cli:app.
License
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 plantod-0.1.7.tar.gz.
File metadata
- Download URL: plantod-0.1.7.tar.gz
- Upload date:
- Size: 47.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a32cf890367deffa62c98c5fb17c7e39f665245c06a7fcaef790cbfc7e512db
|
|
| MD5 |
f486ceeba8267d9d4c443e2e69e1b4cd
|
|
| BLAKE2b-256 |
985ce8963c6420df2605b18f1af0fd417e476f8c388c2a3b3a85ee9b9e270451
|
Provenance
The following attestation bundles were made for plantod-0.1.7.tar.gz:
Publisher:
publish.yml on wandi1209/plantod
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
plantod-0.1.7.tar.gz -
Subject digest:
9a32cf890367deffa62c98c5fb17c7e39f665245c06a7fcaef790cbfc7e512db - Sigstore transparency entry: 2078163712
- Sigstore integration time:
-
Permalink:
wandi1209/plantod@06d7f2c4946cbb89147dd0808cf230aa93172d26 -
Branch / Tag:
refs/tags/v0.1.7 - Owner: https://github.com/wandi1209
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@06d7f2c4946cbb89147dd0808cf230aa93172d26 -
Trigger Event:
release
-
Statement type:
File details
Details for the file plantod-0.1.7-py3-none-any.whl.
File metadata
- Download URL: plantod-0.1.7-py3-none-any.whl
- Upload date:
- Size: 46.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2f57141d27f30956ef0ed5b4443902ffdf3141d253f4d474b81a4d61dc27f38d
|
|
| MD5 |
f69f892bb3815c914e49689b74fcb17d
|
|
| BLAKE2b-256 |
5f0fd09692c4d79547f34422e397535893667ab8621fe43f5a2ad6e96d68e2d9
|
Provenance
The following attestation bundles were made for plantod-0.1.7-py3-none-any.whl:
Publisher:
publish.yml on wandi1209/plantod
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
plantod-0.1.7-py3-none-any.whl -
Subject digest:
2f57141d27f30956ef0ed5b4443902ffdf3141d253f4d474b81a4d61dc27f38d - Sigstore transparency entry: 2078164357
- Sigstore integration time:
-
Permalink:
wandi1209/plantod@06d7f2c4946cbb89147dd0808cf230aa93172d26 -
Branch / Tag:
refs/tags/v0.1.7 - Owner: https://github.com/wandi1209
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@06d7f2c4946cbb89147dd0808cf230aa93172d26 -
Trigger Event:
release
-
Statement type: