planr 1.2.0 → 1.3.0
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 +44 -0
- package/docs/CLI_REFERENCE.md +6 -2
- package/docs/HOOKS.md +34 -0
- package/docs/fixtures/mcp-contract.json +2 -0
- 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/skills/planr-verify-web/SKILL.md +1 -0
package/README.md
CHANGED
|
@@ -22,6 +22,49 @@ Flat todo lists break down the moment real work has structure. Planr models work
|
|
|
22
22
|
|
|
23
23
|
Three layers make that work: **Plans** (reviewable Markdown packages), the **Map** (live dependency graph with picks, reviews, logs), and **Agent loops** (skills, CLI, and MCP workflows for every major coding agent). Full model: [Task Graph Model](docs/TASK_GRAPH_MODEL.md) and [Operating Model](docs/OPERATING_MODEL.md).
|
|
24
24
|
|
|
25
|
+
## New in 1.3.0: Native Host Hooks
|
|
26
|
+
|
|
27
|
+
`planr install codex|claude|cursor` now wires Planr into the host's native hook system by default — every new session (including post-compaction restarts) gets one compact state block injected automatically:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
## planr state
|
|
31
|
+
project: Hookboard | map: 5/5 settled | 0 ready, 0 picked, 0 in_review
|
|
32
|
+
goal contract: DONE when every in-scope map item is closed with log evidence, ...
|
|
33
|
+
routing: registry active (3 profiles; pick packets carry model routing)
|
|
34
|
+
next: planr plan audit pln-fc584c28 --json
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
- **`planr prime`** — the state block behind the hooks: project, map counts, held items with log status, goal contract, and the next command. Silent in repos without a Planr database.
|
|
38
|
+
- **Loop recovery becomes mechanism, not discipline** — an agent that restarts or compacts mid-loop picks up exactly where the map says it left off.
|
|
39
|
+
- **Evidence guard (Cursor)** — a subagent that stops while its own pick has no completion log gets one advisory reminder naming the item and the two ways out.
|
|
40
|
+
- **Fail-open and additive** — hooks never block a session (10s timeout, always exit 0), existing hook files are merged, `--no-hooks` opts out.
|
|
41
|
+
|
|
42
|
+
Full guide: [Hooks](docs/HOOKS.md) · [Release notes](https://github.com/instructa/planr/releases/tag/v1.3.0).
|
|
43
|
+
|
|
44
|
+
## Model Routing (1.2.0)
|
|
45
|
+
|
|
46
|
+
Declare once which model handles which work — every task then carries its own routing, and your agents delegate automatically:
|
|
47
|
+
|
|
48
|
+
```toml
|
|
49
|
+
# .planr/agents.toml (write it with `planr agents init`)
|
|
50
|
+
[profiles.frontender]
|
|
51
|
+
client = "cursor"
|
|
52
|
+
model = "opus"
|
|
53
|
+
skill = "frontend-design"
|
|
54
|
+
|
|
55
|
+
[[routes]]
|
|
56
|
+
match = { work_type = "frontend" }
|
|
57
|
+
profile = "frontender"
|
|
58
|
+
fallbacks = ["driver"]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- **Routing travels in the pick packet** — `planr pick --json` hands the worker its profile, model, and paired skill; `planr pick --peek` lets dispatching drivers read it without taking the lease.
|
|
62
|
+
- **Rendered into your hosts' native config** — `planr install codex|claude|cursor` writes the subagent role files with model pins from the registry, in each host's exact vocabulary.
|
|
63
|
+
- **Declared vs. actual, with receipts** — workers report the profile they ran on, runs record the observed host, and `planr trace item` shows deviations as advisory markers.
|
|
64
|
+
- **Use-case pools** — free-form work types (`frontend`, `backend`, ...) declared right in the plan's task list (`### TASK-001 (backend): ...`), plus per-item pins via `planr item route`.
|
|
65
|
+
|
|
66
|
+
Routing is advisory by design: Planr never dispatches models and never blocks a pick — hosts stay the authority. Full guide: [Model Routing](docs/MODEL_ROUTING.md) · replayable walkthrough: [Worked Example: Web App](docs/EXAMPLE_WEBAPP.md) · [Release notes](https://github.com/instructa/planr/releases/tag/v1.2.0).
|
|
67
|
+
|
|
25
68
|
## Install
|
|
26
69
|
|
|
27
70
|
```bash
|
|
@@ -137,6 +180,7 @@ Mid-project work (a new feature, refactor, or fix on an existing project) works
|
|
|
137
180
|
- [Skills](docs/SKILLS.md)
|
|
138
181
|
- [Long-Running Goals](docs/GOALS.md)
|
|
139
182
|
- [Model Routing](docs/MODEL_ROUTING.md) · [Worked Example: Web App](docs/EXAMPLE_WEBAPP.md)
|
|
183
|
+
- [Host Hooks](docs/HOOKS.md)
|
|
140
184
|
- [CLI Reference](docs/CLI_REFERENCE.md)
|
|
141
185
|
- [MCP Guide](docs/MCP_GUIDE.md)
|
|
142
186
|
- [Codex](docs/CODEX.md) · [Claude Code](docs/CLAUDE_CODE.md) · [Cursor](docs/CURSOR.md)
|
package/docs/CLI_REFERENCE.md
CHANGED
|
@@ -30,6 +30,7 @@ planr item update <item-id> [--title "..."] [--description "..."] [--work-type <
|
|
|
30
30
|
# plan task lists can declare work types inline: `### TASK-001 (backend): ...` / `- [ ] (frontend) ...` seed map build directly
|
|
31
31
|
planr item route <item-id> [--set <profile>|--clear]
|
|
32
32
|
planr link add <from-item> <to-item> --type blocks
|
|
33
|
+
planr prime [--hook-json]
|
|
33
34
|
planr pick [--work-type <type>] [--plan <plan-id>] [--peek]
|
|
34
35
|
planr pick release <item-id> [--force]
|
|
35
36
|
planr pick heartbeat [item-id]
|
|
@@ -43,6 +44,7 @@ planr approval approve <item-id> --by "name" [--comment "..."]
|
|
|
43
44
|
planr approval deny <item-id> --by "name" [--comment "..."]
|
|
44
45
|
planr approval list [--open]
|
|
45
46
|
planr artifact add "name"|--name "name" [--item <item-id>] [--kind evidence] [--path file] [--content "..."] [--mime text/plain]
|
|
47
|
+
# --kind is free-form vocabulary; common values: evidence, screenshot, video, recording, report, review
|
|
46
48
|
planr artifact show <artifact-id>
|
|
47
49
|
planr artifact list [--item <item-id>]
|
|
48
50
|
planr event list [--item <item-id>] [--limit 50]
|
|
@@ -65,7 +67,7 @@ planr agents init --profile|--skill|--route|--default-route|--interactive
|
|
|
65
67
|
planr agents list [--json]
|
|
66
68
|
planr agents check
|
|
67
69
|
planr doctor [--client codex|claude|cursor|all]
|
|
68
|
-
planr install codex|claude|cursor [--dry-run] [--no-mcp] [--force]
|
|
70
|
+
planr install codex|claude|cursor [--dry-run] [--no-mcp] [--force] [--no-hooks]
|
|
69
71
|
planr prompt cli|mcp|http [--client codex|claude|cursor|all]
|
|
70
72
|
planr prompt routing [--client codex|claude|cursor|all]
|
|
71
73
|
planr mcp
|
|
@@ -89,7 +91,7 @@ With `--json`, responses follow one convention so agents never guess where data
|
|
|
89
91
|
|
|
90
92
|
`plan check` validates path, YAML frontmatter, and that required sections have content: build plans need `## Scope Decision`, `## Verification`, and `## Acceptance Criteria` filled; product plans need `## Problem`, `## Requirements`, and `## Success Criteria` filled in `PRODUCT_SPEC.md`. It also flags a task list that still contains only the scaffold placeholder (or no work specs at all) — `map build` would turn that into a single coarse item, so the fix names the granularity contract: one `### TASK-00n:` heading (or `- [ ]` line) per verifiable slice, typically 4-8, in execution order. Each warning is structured — `{"file", "section", "message", "fix"}` — and names the exact file to edit plus the re-run command, so a failed check is a repair instruction, not a riddle.
|
|
91
93
|
|
|
92
|
-
`plan audit <plan-id>` is the one-call contract verdict for a plan's map scope. It evaluates four clauses with evidence: `items_settled` (open items listed), `reviews_complete` (open review items listed), `approvals_clear` (requested/denied approvals listed), and `verification_logged` (logs with `--kind verification` on scope items). The stored goal contract (`planr context --tag goal-contract` mentioning the plan id) is included; the verification clause is binding only when such a contract exists. `holds: true` means the contract is satisfied — loop agents use this as their stop condition instead of stitching the verdict together from `map status`, `log list`, and `approval list`. When the contract is open, `next` carries the exact next command derived from the first actionable gap: build the map, pick the ready review or work item (plan-scoped), resolve the blocking approval, inspect stalled leases, or log the missing verification. Also available as MCP `planr_plan_audit`.
|
|
94
|
+
`plan audit <plan-id>` is the one-call contract verdict for a plan's map scope. JSON shape for machine consumers: top-level `holds` (bool) and `clauses[]` with `clause` (name) and `pass` (bool) — e.g. `planr plan audit <id> --json | jq '.holds, (.clauses[] | {clause, pass})'`. It evaluates four clauses with evidence: `items_settled` (open items listed), `reviews_complete` (open review items listed), `approvals_clear` (requested/denied approvals listed), and `verification_logged` (logs with `--kind verification` on scope items). The stored goal contract (`planr context --tag goal-contract` mentioning the plan id) is included; the verification clause is binding only when such a contract exists. `holds: true` means the contract is satisfied — loop agents use this as their stop condition instead of stitching the verdict together from `map status`, `log list`, and `approval list`. When the contract is open, `next` carries the exact next command derived from the first actionable gap: build the map, pick the ready review or work item (plan-scoped), resolve the blocking approval, inspect stalled leases, or log the missing verification. Also available as MCP `planr_plan_audit`.
|
|
93
95
|
|
|
94
96
|
`map show --plan <plan-id>` narrows the map to one plan's items and the links among them, with plan-scoped counts — plan-scoped goal runs on shared boards audit their slice, not the whole board. An unknown plan id is an error, never a silent unscoped view. Also available on MCP `planr_map_show` (`plan`) and HTTP `GET /v1/projects/{id}/map?plan=<plan-id>`.
|
|
95
97
|
|
|
@@ -127,6 +129,8 @@ With `--json`, responses follow one convention so agents never guess where data
|
|
|
127
129
|
|
|
128
130
|
`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.
|
|
129
131
|
|
|
132
|
+
`prime` prints the compact state block host hooks inject at session start and after context compaction (project, map counts, held items with log status, goal contract, registry presence, next command); `--hook-json` emits the Claude Code SessionStart envelope. In a repo without a Planr database it exits silently and creates nothing — hooks must never break a session. Installs wire it into the host's hook system by default (`--no-hooks` skips; existing hook files are merged additively, never overwritten). Full guide: [Host Hooks](HOOKS.md).
|
|
133
|
+
|
|
130
134
|
`pick --json` returns one flat work packet in which every fact appears exactly once: `item`, `links`, `logs`, `runtime`, `recovery`, `conditions`, `approval`, recall context (`contexts`, `relevant_contexts`, `upstream_handoffs`, `review_history`, `source_links`, `possible_file_conflicts`), `close_effect`, `privacy`, `deeper_reads`, and `remaining`. Worker identity lives in `item.worker_id` and `runtime.worker_id`. Empty collections and inactive gates are omitted — a missing key means "empty". No separate `trace item` call is needed. Evidence written via `log add` or `done` by the pick owner refreshes the runtime heartbeat automatically. The same packet shape is returned by MCP `planr_pick_item`, HTTP `POST /v1/pick`, and `done --next`. `pick --peek` (MCP: `"peek": true`) returns the same packet for the next pickable item *without* leasing it — no lease, heartbeat, or pick event is written, and the packet carries `"peek": true` with the item still `ready`. Built for dispatching drivers: read the routing block, dispatch the worker, and let the worker take the lease under its own identity — replacing the pick → `pick release --force` → re-pick sequence.
|
|
131
135
|
|
|
132
136
|
`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.
|
package/docs/HOOKS.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Host Hooks
|
|
2
|
+
|
|
3
|
+
`planr install codex|claude|cursor` wires Planr into the host's native hook system **by default** — skip it with `--no-hooks`. The hooks solve one problem: loop state must survive the moments context is created or destroyed. Every new session and every context compaction gets the map state injected automatically via `planr prime`, so recovery stops being discipline and becomes mechanism.
|
|
4
|
+
|
|
5
|
+
## What gets installed
|
|
6
|
+
|
|
7
|
+
| Host | File | Events | Runs |
|
|
8
|
+
| --- | --- | --- | --- |
|
|
9
|
+
| Cursor | `.cursor/hooks.json` | `sessionStart` | `planr prime --cursor-json` |
|
|
10
|
+
| | | `subagentStop` | `.cursor/hooks/planr-evidence-guard.sh` |
|
|
11
|
+
| Claude Code | `.claude/settings.json` | `SessionStart` (matcher `startup\|resume\|compact`) | `planr prime --hook-json` |
|
|
12
|
+
| Codex CLI | `.codex/hooks.json` | `SessionStart` | `planr prime` |
|
|
13
|
+
|
|
14
|
+
`planr prime` prints one compact, bounded state block (project, map counts, your held items with log status, the goal contract, registry presence, and the next command) — deterministic and read-only. `--hook-json` wraps it in the Claude Code SessionStart envelope (`hookSpecificOutput.additionalContext`); `--cursor-json` emits Cursor's `additional_context` shape.
|
|
15
|
+
|
|
16
|
+
Only session-start events are wired, deliberately: they are the one place all three hosts inject hook output as context, and Claude's `compact` source plus post-compaction session starts cover the compaction case. Pre/post-compaction events (`preCompact`, `PreCompact`, `PostCompact`) cannot restore model context and are not used.
|
|
17
|
+
|
|
18
|
+
The evidence guard is advisory and identity-scoped: a Cursor subagent that stops while *its own* pick (matched via `PLANR_WORKER_ID`/`PLANR_SESSION_ID`) has no completion log gets one follow-up message naming the item and the two ways out (`planr done ...` or `planr pick release ...`). Without an explicit worker identity it stays silent rather than steering the wrong agent toward foreign items. It never blocks.
|
|
19
|
+
|
|
20
|
+
## Design rules
|
|
21
|
+
|
|
22
|
+
- **Fail open, always.** Every hook command ends in `|| true` with a 10-second timeout; a missing, broken, or uninitialized planr never blocks a session. In a repo without a Planr database, `planr prime` exits silently and creates nothing.
|
|
23
|
+
- **Additive merge, planr owns only its own entries.** Existing hook files keep every foreign entry; planr entries are reconciled on re-install (current ones untouched, outdated ones upgraded in place, entries under retired events removed) — so `planr install <client>` after an update also refreshes the hooks. A file planr cannot parse as a JSON object is left untouched with a note in the install output — add the snippet manually in that case.
|
|
24
|
+
- **Default, not mandatory.** `--no-hooks` skips hook installation entirely; deleting the planr entries from the hook files removes the behavior with no other effect.
|
|
25
|
+
|
|
26
|
+
## Codex trust model
|
|
27
|
+
|
|
28
|
+
Codex requires reviewing hooks before they run: after installing, open `/hooks` in the Codex TUI once and trust the planr entries. Codex records trust against the hook definition's hash, so if a future planr version changes the hook commands, Codex will ask again — that is the host working as intended.
|
|
29
|
+
|
|
30
|
+
## Removing hooks
|
|
31
|
+
|
|
32
|
+
Delete the planr entries from `.cursor/hooks.json` / `.claude/settings.json` / `.codex/hooks.json` (and `.cursor/hooks/planr-evidence-guard.sh`). Nothing else depends on them.
|
|
33
|
+
|
|
34
|
+
See also: [Model Routing](MODEL_ROUTING.md), [Goals](GOALS.md), [CLI Reference](CLI_REFERENCE.md).
|
|
@@ -88,6 +88,8 @@
|
|
|
88
88
|
"planr prompt routing",
|
|
89
89
|
"planr agents init",
|
|
90
90
|
"planr agents init --profile|--skill|--route|--default-route|--interactive",
|
|
91
|
+
"planr prime",
|
|
92
|
+
"planr install codex|claude|cursor [--dry-run] [--no-mcp] [--force] [--no-hooks]",
|
|
91
93
|
"planr mcp",
|
|
92
94
|
"planr review annotate",
|
|
93
95
|
"planr review ingest",
|
|
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.
|
|
4
|
+
"version": "1.3.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "instructa"
|
|
7
7
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "planr",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
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",
|
|
@@ -55,6 +55,7 @@ Attach screenshots or traces as artifacts on the item:
|
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
57
|
planr artifact add "verify-web screenshot" --item <item-id> --path <screenshot-path> --kind screenshot
|
|
58
|
+
planr artifact add "verify-web recording" --item <item-id> --path <recording.mp4> --kind video
|
|
58
59
|
```
|
|
59
60
|
|
|
60
61
|
The replay command is mandatory. The reviewer reruns it instead of trusting this run; a verification that cannot be replayed is not evidence.
|