leos-agent 6.3.0 → 7.0.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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +14 -7
  3. package/adapters/cursor/agents/executor.md +1 -1
  4. package/adapters/cursor/agents/implementer.md +2 -2
  5. package/adapters/cursor/agents/review-lens.md +22 -0
  6. package/adapters/cursor/agents/reviewer.md +2 -2
  7. package/adapters/opencode/agents.json +43 -4
  8. package/adapters/opencode/plugin.js +325 -37
  9. package/config/MCP_PINS.md +17 -0
  10. package/config/models.json +276 -8
  11. package/hooks/bash-guard.py +51 -9
  12. package/package.json +3 -6
  13. package/roles/executor.md +1 -1
  14. package/roles/implementer.md +2 -2
  15. package/roles/review-lens.md +20 -0
  16. package/roles/reviewer.md +2 -2
  17. package/scripts/doctor.py +267 -31
  18. package/scripts/ghreview.py +7 -3
  19. package/scripts/jsonc_bridge.cjs +23 -0
  20. package/scripts/memory.py +74 -35
  21. package/scripts/render_adapters.py +57 -22
  22. package/scripts/resolve_attach_target.py +45 -13
  23. package/scripts/setup.py +1594 -2
  24. package/skills/brainstorming/SKILL.md +3 -1
  25. package/skills/debugging/SKILL.md +4 -2
  26. package/skills/delegation/SKILL.md +10 -8
  27. package/skills/doctor/SKILL.md +33 -14
  28. package/skills/executing-plans/SKILL.md +2 -1
  29. package/skills/finishing-a-branch/SKILL.md +4 -2
  30. package/skills/freshness/SKILL.md +23 -10
  31. package/skills/memory/SKILL.md +12 -2
  32. package/skills/resolve-ticket/SKILL.md +15 -9
  33. package/skills/review-pr/SKILL.md +26 -16
  34. package/skills/setup/SKILL.md +123 -9
  35. package/skills/setup/agents/openai.yaml +5 -0
  36. package/skills/test-first/SKILL.md +3 -1
  37. package/skills/using-leo/SKILL.md +11 -6
  38. package/skills/using-leo/references/claude-mapping.md +2 -1
  39. package/skills/using-leo/references/codex-mapping.md +4 -5
  40. package/skills/using-leo/references/cursor-mapping.md +2 -1
  41. package/skills/using-leo/references/hermes-mapping.md +2 -1
  42. package/skills/using-leo/references/opencode-mapping.md +6 -3
  43. package/skills/verification/SKILL.md +2 -1
  44. package/skills/visual-verification/SKILL.md +2 -1
  45. package/skills/watch-review/SKILL.md +17 -14
  46. package/skills/watch-review/agents/openai.yaml +5 -0
  47. package/skills/worktrees/SKILL.md +3 -1
  48. package/skills/writing-plans/SKILL.md +2 -1
  49. package/skills/writing-skills/SKILL.md +9 -2
  50. package/vendor/jsonc-parser-3.3.1/LICENSE.md +21 -0
  51. package/vendor/jsonc-parser-3.3.1/README.md +26 -0
  52. package/vendor/jsonc-parser-3.3.1/lib/umd/impl/edit.js +201 -0
  53. package/vendor/jsonc-parser-3.3.1/lib/umd/impl/format.js +275 -0
  54. package/vendor/jsonc-parser-3.3.1/lib/umd/impl/parser.js +682 -0
  55. package/vendor/jsonc-parser-3.3.1/lib/umd/impl/scanner.js +456 -0
  56. package/vendor/jsonc-parser-3.3.1/lib/umd/impl/string-intern.js +42 -0
  57. package/vendor/jsonc-parser-3.3.1/lib/umd/main.d.ts +351 -0
  58. package/vendor/jsonc-parser-3.3.1/lib/umd/main.js +194 -0
  59. package/vendor/jsonc-parser-3.3.1/package.json +37 -0
  60. package/workflows/cost-tiered-fix.js +32 -4
@@ -7,7 +7,9 @@ description: >
7
7
  wide blast radius, hard to reverse, or that introduce new surface need
8
8
  genuine, viable alternatives with trade-offs weighed before any code gets
9
9
  written. Produces the chosen approach and its trade-offs, sized to the gate,
10
- handed off to leo:writing-plans.
10
+ handed off to leo:writing-plans. Use when choosing an approach before
11
+ non-trivial code. Do not use for contained reversible tweaks, investigation,
12
+ or writing the plan itself.
11
13
  when_to_use: >
12
14
  Before starting non-trivial code: a new feature, a new integration surface,
13
15
  a schema or data-model change, anything that's expensive or awkward to
@@ -5,7 +5,9 @@ description: >
5
5
  behavior. Five named phases — Reproduce, Localize, Hypothesize, Prove, Fix —
6
6
  each with an exit criterion, so a fix never lands before the cause is
7
7
  pinned to file:line. Diagnosis is read-only judge work (investigator); the
8
- fix happens separately, at the routed tier.
8
+ fix happens separately, at the routed tier. Use when a bug, failure, crash,
9
+ or surprising behavior needs diagnosis. Do not use for planned features,
10
+ reviewing a diff, or post-fix completion verification.
9
11
  when_to_use: >
10
12
  Any bug report, failing test, crash, stack trace, or "why does X happen"
11
13
  before proposing a fix — used by the investigator agent and by the main
@@ -61,7 +63,7 @@ you, and go again.
61
63
 
62
64
  After two failures on the same cause (two hypotheses tried and reverted, still
63
65
  no Prove), step up one tier rather than retrying at the same one — investigator
64
- haiku-assist steps to full investigator, investigator itself steps to a
66
+ explore findings feed a full investigator, and investigator itself steps to a
65
67
  second, more evidence-fed pass, capped at Opus. A genuine deadlock, or two
66
68
  Opus verdicts on the same cause that disagree → `expert`, announced in one
67
69
  line ("escalating to expert: <question>") before it's invoked, never silent.
@@ -4,7 +4,8 @@ description: >
4
4
  Operational mechanics for dispatching subagents — a single spawn or a large
5
5
  fan-out — the companion to the policy's "Delegate the labor" section.
6
6
  Covers brief construction, model/effort pinning, the four-state return
7
- contract, and ledger-backed progress tracking for long multi-agent runs.
7
+ contract, and ledger-backed progress tracking for long multi-agent runs. Use
8
+ when dispatching any subagent or fan-out. Do not use to choose a task's tier.
8
9
  when_to_use: >
9
10
  Any time work is routed to a subagent (explore, investigator, executor,
10
11
  implementer, reviewer, expert) rather than done inline — single dispatch or
@@ -36,8 +37,8 @@ version needs no follow-up question; the first invites three.
36
37
  ## Pin model and effort
37
38
 
38
39
  Every dispatch pins **model AND effort** from the routing table — opus for
39
- judges (reviewer, investigator), sonnet for execution (implementer, executor
40
- on normal work), haiku for mechanical work (executor on boilerplate). expert
40
+ judges (reviewer, investigator), sonnet for normal implementation
41
+ (implementer), haiku for mechanical work (executor). expert
41
42
  never appears in a fan-out — one at a time, never fanned. An unpinned call
42
43
  silently inherits the session's tier: in an opus session that means every
43
44
  executor spawn quietly runs at opus, and a ten-item fan-out burns
@@ -53,7 +54,7 @@ a report that hedges across two of them.
53
54
  |---|---|---|
54
55
  | `done` | Work finished, matches the brief | Verify against artifacts — see leo:verification — never take the self-report at face value |
55
56
  | `concerns` | Finished, but flags something worth a second look | Read the concerns before accepting; they're often the real finding |
56
- | `needs-context` | Blocked on missing information you can supply | Send the missing piece to the same agent (`SendMessage` on Claude Code — elsewhere see the *Follow-up to a live agent* row of your mapping, and where none is established, cold re-dispatch with the context restated is the whole mechanism) so it keeps the context it already built. Either way **once** — a second needs-context on the same gap means the brief itself is broken, escalate the tier |
57
+ | `needs-context` | Blocked on missing information you can supply | Send the missing piece to the same agent (`SendMessage` on Claude Code, `followup_task` on Codex — elsewhere see the *Follow-up to a live agent* row of your mapping, and where none is established, cold re-dispatch with the context restated is the whole mechanism) so it keeps the context it already built. Either way **once** — a second needs-context on the same gap means the brief itself is broken, escalate the tier |
57
58
  | `blocked` | Blocked on something you can't hand over inline | Resolve the blocker, or escalate per the ladder — never a silent same-tier retry |
58
59
 
59
60
  `needs-context` and `blocked` look similar; the test is whether the missing
@@ -89,10 +90,11 @@ between "agent finished" and "ledger written" is exactly the gap this
89
90
  exists to close.
90
91
 
91
92
  `${CLAUDE_PLUGIN_ROOT}` above is the Claude Code spelling of the plugin root,
92
- and it is substituted into this text only there. On another harness, read the
93
- plugin-root form from that harness's appendix in the injected policy (Codex
94
- uses a real `$PLUGIN_ROOT` env var, Cursor `$CURSOR_PLUGIN_ROOT`) the path
95
- after the root is identical everywhere.
93
+ and it is substituted into this text only there. Codex exposes `$PLUGIN_ROOT`
94
+ and Cursor `$CURSOR_PLUGIN_ROOT`. Hermes and OpenCode expose no root variable;
95
+ their injected policy substitutes an absolute payload path into the `state.py`
96
+ and `memory.py` commands, which is the discoverable source to reuse. Do not
97
+ invent an environment variable where the harness exposes none.
96
98
 
97
99
  For a batch of independent, well-scoped fixes, don't hand-roll this loop —
98
100
  the reusable workflow at `${CLAUDE_PLUGIN_ROOT}/workflows/cost-tiered-fix.js`
@@ -6,7 +6,9 @@ description: >
6
6
  machine-local state and the memory store live, and which skills shipped
7
7
  versus which this session can actually invoke. Disk facts come from a
8
8
  helper script; the context facts only the running session can answer, and
9
- a disagreement between the two columns is the diagnosis.
9
+ a disagreement between the two columns is the diagnosis. Use when Leo asks
10
+ about Leo's loading, routing, or skill wiring. Do not use for project health
11
+ checks, project-code debugging, or unprompted inspection.
10
12
  when_to_use: >
11
13
  Leo asks whether the policy loaded, why routing or a skill is misbehaving,
12
14
  or invokes doctor by name after installing, updating, or switching harness.
@@ -37,14 +39,22 @@ argument, not a fault.
37
39
 
38
40
  `${CLAUDE_PLUGIN_ROOT}` is the Claude Code spelling. Codex exports
39
41
  `$PLUGIN_ROOT` and Cursor `$CURSOR_PLUGIN_ROOT`. On Hermes and OpenCode no
40
- plugin-root variable exists at all — but the policy already in your context had
41
- its placeholders substituted before injection, so the absolute path appears in
42
- its machine-local state paragraph. Read it from there. Being unable to locate
42
+ plugin-root variable exists at all — the injected policy instead substitutes an
43
+ absolute payload path into its `state.py` and `memory.py` commands. Read that
44
+ command path from the policy as the discoverable source. Being unable to locate
43
45
  the payload at all is itself the first finding: the harness is not looking where
44
46
  the plugin was installed.
45
47
 
46
48
  Add `--json` when you want the same facts as data.
47
49
 
50
+ Doctor validates the bootstrap that actually belongs to the named harness:
51
+ the session hook and manifest for Claude, Codex, and Cursor;
52
+ `config.instructions` in OpenCode's plugin; and Hermes registration plus its
53
+ first-tool-result fallback. It also reports the running Python version against
54
+ the supported 3.9+ floor. Codex hook trust is not provable from disk: review
55
+ the plugin in `/hooks` and confirm it is trusted before treating the on-disk
56
+ hook as active.
57
+
48
58
  ## Then answer the three it cannot
49
59
 
50
60
  A script can prove the hook is installed and that the policy renders. It cannot
@@ -56,13 +66,17 @@ prove the policy arrived. Only you can see your own context.
56
66
  then points at models that do not exist here.
57
67
  2. **Which skills are actually invocable?** Compare your own skill list against
58
68
  the script's shipped roster. Mind the naming rule: most harnesses namespace
59
- them as `leo:<name>`, while OpenCode registers the directory by path and
60
- names each skill from its own frontmatter, so they appear bare there. A
61
- skill that looks missing on OpenCode may simply be listed without a prefix.
69
+ them as `leo:<name>`, while OpenCode has no namespace and requires a
70
+ skill's frontmatter name to match its directory, so the plugin registers a
71
+ generated shadow copy with every skill renamed `leo-<name>`. A skill that
72
+ looks missing on OpenCode may simply be listed as `leo-<name>` rather than
73
+ `leo:<name>`.
62
74
  3. **Is memory present and delivered?** The script reports whether the store
63
75
  exists and whether each native surface received its generated copy. Whether
64
76
  those facts are in front of you right now is something only you can confirm.
65
- Report the two separately; they disagree more often than expected.
77
+ Report the two separately; they disagree more often than expected. Hermes's
78
+ projection is opt-in, so doctor reports it explicitly as disabled rather
79
+ than silently omitting it.
66
80
 
67
81
  ## Reading the report
68
82
 
@@ -72,10 +86,13 @@ three: **healthy**, **degraded**, or **not loaded**. Never free prose. `not
72
86
  loaded` outranks everything else: if the policy did not arrive, nothing else in
73
87
  the report describes how this session will actually behave.
74
88
 
75
- **Breadcrumb logs are history, not a verdict.** They carry no timestamps, and
76
- the test suite drives the failure paths deliberately, so entries accumulate on
77
- any machine where the tests have ever run. Quote the newest line if it is
78
- useful, but never conclude "the hook failed this session" from it.
89
+ **Most breadcrumb logs are history, not a verdict.** Some older logs carry no
90
+ timestamps, and the test suite drives failure paths deliberately, so entries
91
+ can accumulate on a development machine. Quote the newest line if useful, but
92
+ never conclude "the hook failed this session" from history alone. The one
93
+ capability exception is `opencode-skills.log`: its presence means namespaced
94
+ OpenCode skill registration degraded and doctor reports that state until the
95
+ breadcrumb is cleared after the underlying problem is understood.
79
96
 
80
97
  ## Failure modes
81
98
 
@@ -83,9 +100,11 @@ useful, but never conclude "the hook failed this session" from it.
83
100
  |---|---|---|
84
101
  | Policy absent, bootstrap installed | the hook fired and failed open | read the newest breadcrumb, then confirm it describes this session before believing it |
85
102
  | Policy present, mapping names another harness | detection resolved wrong, usually a stray plugin-root variable exported in an unrelated shell | unset it, restart the session |
86
- | Harness reported as `unknown` | no `--harness`, and this harness exports no plugin-root variable | re-run with `--harness <name>` read off your mapping heading |
103
+ | Harness reported as `unknown` | no `--harness`, and this harness exports no plugin-root variable | degraded until re-run with `--harness <name>` read off your mapping heading; a still-unknown explicit run is invalid wiring |
104
+ | Codex hook is on disk but policy is absent | the new or changed hook may not be trusted | open `/hooks`, review the hook, and explicitly trust it |
105
+ | OpenCode reports `opencode-skills.log` | the namespaced shadow tree failed and no bare-name fallback was registered | inspect the newest breadcrumb, fix the path/permission failure, and restart OpenCode |
87
106
  | Shipped roster exceeds what you can invoke | the harness cached an older payload, or the skills directory is not registered | update the plugin; on OpenCode check `opencode debug skill` for each skill's `location` |
88
- | Skills listed without the `leo:` prefix | OpenCode, working as designed | invoke them bare; not a fault |
107
+ | Skills listed as `leo-<name>` instead of `leo:<name>` | OpenCode, working as designed | invoke them as `leo-<name>`; not a fault |
89
108
  | Tier names resolve to models this harness cannot run | mapping and harness disagree | same as row 2 |
90
109
  | Machine-local state not writable | the path override points somewhere unwritable | fix or unset it |
91
110
  | A skill is genuinely absent from disk | it was never added | see leo:writing-skills |
@@ -5,7 +5,8 @@ description: >
5
5
  with a check at every batch boundary, plan-intent-wins-on-architecture /
6
6
  reality-wins-on-mechanics arbitration, and one fix-then-re-review cycle
7
7
  before stopping to report. Used by the implementer agent, or the main loop
8
- when it executes a plan directly.
8
+ when it executes a plan directly. Use when a written plan is about to become
9
+ code. Do not use for open-ended work without a plan or for reviewing a diff.
9
10
  when_to_use: >
10
11
  A written plan (from planner, an issue, or Leo's own outline) is about to
11
12
  be turned into code. NOT for open-ended implementation with no plan
@@ -6,7 +6,9 @@ description: >
6
6
  a closed set of next steps — merge / PR / keep / discard — routes the
7
7
  chosen path through the right ordering (land the work before removing the
8
8
  worktree, remove the worktree before deleting the branch), and leaves the
9
- repo clean.
9
+ repo clean. Use when reviewed implementation on a branch/worktree needs a
10
+ terminal disposition. Do not use to manage a worktree mid-task or replace
11
+ the review cycle.
10
12
  when_to_use: >
11
13
  A branch or worktree has reached "implementation done" and Leo needs to
12
14
  decide what happens to it. Fires after execute-then-review completes, or
@@ -71,7 +73,7 @@ Per option:
71
73
  | Option | Sequence |
72
74
  |---|---|
73
75
  | merge | merge locally or `gh pr merge` → remove worktree (`leo:worktrees`) → delete local branch |
74
- | PR | push branch → open PR → **stop** (worktree and branch stay; nothing is unmerged yet) |
76
+ | PR | push branch → open PR → **stop** (worktree and branch stay; nothing is merged yet.) |
75
77
  | keep | do nothing destructive; leave worktree and branch as-is |
76
78
  | discard | typed confirmation (below) → remove worktree (`leo:worktrees`) → force-delete branch |
77
79
 
@@ -6,7 +6,9 @@ description: >
6
6
  committed to, its shape is confirmed against a source that reflects the
7
7
  version this project actually runs — the installed package, the lockfile
8
8
  pin, or documentation fetched this turn. Each check is recorded by symbol
9
- and source in the report.
9
+ and source in the report. Use when writing, reviewing, or asserting a
10
+ third-party surface. Do not use for first-party code, pinned standard
11
+ libraries, or as a substitute for verification.
10
12
  when_to_use: >
11
13
  About to write, review, or assert the shape of a third-party surface — an
12
14
  import path, an argument list, a config key, an HTTP field, an auth
@@ -46,14 +48,25 @@ Outside these five, write the code.
46
48
 
47
49
  ## What counts as a source
48
50
 
49
- Ranked by closeness to the bytes that will actually execute.
51
+ Two different questions — which to reach for, and which one wins.
50
52
 
51
- | Rank | Source | Why it counts |
52
- |---|---|---|
53
- | 1 | The installed package read on disk `node_modules`, `site-packages`, `vendor` | It is the version this project runs; it cannot be out of date |
54
- | 2 | The lockfile pin plus that version's changelog or docs, fetched this turn | Right version, second-hand text |
55
- | 3 | Official documentation fetched this turn, version confirmed against the pin | Fetching without checking which version the page describes buys nothing |
56
- | 4 | A documentation tool the harness exposes for that vendor | Vendor-maintained, still confirm which version answered |
53
+ **Lookup order.** Cheapest first; stop at the first that answers.
54
+
55
+ 1. A documentation tool the harness exposes for that vendor (Context7 and
56
+ the like) one call, cheap.
57
+ 2. Official documentation fetched this turn cheap.
58
+ 3. The lockfile pin plus that version's changelog a narrow read.
59
+ 4. The installed package read on disk — `node_modules`, `site-packages`,
60
+ `vendor` — expensive; grep for the specific symbol, never read whole
61
+ files.
62
+
63
+ Rungs 1 and 2 answer for whichever version they happen to describe, which is
64
+ not always yours. Note the version each one reports and compare it to the pin;
65
+ a cheap answer that cannot say which version it describes has not answered.
66
+
67
+ **Authority.** When two sources disagree, the installed package wins — it
68
+ is the version that will execute. A cheap source that contradicts it is
69
+ wrong.
57
70
 
58
71
  Not sources: your recollection; an older file in this repo calling the same API,
59
72
  which may be the stale thing you are about to copy; a blog post; a search
@@ -97,8 +110,8 @@ to the lockfile without rerunning anything.
97
110
  case where they do not is the entire reason for the step.
98
111
  - "Another file here calls it this way" — that file may be what you are about
99
112
  to propagate.
100
- - "The typechecker will catch it" — a typechecker reads installed stubs, which
101
- is a rank-1 source. Say so and cite it, rather than skipping and hoping.
113
+ - "The typechecker will catch it" — a typechecker reads installed stubs, the
114
+ authority source. Say so and cite it, rather than skipping and hoping.
102
115
  - "It's one argument" — argument names are exactly what moves between majors.
103
116
 
104
117
  ## Reviewable finding
@@ -5,7 +5,9 @@ description: >
5
5
  session and every plugin update. Covers what earns a place in the store,
6
6
  how a fact is written and revised, how to read one before acting on it,
7
7
  and when to throw one away. The store is canonical; each harness's own
8
- memory surface receives a generated copy of the global facts.
8
+ memory surface receives a generated copy of the global facts. Use when a
9
+ durable preference, repo rule, decision, or machine quirk surfaces. Do not
10
+ use for current-task state or facts already recorded in the repository.
9
11
  when_to_use: >
10
12
  A fact surfaces that will still be true next month — a stated preference,
11
13
  a repo rule the code does not spell out, a settled decision, a machine
@@ -98,7 +100,15 @@ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/memory.py" write repo convention "Generat
98
100
  ```
99
101
 
100
102
  Writing the same title again revises that file in place and keeps its original
101
- creation date. `list` shows what is stored; `read <ref>` returns one fact whole.
103
+ creation date only when both title and type match exactly. A slug collision with
104
+ a different title or type receives the next `-N` suffix; a corrupt occupied
105
+ slot is never overwritten. `list` shows what is stored; `read <ref>` returns
106
+ one fact whole.
107
+
108
+ Leo-owned memory directories use mode `0700`; fact files and the generated
109
+ `index.json` and `MEMORY.md` use `0600`. A newly generated projection is also
110
+ private, while an existing user-owned projection target retains the mode the
111
+ user chose.
102
112
 
103
113
  ## Read path
104
114
 
@@ -5,7 +5,8 @@ description: >
5
5
  context (Confluence, Slack, GitHub), investigate and plan at Opus tier, get
6
6
  Leo's explicit sign-off, implement on a worktree branch with sonnet/haiku
7
7
  executors, Opus-review the diff, then push and open a DRAFT pull request in
8
- the browser.
8
+ the browser. Use when Leo names a tracked ticket to fix or implement. Do not
9
+ use for ad-hoc fixes without a ticket or independent-item batches.
9
10
  when_to_use: >
10
11
  Leo asks to fix or implement a specific tracked ticket by ID ("fix ENG-123",
11
12
  "/resolve-ticket PLAT-42"). NOT for ad-hoc fixes with no ticket (normal
@@ -23,6 +24,7 @@ allowed-tools:
23
24
  - Bash(git fetch *)
24
25
  - Bash(git rev-parse *)
25
26
  - Bash(git merge-base *)
27
+ - Bash(git check-ignore *)
26
28
  - Bash(git checkout *)
27
29
  - Bash(git switch *)
28
30
  - Bash(git add *)
@@ -137,8 +139,8 @@ Collect URLs from the ticket body, comments, attachments, and (Jira)
137
139
  tell Leo explicitly** that Slack must be configured independently in the
138
140
  current harness, then continue without it.
139
141
  - **GitHub PR/issue/commit** → `gh` view commands.
140
- - **Anything else** → whatever fetch tool this harness offers, one attempt;
141
- `curl` is the fallback.
142
+ - **Anything else** → use an available connector or fetch tool once. If none
143
+ is connected, report that context gap to Leo; do not use a shell HTTP fallback.
142
144
 
143
145
  Every failure or skip goes into a **context-gaps list** shown at the sign-off
144
146
  gate — Leo sees exactly what wasn't read before approving.
@@ -182,7 +184,8 @@ Only after Approve: `git fetch origin`, then create branch
182
184
  `fix/<TICKET-ID>-<slug>` off `origin/<defaultBranch>` in a worktree. Where the
183
185
  harness has a native worktree tool (see the *Worktrees* row of your mapping),
184
186
  use it and pair every enter with an exit. Otherwise, and on every harness that
185
- does not: `git worktree add -b fix/<id>-<slug> ../<repo>-fix-<id>
187
+ does not: first prove `git check-ignore .claude/worktrees/fix-<id>` succeeds,
188
+ then `git worktree add -b fix/<id>-<slug> .claude/worktrees/fix-<id>
186
189
  origin/<default>` and work by absolute paths.
187
190
 
188
191
  Executors in Step 6 must **NOT** be given their own worktree — this is one
@@ -202,8 +205,9 @@ Per plan step:
202
205
  specs, so the executor contract (do exactly this, stop on ambiguity) fits
203
206
  better than implementer's wider latitude. Anywhere the plan is thinner than
204
207
  that, use `implementer` as the policy says.
205
- - Steps touching disjoint files run as parallel spawns; dependent steps
206
- sequential. Executors commit as they go.
208
+ - Parallel spawns are read-only investigation only. All edits, test writes,
209
+ staging, commits, and other mutations are strictly sequential in the one
210
+ canonical `.claude/worktrees/fix-<id>` worktree. Executors commit as they go.
207
211
  - This loop implements directly only for trivial diffs (< ~10 lines) where
208
212
  writing the spec would cost more than the change.
209
213
  - Escalate, don't struggle: an executor reporting ambiguity or failing twice →
@@ -248,9 +252,11 @@ ticket, the approved plan, and the diff scope
248
252
  Same voice rules as /review-pr: no filler, no emoji, no self-praise.
249
253
  If a PR already exists for the branch, open that one instead and say so.
250
254
  3. `gh pr view --web` to open it in the browser.
251
- 4. Leave the worktree (native tool where there is one, otherwise `git worktree
252
- remove` once the branch is pushed). Do **not** write back to the ticket (no comment, no status
253
- transition) deliberate non-action; Leo asks separately if he wants it.
255
+ 4. Retain the worktree through PR merge. After merge, hand cleanup to
256
+ `leo:finishing-a-branch` / `leo:worktrees`; do not remove it merely because
257
+ the draft PR was opened. Do **not** write back to the ticket (no comment,
258
+ no status transition) — deliberate non-action; Leo asks separately if he
259
+ wants it.
254
260
  5. Final report: branch, PR URL, worktree path (left in place for follow-ups),
255
261
  checks run, review rounds used, remaining non-blocking notes.
256
262
 
@@ -6,11 +6,12 @@ description: >
6
6
  submitted. Handles Leo's existing reviews: a stale pending review is
7
7
  replaced; posted threads are left, resolved, or get a staged reply.
8
8
  Reports the staged comments and a merge verdict in chat. Requires gh,
9
- installed and authenticated.
9
+ installed and authenticated. Use when Leo asks to review a GitHub PR. Do not
10
+ use for a local working diff or to submit a review.
10
11
  when_to_use: >
11
12
  Leo asks to review a pull request by number ("review PR 42", "/review-pr 42")
12
13
  or "review the PR for this branch". NOT for reviewing the local working diff
13
- (that is /code-review or the reviewer subagent) and NOT for submitting a
14
+ (that is the local reviewer subagent) and NOT for submitting a
14
15
  review — this only stages draft comments.
15
16
  argument-hint: "[pr-number]"
16
17
  allowed-tools:
@@ -26,6 +27,7 @@ allowed-tools:
26
27
  - Bash(git merge-base *)
27
28
  - Bash(git status *)
28
29
  - Bash(python3 */ghreview.py *)
30
+ - Bash(python3 "*/ghreview.py" *)
29
31
  - Agent
30
32
  ---
31
33
 
@@ -39,8 +41,8 @@ Run this at the **Opus tier** — it ends in a merge verdict, which is judge
39
41
  work. Tier map: Sonnet reads (the lens agents), Opus judges (this main loop).
40
42
  Your harness mapping names the concrete model for each, and says whether a
41
43
  per-spawn model override exists here at all; where it does not, the lenses run
42
- at whatever their registered agent runs. The staged
43
- review is created by `${CLAUDE_PLUGIN_ROOT}/scripts/ghreview.py` in ONE API call
44
+ at whatever their registered agent runs. The staged review is created by
45
+ `"${CLAUDE_PLUGIN_ROOT}/scripts/ghreview.py"` in ONE API call
44
46
  with no `event` field — that is what keeps it PENDING. Never use `gh pr review`
45
47
  (it always submits) and never set an `event` value.
46
48
 
@@ -97,7 +99,7 @@ Two kinds of prior review state, handled differently:
97
99
  it carries the script's own marker (it embeds one in everything it stages):
98
100
 
99
101
  ```
100
- python3 ${CLAUDE_PLUGIN_ROOT}/scripts/ghreview.py clear-pending -R OWNER/REPO -n N
102
+ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/ghreview.py" clear-pending -R OWNER/REPO -n N
101
103
  ```
102
104
 
103
105
  If it exits 0, note what was deleted in the final report. If it exits 3, it
@@ -110,13 +112,17 @@ stage step, `--replace-pending --force`) once he confirms. Still pass
110
112
  **Posted (submitted) review threads of mine** — fetch them:
111
113
 
112
114
  ```
113
- python3 ${CLAUDE_PLUGIN_ROOT}/scripts/ghreview.py threads -R OWNER/REPO -n N
115
+ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/ghreview.py" threads -R OWNER/REPO -n N
114
116
  ```
115
117
 
116
118
  Returns unresolved threads whose root comment is mine (threads from pending
117
- reviews are excluded automatically; `line` is null for file-level threads).
119
+ reviews are excluded automatically). A true file-level thread has both
120
+ `line: null` and `original_line: null`; report it as `path:file-level`. An
121
+ outdated line thread can have `line: null`, `original_line: <N>`, and
122
+ `is_outdated: true`; report it at `path:<N>` with an `outdated` label, not as
123
+ file-level.
118
124
  For each thread, judge the original comment against the **current** diff
119
- (`ghreview.py extract` for that path — `is_outdated` means the nearby code
125
+ (`ghreview.py extract` for that path — `is_outdated: true` means the nearby code
120
126
  changed, which is a hint, not a verdict) and pick one action, defaulting to
121
127
  *leave* when torn:
122
128
 
@@ -132,7 +138,7 @@ adjudication is complete.
132
138
  ## Step 2 — Map the diff and pick a route
133
139
 
134
140
  ```
135
- python3 ${CLAUDE_PLUGIN_ROOT}/scripts/ghreview.py map -R OWNER/REPO -n N
141
+ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/ghreview.py" map -R OWNER/REPO -n N
136
142
  ```
137
143
 
138
144
  Returns per-file addressable-line ranges, `generated` flags (lockfiles, dist,
@@ -149,7 +155,7 @@ the post-exclusion size:
149
155
 
150
156
  Spawn three subagents at once using this harness's spawn mechanism — leo:delegation
151
157
  and the *Subagent spawn* row of your mapping name it. Use the read-only
152
- **explore** role, never a general-purpose agent: a lens is the agent that
158
+ **review-lens** role, never a general-purpose agent: a lens is the agent that
153
159
  actually ingests the attacker-authored diff, and a general-purpose agent
154
160
  carries the full tool set including Write, Edit, and unrestricted Bash. This
155
161
  skill's `allowed-tools` govern this loop's turn, not the agents it spawns, so
@@ -166,7 +172,7 @@ sharded review that hits the agent cap.
166
172
  Do NOT ingest the full diff in this main loop on the standard
167
173
  path — the lenses read, you judge. Each lens gets: PR number, `OWNER/REPO`,
168
174
  title/body, its file list, and instructions to fetch its own diff slice via
169
- `gh pr diff N` or `python3 ${CLAUDE_PLUGIN_ROOT}/scripts/ghreview.py extract -R OWNER/REPO -n N <paths…>`
175
+ `gh pr diff N` or `python3 "${CLAUDE_PLUGIN_ROOT}/scripts/ghreview.py" extract -R OWNER/REPO -n N <paths…>`
170
176
  (resolve the plugin root and pass the absolute path into the prompt — a
171
177
  subagent does not inherit your placeholder).
172
178
 
@@ -185,8 +191,10 @@ Charters:
185
191
  behavior, dead code, misleading names, genuine style nits worth a human's
186
192
  comment.
187
193
 
188
- Each lens returns findings as JSON only:
189
- `[{path, line, side: "RIGHT"|"LEFT", severity: "blocking"|"major"|"minor"|"nit", confidence: 0-100, note, fix?}]`
194
+ Each lens returns JSON only:
195
+ `{"status":"done"|"needs-context","findings":[{path, line, side:
196
+ "RIGHT"|"LEFT", severity: "blocking"|"major"|"minor"|"nit", confidence:
197
+ 0-100, note, fix?}]}`
190
198
  with `line` as the absolute new-file line (RIGHT) it verified against the
191
199
  patch, and an instruction to cite the exact diff line — unverifiable findings
192
200
  get dropped in Step 4, so guessing wastes the lens's own work.
@@ -234,7 +242,7 @@ resolutions are public and go last, only once staging has succeeded):
234
242
  (`{"comments": [{path, line, side, body, start_line?, start_side?}]}`), then:
235
243
 
236
244
  ```
237
- python3 ${CLAUDE_PLUGIN_ROOT}/scripts/ghreview.py stage -R OWNER/REPO -n N \
245
+ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/ghreview.py" stage -R OWNER/REPO -n N \
238
246
  --commit <headRefOid> --input comments.json --replace-pending
239
247
  ```
240
248
 
@@ -255,7 +263,7 @@ resolutions are public and go last, only once staging has succeeded):
255
263
  scratchpad file:
256
264
 
257
265
  ```
258
- python3 ${CLAUDE_PLUGIN_ROOT}/scripts/ghreview.py reply -R OWNER/REPO -n N \
266
+ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/ghreview.py" reply -R OWNER/REPO -n N \
259
267
  --thread-id PRRT_… --body-file reply.txt
260
268
  ```
261
269
 
@@ -266,7 +274,7 @@ resolutions are public and go last, only once staging has succeeded):
266
274
  3. **Resolve stale threads** — one call per Step 1 resolve action:
267
275
 
268
276
  ```
269
- python3 ${CLAUDE_PLUGIN_ROOT}/scripts/ghreview.py resolve-thread -R OWNER/REPO -n N \
277
+ python3 "${CLAUDE_PLUGIN_ROOT}/scripts/ghreview.py" resolve-thread -R OWNER/REPO -n N \
270
278
  --thread-id PRRT_…
271
279
  ```
272
280
 
@@ -283,6 +291,8 @@ error.
283
291
 
284
292
  1. Staged comments as a table: `path:line — comment`.
285
293
  2. Existing threads as a table: `path:line — left / resolved / reply staged`
294
+ (use `path:file-level` only when both anchors are null; otherwise use
295
+ `path:original_line` with an `outdated` label when the current line is null)
286
296
  (+ what was said in staged replies; note if a stale pending review was
287
297
  replaced, and that resolutions are already live).
288
298
  3. Unstaged findings (dropped anchors, overflow past the cap) — clearly marked.