planr 1.1.19 → 1.2.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/docs/ARCHITECTURE.md +18 -10
- package/docs/CLI_REFERENCE.md +19 -6
- package/docs/CURSOR.md +91 -7
- package/docs/EXAMPLE_WEBAPP.md +138 -0
- package/docs/GOALS.md +19 -8
- package/docs/INSTALL.md +3 -1
- package/docs/MCP_CONTRACT.md +6 -4
- package/docs/MODEL_ROUTING.md +190 -0
- package/docs/RELEASE.md +16 -0
- package/docs/SKILLS.md +7 -9
- package/docs/TROUBLESHOOTING.md +4 -0
- package/docs/fixtures/mcp-contract.json +12 -1
- package/npm/native/darwin-arm64/planr +0 -0
- package/npm/native/darwin-x86_64/planr +0 -0
- package/npm/native/linux-arm64/planr +0 -0
- package/npm/native/linux-x86_64/planr +0 -0
- package/package.json +1 -1
- package/plugins/planr/.claude-plugin/plugin.json +1 -1
- package/plugins/planr/.codex-plugin/plugin.json +1 -1
- package/plugins/planr/agents/planr-reviewer.md +5 -2
- package/plugins/planr/skills/planr-goal/SKILL.md +2 -0
- package/plugins/planr/skills/planr-loop/SKILL.md +4 -2
- package/plugins/planr/skills/planr-loop/agents/planr-reviewer.md +17 -0
- package/plugins/planr/skills/planr-loop/agents/planr-reviewer.toml +5 -2
- package/plugins/planr/skills/planr-loop/agents/planr-worker.md +13 -0
- package/plugins/planr/skills/planr-plan/SKILL.md +14 -0
- package/plugins/planr/skills/planr-review/SKILL.md +1 -1
- package/plugins/planr/skills/planr-task-graph/SKILL.md +2 -0
- package/plugins/planr/skills/planr-work/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -82,13 +82,15 @@ Restart Claude Code afterwards. Skills are namespaced (`/planr:planr`, `/planr:p
|
|
|
82
82
|
<details>
|
|
83
83
|
<summary><strong>Cursor</strong></summary>
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
One command installs everything the plugin would carry:
|
|
86
86
|
|
|
87
87
|
```bash
|
|
88
|
-
planr install cursor
|
|
89
|
-
planr
|
|
88
|
+
planr install cursor # writes .cursor/mcp.json, .cursor/agents/, and .cursor/skills/
|
|
89
|
+
planr install cursor --no-mcp # skills and subagents only, no MCP config
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
The dry-run also prints a one-click `cursor://` deeplink for user-level MCP install. Marketplace listing is pending review. Multitasking with Cursor subagents: [Cursor guide](docs/CURSOR.md).
|
|
93
|
+
|
|
92
94
|
</details>
|
|
93
95
|
|
|
94
96
|
<a id="install-plugin-opencode"></a>
|
|
@@ -134,6 +136,7 @@ Mid-project work (a new feature, refactor, or fix on an existing project) works
|
|
|
134
136
|
- [Install](docs/INSTALL.md)
|
|
135
137
|
- [Skills](docs/SKILLS.md)
|
|
136
138
|
- [Long-Running Goals](docs/GOALS.md)
|
|
139
|
+
- [Model Routing](docs/MODEL_ROUTING.md) · [Worked Example: Web App](docs/EXAMPLE_WEBAPP.md)
|
|
137
140
|
- [CLI Reference](docs/CLI_REFERENCE.md)
|
|
138
141
|
- [MCP Guide](docs/MCP_GUIDE.md)
|
|
139
142
|
- [Codex](docs/CODEX.md) · [Claude Code](docs/CLAUDE_CODE.md) · [Cursor](docs/CURSOR.md)
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Planr Architecture
|
|
2
2
|
|
|
3
|
-
Planr V1 is a single Rust binary with explicit module ownership. The crate stays small enough that a Cargo workspace would add more process overhead than value today, and there is only one deployable: the `planr` CLI. The source tree is split by ownership boundary inside that crate instead of using a premature workspace.
|
|
3
|
+
Planr V1 is a single Rust binary with explicit module ownership. The crate stays small enough that a Cargo workspace would add more process overhead than value today, and there is only one deployable: the `planr` CLI. The source tree is split by ownership boundary inside that crate instead of using a premature workspace. Shared mutations that must behave identically across CLI, MCP, and HTTP live in one place (`src/app/application.rs`) so the three surfaces cannot drift.
|
|
4
4
|
|
|
5
5
|
## Repository Layout
|
|
6
6
|
|
|
@@ -32,12 +32,18 @@ Planr V1 is a single Rust binary with explicit module ownership. The crate stays
|
|
|
32
32
|
- `src/app/surfaces.rs`: non-CLI runtime surfaces. Owns trace, scrub, artifact, event, debug, export, and import command handlers.
|
|
33
33
|
- `src/app/inspection.rs`: local inspection helpers. Owns debug bundles, context/link snapshots, pick context, secret scans, export value assembly, run recording, search results, and Planr-directory import parsing.
|
|
34
34
|
- `src/app/audit.rs`: goal contract audit boundary. Owns the clause-by-clause `plan audit` verdict (items settled, reviews complete, approvals clear, verification logged) and its human rendering.
|
|
35
|
-
- `src/
|
|
35
|
+
- `src/app/application.rs`: shared surface-mutation boundary. Owns the approval request/approve/deny, context, log, artifact, and close mutations reused verbatim by CLI, MCP, and HTTP handlers so the three surfaces cannot drift.
|
|
36
|
+
- `src/app/repository/`: focused data-access submodules (`item.rs`, `plan.rs`, `project.rs`, `link.rs`, `context.rs`, `evidence.rs`, `search.rs`) split out of `src/app/repository.rs` by entity ownership.
|
|
37
|
+
- `src/model.rs`: JSON-facing data transfer types and typed vocabulary. Owns serializable Planr DTOs plus the `ItemStatus`, `WorkType`, `LinkKind`, and `ApprovalStatus` enums with their parsing and display behavior, used by CLI JSON, MCP, HTTP, storage rows, and tests.
|
|
36
38
|
- `src/storage/mod.rs`: SQLite connection boundary. Owns default database path, connection setup, pragma configuration, and storage submodule exports.
|
|
37
39
|
- `src/storage/schema.rs`: SQLite schema boundary. Owns DDL, additive schema upgrade helpers, and schema version recording.
|
|
38
40
|
- `src/storage/rows.rs`: SQLite row mapping boundary. Owns row-to-DTO and row-to-JSON mapping functions.
|
|
39
|
-
- `src/planpack.rs`: Markdown package generation. Owns project context templates
|
|
40
|
-
- `src/
|
|
41
|
+
- `src/planpack.rs`: Markdown package generation and parsing. Owns project context templates, product/build plan templates, plan metadata parsing, hashes, search body extraction, and task extraction.
|
|
42
|
+
- `src/agents.rs`: agent profile registry core. Owns `.planr/agents.toml` parsing, registry validation warnings, and the pure advisory `resolve_route` precedence logic (override > work_type > plan > default); no storage or host concerns.
|
|
43
|
+
- `src/app/agents.rs`: routing application boundary. Owns the `agents` and `item route` command handlers, the shared `*_value` JSON shapes reused by MCP, per-item route facts assembly, and registry-aware role content selection for installs.
|
|
44
|
+
- `src/app/agents_init.rs`: registry bootstrap boundary. Owns `planr agents init` — the static cost-tiering scaffold and, per the agent-pool plan, the flag-spec builder and interactive wizard.
|
|
45
|
+
- `src/integrations.rs`: agent-client integration descriptor boundary. Owns Codex, Claude Code, Cursor, MCP install metadata, MCP tool schemas, MCP resources, and MCP text response wrapping.
|
|
46
|
+
- `src/rolefiles.rs`: host role file boundary. Owns the shipped static subagent role files and Cursor skills payloads, plus the pure renderers that re-pin role files from registry profiles (Codex TOML with strict field names, Claude/Cursor markdown frontmatter) under the generated-from header.
|
|
41
47
|
- `src/util.rs`: small CLI-boundary utilities. Owns ids, timestamps, path helpers, output formatting, and safe file writes.
|
|
42
48
|
|
|
43
49
|
## Boundary Rules
|
|
@@ -45,10 +51,12 @@ Planr V1 is a single Rust binary with explicit module ownership. The crate stays
|
|
|
45
51
|
- Command parsing belongs in `src/cli.rs`; process startup belongs in `src/main.rs`; command execution belongs under `src/app/`.
|
|
46
52
|
- `src/main.rs` must stay small enough to be only a composition root. It must not own product use cases.
|
|
47
53
|
- `src/app/mod.rs` must stay small enough to wire runtime state and dispatch. It must not absorb app submodule behavior.
|
|
48
|
-
- SQLite schema belongs in `src/storage/schema.rs`; row mapping belongs in `src/storage/rows.rs`; app data access helpers belong in `src/app/repository.rs
|
|
54
|
+
- SQLite schema belongs in `src/storage/schema.rs`; row mapping belongs in `src/storage/rows.rs`; app data access helpers belong in `src/app/repository.rs` and its submodules.
|
|
55
|
+
- Mutations shared by more than one surface (CLI, MCP, HTTP) belong in `src/app/application.rs`; surface handlers must call the shared helper instead of repeating SQL.
|
|
49
56
|
- Markdown templates belong in `planpack.rs`; command handlers should request generated file sets instead of embedding large template bodies.
|
|
50
|
-
- Agent install metadata
|
|
51
|
-
- DTO changes belong in `model.rs`; JSON response shapes should reuse those DTOs before adding ad hoc maps.
|
|
57
|
+
- Agent install metadata and MCP schema descriptors belong in `src/integrations.rs`; client-specific strings should not drift across command handlers and docs.
|
|
58
|
+
- DTO and vocabulary-enum changes belong in `src/model.rs`; JSON response shapes should reuse those DTOs before adding ad hoc maps.
|
|
59
|
+
- Item status, work type, link kind, and approval status values are typed enums; new states must be added to the enum, not smuggled in as strings.
|
|
52
60
|
- Utility code must stay narrow. If a helper starts owning product behavior, move it to the owning module instead of growing `util.rs`.
|
|
53
61
|
- Do not add catch-all `common`, `shared`, or broad utility modules. New modules must name a durable ownership boundary.
|
|
54
62
|
|
|
@@ -58,10 +66,10 @@ Planr remains a single crate for V1 because:
|
|
|
58
66
|
|
|
59
67
|
- there is one deployable binary and no separate service or reusable library boundary;
|
|
60
68
|
- the current behavior contract is tighter when CLI, MCP, HTTP, storage, and docs ship together;
|
|
61
|
-
- module-level ownership
|
|
69
|
+
- module-level ownership gives the needed architecture separation without duplicating Cargo settings or release packaging;
|
|
62
70
|
- npm, release, and external consumer tests assume one native binary named `planr`.
|
|
63
71
|
|
|
64
|
-
A Cargo workspace should be introduced only after a concrete deployable, reuse, compilation, or team ownership boundary exists and package/release scripts are updated in the same change.
|
|
72
|
+
A Cargo workspace was tried and reverted: it produced anemic crates whose only job was being a layer, plus re-export shims in the binary. A workspace should be introduced only after a concrete deployable, reuse, compilation, or team ownership boundary exists and package/release scripts are updated in the same change.
|
|
65
73
|
|
|
66
74
|
## Future Extract Points
|
|
67
75
|
|
|
@@ -72,4 +80,4 @@ If Planr grows past the V1 binary shape, the first clean extraction path is:
|
|
|
72
80
|
- `planr-cli`: `src/cli.rs`, human output, and install helpers.
|
|
73
81
|
- `planr-server`: `src/app/http.rs`, `src/app/mcp.rs`, and runtime server adapters.
|
|
74
82
|
|
|
75
|
-
Do not extract those crates until a real reuse, compile-time, or ownership boundary exists.
|
|
83
|
+
Do not extract those crates until a real reuse, compile-time, or ownership boundary exists. Every extraction must leave a real owner with runtime code, tests, and one-way dependencies.
|
package/docs/CLI_REFERENCE.md
CHANGED
|
@@ -26,6 +26,8 @@ planr item breakdown <item-id> --into "A" --into "B" [--into "C"]
|
|
|
26
26
|
planr item insert "title" --description "..." --after <item-id> [--before <item-id>] [--preview|--confirm]
|
|
27
27
|
planr item amend <item-id> --note "..." [--tag amendment]
|
|
28
28
|
planr item replan <parent-id> --into "A, B, C" [--preview|--confirm]
|
|
29
|
+
planr item update <item-id> [--title "..."] [--description "..."] [--work-type <type>]
|
|
30
|
+
planr item route <item-id> [--set <profile>|--clear]
|
|
29
31
|
planr link add <from-item> <to-item> --type blocks
|
|
30
32
|
planr pick
|
|
31
33
|
planr pick release <item-id> [--force]
|
|
@@ -44,7 +46,7 @@ planr artifact show <artifact-id>
|
|
|
44
46
|
planr artifact list [--item <item-id>]
|
|
45
47
|
planr event list [--item <item-id>] [--limit 50]
|
|
46
48
|
planr debug bundle [--item <item-id>] --preview
|
|
47
|
-
planr log add --item <item-id> --summary "..." [--files a --files b | --files a,b] [--cmd "..."] [--kind completion|progress|verification]
|
|
49
|
+
planr log add --item <item-id> --summary "..." [--files a --files b | --files a,b] [--cmd "..."] [--kind completion|progress|verification] [--profile <id>]
|
|
48
50
|
planr review request <item-id>
|
|
49
51
|
planr review annotate <item-id> --message "..." [--severity info|warning|blocking] [--file path] [--line N] [--author "..."]
|
|
50
52
|
planr review ingest <item-id> (--from feedback.json|--stdin)
|
|
@@ -52,13 +54,18 @@ planr review artifact <review-item-id> [--out .planr/reviews/custom.review.md]
|
|
|
52
54
|
planr review evidence <item-id> [--pr-url https://...]
|
|
53
55
|
planr review close <review-item-id> --verdict complete|not-complete|unclear [--close-target]
|
|
54
56
|
planr close [item-id] --summary "..." [--next]
|
|
55
|
-
planr done [item-id] --summary "..." [--files a --files b] [--cmd "..."] [--tests "..."] [--review] [--next]
|
|
57
|
+
planr done [item-id] --summary "..." [--files a --files b] [--cmd "..."] [--tests "..."] [--review] [--next] [--profile <id>]
|
|
56
58
|
planr context add "text" [--item <item-id>] [--tag discovery]
|
|
57
59
|
planr context list [--item <item-id>] [--tag <tag>]
|
|
58
60
|
planr search "query"
|
|
61
|
+
planr agents init [--force]
|
|
62
|
+
planr agents init --profile|--skill|--route|--default-route|--interactive
|
|
63
|
+
planr agents list [--json]
|
|
64
|
+
planr agents check
|
|
59
65
|
planr doctor [--client codex|claude|cursor|all]
|
|
60
|
-
planr install codex|claude|cursor [--dry-run]
|
|
66
|
+
planr install codex|claude|cursor [--dry-run] [--no-mcp] [--force]
|
|
61
67
|
planr prompt cli|mcp|http [--client codex|claude|cursor|all]
|
|
68
|
+
planr prompt routing [--client codex|claude|cursor|all]
|
|
62
69
|
planr mcp
|
|
63
70
|
planr serve --port 7526
|
|
64
71
|
planr import <file> [--preview] [--confirm]
|
|
@@ -112,7 +119,9 @@ With `--json`, responses follow one convention so agents never guess where data
|
|
|
112
119
|
|
|
113
120
|
`review close` writes `.planr/reviews/<review-item-id>.review.md` and registers it as a review artifact. A `not-complete` or `unclear` verdict creates fix and follow-up review work; the follow-up review gates the same target item, so the chain keeps working with `--close-target`. With `--close-target` (complete verdicts only) the reviewed item is closed in the same command, provided it already has a completion log; the artifact is rendered after the target transition, so it snapshots the final target status. `--close-target` is also available through MCP `planr_review_close` and HTTP `POST /v1/reviews/{id}/close` (`"close_target": true`). `review close` responses include the same `remaining` progress snapshot as `done` and `close`. `--reviewer <id>` records the checker's identity on the review log, artifact, and event (defaults to the worker id), keeping maker and checker distinguishable in the audit trail. Closing an already-settled review fails with error code `already_closed` instead of silently duplicating evidence logs. The maker/checker split is derived, not declared: `review_mode` compares the closing reviewer identity against the target item's lease holder and reports `single_agent`, `independent`, or `unattributed` in the response, review log, artifact, and event — no ceremony note required. An `unattributed` close explains itself in the output: it means the target has no recorded lease (work was never picked or its lease was released), not that the reviewer's identity was missing.
|
|
114
121
|
|
|
115
|
-
`trace item` on a review item inlines the target item and its evidence logs under `target`, so a reviewer's first trace already contains what is being audited. The human (non-JSON) mode renders the packet: status, owner, links, logs.
|
|
122
|
+
`trace item` on a review item inlines the target item and its evidence logs under `target`, so a reviewer's first trace already contains what is being audited. The human (non-JSON) mode renders the packet: status, owner, links, logs. When the item has a declared route or a run recorded a profile, the trace gains a `routing` section — the declared route next to every run's actual client/profile with an advisory `mismatch` marker and a mismatch count; items without either keep the exact pre-routing trace shape.
|
|
123
|
+
|
|
124
|
+
`doctor` also reports the agent registry in all states without ever failing: absent (informational hint), degraded (parse error with line context), or loaded with profile/route counts, validation warnings, and per-artifact drift — a rendered role file whose content no longer matches what the current registry would render is flagged `drifted` with a `planr install <client> --force` hint, while files without the generated-from header are the user's (`manual`) and never flagged.
|
|
116
125
|
|
|
117
126
|
`done` is the compound worker command: it writes a completion log, then requests a review (`--review`) or closes the item, and optionally picks the next ready item (`--next`). Without `--next`, the response's `next` field carries the exact follow-up command instead (`planr pick --work-type review --json` after a review request, `planr pick --json` after a close), so every settlement output ends in an action. It chains the same single-owner operations as `log add`, `review request`, `close`, and `pick` — identical evidence, fewer commands. `done`, `close`, and `review close` report what the settlement `unlocked` (id, title, work type of every item that became ready), `done` and `close` echo the item's `post_condition` at completion time, and a `hint` asks for `--cmd`/`--tests` evidence when downstream items depend on an item that closed without any. `done`, `close`, `review close`, and the pick packet include a `remaining` progress snapshot so loop agents can evaluate their stop condition without an extra `map status` call. `remaining.counts` always carries the full status vocabulary (`pending`, `ready`, `picked`, `running`, `in_review`, `blocked`, `failed`, `cancelled`, `closed`, `closed_partial`) with explicit zeros — a missing count never has to be inferred.
|
|
118
127
|
|
|
@@ -120,6 +129,10 @@ With `--json`, responses follow one convention so agents never guess where data
|
|
|
120
129
|
|
|
121
130
|
`pick --work-type <type>` restricts the lease to one work type, so checker agents pick only `review` items and makers only work items. `pick --plan <plan-id>` restricts the lease to one plan's items, so plan-scoped goal runs never pick work outside their contract even when other plans share the board; an unknown plan id is an error, never a silent unscoped pick. Both filters are available on MCP `planr_pick_item` and HTTP `POST /v1/pick` (`work_type`, `plan`). A null pick is never blind: `{"item": null}` carries a `reason` (`empty_map`, `all_settled`, `nothing_ready`, `ready_items_excluded_by_filter`) and the `remaining` snapshot. When ready work exists but the active filters rejected all of it, `excluded` lists each ready item with the cause (`work_type` mismatch, outside the `--plan` scope, or just requested by this worker) and `repair` carries the exact pick commands that would lease that work — across CLI, MCP, and HTTP. On a review item, `close_effect` previews the full `--close-target` cascade: it lists the work that closing the review (and with it the reviewed item) would unlock.
|
|
122
131
|
|
|
132
|
+
`agents init` writes a commented starter registry with the cost-tiering defaults (premium driver that keeps the verdicts, standard steerable implementer, budget helper for token-hungry side work, plus code/fix/review/default routes) and names the follow-up commands; an existing `.planr/agents.toml` is never overwritten without `--force`. Individual pools generate from repeatable spec flags — `--profile designer=claude-code/opus@high#premium` declares a profile, `--skill designer=frontend-design` pairs it with a skill, `--route frontend=designer,driver` routes a use-case work type with fallbacks, `--default-route` catches the rest; validation is fail-closed (unknown profile references and malformed specs error, naming the grammar, before anything is written), and consistent specs generate a zero-warning registry. `--interactive` walks the same questions as guided prompts (requires a terminal; conflicts with spec flags) and can render the host role files at the end. `agents list` and `agents check` inspect the agent profile registry (`.planr/agents.toml`): named profiles (host client, model, effort, cost tier) and advisory routes from work selectors to profiles. When a registry resolves for a picked item, the pick packet carries a `routing` block — profile, client, model, effort, cost tier, fallback chain, and the matched selector — resolved with precedence per-item override > `work_type` > `plan` > default route. `item route` shows an item's resolved route and its source (`override` or `policy`), pins a registry profile with `--set <profile>` (emits `route_overridden`), and unpins with `--clear` (emits `route_override_cleared`); a pinned profile that later disappears from the registry falls back to policy routing with a repair hint. The same JSON shapes are exposed over MCP: `planr_agents_list` and `planr_item_route` read, `planr_item_route_set` and `planr_item_route_clear` mutate. Routing is advisory: Planr never dispatches models, a missing registry just omits the block, a malformed one degrades picking to no-routing (only `agents check` fails, with the parser's line context), and validation warnings (unknown profile references, duplicate selectors, review work on a budget tier, secret-like values) never affect exit codes. When a registry is present, `install codex|claude|cursor` renders the subagent role files with model pins from it — the `work_type=code` route pins the worker, the `work_type=review` route pins the reviewer, and a role only pins profiles whose `client` matches the install target (a Cursor-profile review route never writes a Cursor model into a Codex TOML). Rendered files carry a `# generated from .planr/agents.toml` header; without a registry (or when no matching route resolves) the shipped static role files are written byte-identically, and existing files are never overwritten without `install --force`. Full guide: [Model Routing](MODEL_ROUTING.md).
|
|
133
|
+
|
|
134
|
+
`log add` and `done` accept `--profile <id>` — the registry profile the run actually executed on (`PLANR_PROFILE` env is the fallback, so rendered role files can export it). The profile is stored on the recorded run; when it differs from the item's declared route, Planr emits an advisory `route_mismatch_observed` event (declared, actual, run id) visible in `event list --item`. Runs without a profile, logs without commands/tests, and projects without a registry produce no comparison and no event, and mismatches never block logging, review, or closure. The MCP `planr_log_add` tool and `POST .../log` accept the same optional `profile`.
|
|
135
|
+
|
|
123
136
|
`artifact add` infers the mime type from the file extension when `--path` is given without `--mime` (PNG screenshots land as `image/png`, not `text/plain`); inline `--content` defaults to `text/plain`. The same inference applies on MCP `planr_artifact_add` and HTTP `POST /v1/artifacts`.
|
|
124
137
|
|
|
125
138
|
`review evidence` reports Git worktree status scoped to files named by item logs or artifacts. Dirty files without item provenance are listed as unrelated and are not treated as agent-owned evidence. `--pr-url` records an item-scoped PR reference before returning the evidence package.
|
|
@@ -128,6 +141,6 @@ With `--json`, responses follow one convention so agents never guess where data
|
|
|
128
141
|
|
|
129
142
|
`serve` exposes the local review workspace at `/review` and its JSON projection at `/v1/review-workspace`.
|
|
130
143
|
|
|
131
|
-
`prompt` prints ready-to-use agent instructions without editing global config. Use `prompt cli` for shell agents, `prompt mcp` for MCP setup text, and `prompt http` for localhost automation/review workspace usage.
|
|
144
|
+
`prompt` prints ready-to-use agent instructions without editing global config. Use `prompt cli` for shell agents, `prompt mcp` for MCP setup text, and `prompt http` for localhost automation/review workspace usage. `prompt routing` emits a paste-ready model-prioritization block for the driver session: the route table from `.planr/agents.toml` (every route, profile, and fallback), per-host dispatch guidance with the known traps (Codex `fork_turns: "none"` and session-restart requirement, the Claude `CLAUDE_CODE_SUBAGENT_MODEL` env preemption, Cursor's silent plan/policy/Max-Mode overrides), and process-dispatch snippets (`codex exec`, `pi`, `opencode run`) for hosts without role files; `--json` carries the same content structured, and a missing or unreadable registry still prints the host guidance with a pointer instead of failing.
|
|
132
145
|
|
|
133
|
-
`export` writes a reusable Planr JSON package with package requirements metadata, graph state, contexts, optional logs, optional plan file snapshots,
|
|
146
|
+
`export` writes a reusable Planr JSON package with package requirements metadata, graph state, contexts, optional logs, optional plan file snapshots, review artifact snapshots, and — when present — the agent registry (`.planr/agents.toml`) as a raw snapshot. `import` previews JSON packages by default and mutates only with `--confirm`; the preview names the registry with its exact action (`create`, `identical`, or `conflict`), and an existing registry that differs is never overwritten — the conflict is reported with a hint to remove the local file first. Packages without a registry import unchanged.
|
package/docs/CURSOR.md
CHANGED
|
@@ -1,18 +1,102 @@
|
|
|
1
1
|
# Cursor Integration
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Cursor is a first-class Planr client. One command wires a project completely — MCP tools, the worker/reviewer subagents, and the full skill set:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
planr install cursor
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
This writes only repository-local files (never global Cursor config):
|
|
4
10
|
|
|
5
|
-
|
|
11
|
+
| Path | Purpose |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| `.cursor/mcp.json` | project-scoped stdio MCP server (`planr mcp`) with the 44 Planr tools |
|
|
14
|
+
| `.cursor/agents/planr-worker.md` | worker subagent: implements exactly one picked item, then requests review and stops |
|
|
15
|
+
| `.cursor/agents/planr-reviewer.md` | reviewer subagent: audits evidence and closes the review with a verdict |
|
|
16
|
+
| `.cursor/skills/planr*/SKILL.md` | all ten Planr skills (`planr`, `planr-goal`, `planr-loop`, stage and capability skills) |
|
|
6
17
|
|
|
7
|
-
|
|
18
|
+
Re-running the command refreshes `.cursor/mcp.json` but never overwrites edited agent or skill files. `planr project init "My Product" --client cursor` (or `--client all`) provisions the same subagent roles at init time. Verify with `planr doctor --client cursor`.
|
|
19
|
+
|
|
20
|
+
## Skills And Agents Only (No MCP)
|
|
21
|
+
|
|
22
|
+
The skills are CLI-first — they drive the `planr` binary directly through the terminal — so MCP is optional. For a plugin-style install that writes only the subagents and skills:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
planr install cursor --no-mcp
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
This writes `.cursor/agents/` and `.cursor/skills/` and nothing else: no `.cursor/mcp.json`, no deeplink. Everything below (subagent dispatch, parallel work, goal loops) works identically in this mode because the skills call `planr pick`, `planr done`, `planr review ...` as shell commands. `--no-mcp --dry-run` lists the files that would be written. The flag works for `claude` and `codex` too (roles only; their skills ship via each host's plugin system).
|
|
29
|
+
|
|
30
|
+
## One-Click MCP Install
|
|
31
|
+
|
|
32
|
+
For user-level setup (all projects, no repo files), the dry-run prints a Cursor deeplink:
|
|
8
33
|
|
|
9
34
|
```bash
|
|
10
35
|
planr install cursor --dry-run
|
|
11
|
-
planr install cursor
|
|
12
|
-
planr doctor --client cursor
|
|
13
36
|
```
|
|
14
37
|
|
|
15
|
-
|
|
38
|
+
```text
|
|
39
|
+
cursor://anysphere.cursor-deeplink/mcp/install?name=planr&config=eyJhcmdzIjpbIm1jcCJdLCJjb21tYW5kIjoicGxhbnIifQ==
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Open the link (or paste it into a browser) and Cursor prompts to install the server. The embedded config is `{"command": "planr", "args": ["mcp"]}` with no `--db` path on purpose: Cursor starts the server in the workspace directory, so every project resolves its own `.planr/planr.sqlite`.
|
|
43
|
+
|
|
44
|
+
## Plugin
|
|
45
|
+
|
|
46
|
+
The repository carries a Cursor plugin manifest (`.cursor-plugin/plugin.json`) bundling the skills and both subagents. Marketplace listing is pending review; until it is listed:
|
|
47
|
+
|
|
48
|
+
- `planr install cursor` (above) provides the identical component set per project, or
|
|
49
|
+
- install the plugin locally: copy the repository to `~/.cursor/plugins/local/planr` and reload Cursor.
|
|
50
|
+
|
|
51
|
+
## Multitasking With Cursor's Built-In Features
|
|
52
|
+
|
|
53
|
+
Cursor's subagents, background execution, and parallel agents map directly onto Planr's maker/checker loop. The Planr map is the shared state, so concurrent Cursor agents coordinate through `planr pick` leases instead of stepping on each other.
|
|
54
|
+
|
|
55
|
+
### Subagent dispatch (maker/checker)
|
|
56
|
+
|
|
57
|
+
The installed subagents are dispatched from any Agent chat with `/name` or by mention:
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
/planr-worker implement item <item-id>
|
|
61
|
+
/planr-reviewer review item <item-id>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The worker preloads the `planr-work` protocol (implement one item, log evidence, request review, stop); the reviewer preloads `planr-review` (audit the diff, rerun logged commands, close with a verdict). Because each subagent runs in its own context window, the driving chat stays focused on `plan audit`, dispatch decisions, and synthesis — the same division of labor as Codex `/goal` and Claude Code (see [Long-Running Goals](GOALS.md)).
|
|
65
|
+
|
|
66
|
+
Cost tiering: the worker file ships with `model: inherit`; edit its frontmatter to pin a cheaper Cursor model id when dispatch cost matters. Leave the reviewer on `inherit` — make workers cheap, not the verdict.
|
|
67
|
+
|
|
68
|
+
### Parallel and background work
|
|
69
|
+
|
|
70
|
+
- **Parallel subagents:** ask the driver to dispatch several workers at once ("implement items A, B, and C in parallel with planr-worker subagents"). Each worker calls `planr pick` (or is handed its item id); the map's single-owner lease guarantees no two agents hold the same item, and `planr recover sweep` reclaims work from any that die.
|
|
71
|
+
- **Background subagents:** long items can run as background subagents; evidence logging doubles as the liveness heartbeat, so `planr pick stale` shows honestly stuck work.
|
|
72
|
+
- **Cloud agents (`/in-cloud`):** cloud VMs do not share your local `.planr` database or local MCP config. Give cloud agents the CLI workflow instead (`planr prompt cli --client cursor`) and let them commit evidence through normal Planr CLI calls in their own checkout.
|
|
73
|
+
|
|
74
|
+
### Parallel agents in worktrees
|
|
75
|
+
|
|
76
|
+
Cursor's parallel agents run in separate git worktrees, and each worktree would otherwise get its own copy of `.planr/planr.sqlite` — forking the map. Keep one authoritative map by pointing every worktree at a shared absolute database path:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"mcpServers": {
|
|
81
|
+
"planr": {"command": "planr", "args": ["--db", "/absolute/path/to/main-checkout/.planr/planr.sqlite", "mcp"]}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`planr install cursor` already writes the absolute path of the current checkout, so worktrees created from an installed project inherit the right value — verify with `planr project show --json` from each worktree. One picked item per agent instance stays the rule (`plugins/planr/skills/planr-loop/SKILL.md`, Hard Rules).
|
|
87
|
+
|
|
88
|
+
### Goal loops
|
|
89
|
+
|
|
90
|
+
Cursor has no `/goal` primitive; the human (or an automation) is the re-dispatcher:
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
Use $planr-goal: <your goal>
|
|
94
|
+
Use $planr-loop on plan <plan-id>. The loop contract is stored in planr context (tag: goal-contract).
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`$planr-loop` iterates inside the session, dispatching `/planr-worker` and `/planr-reviewer` per item. If the session ends before `planr plan audit <plan-id>` holds, send the same starter line in a fresh session — the map, the stored contract, and open reviews carry the run, not chat history.
|
|
98
|
+
|
|
99
|
+
## HTTP And Review Feedback
|
|
16
100
|
|
|
17
101
|
Planr V1 defaults to MCP stdio. Local HTTP/SSE is available through:
|
|
18
102
|
|
|
@@ -27,4 +111,4 @@ planr review annotate <item-id> --message "Cursor review note" --severity warnin
|
|
|
27
111
|
planr review ingest <item-id> --from .planr/tmp/cursor-review.json
|
|
28
112
|
```
|
|
29
113
|
|
|
30
|
-
|
|
114
|
+
Review ingestion does not auto-close or auto-approve work.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Worked Example: Routing a Small Web App
|
|
2
|
+
|
|
3
|
+
A complete, replayable walkthrough of [model routing](MODEL_ROUTING.md) on a real shape of work: a todo web app with a frontend and a backend, implemented by different models. Every output below is from an actual run — copy the commands into an empty directory and you get the same results.
|
|
4
|
+
|
|
5
|
+
The pool we want:
|
|
6
|
+
|
|
7
|
+
| Use case | Who runs it | Paired skill |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| planning + review | Fable on Cursor (the driver session) | — |
|
|
10
|
+
| frontend, design | Opus on Claude Code | `frontend-design` |
|
|
11
|
+
| backend | GPT-5.5 on Codex | `planr-work` |
|
|
12
|
+
|
|
13
|
+
## Step 1: Project and pool — two commands, once
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
planr project init "Todo Webapp"
|
|
17
|
+
|
|
18
|
+
planr agents init \
|
|
19
|
+
--profile driver=cursor/fable-5@high#premium \
|
|
20
|
+
--profile frontender=claude-code/opus@high#premium \
|
|
21
|
+
--profile backender=codex/gpt-5.5@xhigh#standard \
|
|
22
|
+
--skill frontender=frontend-design \
|
|
23
|
+
--skill backender=planr-work \
|
|
24
|
+
--route frontend=frontender,driver \
|
|
25
|
+
--route backend=backender,driver \
|
|
26
|
+
--route review=driver \
|
|
27
|
+
--default-route backender,driver
|
|
28
|
+
|
|
29
|
+
planr agents check
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
wrote .planr/agents.toml (generated from the flag specs)
|
|
34
|
+
agent registry check passed
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Prefer questions over flags? `planr agents init --interactive` walks the same inputs as guided prompts and can render the host role files at the end. Either way, validation is fail-closed: a typo in a profile reference errors before anything is written.
|
|
38
|
+
|
|
39
|
+
Note what the routes encode: review work is pinned to the premium driver (verdicts stay strong), everything else runs on the cheaper tier its use case calls for, and every route falls back to the driver when the primary hits a rate limit.
|
|
40
|
+
|
|
41
|
+
## Step 2: Items carry the use case as their work type
|
|
42
|
+
|
|
43
|
+
Work types are free-form, so the use case is just `--work-type`:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
planr item create "API: task CRUD endpoints" \
|
|
47
|
+
--description "Express routes GET/POST/PATCH/DELETE /api/tasks with sqlite store" \
|
|
48
|
+
--work-type backend
|
|
49
|
+
|
|
50
|
+
planr item create "UI: task list and add form" \
|
|
51
|
+
--description "React list view + optimistic add form against /api/tasks" \
|
|
52
|
+
--work-type frontend
|
|
53
|
+
|
|
54
|
+
planr item create "UI: design pass on empty and error states" \
|
|
55
|
+
--description "Empty state, loading skeleton, error toast" \
|
|
56
|
+
--work-type frontend
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Step 3: The pick decides who works
|
|
60
|
+
|
|
61
|
+
Same board, two different answers. The backend pull:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
planr pick --work-type backend --json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
"routing": {
|
|
69
|
+
"profile": "backender",
|
|
70
|
+
"client": "codex",
|
|
71
|
+
"model": "gpt-5.5",
|
|
72
|
+
"effort": "xhigh",
|
|
73
|
+
"cost_tier": "standard",
|
|
74
|
+
"skill": "planr-work",
|
|
75
|
+
"fallbacks": ["driver"],
|
|
76
|
+
"matched_selector": "work_type=backend"
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
And the frontend pull:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
planr pick --work-type frontend --json
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
"routing": {
|
|
88
|
+
"profile": "frontender",
|
|
89
|
+
"client": "claude-code",
|
|
90
|
+
"model": "opus",
|
|
91
|
+
"skill": "frontend-design",
|
|
92
|
+
"fallbacks": ["driver"],
|
|
93
|
+
"matched_selector": "work_type=frontend"
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The driver session dispatches from the packet alone: the backend item goes to Codex (`codex exec --model gpt-5.5 -c model_reasoning_effort="xhigh" ...` — `planr prompt routing` prints this snippet pre-filled), the frontend item to a Claude Code subagent dispatched with its paired skill (`Use $frontend-design on item <id>`). Two workers can pull in parallel — each `pick --work-type <use-case>` takes its own lane, and the pick lease keeps one owner per item.
|
|
98
|
+
|
|
99
|
+
## Step 4: Workers close with evidence and their profile
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
planr done <item-id> \
|
|
103
|
+
--summary "CRUD endpoints implemented; 8 supertest cases green" \
|
|
104
|
+
--files server/routes/tasks.ts \
|
|
105
|
+
--tests "vitest --run: 8 passed" \
|
|
106
|
+
--profile backender
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`--profile` (or the `PLANR_PROFILE` env var) records which registry profile the run actually executed on. It is part of the evidence, not a formality — the next step is why.
|
|
110
|
+
|
|
111
|
+
## Step 5: The trace proves the plan was followed
|
|
112
|
+
|
|
113
|
+
Every host has a silent override path (env clamps, admin policies, fork semantics — see the [host matrix](MODEL_ROUTING.md#host-matrix)), so a pin alone is not proof. In this run, the backend item was closed on the declared profile and the frontend item deliberately on the wrong one:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
planr trace item <backend-item>
|
|
117
|
+
planr trace item <frontend-item>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
routing declared: backender (work_type=backend)
|
|
122
|
+
run run-5552fad6 profile backender
|
|
123
|
+
|
|
124
|
+
routing declared: frontender (work_type=frontend)
|
|
125
|
+
run run-bc0994cb profile driver (differs from declared route; advisory)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The mismatch also lands as a `route_mismatch_observed` event (`planr event list`). Advisory by design: nothing blocked, but the drift is on record instead of invisible.
|
|
129
|
+
|
|
130
|
+
## The whole model in one line each
|
|
131
|
+
|
|
132
|
+
1. **Declare** the pool once: `planr agents init` (flags, wizard, or the plain scaffold).
|
|
133
|
+
2. **Tag** items with their use case: `--work-type frontend|backend|...` — free-form.
|
|
134
|
+
3. **Pick** routes: every packet carries profile, model, skill, and fallbacks.
|
|
135
|
+
4. **Deliver** with `--profile` as part of the evidence.
|
|
136
|
+
5. **Trace** proves declared vs. actual — silent overrides get caught.
|
|
137
|
+
|
|
138
|
+
One-off exceptions never need a registry edit: `planr item route <id> --set driver` pins a single gnarly item to the premium tier and `--clear` restores policy. Details and failure behavior: [Model Routing](MODEL_ROUTING.md); the cost logic behind the tiers: [GOALS.md Cost Tiering](GOALS.md#cost-tiering).
|
package/docs/GOALS.md
CHANGED
|
@@ -115,16 +115,26 @@ Same shape via the plugin (`/plugin install planr@planr`), which registers the `
|
|
|
115
115
|
|
|
116
116
|
`/loop` works for fixed-cadence runs instead of goal-conditioned ones. The registered worker subagent pins a cheaper model tier; see [Cost Tiering](#cost-tiering).
|
|
117
117
|
|
|
118
|
-
### Cursor
|
|
118
|
+
### Cursor
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
No `/goal` primitive, but full subagent support. Install once:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
planr install cursor # writes .cursor/mcp.json, .cursor/agents/planr-worker.md + planr-reviewer.md, and the skills
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Then run the identical protocol with the human (or an automation) as the re-dispatcher:
|
|
121
127
|
|
|
122
128
|
```text
|
|
123
129
|
Use $planr-goal: <your goal>
|
|
124
130
|
Use $planr-loop on plan <plan-id>. The loop contract is stored in planr context (tag: goal-contract).
|
|
125
131
|
```
|
|
126
132
|
|
|
127
|
-
`$planr-loop` iterates within the session under its own budget. If the session ends before the contract holds, dispatch the same line again — recovery is identical to the `/goal` case.
|
|
133
|
+
`$planr-loop` iterates within the session under its own budget, dispatching `/planr-worker` and `/planr-reviewer` per item — the provisioned subagents preload the work and review protocols, so dispatches stay one line. If the session ends before the contract holds, dispatch the same line again — recovery is identical to the `/goal` case. Parallelism, background subagents, and worktree caveats: [Cursor](CURSOR.md).
|
|
134
|
+
|
|
135
|
+
### Hosts without a loop primitive
|
|
136
|
+
|
|
137
|
+
Identical protocol; the human (or a background agent) is the re-dispatcher, and worker/reviewer run as separate sequential dispatches when the host has no subagents.
|
|
128
138
|
|
|
129
139
|
### Plain MCP clients
|
|
130
140
|
|
|
@@ -144,16 +154,17 @@ Where each host configures the worker tier (the shipped role files carry these d
|
|
|
144
154
|
| --- | --- | --- | --- |
|
|
145
155
|
| Codex | session default (e.g. `gpt-5.5` at `high`) | `model = "gpt-5.5"`, `model_reasoning_effort = "medium"` | `.codex/agents/planr-worker.toml` |
|
|
146
156
|
| Claude Code | session model (e.g. `fable` at `high` via `/model` + `/effort`) | `model: opus`, `effort: medium` | `planr-worker.md` frontmatter |
|
|
147
|
-
| Cursor | chat model of the driving session |
|
|
157
|
+
| Cursor | chat model of the driving session | `model: inherit` by default; pin a cheaper Cursor model id | `.cursor/agents/planr-worker.md` frontmatter |
|
|
148
158
|
|
|
149
159
|
The defaults use aliases and generic names so they track model generations; pin a full model id (e.g. `claude-opus-4-8`) only if you need determinism, and use `model: sonnet` as the budget alternative. The role files are user-owned copies — `planr project init` provisions them once and never overwrites local edits — so changing the tier is editing one line.
|
|
150
160
|
|
|
151
|
-
|
|
161
|
+
To declare the tiering itself — which model handles which work type, with fallbacks for rate limits — use the agent profile registry (`.planr/agents.toml`): the routing recommendation then travels inside every `planr pick --json` packet, so the driver dispatches the right tier without consulting this table mid-run. `planr agents init` writes a working starter registry with these defaults; see [Model Routing](MODEL_ROUTING.md). The role files above still carry the host-native pins (`planr install <client> --force` re-renders them from the registry); the registry is the declared source of truth for the routing decision.
|
|
152
162
|
|
|
153
|
-
|
|
154
|
-
- **Codex:** some versions ignore custom agent files on spawn ([openai/codex#26868](https://github.com/openai/codex/issues/26868)) — the child then inherits the parent model. Spawn `planr_worker` on a trivial item and confirm the child metadata shows the pinned model and effort with a non-null `agent_path`.
|
|
163
|
+
Traps to verify once per setup — every one of them is silent (the run still works, just at driver prices), which is why the smoke test is worth the two minutes:
|
|
155
164
|
|
|
156
|
-
|
|
165
|
+
- **Claude Code:** the `CLAUDE_CODE_SUBAGENT_MODEL` environment variable clamps every subagent's `model:` frontmatter and per-invocation model with no signal in the `tool_result` ([anthropics/claude-code#57718](https://github.com/anthropics/claude-code/issues/57718)); since v2.1.196, setting it to `inherit` behaves like unset, so that is the safe pin for "driver decides". Org-managed `availableModels` allowlists are equally quiet: a frontmatter model outside the allowlist falls back to inherit without warning. Make sure the env var is unset (or `inherit`), then dispatch the worker on a trivial item and confirm the subagent's messages in the session log (`~/.claude/projects/<project>/*.jsonl`) carry the worker model, not the driver's.
|
|
166
|
+
- **Codex:** the spawn regressions that made children ignore custom agent files ([openai/codex#26868](https://github.com/openai/codex/issues/26868), [#26363](https://github.com/openai/codex/issues/26363)) are fixed in v0.138+, but two traps remain. First, `fork_turns = "all"` (the multi-agent v2 default) intentionally drops the child's `agent_type` and `model` so the fork replays the parent exactly — dispatch that should honor the worker pin needs `fork_turns = "none"` or a partial fork. Second, the role registry is loaded once at session start ([#26408](https://github.com/openai/codex/issues/26408)): after editing or re-rendering `.codex/agents/*.toml`, restart the session, or the child spawns from the stale definitions. Spawn `planr_worker` on a trivial item and confirm the child metadata shows the pinned model and effort with a non-null `agent_path`.
|
|
167
|
+
- **Cursor:** `model: <id>` in `.cursor/agents/planr-worker.md` frontmatter can be overridden without an error by team-admin model policies, plan availability (a model your plan lacks resolves to a fallback), and Max-Mode-only models. On legacy request-based plans, subagents are forced onto Composer regardless of the pin. Dispatch the worker once and check which model the subagent transcript reports before trusting the pin.
|
|
157
168
|
|
|
158
169
|
## Coming From Other Goal Tools
|
|
159
170
|
|
package/docs/INSTALL.md
CHANGED
|
@@ -72,7 +72,9 @@ planr prompt cli --client codex
|
|
|
72
72
|
planr prompt http
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
`planr install claude` writes a project `.mcp.json`; `planr install cursor` writes `.cursor/mcp.json
|
|
75
|
+
`planr install claude` writes a project `.mcp.json`; `planr install cursor` writes `.cursor/mcp.json` plus the Planr skills under `.cursor/skills/` and prints a one-click user-level MCP deeplink; `planr install codex` writes a project MCP snippet. All install commands also provision the `planr-worker` and `planr-reviewer` subagent role files (`.codex/agents/`, `.claude/agents/`, `.cursor/agents/`) without overwriting existing edits; `planr project init --client <client|all>` does the same at init time. Dry-runs print the exact config and scope notes first.
|
|
76
|
+
|
|
77
|
+
Skills-and-agents-only setups (no MCP) use `--no-mcp`: `planr install cursor --no-mcp` writes the subagents and skills but no MCP config — the skills drive the `planr` CLI directly, so nothing is lost except MCP tool access.
|
|
76
78
|
|
|
77
79
|
Runtime surfaces:
|
|
78
80
|
|
package/docs/MCP_CONTRACT.md
CHANGED
|
@@ -63,8 +63,10 @@ HTTP mirrors the same rule: `GET /v1/reviews/:id/artifact` is read-only; `POST /
|
|
|
63
63
|
|
|
64
64
|
`planr install <client> --dry-run` prints project-scoped configuration for Codex, Claude Code, and Cursor. Non-dry install writes only repository-local files:
|
|
65
65
|
|
|
66
|
-
- Codex: `.planr/integrations/codex-mcp.toml`
|
|
67
|
-
- Claude Code: `.mcp.json`
|
|
68
|
-
- Cursor: `.cursor/mcp.json`
|
|
66
|
+
- Codex: `.planr/integrations/codex-mcp.toml` plus `.codex/agents/` roles
|
|
67
|
+
- Claude Code: `.mcp.json` plus `.claude/agents/` roles
|
|
68
|
+
- Cursor: `.cursor/mcp.json` plus `.cursor/agents/` roles and `.cursor/skills/` skill copies
|
|
69
69
|
|
|
70
|
-
Planr does not edit global client configuration without a separate explicit operator action.
|
|
70
|
+
The Cursor dry-run additionally prints a `cursor://anysphere.cursor-deeplink/mcp/install` link whose embedded config (`planr mcp`, no `--db`) is safe at user scope because each workspace resolves its own database. Planr does not edit global client configuration without a separate explicit operator action; the deeplink requires the operator to click it and confirm inside Cursor.
|
|
71
|
+
|
|
72
|
+
`planr install <client> --no-mcp` is the plugin-style variant: it writes the subagent roles (and, for Cursor, the skills) but no MCP configuration at all, for setups that use skills and agents over the CLI only.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Model Routing
|
|
2
|
+
|
|
3
|
+
Declare which agent and model each kind of work should run on — once, in one file — and Planr hands the recommendation to whoever picks the work.
|
|
4
|
+
|
|
5
|
+
The pattern this makes declarative: strongest model plans and judges, a cheap fast steerable model implements, token-hungry side work (browser verification, codebase analysis) goes to budget profiles. Without a registry that knowledge lives in CLAUDE.md prose, Codex agent TOMLs, and Cursor frontmatter — three dialects that drift. With a registry it travels inside every pick packet.
|
|
6
|
+
|
|
7
|
+
Routing is **advisory by design**: Planr never calls model providers and never blocks a pick because a profile is unavailable. Your host (Codex, Claude Code, Cursor, any MCP client) stays the dispatch authority.
|
|
8
|
+
|
|
9
|
+
Want the whole flow on a concrete project first? [Worked Example: Routing a Small Web App](EXAMPLE_WEBAPP.md) walks a frontend/backend todo app from pool declaration to the audit trail, with real outputs.
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
One command writes a working starter registry — the cost-tiering defaults with a premium driver, a standard implementer, and a budget helper, commented so the tiers explain themselves:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
planr agents init # writes .planr/agents.toml; never overwrites without --force
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or declare `.planr/agents.toml` by hand:
|
|
20
|
+
|
|
21
|
+
```toml
|
|
22
|
+
[profiles.fable-driver]
|
|
23
|
+
client = "cursor"
|
|
24
|
+
model = "fable-5"
|
|
25
|
+
effort = "high"
|
|
26
|
+
cost_tier = "premium"
|
|
27
|
+
capabilities = ["orchestration", "review", "planning"]
|
|
28
|
+
notes = "Planner/architect and judge. Verdicts stay on this tier."
|
|
29
|
+
|
|
30
|
+
[profiles.gpt55-coder]
|
|
31
|
+
client = "codex"
|
|
32
|
+
model = "gpt-5.5"
|
|
33
|
+
effort = "xhigh"
|
|
34
|
+
cost_tier = "standard"
|
|
35
|
+
capabilities = ["code", "steerable"]
|
|
36
|
+
notes = "Primary implementer: strong, fast, cheap on subscription."
|
|
37
|
+
|
|
38
|
+
[[routes]]
|
|
39
|
+
match = { work_type = "code" }
|
|
40
|
+
profile = "gpt55-coder"
|
|
41
|
+
fallbacks = ["fable-driver"]
|
|
42
|
+
|
|
43
|
+
[[routes]]
|
|
44
|
+
match = { work_type = "review" }
|
|
45
|
+
profile = "fable-driver"
|
|
46
|
+
|
|
47
|
+
[route_default]
|
|
48
|
+
profile = "gpt55-coder"
|
|
49
|
+
fallbacks = ["fable-driver"]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Validate and inspect:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
planr agents check # non-zero exit only on parse failure; warnings pass
|
|
56
|
+
planr agents list # resolved profiles, routes, and warnings
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
From then on, every pick carries the recommendation:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
planr pick --json
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
"routing": {
|
|
67
|
+
"profile": "gpt55-coder",
|
|
68
|
+
"client": "codex",
|
|
69
|
+
"model": "gpt-5.5",
|
|
70
|
+
"effort": "xhigh",
|
|
71
|
+
"cost_tier": "standard",
|
|
72
|
+
"fallbacks": ["fable-driver"],
|
|
73
|
+
"matched_selector": "work_type=code"
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
A driver session dispatches the right worker from the packet alone — and when the primary hits a rate limit, the fallback order is already in hand. No mid-run config edits.
|
|
78
|
+
|
|
79
|
+
## The Registry File
|
|
80
|
+
|
|
81
|
+
`.planr/agents.toml` has three parts:
|
|
82
|
+
|
|
83
|
+
- `[profiles.<id>]` — a named agent setting. `client` (which host dispatches it: `codex`, `claude-code`, `cursor`, `generic-mcp`) and `model` are required; `effort`, `cost_tier` (`premium` | `standard` | `budget`), `capabilities`, and `notes` are optional. Model ids and aliases pass through verbatim — Planr does not validate them against provider catalogs, so new models need no Planr release.
|
|
84
|
+
- `[[routes]]` — `match` selects work (`work_type = "code"` or `plan = "pln-1234abcd"`), `profile` names the primary, `fallbacks` the ordered alternatives.
|
|
85
|
+
- `[route_default]` — catches everything no route matched.
|
|
86
|
+
|
|
87
|
+
Resolution precedence per item: **per-item override > `work_type` route > `plan` route > default**. Within a level, the first declared route wins. If a chain's primary profile id is unknown, the first known fallback is promoted; a chain with no known profiles falls through to the next precedence level, so a typo never swallows lower routes. `matched_selector` in the output tells you which rule fired (`override`, `work_type=<v>`, `plan=<v>`, or `default`).
|
|
88
|
+
|
|
89
|
+
## Per-Item Overrides
|
|
90
|
+
|
|
91
|
+
When one item needs a different setting than its policy route — a gnarly refactor that deserves the premium tier, a bulk doc pass that can run on budget — pin it:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
planr item route <item-id> # resolved route + source: override or policy
|
|
95
|
+
planr item route <item-id> --set fable-driver # pin; validates the profile id, emits route_overridden
|
|
96
|
+
planr item route <item-id> --clear # unpin; policy applies again, emits route_override_cleared
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The pin beats every policy route and shows up in the pick packet with `"matched_selector": "override"`. Overrides are repair-friendly: `--set` rejects a profile id the registry does not declare (when the registry is missing or malformed it warns and stores anyway, so offline edits stay possible), and a pin whose profile is later deleted from the registry is never an error — policy routing takes over and `item route` prints a repair hint. Both mutations are recorded as graph events, so `planr event list --item <id>` shows who re-routed what, when.
|
|
100
|
+
|
|
101
|
+
Tier the roles, not just the models: workers run safely on cheaper tiers because the pick packet bounds their scope, while review verdicts should stay on the strongest tier — `agents check` warns when review work routes to a `budget` profile. Background: [Cost Tiering](GOALS.md#cost-tiering).
|
|
102
|
+
|
|
103
|
+
## Host-Native Rendering
|
|
104
|
+
|
|
105
|
+
Routes only matter if the host actually dispatches the declared model, so `planr install codex|claude|cursor` closes the gap: when a registry is present, the provisioned subagent role files are rendered with pins taken from it instead of the shipped static defaults. The `work_type=code` route pins the worker role, the `work_type=review` route pins the reviewer role, and each render uses the host's exact vocabulary — Codex TOML gets `model` and `model_reasoning_effort` (with `developer_instructions` always present, since Codex silently ignores a role file without it), Claude frontmatter gets `model:` and `effort:`, Cursor frontmatter gets `model:` only.
|
|
106
|
+
|
|
107
|
+
Two safety rules keep this predictable:
|
|
108
|
+
|
|
109
|
+
- **Client matching**: a role file only pins profiles whose `client` matches the install target, scanning the route's fallback chain for the first match. A review route pointing at a Cursor profile never writes a Cursor model id into a Codex TOML — that role keeps its static default instead.
|
|
110
|
+
- **Provision-once**: existing files are never overwritten. After editing the registry, re-render explicitly with `planr install <client> --force`. Rendered files start with a `# generated from .planr/agents.toml` header so you (and future audit tooling) can tell them from hand-maintained ones.
|
|
111
|
+
|
|
112
|
+
Without a registry, installs write the static role files byte-identically to previous releases.
|
|
113
|
+
|
|
114
|
+
## Prompt Routing
|
|
115
|
+
|
|
116
|
+
`planr prompt routing [--client codex|claude|cursor|all]` prints a paste-ready block for the driver session: the prioritization table (every route, profile, and fallback in precedence order), per-host dispatch guidance including the traps that silently defeat pins (Codex requires `fork_turns: "none"` and a session restart after re-rendering; the `CLAUDE_CODE_SUBAGENT_MODEL` env var preempts Claude frontmatter; Cursor plan mode, admin policy, and Max Mode override silently), and process-dispatch snippets (`codex exec`, `pi`, `opencode run`) for hosts without role files, pre-filled from the `work_type=code` route. `--json` carries the same content structured.
|
|
117
|
+
|
|
118
|
+
## Run Audit
|
|
119
|
+
|
|
120
|
+
Every host has a silent override path — the `CLAUDE_CODE_SUBAGENT_MODEL` env var, Cursor plan/admin/Max-Mode policy, Codex full-history forks, org allowlists — so a pin alone is not proof. The audit loop closes this: workers report the profile they actually ran on via `planr log add`/`planr done --profile <id>` (or the `PLANR_PROFILE` env var, which rendered role files can export), the profile lands on the recorded run, and when it differs from the item's declared route Planr emits an advisory `route_mismatch_observed` event with the declared and actual ids.
|
|
121
|
+
|
|
122
|
+
- `planr trace item <id>` shows the declared route next to every run's actual client/profile with a `mismatch` marker.
|
|
123
|
+
- Runs also record the host they observably executed under (`observed_client`, detected from environment variables the hosts set themselves — no flags); a run whose host differs from the declared route's client emits an advisory `client_mismatch_observed` event, which catches exactly the deviation profile self-report cannot: a different host standing in for the declared client, even when the model matched.
|
|
124
|
+
- `planr doctor` reports the registry state (absent, degraded with parse context, loaded with counts and warnings) and flags rendered role files that drifted from the current registry (`planr install <client> --force` re-renders).
|
|
125
|
+
- `planr export`/`import` carry the registry with the package, preview-first; an existing registry at the destination is never silently overwritten.
|
|
126
|
+
|
|
127
|
+
Everything here is advisory (ADR-001): mismatches never fail logging, reviews, or closes. No profile reported, no run recorded, or no registry means no comparison and no event.
|
|
128
|
+
|
|
129
|
+
## Failure Behavior
|
|
130
|
+
|
|
131
|
+
- **No registry file**: nothing changes. Pick packets simply have no `routing` key.
|
|
132
|
+
- **Malformed registry**: `planr agents check` fails with the parser's line context; everything else (`pick`, `map`, `install`) keeps working with routing omitted — installs fall back to the static role files.
|
|
133
|
+
- **Warnings** (unknown profile references, empty or duplicate selectors, budget-tier review routes, secret-like values) never block anything; `agents check` lists them and still exits zero.
|
|
134
|
+
- Never put credentials in the registry — it holds configuration strings only, and secret-like values are flagged.
|
|
135
|
+
|
|
136
|
+
## Use-Case Pools
|
|
137
|
+
|
|
138
|
+
Work types are free-form, and that makes them the use-case dimension: beyond the built-in vocabulary (`code`, `fix`, `review`, `docs`, ...), any string you pass to `--work-type` routes. Combined with per-profile skill pairing, the registry becomes a small agent pool — each use case names who runs it, on what model, with which skill:
|
|
139
|
+
|
|
140
|
+
```toml
|
|
141
|
+
[profiles.designer]
|
|
142
|
+
client = "claude-code"
|
|
143
|
+
model = "opus"
|
|
144
|
+
effort = "high"
|
|
145
|
+
cost_tier = "premium"
|
|
146
|
+
skill = "frontend-design" # dispatch this profile *with* this skill
|
|
147
|
+
|
|
148
|
+
[profiles.backender]
|
|
149
|
+
client = "codex"
|
|
150
|
+
model = "gpt-5.5"
|
|
151
|
+
effort = "xhigh"
|
|
152
|
+
cost_tier = "standard"
|
|
153
|
+
skill = "planr-work"
|
|
154
|
+
|
|
155
|
+
[[routes]]
|
|
156
|
+
match = { work_type = "frontend" }
|
|
157
|
+
profile = "designer"
|
|
158
|
+
fallbacks = ["driver"]
|
|
159
|
+
|
|
160
|
+
[[routes]]
|
|
161
|
+
match = { work_type = "design" }
|
|
162
|
+
profile = "designer"
|
|
163
|
+
|
|
164
|
+
[[routes]]
|
|
165
|
+
match = { work_type = "backend" }
|
|
166
|
+
profile = "backender"
|
|
167
|
+
fallbacks = ["driver"]
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Create items with the use-case work type (`planr item create ... --work-type frontend`) — or retag existing ones with `planr item update <id> --work-type frontend`, which is how planning agents tag `map build` output against the declared routes (the planning skills read `agents list` and do this without user involvement) — and the pick packet carries the full pairing — `"profile": "designer"`, `"model": "opus"`, `"skill": "frontend-design"` — so the driver dispatches profile and skill together (`Use $frontend-design on item <id>` on the profile's client/model). Workers pull their slice of the pool with `planr pick --work-type frontend`. `skill` is passthrough vocabulary like model ids: Planr never validates it against installed skills, and profiles without one omit the key entirely. A profile that needs different skills for different use cases is simply two profiles.
|
|
171
|
+
|
|
172
|
+
Declare the `client` you will actually dispatch on. A loop running inside one host dispatches that host's subagents — an in-Cursor driver that dispatches Cursor subagents with per-dispatch models is running `client = "cursor"` profiles in practice, even when the model matches. A `client = "codex"` profile is only honest when the driver really spawns a Codex process (`codex exec ...`). This matters for the audit: workers report the *profile id*, so a profile whose declared client differs from the real dispatch host passes the mismatch check on the model alone — the client deviation stays invisible.
|
|
173
|
+
|
|
174
|
+
## Host Matrix
|
|
175
|
+
|
|
176
|
+
Where each host reads its model configuration from, and what silently defeats a pin there (state of July 2026):
|
|
177
|
+
|
|
178
|
+
| Host | Native mechanism | Rendered by `planr install`? | Silent overrides / traps |
|
|
179
|
+
| --- | --- | --- | --- |
|
|
180
|
+
| Cursor | `.cursor/agents/*.md` frontmatter `model: <id>` (default `inherit`) | yes (`cursor`) | Team-admin model policy, plan availability, and Max-Mode-only models override without error; legacy request-based plans force Composer for subagents; subagent transcripts record no model field, so the actual model cannot be verified from artifacts after the fact — the dispatch parameters in the driver session are the only record |
|
|
181
|
+
| Claude Code | `planr-worker.md`/`planr-reviewer.md` frontmatter `model:` + `effort:` | yes (`claude`) | `CLAUDE_CODE_SUBAGENT_MODEL` clamps frontmatter and per-invocation models with no signal ([#57718](https://github.com/anthropics/claude-code/issues/57718)); since v2.1.196 `inherit` behaves as unset; org `availableModels` allowlists fall back silently |
|
|
182
|
+
| Codex CLI | `.codex/agents/*.toml` with `model` + `model_reasoning_effort` | yes (`codex`) | `fork_turns = "all"` intentionally drops the child's `agent_type`/`model` — use `fork_turns = "none"` or a partial fork; the role registry loads at session start ([#26408](https://github.com/openai/codex/issues/26408)), so re-renders need a restart |
|
|
183
|
+
| opencode | `opencode.json` `agent.<name>.model = "provider/model-id"` or `.opencode/agents/*.md` frontmatter | no — use the `planr prompt routing` process snippet | Subagent inherits the primary model when unset; malformed `provider/model-id` strings (quoting, trailing newline) raise `ProviderModelNotFoundError` ([#5623](https://github.com/sst/opencode/issues/5623)) |
|
|
184
|
+
| Pi | none by design — process-level dispatch (`pi --provider --model --thinking`) or the `pi-subagents` extension (`.pi/agents/*.md`) | no — use the `planr prompt routing` process snippet | Extension model-scope enforcement against `enabledModels` is opt-in; without it, pins are best-effort |
|
|
185
|
+
|
|
186
|
+
For the hosts without rendered role files, `planr prompt routing` prints ready process-dispatch snippets pre-filled from the registry. Whatever the host does, the [run audit](#run-audit) catches silent overrides after the fact.
|
|
187
|
+
|
|
188
|
+
## Command Summary
|
|
189
|
+
|
|
190
|
+
The registry surface end to end: `planr agents init [--force]` scaffolds, `planr agents list|check` inspect and validate, `planr pick --json` carries the `routing` block, `planr item route [--set|--clear]` pins per item, the MCP tools (`planr_agents_list`, `planr_item_route`, `planr_item_route_set`, `planr_item_route_clear`) return identical JSON shapes, `planr install <client> [--force]` renders host role files from the registry, `planr prompt routing` prints the driver dispatch block, `planr log add`/`done --profile` (or `PLANR_PROFILE`) feed the run audit, `planr trace item` and `planr doctor` surface mismatches and drift, and `planr export`/`import` carry the registry preview-first.
|
package/docs/RELEASE.md
CHANGED
|
@@ -32,6 +32,22 @@ Two independent gates back the script:
|
|
|
32
32
|
- `cargo test` fails on every push when any manifest version drifts from `Cargo.toml`.
|
|
33
33
|
- The release workflow's `Verify release versions are consistent` step refuses the tag when the tag, any manifest, or the `CHANGELOG.md` section disagree.
|
|
34
34
|
|
|
35
|
+
## Alpha Channel (Pre-Releases)
|
|
36
|
+
|
|
37
|
+
Pre-release versions use the same script and pipeline with a semver pre-release suffix:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
scripts/release.sh 1.2.0-alpha.1 "one-line summary"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The changelog section requirement applies verbatim (`## [1.2.0-alpha.1]`). What changes downstream:
|
|
44
|
+
|
|
45
|
+
- The GitHub Release is marked as a **prerelease**, so `releases/latest` — and with it the curl installer's default — stays on the last stable version. Testers pin explicitly: `PLANR_VERSION=1.2.0-alpha.1 sh install.sh`.
|
|
46
|
+
- npm publishes under the **`alpha` dist-tag** instead of `latest`: plain `npm install -g planr` keeps resolving stable, testers opt in with `npm install -g planr@alpha`.
|
|
47
|
+
- The **Homebrew tap never moves** on pre-release tags.
|
|
48
|
+
|
|
49
|
+
Only `-alpha.N`, `-beta.N`, and `-rc.N` suffixes are accepted; everything else the script rejects.
|
|
50
|
+
|
|
35
51
|
## Automated Release Pipeline
|
|
36
52
|
|
|
37
53
|
Pushing a tag `vX.Y.Z` runs `.github/workflows/release.yml`:
|
package/docs/SKILLS.md
CHANGED
|
@@ -23,7 +23,7 @@ Claude Code:
|
|
|
23
23
|
|
|
24
24
|
Skills are namespaced in Claude Code: `/planr:planr`, `/planr:planr-loop`. The plugin also registers the `planr-worker` and `planr-reviewer` subagents from the plugin's `agents/` directory.
|
|
25
25
|
|
|
26
|
-
Cursor: pending marketplace review;
|
|
26
|
+
Cursor: pending marketplace review; `planr install cursor` provides the identical component set today — it writes the skills to `.cursor/skills/`, the subagents to `.cursor/agents/`, and the MCP config in one command (see below and [Cursor](CURSOR.md)).
|
|
27
27
|
|
|
28
28
|
opencode: no plugin yet; use `planr mcp` as an MCP server (below). A JS plugin wrapping the CLI as custom tools is a possible follow-up.
|
|
29
29
|
|
|
@@ -164,11 +164,11 @@ Rules that hold in both journeys:
|
|
|
164
164
|
The CLI provisions the role files automatically — no manual copying:
|
|
165
165
|
|
|
166
166
|
```bash
|
|
167
|
-
planr project init "My Product" --client all # writes .codex/agents/*.toml and .
|
|
168
|
-
planr install codex # provisions roles for an existing project
|
|
167
|
+
planr project init "My Product" --client all # writes .codex/agents/*.toml, .claude/agents/*.md, and .cursor/agents/*.md
|
|
168
|
+
planr install codex # provisions roles for an existing project (same for claude and cursor)
|
|
169
169
|
```
|
|
170
170
|
|
|
171
|
-
Codex needs these project-scoped files because its plugin system carries skills only; the Claude Code
|
|
171
|
+
Codex needs these project-scoped files because its plugin system carries skills only; the Claude Code and Cursor plugins register the same roles automatically, so the provisioned `.claude/agents/` and `.cursor/agents/` copies only matter for standalone (non-plugin) installs. Existing role files are never overwritten.
|
|
172
172
|
|
|
173
173
|
Dispatches stay one line: `Use $planr-work on item <id>` and `Use $planr-review on item <id>`. The map and logs are the loop memory, so any iteration can resume from zero context.
|
|
174
174
|
|
|
@@ -228,16 +228,14 @@ planr prompt cli --client claude
|
|
|
228
228
|
|
|
229
229
|
## Install For Cursor
|
|
230
230
|
|
|
231
|
-
|
|
231
|
+
One command wires everything — MCP, the skills, and the subagent roles:
|
|
232
232
|
|
|
233
233
|
```bash
|
|
234
234
|
planr project init "Example Product" --client cursor
|
|
235
|
-
planr install cursor
|
|
236
|
-
planr prompt mcp --client cursor
|
|
237
|
-
planr prompt cli --client cursor
|
|
235
|
+
planr install cursor
|
|
238
236
|
```
|
|
239
237
|
|
|
240
|
-
`planr install cursor` writes `.cursor/mcp.json`
|
|
238
|
+
`planr install cursor` writes `.cursor/mcp.json`, copies the ten skills to `.cursor/skills/`, provisions `.cursor/agents/planr-worker.md` and `planr-reviewer.md`, and prints a one-click deeplink for user-level MCP install. Prefer skills and agents without MCP? `planr install cursor --no-mcp` writes only the subagents and skills — the skills are CLI-first, so the whole workflow runs through the `planr` binary. Invoke skills with `/planr` or `/planr-loop` in Agent chat, and dispatch subagents with `/planr-worker` and `/planr-reviewer`. Use `planr serve --port 7526` and `planr prompt http --client cursor` if a Cursor workflow should inspect the local HTTP/review workspace. Subagent multitasking and worktree guidance: [Cursor](CURSOR.md).
|
|
241
239
|
|
|
242
240
|
## MCP-Only Clients
|
|
243
241
|
|
package/docs/TROUBLESHOOTING.md
CHANGED
|
@@ -17,6 +17,10 @@ planr install claude --dry-run
|
|
|
17
17
|
planr install cursor --dry-run
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
## A Planr Command Appears To Hang
|
|
21
|
+
|
|
22
|
+
Planr bounds every database wait: `busy_timeout` is 5 seconds, and no command loops indefinitely — SQLite contention resolves or errors within that bound (a parallel first-pick storm is regression-tested). If a command still appears hung inside an agent-host tool call, the wait is almost certainly outside Planr: host tool harnesses that stop draining the child's stdout block the process on a full pipe, which looks exactly like a hang and works on retry. Kill and re-run the command; if it reproduces outside the host harness (plain terminal), capture a stack (`lldb -p <pid>` then `bt all`) and file it with the output — that would be a Planr bug we want.
|
|
23
|
+
|
|
20
24
|
## Database Or Import Issues
|
|
21
25
|
|
|
22
26
|
```bash
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"planr_item_insert",
|
|
19
19
|
"planr_item_amend",
|
|
20
20
|
"planr_item_replan",
|
|
21
|
+
"planr_agents_list",
|
|
22
|
+
"planr_item_route",
|
|
23
|
+
"planr_item_route_set",
|
|
24
|
+
"planr_item_route_clear",
|
|
21
25
|
"planr_pick_item",
|
|
22
26
|
"planr_pick_heartbeat",
|
|
23
27
|
"planr_pick_progress",
|
|
@@ -74,12 +78,16 @@
|
|
|
74
78
|
"cursor": [
|
|
75
79
|
".cursor/mcp.json",
|
|
76
80
|
"\"mcpServers\"",
|
|
77
|
-
"\"planr\""
|
|
81
|
+
"\"planr\"",
|
|
82
|
+
"cursor://anysphere.cursor-deeplink/mcp/install?name=planr&config="
|
|
78
83
|
]
|
|
79
84
|
},
|
|
80
85
|
"cli_reference_commands": [
|
|
81
86
|
"planr install codex|claude|cursor",
|
|
82
87
|
"planr prompt cli|mcp|http",
|
|
88
|
+
"planr prompt routing",
|
|
89
|
+
"planr agents init",
|
|
90
|
+
"planr agents init --profile|--skill|--route|--default-route|--interactive",
|
|
83
91
|
"planr mcp",
|
|
84
92
|
"planr review annotate",
|
|
85
93
|
"planr review ingest",
|
|
@@ -87,6 +95,9 @@
|
|
|
87
95
|
"planr review evidence",
|
|
88
96
|
"planr review close",
|
|
89
97
|
"planr recover sweep",
|
|
98
|
+
"planr agents list",
|
|
99
|
+
"planr agents check",
|
|
100
|
+
"planr item route",
|
|
90
101
|
"planr serve --port"
|
|
91
102
|
]
|
|
92
103
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "planr",
|
|
3
3
|
"description": "Skill-driven planning and execution loop for coding agents: one planr entry point, an autonomous planr-loop, and evidence-backed task graph skills powered by the planr CLI.",
|
|
4
|
-
"version": "1.1
|
|
4
|
+
"version": "1.2.0-alpha.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "instructa"
|
|
7
7
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "planr",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.0-alpha.1",
|
|
4
4
|
"description": "Skill-driven planning and execution loop for coding agents: one $planr entry point, an autonomous $planr-loop, and evidence-backed task graph skills powered by the planr CLI.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "instructa",
|
|
@@ -10,5 +10,8 @@ skills:
|
|
|
10
10
|
Use the preloaded planr-review skill exactly as written for the single item id you are given.
|
|
11
11
|
You did not write this code; audit it like an owner. Inspect the actual diff and rerun the
|
|
12
12
|
logged verification commands instead of trusting the worker's summary.
|
|
13
|
-
Close the review with `planr review close <review-id> --verdict
|
|
14
|
-
|
|
13
|
+
Close the review with `planr review close <review-id> --verdict ... --reviewer <your-id>` and
|
|
14
|
+
always pass `--reviewer` explicitly (e.g. `checker-1`): shell `export`s do not survive between
|
|
15
|
+
tool calls, and a review closed under the default identity corrupts the independence audit.
|
|
16
|
+
Findings must be specific and actionable. Do not edit implementation files; your only writes
|
|
17
|
+
are planr review commands.
|
|
@@ -42,6 +42,8 @@ Before `map build`, expand the plan's task list: the scaffold ships a single pla
|
|
|
42
42
|
planr link add <earlier-item> <later-item> --type blocks
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
If `planr agents list --json` shows routes with use-case `work_type` selectors (e.g. `frontend`, `backend`), retag the built items to match their work (`planr item update <id> --work-type frontend`) so every pick packet carries the declared profile and paired skill — this is prep-agent work, never a question for the user.
|
|
46
|
+
|
|
45
47
|
## Store The Goal Contract
|
|
46
48
|
|
|
47
49
|
The contract must survive compaction, session loss, and host switches, so it lives in Planr, not in chat:
|
|
@@ -58,7 +58,7 @@ The loop never closes its own reviews when the host supports a second agent. Mak
|
|
|
58
58
|
|
|
59
59
|
## Skills Are The Prompts
|
|
60
60
|
|
|
61
|
-
When the host supports subagents, the driver never implements: it dispatches, audits, and synthesizes. Driver tokens go into `plan audit`, dispatch decisions, and conflict resolution — implementation and review run in the subagent roles, which the host wiring can pin to a cheaper tier (see the role files and `docs/GOALS.md` "Cost Tiering"). Delegate with skill references plus an item id, nothing more:
|
|
61
|
+
When the host supports subagents, the driver never implements: it dispatches, audits, and synthesizes. Driver tokens go into `plan audit`, dispatch decisions, and conflict resolution — implementation and review run in the subagent roles, which the host wiring can pin to a cheaper tier (see the role files and `docs/GOALS.md` "Cost Tiering"). When the pick packet carries a `routing` block, dispatch on it: run the worker on the named profile's client and model, and move down the `fallbacks` chain when the primary hits a rate limit or is unavailable — the chain is ordered, so no mid-run registry edits. Workers report the profile they actually ran on (`done --profile <id>` or `PLANR_PROFILE`), so a host that silently overrode the pin shows up in `planr trace item` instead of staying invisible. Delegate with skill references plus an item id, nothing more:
|
|
62
62
|
|
|
63
63
|
- Worker dispatch: `Use $planr-work on item <item-id>. Stop after requesting review.`
|
|
64
64
|
- Checker dispatch: `Use $planr-review on item <item-id>. Close the review with a verdict.`
|
|
@@ -69,6 +69,7 @@ Host wiring:
|
|
|
69
69
|
|
|
70
70
|
- Codex: project agents in `.codex/agents/*.toml` preload the skill via `[[skills.config]]` (TOML templates in `agents/` next to this skill). Spawn explicitly: "spawn the planr_worker agent for item X". Keep `[agents] max_depth = 1`.
|
|
71
71
|
- Claude Code: subagents preload via the `skills:` frontmatter field. The Planr plugin registers `planr-worker` and `planr-reviewer` automatically from its `agents/` directory; standalone installs copy them to `.claude/agents/`. The reviewer subagent is read-only except for `planr review` commands.
|
|
72
|
+
- Cursor: project subagents in `.cursor/agents/*.md` (Markdown templates in `agents/` next to this skill; `planr install cursor` provisions them). Dispatch explicitly: `/planr-worker implement item X`, `/planr-reviewer review item X`. Parallel dispatches are safe — the map's pick lease keeps one owner per item.
|
|
72
73
|
- Single-agent hosts: run worker and checker as separate sequential dispatches with a fresh read of map state in between; never carry the worker's self-assessment into the review step. The mode is recorded automatically: `review close` derives `review_mode` (`single_agent`/`independent`) from worker identity.
|
|
73
74
|
|
|
74
75
|
## Live Verification By Platform
|
|
@@ -103,7 +104,8 @@ Prefer the host's loop primitive over a bash while-loop so a separate model chec
|
|
|
103
104
|
|
|
104
105
|
- Codex: `/goal Use $planr-loop on plan <plan-id>. The loop contract is stored in planr context (tag: goal-contract).` — or an Automation with the same prompt. Full workflow: `docs/GOALS.md` in the Planr repository.
|
|
105
106
|
- Claude Code: `/goal` with the same prompt shape, or `/loop` for a fixed cadence.
|
|
106
|
-
-
|
|
107
|
+
- Cursor: no /goal primitive; re-dispatch `Use $planr-loop on plan <plan-id> ...` manually or per session. Within a session the loop dispatches the provisioned `/planr-worker` and `/planr-reviewer` subagents per item.
|
|
108
|
+
- Anywhere else (plain MCP clients, hosts without /goal): re-dispatch `Use $planr-loop on plan <plan-id> ...` manually or per session. Nothing is lost except automatic re-prompting.
|
|
107
109
|
|
|
108
110
|
Recovery is the same in all cases: a fresh session starts at step 1 (`$planr-status`), reads the map and the stored goal-contract, and continues exactly where the last iteration stopped — zero chat context required.
|
|
109
111
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: planr-reviewer
|
|
3
|
+
description: Independent findings-first reviewer for one Planr item. Audits evidence and closes the review with a verdict. Dispatch with the item id.
|
|
4
|
+
# Deliberately no model pin: the reviewer is the truth gate and inherits the
|
|
5
|
+
# driver's model. Make workers cheap, not the verdict.
|
|
6
|
+
model: inherit
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Read the planr-review skill (`.cursor/skills/planr-review/SKILL.md`, or the planr-review skill
|
|
10
|
+
registered by the Planr plugin) and follow it exactly for the single item id you are given.
|
|
11
|
+
You did not write this code; audit it like an owner. Inspect the actual diff and rerun the
|
|
12
|
+
logged verification commands instead of trusting the worker's summary.
|
|
13
|
+
Close the review with `planr review close <review-id> --verdict ... --reviewer <your-id>` and
|
|
14
|
+
always pass `--reviewer` explicitly (e.g. `checker-1`): shell `export`s do not survive between
|
|
15
|
+
tool calls, and a review closed under the default identity corrupts the independence audit.
|
|
16
|
+
Findings must be specific and actionable. Do not edit implementation files; your only writes
|
|
17
|
+
are planr review commands.
|
|
@@ -11,8 +11,11 @@ developer_instructions = """
|
|
|
11
11
|
Use the planr-review skill exactly as written for the single item id you are given.
|
|
12
12
|
You did not write this code; audit it like an owner. Inspect the actual diff and rerun the
|
|
13
13
|
logged verification commands instead of trusting the worker's summary.
|
|
14
|
-
Close the review with `planr review close <review-id> --verdict
|
|
15
|
-
|
|
14
|
+
Close the review with `planr review close <review-id> --verdict ... --reviewer <your-id>` and
|
|
15
|
+
always pass `--reviewer` explicitly (e.g. `checker-1`): shell exports do not survive between
|
|
16
|
+
tool calls, and a review closed under the default identity corrupts the independence audit.
|
|
17
|
+
Findings must be specific and actionable. Do not edit implementation files; your only writes
|
|
18
|
+
are planr review commands.
|
|
16
19
|
"""
|
|
17
20
|
|
|
18
21
|
[[skills.config]]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: planr-worker
|
|
3
|
+
description: Implements exactly one picked Planr map item to evidence-backed completion, then requests review and stops. Dispatch with the item id.
|
|
4
|
+
# Cost tiering: the pick packet bounds the worker's scope, so it can run on a
|
|
5
|
+
# cheaper tier than the driver. Replace inherit with a cheaper Cursor model id
|
|
6
|
+
# when dispatch cost matters. See docs/GOALS.md "Cost Tiering".
|
|
7
|
+
model: inherit
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
Read the planr-work skill (`.cursor/skills/planr-work/SKILL.md`, or the planr-work skill
|
|
11
|
+
registered by the Planr plugin) and follow it exactly for the single item id you are given.
|
|
12
|
+
Implement only that item. Log changed files and the real verification commands you ran.
|
|
13
|
+
Request review with `planr review request <item-id>` and stop. Never close reviews or items yourself.
|
|
@@ -51,8 +51,22 @@ A build plan must include:
|
|
|
51
51
|
- verification;
|
|
52
52
|
- acceptance criteria.
|
|
53
53
|
|
|
54
|
+
## Route-Aware Tagging
|
|
55
|
+
|
|
56
|
+
Before `map build`, check whether the project declares model routing: `planr agents list --json`. If routes exist, their `work_type` selectors are the project's use-case vocabulary (e.g. `frontend`, `backend`, `design`) — and tagging is your job, not the user's; never ask a human to name work types. `map build` seeds every item as `code`, so after building the map, retag each item whose work matches a declared route:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
planr agents list --json # route selectors = the use-case vocabulary
|
|
60
|
+
planr map build --from <build-plan-id>
|
|
61
|
+
planr item update <item-id> --work-type frontend # per item that matches a route
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Match by the item's actual work (UI/components/styling -> a `frontend` route, API/server/storage -> `backend`, and so on). Items matching no route keep `code` — the default route covers them. The payoff: every pick packet then carries the right profile, model, and paired skill for its use case, so dispatch needs no human routing knowledge.
|
|
65
|
+
|
|
54
66
|
## Done
|
|
55
67
|
|
|
56
68
|
Planning is complete only when `planr plan check <plan-id>` passes and the next command is clear: split further, build map, or ask the user for a blocking decision.
|
|
57
69
|
|
|
70
|
+
When the map is built, linked, and tagged, end by naming the execution handoff explicitly — the user should never have to guess the next prompt: `Use $planr-loop on plan <build-plan-id>. Stop condition: all items closed with evidence, reviews complete, live verification logged.` (On hosts with a /goal primitive, `$planr-goal` wraps the same loop for long-running autonomous runs.)
|
|
71
|
+
|
|
58
72
|
`plan check` rejects empty scaffolds: build plans must have content in `## Scope Decision`, `## Verification`, and `## Acceptance Criteria`; product plans must have content in `## Problem`, `## Requirements`, and `## Success Criteria` of `PRODUCT_SPEC.md`. Write those sections before checking — do not pad them to satisfy the gate.
|
|
@@ -43,7 +43,7 @@ planr review close <review-id> --verdict not-complete --reviewer <your-id> --fin
|
|
|
43
43
|
|
|
44
44
|
When no independent reviewer instance is available (single-agent host), do not pretend a second instance reviewed the work. Re-read the diff, logs, and evidence with fresh eyes, then close normally. The review mode is recorded automatically: `review close` compares your identity against the maker's lease and stamps `review_mode: single_agent | independent | unattributed` on the close response, review log, artifact, and event. No extra context note is needed — honesty is derived from recorded identity.
|
|
45
45
|
|
|
46
|
-
Identity honesty rule: one agent instance keeps one `PLANR_WORKER_ID` for its whole session. Never export a second identity (e.g. `maker-x` and `checker-x`) inside the same instance to make a review look independent — that stamps `independent` on a review that was not, which is worse than an honest `single_agent`. `independent` is only real when a separate agent process (a subagent, another terminal, another client) picks the review under its own identity.
|
|
46
|
+
Identity honesty rule: one agent instance keeps one `PLANR_WORKER_ID` for its whole session. Never export a second identity (e.g. `maker-x` and `checker-x`) inside the same instance to make a review look independent — that stamps `independent` on a review that was not, which is worse than an honest `single_agent`. `independent` is only real when a separate agent process (a subagent, another terminal, another client) picks the review under its own identity — and only when that identity is explicitly set: a review closed under the anonymous fallback identity stamps `single_agent` even if the strings happen to differ, so always pass `--reviewer <id>` (shell exports do not survive between tool calls).
|
|
47
47
|
|
|
48
48
|
## Completion Rule
|
|
49
49
|
|
|
@@ -118,6 +118,8 @@ planr map lane --critical
|
|
|
118
118
|
|
|
119
119
|
Do not pick from a freshly built map that has zero links unless the items are genuinely independent.
|
|
120
120
|
|
|
121
|
+
When `planr agents list --json` shows routes with use-case `work_type` selectors (e.g. `frontend`, `backend`), retag freshly built items to match their work (`planr item update <id> --work-type frontend`) so pick packets carry the declared profile, model, and paired skill. This is the agent's job — never the user's; items matching no route keep `code` and fall to the default route.
|
|
122
|
+
|
|
121
123
|
## Parent Gate Pattern
|
|
122
124
|
|
|
123
125
|
Model material changes as parent gates. The parent is the completion gate; linked children do the work.
|
|
@@ -22,7 +22,7 @@ The pick output is one flat work packet — item, links, logs, runtime, recovery
|
|
|
22
22
|
planr done <item-id> --summary "what changed" --files path-a --files path-b --cmd "exact verification command" --tests "exact test command" --review
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Put build/serve commands in `--cmd` and test runs in `--tests` — both are recorded as evidence. Include the decisive output line in `--summary` (e.g. "12 tests passed", "GET /videos returned 3 entries"): reviewers see your recorded command strings, not your terminal, so the summary must carry what you observed, not just what you ran. Single-quote `--files` values that contain `$` (route files like `watch.$videoId.tsx`), or the shell expands them before planr sees them. `done --review` writes the completion log, requests the review, and moves the item to `in_review` (you keep ownership; it is waiting on the gate, not abandoned) — the response names the target's new status and the plan-scoped reviewer pick command; add `--next` to pick the following item in the same call. Without `--review` it closes the item directly (only for items that need no review gate). Running `done` on a ready item you never picked adopts it: the lease is written retroactively under your worker id so the review always has a maker. The response reports what your settlement `unlocked`, echoes the item's post condition, and hints when downstream work depends on an item closed without command/test evidence.
|
|
25
|
+
Put build/serve commands in `--cmd` and test runs in `--tests` — both are recorded as evidence. When the pick packet carries a `routing` block, also report the registry profile you actually ran on: add `--profile <profile-id>` to `done`/`log add`, or export `PLANR_PROFILE` once per session. It is part of the evidence — a mismatch with the declared route is advisory (never blocks the close) and surfaces in `planr trace item` so silent host overrides get caught. Include the decisive output line in `--summary` (e.g. "12 tests passed", "GET /videos returned 3 entries"): reviewers see your recorded command strings, not your terminal, so the summary must carry what you observed, not just what you ran. Single-quote `--files` values that contain `$` (route files like `watch.$videoId.tsx`), or the shell expands them before planr sees them. `done --review` writes the completion log, requests the review, and moves the item to `in_review` (you keep ownership; it is waiting on the gate, not abandoned) — the response names the target's new status and the plan-scoped reviewer pick command; add `--next` to pick the following item in the same call. Without `--review` it closes the item directly (only for items that need no review gate). Running `done` on a ready item you never picked adopts it: the lease is written retroactively under your worker id so the review always has a maker. The response reports what your settlement `unlocked`, echoes the item's post condition, and hints when downstream work depends on an item closed without command/test evidence.
|
|
26
26
|
|
|
27
27
|
Live verification (browser flow, executed binary, real requests) gets its own log kind so `plan audit` can find it:
|
|
28
28
|
|