@iceinvein/agent-skills 0.22.0 → 0.23.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.
Files changed (90) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/skills/bug-hunter/SKILL.md +8 -1
  4. package/skills/bug-hunter/evals/README.md +6 -1
  5. package/skills/bug-hunter/evals/callers-tree-unchanged/graders/output-ignores-itself.md +11 -0
  6. package/skills/bug-hunter/evals/rules.json +8 -0
  7. package/skills/bug-hunter/skill.json +1 -1
  8. package/skills/index.json +6 -6
  9. package/skills/magpie/README.md +5 -3
  10. package/skills/magpie/SKILL.md +41 -15
  11. package/skills/magpie/bin/magpie.ts +83 -7
  12. package/skills/magpie/evals/README.md +7 -4
  13. package/skills/magpie/evals/local-mode-hands-back/case.yaml +4 -0
  14. package/skills/magpie/evals/local-mode-hands-back/fixture.sh +300 -0
  15. package/skills/magpie/evals/local-mode-hands-back/graders/branch-was-reachable.md +5 -0
  16. package/skills/magpie/evals/local-mode-hands-back/graders/cleanup-after-handoff.md +6 -0
  17. package/skills/magpie/evals/local-mode-hands-back/graders/handoff-was-run.md +6 -0
  18. package/skills/magpie/evals/local-mode-hands-back/graders/nothing-served-or-posted.md +7 -0
  19. package/skills/magpie/evals/local-mode-hands-back/graders/reply-lists-both-findings.md +10 -0
  20. package/skills/magpie/evals/local-mode-hands-back/graders/skill-fired.md +5 -0
  21. package/skills/magpie/evals/local-mode-hands-back/prompt.md +11 -0
  22. package/skills/magpie/evals/quality/README.md +73 -2
  23. package/skills/magpie/evals/quality/__tests__/recall.test.ts +234 -0
  24. package/skills/magpie/evals/quality/__tests__/replay-full.test.ts +164 -0
  25. package/skills/magpie/evals/quality/corpus.ts +4 -1
  26. package/skills/magpie/evals/quality/recall.ts +249 -0
  27. package/skills/magpie/evals/quality/replay-critic.ts +3 -0
  28. package/skills/magpie/evals/quality/replay-full.ts +168 -6
  29. package/skills/magpie/evals/rules.json +9 -1
  30. package/skills/magpie/references/checks.md +78 -0
  31. package/skills/magpie/references/peer-review.md +7 -0
  32. package/skills/magpie/references/scout.md +12 -5
  33. package/skills/magpie/references/specialists.md +10 -8
  34. package/skills/magpie/scripts/__tests__/cleanup-cmd.test.ts +94 -0
  35. package/skills/magpie/scripts/__tests__/critic-cmd.test.ts +18 -0
  36. package/skills/magpie/scripts/__tests__/dedupe-cmd.test.ts +80 -1
  37. package/skills/magpie/scripts/__tests__/handoff-cmd.test.ts +319 -0
  38. package/skills/magpie/scripts/__tests__/housekeeping-cmd.test.ts +14 -0
  39. package/skills/magpie/scripts/__tests__/local-diff.test.ts +275 -0
  40. package/skills/magpie/scripts/__tests__/pipeline-e2e.test.ts +1 -0
  41. package/skills/magpie/scripts/__tests__/resolve-run.test.ts +28 -0
  42. package/skills/magpie/scripts/__tests__/server.test.ts +30 -1
  43. package/skills/magpie/scripts/__tests__/setup-cmd.test.ts +324 -1
  44. package/skills/magpie/scripts/__tests__/skill-lint.test.ts +8 -5
  45. package/skills/magpie/scripts/__tests__/status-cmd.test.ts +44 -0
  46. package/skills/magpie/scripts/__tests__/target.test.ts +84 -0
  47. package/skills/magpie/scripts/__tests__/types.test.ts +2 -1
  48. package/skills/magpie/scripts/cleanup-cmd.ts +5 -1
  49. package/skills/magpie/scripts/critic-cmd.ts +2 -1
  50. package/skills/magpie/scripts/dedupe-cmd.ts +15 -5
  51. package/skills/magpie/scripts/handoff-cmd.ts +132 -0
  52. package/skills/magpie/scripts/housekeeping-cmd.ts +7 -5
  53. package/skills/magpie/scripts/local-diff.ts +228 -0
  54. package/skills/magpie/scripts/post-cmd.ts +1 -0
  55. package/skills/magpie/scripts/render-annotation.ts +1 -0
  56. package/skills/magpie/scripts/resolve-run.ts +17 -3
  57. package/skills/magpie/scripts/server.ts +9 -1
  58. package/skills/magpie/scripts/setup-cmd.ts +241 -49
  59. package/skills/magpie/scripts/status-cmd.ts +22 -6
  60. package/skills/magpie/scripts/target.ts +56 -0
  61. package/skills/magpie/scripts/types.ts +1 -0
  62. package/skills/magpie/skill.json +2 -2
  63. package/skills/scope-estimate/SKILL.md +1 -1
  64. package/skills/scope-estimate/skill.json +16 -5
  65. package/skills/sluice/SKILL.md +6 -2
  66. package/skills/sluice/evals/README.md +10 -2
  67. package/skills/sluice/evals/fast-leaves-an-unrelated-red-test-alone/case.yaml +4 -0
  68. package/skills/sluice/evals/fast-leaves-an-unrelated-red-test-alone/fixture.sh +108 -0
  69. package/skills/sluice/evals/fast-leaves-an-unrelated-red-test-alone/graders/deploy-names-its-steps.md +9 -0
  70. package/skills/sluice/evals/fast-leaves-an-unrelated-red-test-alone/graders/kept-status-by-changing-code.md +12 -0
  71. package/skills/sluice/evals/fast-leaves-an-unrelated-red-test-alone/graders/sluice-fired.md +5 -0
  72. package/skills/sluice/evals/fast-leaves-an-unrelated-red-test-alone/graders/status-test-unchanged.md +6 -0
  73. package/skills/sluice/evals/fast-leaves-an-unrelated-red-test-alone/prompt.md +11 -0
  74. package/skills/sluice/evals/fast-updates-the-test-the-change-replaces/case.yaml +4 -0
  75. package/skills/sluice/evals/fast-updates-the-test-the-change-replaces/fixture.sh +73 -0
  76. package/skills/sluice/evals/fast-updates-the-test-the-change-replaces/graders/proved-after-the-last-edit.md +18 -0
  77. package/skills/sluice/evals/fast-updates-the-test-the-change-replaces/graders/sluice-fired.md +5 -0
  78. package/skills/sluice/evals/fast-updates-the-test-the-change-replaces/graders/test-expects-the-new-line.md +5 -0
  79. package/skills/sluice/evals/fast-updates-the-test-the-change-replaces/graders/updated-without-asking.md +11 -0
  80. package/skills/sluice/evals/fast-updates-the-test-the-change-replaces/prompt.md +11 -0
  81. package/skills/sluice/evals/rules.json +11 -0
  82. package/skills/sluice/references/review.md +6 -4
  83. package/skills/sluice/references/test-first.md +24 -4
  84. package/skills/sluice/scripts/content-digest.sh +1 -1
  85. package/skills/sluice/scripts/run-stats.sh +14 -0
  86. package/skills/sluice/scripts/session-start.sh +44 -2
  87. package/skills/sluice/scripts/status.sh +293 -0
  88. package/skills/sluice/scripts/stop-guard.sh +80 -6
  89. package/skills/sluice/scripts/test-changes.sh +135 -0
  90. package/skills/sluice/skill.json +2 -2
package/README.md CHANGED
@@ -121,7 +121,7 @@ Skills that compose the other audit skills into higher-level workflows.
121
121
  | Skill | What it does |
122
122
  |-------|--------------|
123
123
  | **improve-my-codebase** | Runs every applicable audit skill in parallel against your codebase, ranks findings by convergence (multiple audits agreeing) and per-file rollup, and writes a prioritized improvement report. Default sweeps everything; positional args switch modes (`quick`, `diff`, `interactive`) or narrow scope (`focus <area>`, `module <path>`). |
124
- | **magpie** | Interactive PR review pipeline. Runs five parallel specialist subagents (security, bugs, performance, code-smells, architecture), dedupes findings, applies a critic rubric, peer-reviews via `codex exec`, and serves an interactive HTML report for selecting findings to post via `gh`. |
124
+ | **magpie** | Interactive PR review pipeline. Runs five parallel specialist subagents (security, bugs, performance, code-smells, architecture), dedupes findings, applies a critic rubric, peer-reviews via `codex exec`, and serves an interactive HTML report for selecting findings to post via `gh`. A local mode reviews the current branch diff before a PR exists and hands the findings back in the terminal. |
125
125
 
126
126
  ## Commands
127
127
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iceinvein/agent-skills",
3
- "version": "0.22.0",
3
+ "version": "0.23.1",
4
4
  "description": "Install agent skills into AI coding tools",
5
5
  "author": "iceinvein",
6
6
  "license": "MIT",
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: bug-hunter
3
3
  description: Use when asked to hunt for bugs or edge cases in a change or area, to say what QA will find before QA does, or to prove a change correct. Runs QA-style scenarios as failing reproductions in the repo's own test runner and reports only bugs it can prove, plus the questions intent leaves open.
4
+ argument-hint: "[<commit>|<base>..<head>|<path>...]"
4
5
  ---
5
6
 
6
7
  # Bug hunter
@@ -12,7 +13,8 @@ a failing reproduction, checked against what the change was meant to do.
12
13
  ## Method
13
14
 
14
15
  1. **Name the target** in one line: a diff (a commit, a range, the working
15
- changes) or a set of paths. Every later step is scoped to it.
16
+ changes) or a set of paths, taken from the arguments when given and the
17
+ working changes otherwise. Every later step is scoped to it.
16
18
  2. **Collect intent** into `intent.md`: numbered claims (`I1`, `I2`, ...),
17
19
  each one checkable statement with its source. Sources are the request or
18
20
  ticket, commit messages, specs and designs, docs, `CLAUDE.md`/`AGENTS.md`,
@@ -76,6 +78,11 @@ The `.bug-hunter/` directory is the one path in the caller's tree the skill
76
78
  writes. Promoting a repro into the tree as a regression test is the caller's
77
79
  job.
78
80
 
81
+ The directory ignores itself, so no repo has to add a line for it: if
82
+ `.bug-hunter/.gitignore` is absent, write it holding `*` before anything else
83
+ in the run. Leave an existing one alone; deleting it is how a repo chooses to
84
+ commit its runs.
85
+
79
86
  ## Hand-back
80
87
 
81
88
  The run directory, the counts per bucket, then each bug in one line:
@@ -11,7 +11,7 @@ skill.
11
11
  | `proves-a-spec-boundary-bug` | Usernames are 3 to 20 characters inclusive; the code rejects 20 | A bug in `findings.json` citing the limit, with a repro that fails against the fixture |
12
12
  | `silent-spec-is-an-ambiguity` | Quantities are "capped"; the spec never says where, the code uses 99 | The cap lands in `ambiguities` and `bugs` is empty |
13
13
  | `correct-code-has-no-bugs` | Pagination that meets every stated range at its edge | `bugs` is empty |
14
- | `callers-tree-unchanged` | A backoff whose first delay is doubled | Nothing changes in the caller's tree outside `.bug-hunter/` |
14
+ | `callers-tree-unchanged` | A backoff whose first delay is doubled | Nothing changes in the caller's tree outside `.bug-hunter/`, and `.bug-hunter/.gitignore` is written holding `*` |
15
15
  | `bug-cites-intent-from-spec` | A coupon that floors where the spec rounds half up | `intent.md` is written, and the bug cites a claim sourced from `SPEC.md`, not from the code's floor |
16
16
  | `spec-outranks-target-docstring` | The spec keeps a sealed export on offer when its rewrite fails; the module's docstring and code drop it | A bug citing the spec claim, not an ambiguity between spec and docstring |
17
17
  | `impossible-value-is-a-bug` | The spec is silent on rounding; the code shows `60s` and `1m 60s` | A bug for the seconds field of 60, which no rounding rule gives |
@@ -97,6 +97,11 @@ a file needs its exact path and throws on a glob. So:
97
97
  An edit to `package.json` or `SPEC.md` is still unseen. A run that writes
98
98
  nothing passes all three, which is why the case also has
99
99
  `bug-hunter-fired`.
100
+ - `output-ignores-itself` in the same case reads the trace for a Write of
101
+ `.bug-hunter/.gitignore` whose content is `*`, or a shell command that
102
+ writes a `*` into it. The `files` target would show the path but not its
103
+ content, and an empty `.gitignore` ignores nothing. A script that opens the
104
+ file itself is not caught.
100
105
  - `bug-cites-the-spec` in `bug-cites-intent-from-spec` is an `llm` judge over
101
106
  the trace, with the same window and last-write limits as
102
107
  `bug-cites-the-limit`. It resolves the bug's `claim` through the `intent`
@@ -0,0 +1,11 @@
1
+ ---
2
+ type: regex
3
+ # SKILL.md has the run drop a .gitignore holding `*` into .bug-hunter/ when
4
+ # none is there, so no repo has to ignore the directory itself. The files
5
+ # target would show the path but not what it holds, and an empty .gitignore
6
+ # ignores nothing, so this reads the trace: a Write call whose content is `*`,
7
+ # or a shell command that writes a `*` into it.
8
+ pattern: '\.bug-hunter/\.gitignore"\s*,\s*"content"\s*:\s*"\*(?:\\n)?"|"command":"(?:\\.|[^"\\])*?\*(?:\\.|[^"\\])*?>\s*[^"\s]*\.bug-hunter/\.gitignore'
9
+ target: trace
10
+ weight: 2
11
+ ---
@@ -153,6 +153,14 @@
153
153
  "callers-tree-unchanged"
154
154
  ]
155
155
  },
156
+ {
157
+ "id": "bug-hunter-dir-ignores-itself",
158
+ "states": "SKILL.md: Output",
159
+ "origin": "skills/sluice/references/status.md",
160
+ "cases": [
161
+ "callers-tree-unchanged"
162
+ ]
163
+ },
156
164
  {
157
165
  "id": "promoting-a-repro-is-the-callers-job",
158
166
  "states": "SKILL.md: Output",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bug-hunter",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Hunts logic and correctness bugs in a change or area and proves each one with a failing reproduction checked against stated intent. One agent collects intent from the request, specs, docs and unchanged code, runs QA-style scenarios from a catalog as repros in the repo's own test runner inside a scratch copy, and re-runs each failure clean before reporting it. Counts a contradicted claim, or a result wrong under every reading, as a bug; keeps ambiguities for questions with more than one acceptable answer; reports latent defects without filing them. No repro, no finding. Writes report.md and findings.json (bugs, ambiguities, covered) to .bug-hunter/<date>-<slug>/ and never modifies the caller's tree. Language-agnostic; ships no per-language scripts.",
5
5
  "author": "iceinvein",
6
6
  "type": "prompt",
package/skills/index.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "name": "bug-hunter",
14
14
  "description": "Hunts logic and correctness bugs in a change or area and proves each one with a failing reproduction checked against stated intent. One agent collects intent from the request, specs, docs and unchanged code, runs QA-style scenarios from a catalog as repros in the repo's own test runner inside a scratch copy, and re-runs each failure clean before reporting it. Counts a contradicted claim, or a result wrong under every reading, as a bug; keeps ambiguities for questions with more than one acceptable answer; reports latent defects without filing them. No repro, no finding. Writes report.md and findings.json (bugs, ambiguities, covered) to .bug-hunter/<date>-<slug>/ and never modifies the caller's tree. Language-agnostic; ships no per-language scripts.",
15
15
  "type": "prompt",
16
- "version": "0.4.0"
16
+ "version": "0.4.2"
17
17
  },
18
18
  {
19
19
  "name": "codebase-architecture",
@@ -225,9 +225,9 @@
225
225
  },
226
226
  {
227
227
  "name": "magpie",
228
- "description": "Interactive PR review pipeline. Runs five parallel specialist subagents (security, bugs, performance, code-smells, architecture), drops findings whose quoted evidence is not in the code, dedupes, runs critic subagents that re-check each finding against the worktree and set its risk label, peer-reviews via codex exec (falling back to a Claude second opinion when codex is unavailable), and serves an interactive HTML report for selecting or dismissing findings to post via gh, recording the outcome per finding as labels. Bundles a Bun CLI installed onto PATH via the skill's postinstall step. Use when the user asks to review a GitHub pull request. Splits oversized diffs into budgeted shards and rebuilds the diff from the local clone when gh pr diff refuses it.",
228
+ "description": "Interactive PR review pipeline. Runs five parallel specialist subagents (security, bugs, performance, code-smells, architecture) plus a once-per-PR checks pass that finds tests and controls able to pass without proving their purpose, drops findings whose quoted evidence is not in the code, dedupes, runs critic subagents that re-check each finding against the worktree and set its risk label, peer-reviews via codex exec (falling back to a Claude second opinion when codex is unavailable), and serves an interactive HTML report for selecting or dismissing findings to post via gh, recording the outcome per finding as labels. Bundles a Bun CLI installed onto PATH via the skill's postinstall step. Use when the user asks to review a GitHub pull request. Local mode (setup --local) runs the same pipeline on the current branch's diff from its merge base, uncommitted and untracked work included, against the caller's live tree, with no server or posting, and hands the ranked findings back in the terminal for an implementing or orchestrating agent to address. Splits oversized diffs into budgeted shards and rebuilds the diff from the local clone when gh pr diff refuses it.",
229
229
  "type": "prompt",
230
- "version": "0.13.1"
230
+ "version": "0.14.1"
231
231
  },
232
232
  {
233
233
  "name": "migrate",
@@ -269,7 +269,7 @@
269
269
  "name": "scope-estimate",
270
270
  "description": "Scopes and estimates a rebuild or respec of an existing system for a proposal: read-only discovery agents write cited inventories, a capability register gives every capability a status and a proposed disposition, area agents turn it into sized functional requirements, and a bundled Bun scheduler groups them into slices and contracts-first work packages and schedules them across engineers x worktrees at fast, planning and floor rates. The scheduler refuses a plan that misses or double-counts a requirement or has a dependency cycle, so every figure in the docs comes from one reproducible run. Also covers current and target architecture pages, proposal review, and a two-week calibration before committing to a date. Lighter than migrate: no store and no phase gates.",
271
271
  "type": "prompt",
272
- "version": "0.1.0"
272
+ "version": "0.1.1"
273
273
  },
274
274
  {
275
275
  "name": "seam-finder",
@@ -293,9 +293,9 @@
293
293
  },
294
294
  {
295
295
  "name": "sluice",
296
- "description": "Routes work by change shape into four channels (bypass, fast, main, deep) and applies only the rules each channel needs, so a one-line fix does not pay the cost of a multi-subsystem build. Carries nine rules as one-liners in the router and the full treatment in references read only on friction. Checks the finished plan with plan.sh validate rather than trusting it to memory, seeds the run state from it, keeps a deep run's task breakdown in .sluice/run.json so a statusline segment, one status command and a SessionStart hook can answer where the run is (the hook prints a live run at every session start, compaction included), and closes each run with a ledger read out of the session transcript: elapsed, tools, tokens, and what each dispatched agent cost where the transcript recorded it. Claude Code only; stands down where the superpowers pipeline governs the repo.",
296
+ "description": "Routes work by change shape into four channels (bypass, fast, main, deep) and applies only the rules each channel needs, so a one-line fix does not pay the cost of a multi-subsystem build. Carries nine rules as one-liners in the router and the full treatment in references read only on friction. Checks the finished plan with plan.sh validate rather than trusting it to memory, seeds the run state from it, keeps a deep run's task breakdown in .sluice/run.json so a statusline segment, one status command and a SessionStart hook can answer where the run is (the hook prints a live run at every session start, compaction included), and closes each run with a ledger read out of the session transcript: elapsed, tools, tokens, and what each dispatched agent cost where the transcript recorded it. Two Stop hooks hold a handback: one refuses a tree that moved since its last recorded check, the other refuses a changed existing test not proven to fail on the session's starting code (or recorded with a reason). Claude Code only; stands down where the superpowers pipeline governs the repo.",
297
297
  "type": "prompt",
298
- "version": "0.24.1"
298
+ "version": "0.25.1"
299
299
  },
300
300
  {
301
301
  "name": "temporal-coupling-detector",
@@ -4,7 +4,7 @@ Interactive Claude Code skill that runs a multi-stage PR review pipeline inside
4
4
 
5
5
  ## What it does
6
6
 
7
- Given a GitHub PR number, dispatches five specialist subagents in parallel (security, bugs, performance, code-smells, architecture), each quoting an evidence snippet for every anchored finding. Dedupe drops findings whose snippet is not in the file and groups nearby findings from different domains as merge candidates. Critic subagents then re-check each surviving finding against the worktree, keep, drop or merge it, and set its risk label, from which the severity is derived. Peer review runs via `codex exec` (falling back to a Claude second-opinion subagent when codex is unavailable). The interactive HTML report puts the top recommended findings first, lets the user dismiss findings with a reason, and posts the ones they select via `gh`; cleanup records what was posted, dismissed or ignored in `labels.json`.
7
+ Given a GitHub PR number, dispatches five specialist subagents in parallel (security, bugs, performance, code-smells, architecture), each quoting an evidence snippet for every anchored finding, plus one checks pass per PR that reviews the tests, harnesses and controls for ways they can pass without proving what they exist for. Dedupe drops findings whose snippet is not in the file and groups nearby findings from different domains as merge candidates. Critic subagents then re-check each surviving finding against the worktree, keep, drop or merge it, and set its risk label, from which the severity is derived. Peer review runs via `codex exec` (falling back to a Claude second-opinion subagent when codex is unavailable). The interactive HTML report puts the top recommended findings first, lets the user dismiss findings with a reason, and posts the ones they select via `gh`; cleanup records what was posted, dismissed or ignored in `labels.json`.
8
8
 
9
9
  ## Requirements
10
10
 
@@ -27,6 +27,8 @@ If you cloned this repo and want to run from source, you can also invoke `./inst
27
27
 
28
28
  Inside Claude Code, ask: "Review PR 1234" (or paste a PR URL). The agent reads SKILL.md and walks the pipeline; the CLI runs as `magpie setup`, `magpie serve`, etc.
29
29
 
30
+ An implementing or orchestrating agent can also ask for a review of its own branch before a PR exists: local mode (`magpie setup <run-dir> --local [--base <ref>]`) reviews the merge base to working tree diff in place and hands the ranked findings back as terminal text via `magpie handoff`, with no server, report or posting.
31
+
30
32
  ## Development
31
33
 
32
34
  ```
@@ -54,7 +56,7 @@ The fixture lives at `fixtures/example-pr/` (pr.json + findings.final.json + pos
54
56
  ## Layout
55
57
 
56
58
  - `SKILL.md` is the agent-facing prompt: the stage walkthrough and nothing else. Installed by the agent-skills CLI.
57
- - `references/` holds the prompt bodies the walkthrough loads on demand, one file per stage that needs one: `scout.md` (stage 3, the PR-brief prompt and the `brief.json` contract, including the repository review rules), `specialists.md` (stage 4, the five focus blocks plus the shared output contract), `critic.md` (stage 6, the critic subagent prompt `magpie critic-prompt` fills), `peer-review.md` (stage 7, including the Claude-fallback preamble). They ship in the bundle and sit next to `SKILL.md` once installed.
59
+ - `references/` holds the prompt bodies the walkthrough loads on demand, one file per stage that needs one: `scout.md` (stage 3, the PR-brief prompt and the `brief.json` contract, including the repository review rules), `checks.md` (stage 3, the once-per-PR checks pass that writes `findings/checks.json`), `specialists.md` (stage 4, the five focus blocks plus the shared output contract), `critic.md` (stage 6, the critic subagent prompt `magpie critic-prompt` fills), `peer-review.md` (stage 7, including the Claude-fallback preamble). They ship in the bundle and sit next to `SKILL.md` once installed.
58
60
  - `skill.json` is the agent-skills manifest.
59
61
  - `bin/magpie` is the CLI invoked by the agent during stages; symlinked onto PATH by `install.sh`.
60
62
  - `scripts/` holds the implementation (server, dedupe, render, setup, cleanup, etc.).
@@ -66,4 +68,4 @@ The fixture lives at `fixtures/example-pr/` (pr.json + findings.final.json + pos
66
68
 
67
69
  ## Run directory layout
68
70
 
69
- Each invocation creates `~/.magpie/pr-<n>-<ts>/` with `pr.json`, `diff.patch`, `findings/`, `findings.deduped.json`, `merge-candidates.json`, `critic-prompt.md` and `critic.json` (numbered per batch when there is more than one), `findings.kept.json`, `critic-dropped.json`, `findings.final.json`, `labels.json`, `screen/`, `state/`, `log.jsonl`. On completion the directory is renamed to `<run-dir>.archived-<timestamp>` rather than deleted, so logs survive for postmortem.
71
+ Each invocation creates `~/.magpie/pr-<n>-<ts>/` (`~/.magpie/local-<branch-slug>-<ts>/` for a local run, with `local.json` and `target.json`, no `pr.json` and no worktree) with `pr.json`, `diff.patch`, `findings/`, `findings.deduped.json`, `merge-candidates.json`, `critic-prompt.md` and `critic.json` (numbered per batch when there is more than one), `findings.kept.json`, `critic-dropped.json`, `findings.final.json`, `labels.json`, `screen/`, `state/`, `log.jsonl`. On completion the directory is renamed to `<run-dir>.archived-<timestamp>` rather than deleted, so logs survive for postmortem.
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: magpie
3
- description: Use when the user asks to review a GitHub pull request (a PR number, PR URL, or "review this PR"). Interactive PR review pipeline; five parallel specialist subagents, dedupe, critic, codex/Claude peer review, and an interactive HTML report for posting selected findings via gh. Follow the stage walkthrough in this skill; the description is not the procedure.
3
+ description: Use when the user asks to review a GitHub pull request (a PR number, PR URL, or "review this PR"), or to self-review the current branch diff before a PR exists, typically requested by an implementing or orchestrating agent, with findings handed back in the terminal. Interactive PR review pipeline; five parallel specialist subagents, dedupe, critic, codex/Claude peer review, and an interactive HTML report for posting selected findings via gh. Follow the stage walkthrough in this skill; the description is not the procedure.
4
+ argument-hint: "[<pr#>|<pr-url>|local] [--base <ref>] [--intent-file <path>]"
4
5
  ---
5
6
 
6
7
  # Magpie
@@ -15,6 +16,8 @@ Stop reading and follow these steps in order. Do not skip stages. Use the exact
15
16
 
16
17
  ### 0. Identify the PR
17
18
 
19
+ A request with no PR that asks to review the current branch, the working tree, or work in progress, or arguments starting `local` (any `--base` or `--intent-file` after it go to setup), is local mode: follow "Local mode" below in place of this step and stages 1 and 2, then run stages 3 to 7 as written, taking `TARGET` from Local mode.
20
+
18
21
  Parse the user's request for a PR number, URL, or "this PR" (current branch). If ambiguous, ask one clarifying terminal question. Capture the PR number into `$PR_NUMBER` and the repo path into `$REPO` (default: the current working directory).
19
22
 
20
23
  Before minting a new id, check whether an earlier run on this PR is unfinished:
@@ -42,13 +45,10 @@ If exit is non-zero, surface stderr verbatim and stop. The CLI removes the workt
42
45
 
43
46
  Setup filters lockfiles, build output, generated source, and snapshot fixtures from `diff.patch` before specialists see it. Users override with `.magpie.json` at the repo root: `{"exclude": [...glob], "include": [...glob], "useDefaults": true|false}`. When anything is filtered, the raw diff is kept as `$RUN_DIR/diff.full.patch` and the exclusion list as `$RUN_DIR/excluded-files.json`.
44
47
 
45
- When `gh pr diff` refuses the diff (HTTP 406 above roughly 300 files) or returns an
46
- empty diff for a PR with changed files, setup rebuilds it from the local clone instead
47
- of aborting: it fetches `pull/<n>/head` and diffs from the merge base against the PR's
48
- base branch, reproducing the three-dot semantics `gh pr diff` uses. A local head that
49
- does not match the PR's `headRefOid` is a hard error, not a silently stale review. The
50
- `fetch-pr` log entry records `source: "gh" | "git"` and the merge base, and
51
- `$RUN_DIR/diff-source.json` carries the same for the report.
48
+ When `gh pr diff` refuses the diff (HTTP 406 above roughly 300 files) or returns it
49
+ empty, setup rebuilds it from the local clone: `pull/<n>/head` diffed from the merge
50
+ base, as `gh pr diff` does. A local head that does not match the PR's `headRefOid` is a
51
+ hard error. `$RUN_DIR/diff-source.json` records `source: "gh" | "git"` and the merge base.
52
52
 
53
53
  Setup then splits the filtered diff into shards, writing `$RUN_DIR/shards/manifest.json`
54
54
  and, when more than one shard results, `$RUN_DIR/shards/shard-<n>.patch`. `diff.patch`
@@ -61,7 +61,7 @@ nothing reviewed.
61
61
 
62
62
  When a prior run exists for the same PR (active or archived under `~/.magpie/`), setup writes `$RUN_DIR/incremental.json` with `{previousRunId, previousSha, currentSha, sameSha}`. The post stage surfaces this as a "Incremental review since `<sha>`" trailer on the summary comment.
63
63
 
64
- Setup also runs a deterministic test-coverage check: when the diff contains zero test or spec files anywhere, each non-test source file with `>= 10` added code lines gets a `domain: "tests"` finding in `$RUN_DIR/findings/tests.json`. That sixth domain flows through dedupe/critic/peer-review alongside the five LLM specialists; no subagent is dispatched for it.
64
+ Setup also runs a deterministic test-coverage check: when the diff contains zero test or spec files anywhere, each non-test source file with `>= 10` added code lines gets a `domain: "tests"` finding in `$RUN_DIR/findings/tests.json`. That domain flows through dedupe/critic/peer-review like the LLM findings; no subagent is dispatched for it.
65
65
 
66
66
  ### 2. Serve
67
67
 
@@ -73,7 +73,7 @@ magpie serve "$RUN_DIR"
73
73
 
74
74
  Read `$RUN_DIR/state/server-info` for the URL; the server writes it asynchronously, so if it is missing, wait a moment and re-read (it appears within ~1s). Print to the user: "Open <url> in your browser to follow along."
75
75
 
76
- The server shuts down after 30 idle minutes (an open report tab heartbeats every 30s, so it stays up while the user is looking) and deletes `state/server-info` on the way out. Nothing in the pipeline depends on it: re-run `magpie serve "$RUN_DIR"` to bring the report back.
76
+ The server shuts down after 30 idle minutes (an open report tab keeps it up) and deletes `state/server-info`. Nothing in the pipeline depends on it: re-run `magpie serve "$RUN_DIR"` to bring the report back.
77
77
 
78
78
  Render the first progress paint:
79
79
 
@@ -85,9 +85,11 @@ magpie render "$RUN_DIR" progress
85
85
 
86
86
  Append `{stage: context, status: running}` to `$RUN_DIR/log.jsonl` and re-render progress. This stage never aborts the run.
87
87
 
88
- Read `references/scout.md` and dispatch one subagent (Agent tool, `general-purpose`) carrying the `magpie-scout` block with `<<RUN_DIR>>` and `<<PR_NUMBER>>` substituted. It writes `$RUN_DIR/brief.json`.
88
+ Set `WORKTREE=$(magpie target "$RUN_DIR")`, the directory findings are anchored against (the run's own `worktree/` checkout on a PR run), and `TARGET` to `PR #$PR_NUMBER`. Every prompt from here on takes both.
89
+
90
+ Read `references/scout.md` and `references/checks.md`, then dispatch two subagents (Agent tool, `general-purpose`) in one message: the scout, carrying the `magpie-scout` block with `<<RUN_DIR>>`, `<<WORKTREE>>` and `<<TARGET>>` substituted, which writes `$RUN_DIR/brief.json`; and the checks pass, built as `references/checks.md` says, which writes `$RUN_DIR/findings/checks.json` once for the run.
89
91
 
90
- Append `{stage: context, status: done}` and re-render progress. If the scout returned without writing `brief.json`, append `{stage: context, status: skipped}` instead and continue: the brief is optional everywhere it is read.
92
+ Append `{stage: context, status: done}` and re-render progress. If the scout wrote no `brief.json`, append `{stage: context, status: skipped}` instead and continue: the brief is optional everywhere it is read. If the checks pass wrote no `checks.json`, append `{stage: checks, status: skipped}`; nothing downstream needs it.
91
93
 
92
94
  ### 4. Specialists
93
95
 
@@ -134,7 +136,7 @@ advances `magpie status`.)
134
136
 
135
137
  Before leaving this stage, list `$RUN_DIR/findings` and confirm one file per expected
136
138
  `(focus, shard)` pair: `5 × <shard count>` named `findings/<focus>.shard-<n>.json` when
137
- sharded, five `findings/<focus>.json` otherwise, plus `findings/tests.json` from setup.
139
+ sharded, five `findings/<focus>.json` otherwise, plus `findings/tests.json` from setup and `findings/checks.json` from stage 3.
138
140
  Re-dispatch any pair missing from a shard you meant to review; a shard skipped at the
139
141
  gate is expected to have none. `magpie dedupe` re-checks this against the manifest and
140
142
  names every missing pair on stdout, as a backstop rather than a substitute.
@@ -148,7 +150,7 @@ If every specialist fails (no findings files written), log
148
150
  magpie dedupe "$RUN_DIR" [--threshold <0-10>]
149
151
  ```
150
152
 
151
- `magpie dedupe` also checks evidence against the worktree, dropping an anchored finding whose file is missing (`hallucinated-file`), whose line is out of range (`invented-line`), that has no `evidence` snippet (`missing-evidence`), or whose snippet is not near its line (`evidence-not-found`); a snippet found exactly once elsewhere in the file re-anchors it. Both go to `$RUN_DIR/evidence-dropped.json` as `{dropped, reanchored}`, written only when non-empty, so no file is normal. The check is skipped when the worktree is gone (archived run replay).
153
+ `magpie dedupe` also checks evidence against `$WORKTREE`, dropping an anchored finding whose file is missing (`hallucinated-file`), whose line is out of range (`invented-line`), that has no `evidence` snippet (`missing-evidence`), or whose snippet is not near its line (`evidence-not-found`); a snippet found exactly once elsewhere in the file re-anchors it. Both go to `$RUN_DIR/evidence-dropped.json` as `{dropped, reanchored}`, written only when non-empty, so no file is normal. The check is skipped when the worktree is gone (archived run replay).
152
154
 
153
155
  It always writes `$RUN_DIR/merge-candidates.json`: ids from different domains anchored close together in one file, for the critic to merge or keep apart.
154
156
 
@@ -158,7 +160,7 @@ Re-render progress.
158
160
 
159
161
  ### 6. Critic
160
162
 
161
- The critic runs as worktree-reading subagents. The CLI fills its prompt from `references/critic.md`; substitute nothing by hand. Append `{stage: critic, status: running}` to `$RUN_DIR/log.jsonl`, re-render progress, then:
163
+ The critic runs as subagents that read `$WORKTREE`. The CLI fills its prompt from `references/critic.md`; substitute nothing by hand. Append `{stage: critic, status: running}` to `$RUN_DIR/log.jsonl`, re-render progress, then:
162
164
 
163
165
  ```
164
166
  magpie critic-prompt "$RUN_DIR"
@@ -254,6 +256,27 @@ The archived `findings.html` is self-contained and auto-switches to read-only "a
254
256
  - `magpie serve <id>` re-spins the Bun server against an archived run for the live interactive surface (posts still work: `pr.json` retains the head SHA).
255
257
  - `magpie --list-runs` enumerates all runs in `~/.magpie/`.
256
258
 
259
+ ## Local mode
260
+
261
+ Reviews the current branch's diff for an agent that wants findings before a PR exists. Nothing is posted, served or rendered. Deltas from the walkthrough:
262
+
263
+ `REPO` is the repo root the caller is working in (default: the current working directory). Resume an `active` run from `magpie --list-runs` only when its id is exactly `local-<slug>-<digits>` and `branch` in its `local.json` is the current branch. Otherwise:
264
+
265
+ ```
266
+ SLUG=$(git -C "$REPO" branch --show-current | sed -E 's/[^A-Za-z0-9._-]+/-/g')
267
+ RUN_DIR="$HOME/.magpie/local-${SLUG:-detached}-$(date +%s)"
268
+ magpie setup "$RUN_DIR" --local --repo "$REPO" [--base <ref>] [--intent-file <path>]
269
+ ```
270
+
271
+ On a non-zero exit, show stderr verbatim to the caller and stop. The diff runs from the merge base with `<ref>` to the working tree, untracked files included. `<ref>` defaults to `origin/HEAD`; when that is not the branch the work merges into, pass `--base` naming it (setup fails on an unresolvable ref rather than guessing). Pass `--intent-file` for the caller's spec or plan.
272
+
273
+ - `WORKTREE=$(magpie target "$RUN_DIR")` is the repo itself. `TARGET` is `local branch <branch> against <baseRef>` (`detached HEAD against <baseRef>` when `branch` is null), from `local.json`.
274
+ - Skip every `magpie render`. Peer review uses the "Local runs" list in `references/peer-review.md`. The stage 4 shard question goes to whoever asked for the review, which may be an orchestrating agent.
275
+ - Stage 8: `magpie handoff "$RUN_DIR"`. On exit 4 (`findings.final.json` or `local.json` missing) or 2 (not a local run), show stderr to the caller and stop.
276
+ - Then `magpie cleanup "$RUN_DIR" --repo "$REPO"` at once. Reply to the caller with the handoff stdout verbatim, then one line `Run archived at <path>`, `<path>` from cleanup's `archived to <path>`. End the turn.
277
+
278
+ Do not edit the tree mid-run: anchors and evidence are read from it live. Without the Agent tool, stop and say so rather than reviewing single-handed. The local ladder ends at handoff, then cleanup; there is no server to restart.
279
+
257
280
  ## Resuming a crashed run
258
281
 
259
282
  A run is resumable while `$RUN_DIR/log.jsonl` exists and the run has not been archived. Do not test for `state/server-info`: the server deletes it whenever it stops, so a perfectly resumable run fails that check. Step 0 finds the run directory via `magpie --list-runs` when you don't already have it in `$RUN_DIR`.
@@ -265,7 +288,10 @@ magpie status "$RUN_DIR"
265
288
  The JSON output tells you `lastCompleted` and `next`. Resume from `next`:
266
289
 
267
290
  - `context`: if `$RUN_DIR/brief.json` exists, append `{stage: context, status: done}` and move on; otherwise re-run stage 3.
291
+ - Any resume: re-dispatch the checks pass when `findings/checks.json` is missing and no `checks: skipped` is logged.
292
+ - Any stage after context: first set `WORKTREE` and `TARGET` as in stage 3 (on a local run, as in Local mode).
268
293
  - Any other stage: run it as written in the walkthrough.
294
+ - Local run at `next: cleanup`: if the caller has not been sent the findings, re-run `magpie handoff` first (it only reads files).
269
295
  - If a specialist focus has no findings file but its sibling stages are done,
270
296
  re-dispatch only that focus. On a sharded run the unit is the `(focus, shard)` pair:
271
297
  read `shards/manifest.json`, and re-dispatch only the pairs with no
@@ -8,6 +8,11 @@ const USAGE = `Usage: magpie <subcommand> [args]
8
8
 
9
9
  Subcommands:
10
10
  setup <run-dir> --pr <n> Pre-flight, fetch PR, create worktree
11
+ setup <run-dir> --local [--base <ref>] [--intent-file <path>]
12
+ Review the current branch in place against <ref>
13
+ (default origin/HEAD), committed and uncommitted
14
+ target <run-dir> Print the directory findings are anchored against
15
+ handoff <run-dir> Print local review findings as a ranked list
11
16
  serve <run-dir-or-id> Start the HTML server (accepts active or archived run id)
12
17
  dedupe <run-dir> Merge specialist findings into deduped set
13
18
  critic-prompt <run-dir> [--batch-size N]
@@ -57,20 +62,84 @@ const HANDLERS: Record<string, Handler> = {
57
62
  setup: async (args) => {
58
63
  const runDir = args[0]
59
64
  const prFlag = args.indexOf('--pr')
60
- const prValue = prFlag !== -1 ? args[prFlag + 1] : undefined
61
- const repoFlag = args.indexOf('--repo')
62
- if (!runDir || prFlag === -1 || !prValue) {
63
- process.stderr.write('setup: missing <run-dir> --pr <n> [--repo <path>]\n')
65
+ const isLocal = args.includes('--local')
66
+ // A flag in the run-dir slot is usually an unset, unquoted $RUN_DIR.
67
+ if (!runDir || runDir.startsWith('-')) {
68
+ process.stderr.write(
69
+ 'setup: missing <run-dir> (--pr <n> | --local [--base <ref>] [--intent-file <path>]) [--repo <path>]\n',
70
+ )
71
+ return 2
72
+ }
73
+ if ((prFlag !== -1) === isLocal) {
74
+ process.stderr.write('setup: pass exactly one of --pr <n> or --local\n')
64
75
  return 2
65
76
  }
77
+ const flagValue = (name: string): string | undefined | null => {
78
+ const idx = args.indexOf(name)
79
+ if (idx === -1) return undefined
80
+ const value = args[idx + 1]
81
+ if (!value || value.startsWith('--')) {
82
+ process.stderr.write(`setup: ${name} needs a value\n`)
83
+ return null
84
+ }
85
+ return value
86
+ }
87
+ const repoValue = flagValue('--repo')
88
+ if (repoValue === null) return 2
89
+ const repoPath = repoValue ?? process.cwd()
90
+ const { runSetup } = await import('../scripts/setup-cmd.ts')
91
+ if (isLocal) {
92
+ const baseRef = flagValue('--base')
93
+ if (baseRef === null) return 2
94
+ const intentFile = flagValue('--intent-file')
95
+ if (intentFile === null) return 2
96
+ return runSetup({
97
+ mode: 'local',
98
+ runDir,
99
+ repoPath,
100
+ ...(baseRef !== undefined ? { baseRef } : {}),
101
+ ...(intentFile !== undefined ? { intentFile } : {}),
102
+ })
103
+ }
104
+ const prValue = flagValue('--pr')
105
+ if (prValue === null || prValue === undefined) return 2
66
106
  const prNumber = Number(prValue)
67
107
  if (!Number.isFinite(prNumber)) {
68
108
  process.stderr.write(`setup: invalid PR number ${prValue}\n`)
69
109
  return 2
70
110
  }
71
- const repoPath = (repoFlag !== -1 ? args[repoFlag + 1] : undefined) ?? process.cwd()
72
- const { runSetup } = await import('../scripts/setup-cmd.ts')
73
- return runSetup({ runDir, prNumber, repoPath })
111
+ return runSetup({ mode: 'pr', runDir, prNumber, repoPath })
112
+ },
113
+ target: async (args) => {
114
+ const runDir = args[0]
115
+ if (!runDir || runDir.startsWith('-')) {
116
+ process.stderr.write('target: missing <run-dir>\n')
117
+ return 2
118
+ }
119
+ const { reviewRoot } = await import('../scripts/target.ts')
120
+ try {
121
+ process.stdout.write(`${await reviewRoot(runDir)}\n`)
122
+ } catch (err) {
123
+ process.stderr.write(`target: ${err instanceof Error ? err.message : String(err)}\n`)
124
+ return 2
125
+ }
126
+ return 0
127
+ },
128
+ handoff: async (args) => {
129
+ const runDir = args[0]
130
+ if (!runDir || runDir.startsWith('-')) {
131
+ process.stderr.write('handoff: missing <run-dir>\n')
132
+ return 2
133
+ }
134
+ const { readTarget } = await import('../scripts/target.ts')
135
+ try {
136
+ await readTarget(runDir)
137
+ } catch (err) {
138
+ process.stderr.write(`handoff: ${err instanceof Error ? err.message : String(err)}\n`)
139
+ return 2
140
+ }
141
+ const { runHandoff } = await import('../scripts/handoff-cmd.ts')
142
+ return runHandoff(runDir, { gitBin: process.env.MAGPIE_GIT_BIN || 'git' })
74
143
  },
75
144
  serve: async (args) => {
76
145
  const idOrPath = args[0]
@@ -275,6 +344,13 @@ const HANDLERS: Record<string, Handler> = {
275
344
  process.stderr.write('status: missing <run-dir>\n')
276
345
  return 2
277
346
  }
347
+ const { readTarget } = await import('../scripts/target.ts')
348
+ try {
349
+ await readTarget(runDir)
350
+ } catch (err) {
351
+ process.stderr.write(`status: ${err instanceof Error ? err.message : String(err)}\n`)
352
+ return 2
353
+ }
278
354
  const { runStatus } = await import('../scripts/status-cmd.ts')
279
355
  const result = await runStatus(runDir)
280
356
  process.stdout.write(`${JSON.stringify(result)}\n`)
@@ -1,6 +1,6 @@
1
1
  # magpie evals
2
2
 
3
- Five cases for `claude plugin eval`. Every one starts mid-pipeline, because the
3
+ Six cases for `claude plugin eval`. Every one starts mid-pipeline, because the
4
4
  decisions this skill owns are the ones between the CLI calls: `scripts/__tests__/`
5
5
  already pins what `magpie setup`, `dedupe`, `shard`, `post` and `status` compute.
6
6
  What no unit test can reach is whether the agent stops where the walkthrough says
@@ -14,6 +14,7 @@ must not touch.
14
14
  | `report-ends-the-turn` | Stage 8 reached | Renders, logs the stage done, hands back for selection, posts nothing |
15
15
  | `post-folds-selection-events` | The user typed `post` after re-ticking | Folds `state/events` last-event-wins, posts `bugs-1,perf-1` only |
16
16
  | `resume-finds-active-run` | A fresh review ask on a PR with a live run | Checks `--list-runs` first, never calls `setup`, surfaces the interrupted run |
17
+ | `local-mode-hands-back` | A local run resumed after peer review | Runs `handoff` then `cleanup`, replies with both finding titles, never serves, renders, posts or calls `gh` |
17
18
 
18
19
  ## Running
19
20
 
@@ -83,9 +84,11 @@ Three things follow from the sandbox, and cases are written around them:
83
84
  reports the workspace path.
84
85
  - **Fakes compute rather than answer.** `magpie status` reads `log.jsonl` with
85
86
  the same stage ladder as `scripts/status-cmd.ts`, so a stage the agent logs
86
- moves `next` exactly as the real CLI would. A canned answer went stale the
87
- moment the agent appended to the log, and the agent noticed and spent a
88
- paragraph on it.
87
+ moves `next` exactly as the real CLI would. A local run uses the local ladder
88
+ (ending at `handoff`): the `local-mode-hands-back` fake reads `target.json` to
89
+ pick it, as the real CLI does, while the older fakes only know the PR ladder.
90
+ A canned answer went stale the moment the agent appended to the log, and the
91
+ agent noticed and spent a paragraph on it.
89
92
 
90
93
  Each fake also appends its argv to `.magpie-calls.log` in the workspace. That
91
94
  file is what most of the graders read: "posted exactly these ids", "never ran
@@ -0,0 +1,4 @@
1
+ schema_version: "1.1"
2
+ name: local-mode-hands-back
3
+ context:
4
+ scaffold_script: fixture.sh