@iceinvein/agent-skills 0.1.40 → 0.2.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 +18 -2
- package/dist/cli/index.js +105 -28
- package/package.json +1 -1
- package/skills/index.json +14 -2
- package/skills/magpie/SKILL.md +118 -40
- package/skills/magpie/bin/magpie.ts +43 -0
- package/skills/magpie/fixtures/fake-gh-nodiff.sh +38 -0
- package/skills/magpie/package.json +1 -1
- package/skills/magpie/references/peer-review.md +7 -2
- package/skills/magpie/references/specialists.md +38 -7
- package/skills/magpie/scripts/__tests__/cli.test.ts +101 -1
- package/skills/magpie/scripts/__tests__/dedupe-cmd.test.ts +187 -0
- package/skills/magpie/scripts/__tests__/diff-chunks.test.ts +51 -0
- package/skills/magpie/scripts/__tests__/filter-diff-preservation.test.ts +54 -0
- package/skills/magpie/scripts/__tests__/findings-files.test.ts +35 -0
- package/skills/magpie/scripts/__tests__/gh.test.ts +69 -0
- package/skills/magpie/scripts/__tests__/git-diff.test.ts +83 -0
- package/skills/magpie/scripts/__tests__/helpers/git-fixture.ts +47 -0
- package/skills/magpie/scripts/__tests__/path-filter.test.ts +27 -0
- package/skills/magpie/scripts/__tests__/render-cmd.test.ts +95 -0
- package/skills/magpie/scripts/__tests__/render-findings.test.ts +33 -0
- package/skills/magpie/scripts/__tests__/render-progress.test.ts +42 -0
- package/skills/magpie/scripts/__tests__/setup-cmd.test.ts +83 -1
- package/skills/magpie/scripts/__tests__/shard.test.ts +165 -0
- package/skills/magpie/scripts/__tests__/skill-lint.test.ts +96 -1
- package/skills/magpie/scripts/dedupe-cmd.ts +58 -3
- package/skills/magpie/scripts/diff-chunks.ts +28 -0
- package/skills/magpie/scripts/findings-files.ts +32 -0
- package/skills/magpie/scripts/gh.ts +64 -13
- package/skills/magpie/scripts/git-diff.ts +111 -0
- package/skills/magpie/scripts/path-filter.ts +9 -5
- package/skills/magpie/scripts/refresh.ts +8 -0
- package/skills/magpie/scripts/render-cmd.ts +28 -9
- package/skills/magpie/scripts/render-findings.ts +11 -1
- package/skills/magpie/scripts/render-progress.ts +6 -1
- package/skills/magpie/scripts/setup-cmd.ts +38 -1
- package/skills/magpie/scripts/shard.ts +171 -0
- package/skills/magpie/scripts/status-cmd.ts +4 -1
- package/skills/magpie/skill.json +2 -2
- package/skills/magpie/templates/styles.css +5 -0
- package/skills/migrate/README.md +194 -0
- package/skills/migrate/SKILL.md +197 -0
- package/skills/migrate/bin/migrate +15 -0
- package/skills/migrate/bin/migrate.ts +309 -0
- package/skills/migrate/biome.json +35 -0
- package/skills/migrate/bun.lock +24 -0
- package/skills/migrate/docs/architecture.md +294 -0
- package/skills/migrate/docs/reference.md +590 -0
- package/skills/migrate/fixtures/tiny-express/GROUND-TRUTH.md +39 -0
- package/skills/migrate/fixtures/tiny-express/app.js +29 -0
- package/skills/migrate/fixtures/tiny-express/cron.js +6 -0
- package/skills/migrate/fixtures/tiny-express/reports/daily-users.json +6 -0
- package/skills/migrate/fixtures/tiny-express/schema.sql +12 -0
- package/skills/migrate/fixtures/tiny-express/settings.json +4 -0
- package/skills/migrate/fixtures/tiny-express/views/users.html +9 -0
- package/skills/migrate/fixtures/tiny-webforms/Controllers/UsersController.cs +68 -0
- package/skills/migrate/fixtures/tiny-webforms/Default.aspx +7 -0
- package/skills/migrate/fixtures/tiny-webforms/Default.aspx.cs +14 -0
- package/skills/migrate/fixtures/tiny-webforms/GROUND-TRUTH.md +50 -0
- package/skills/migrate/fixtures/tiny-webforms/Integrations/BillingClient.cs +16 -0
- package/skills/migrate/fixtures/tiny-webforms/Jobs/NightlyDigestJob.cs +33 -0
- package/skills/migrate/fixtures/tiny-webforms/Reports/DailyUsers.rdl +11 -0
- package/skills/migrate/fixtures/tiny-webforms/Schema.sql +12 -0
- package/skills/migrate/fixtures/tiny-webforms/Site.master +16 -0
- package/skills/migrate/fixtures/tiny-webforms/Users.aspx +8 -0
- package/skills/migrate/fixtures/tiny-webforms/Users.aspx.cs +14 -0
- package/skills/migrate/fixtures/tiny-webforms/web.config +10 -0
- package/skills/migrate/install.sh +68 -0
- package/skills/migrate/package.json +17 -0
- package/skills/migrate/references/phases/enumerate.md +291 -0
- package/skills/migrate/references/phases/extract.md +652 -0
- package/skills/migrate/references/phases/parity.md +275 -0
- package/skills/migrate/references/phases/probe.md +135 -0
- package/skills/migrate/references/phases/queue.md +242 -0
- package/skills/migrate/references/phases/seam.md +416 -0
- package/skills/migrate/references/recipes/README.md +116 -0
- package/skills/migrate/references/recipes/aspnet.md +287 -0
- package/skills/migrate/references/run-ops.md +280 -0
- package/skills/migrate/scripts/__tests__/census.test.ts +775 -0
- package/skills/migrate/scripts/__tests__/check.test.ts +458 -0
- package/skills/migrate/scripts/__tests__/citations.test.ts +156 -0
- package/skills/migrate/scripts/__tests__/cli.test.ts +183 -0
- package/skills/migrate/scripts/__tests__/concurrency.test.ts +164 -0
- package/skills/migrate/scripts/__tests__/config.test.ts +112 -0
- package/skills/migrate/scripts/__tests__/e2e-express.test.ts +1093 -0
- package/skills/migrate/scripts/__tests__/e2e-webforms.test.ts +1276 -0
- package/skills/migrate/scripts/__tests__/e2e.test.ts +320 -0
- package/skills/migrate/scripts/__tests__/ids.test.ts +38 -0
- package/skills/migrate/scripts/__tests__/import.test.ts +155 -0
- package/skills/migrate/scripts/__tests__/init.test.ts +192 -0
- package/skills/migrate/scripts/__tests__/leaks.test.ts +176 -0
- package/skills/migrate/scripts/__tests__/lock.test.ts +183 -0
- package/skills/migrate/scripts/__tests__/paths.test.ts +129 -0
- package/skills/migrate/scripts/__tests__/phase-cmd.test.ts +151 -0
- package/skills/migrate/scripts/__tests__/phases.test.ts +70 -0
- package/skills/migrate/scripts/__tests__/queue.test.ts +475 -0
- package/skills/migrate/scripts/__tests__/report.test.ts +150 -0
- package/skills/migrate/scripts/__tests__/run-state.test.ts +136 -0
- package/skills/migrate/scripts/__tests__/status-reset.test.ts +318 -0
- package/skills/migrate/scripts/__tests__/store.test.ts +132 -0
- package/skills/migrate/scripts/__tests__/validate.test.ts +54 -0
- package/skills/migrate/scripts/census-cmd.ts +109 -0
- package/skills/migrate/scripts/census.ts +342 -0
- package/skills/migrate/scripts/check-cmd.ts +24 -0
- package/skills/migrate/scripts/check.ts +376 -0
- package/skills/migrate/scripts/citations.ts +92 -0
- package/skills/migrate/scripts/config.ts +237 -0
- package/skills/migrate/scripts/ids.ts +31 -0
- package/skills/migrate/scripts/import-cmd.ts +141 -0
- package/skills/migrate/scripts/init-cmd.ts +118 -0
- package/skills/migrate/scripts/leaks.ts +184 -0
- package/skills/migrate/scripts/lock.ts +188 -0
- package/skills/migrate/scripts/paths.ts +103 -0
- package/skills/migrate/scripts/phase-cmd.ts +63 -0
- package/skills/migrate/scripts/phases.ts +113 -0
- package/skills/migrate/scripts/queue-cmd.ts +98 -0
- package/skills/migrate/scripts/queue.ts +258 -0
- package/skills/migrate/scripts/report-cmd.ts +47 -0
- package/skills/migrate/scripts/report.ts +131 -0
- package/skills/migrate/scripts/reset-cmd.ts +120 -0
- package/skills/migrate/scripts/status-cmd.ts +52 -0
- package/skills/migrate/scripts/store.ts +159 -0
- package/skills/migrate/scripts/types.ts +137 -0
- package/skills/migrate/scripts/validate.ts +221 -0
- package/skills/migrate/skill.json +33 -0
- package/skills/migrate/templates/config.toml +27 -0
- package/skills/migrate/templates/queue-item.md +17 -0
- package/skills/migrate/tsconfig.json +18 -0
- package/skills/migrate/uninstall.sh +31 -0
- package/skills/sluice/SKILL.md +82 -0
- package/skills/sluice/references/deep-channel.md +94 -0
- package/skills/sluice/references/finish.md +35 -0
- package/skills/sluice/references/intent.md +29 -0
- package/skills/sluice/references/review.md +42 -0
- package/skills/sluice/references/root-cause.md +38 -0
- package/skills/sluice/references/show-or-say.md +36 -0
- package/skills/sluice/references/test-first.md +35 -0
- package/skills/sluice/references/verify.md +26 -0
- package/skills/sluice/skill.json +32 -0
package/README.md
CHANGED
|
@@ -24,6 +24,12 @@ Auto-detects Claude Code, Cursor, Codex, or Gemini CLI. Use `--tool claude` to t
|
|
|
24
24
|
|
|
25
25
|
## Skills
|
|
26
26
|
|
|
27
|
+
### Migration
|
|
28
|
+
|
|
29
|
+
| Skill | What it does |
|
|
30
|
+
|-------|--------------|
|
|
31
|
+
| **migrate** | Walks a legacy codebase through probe, enumerate, seam, extract, parity, and queue: measured surface coverage, an empirically derived capability seam, cited requirements, a parity plan, and a batch decision queue. Bundles a Bun CLI that enforces the coverage arithmetic and phase ordering instead of trusting it. |
|
|
32
|
+
|
|
27
33
|
### Code Architecture
|
|
28
34
|
|
|
29
35
|
Skills that analyze how code is structured: module boundaries, coupling, complexity, contracts, and evolution over time.
|
|
@@ -91,6 +97,14 @@ Skills for producing job application materials that read like a human wrote them
|
|
|
91
97
|
| **cover-letter-rewrite** | Reviser | Audit-driven targeted rewrite of an existing letter. Focus modes: humanize, align, tighten, structure, tone. Preserves voice where it already works. |
|
|
92
98
|
| **cover-letter-persona** | Voice profile | Reusable writing personas using the NNGroup 4-dimension tone framework (funny-serious, formal-casual, respectful-irreverent, enthusiastic-matter-of-fact) adapted for professional correspondence. |
|
|
93
99
|
|
|
100
|
+
### Workflow
|
|
101
|
+
|
|
102
|
+
Skills that shape how work gets done rather than analyzing code.
|
|
103
|
+
|
|
104
|
+
| Skill | What it does |
|
|
105
|
+
|-------|--------------|
|
|
106
|
+
| **sluice** | 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. Six rules live as one-liners in the router; the full treatment sits in references read only on friction. Claude Code only. Conflicts with the superpowers plugin, which requires its own fixed pipeline up front for anything that adds to or changes what the software does, not just code edits; disable superpowers before installing. |
|
|
107
|
+
|
|
94
108
|
### Orchestration
|
|
95
109
|
|
|
96
110
|
Skills that compose the other audit skills into higher-level workflows.
|
|
@@ -145,11 +159,13 @@ To pin to the current version, do nothing: skills are not auto-updated. `update`
|
|
|
145
159
|
|
|
146
160
|
## Activation Modes (Claude Code)
|
|
147
161
|
|
|
148
|
-
Some skills (like `terse`) support activation modes. Pick one at install time:
|
|
162
|
+
Some skills (like `terse` and `sluice`) support activation modes. Pick one at install time:
|
|
149
163
|
|
|
150
|
-
- **session
|
|
164
|
+
- **session**: invoke the skill manually with `/<skill>` each session
|
|
151
165
|
- **global**: auto-activate every Claude Code session via a `SessionStart` hook in `.claude/settings.json`
|
|
152
166
|
|
|
167
|
+
Each skill declares its own default: `terse` defaults to session, `sluice` defaults to global because a router that is not always on does not route.
|
|
168
|
+
|
|
153
169
|
If a skill declares activation support and you're installing for Claude Code interactively, the CLI prompts you. Use `--activation session` or `--activation global` for scripted installs. `remove` strips the hook cleanly; `update` preserves your choice across version bumps.
|
|
154
170
|
|
|
155
171
|
## Supported Tools
|
package/dist/cli/index.js
CHANGED
|
@@ -423,8 +423,10 @@ function runScript(scriptPath, cwd) {
|
|
|
423
423
|
});
|
|
424
424
|
return { ok: result.exitCode === 0, code: result.exitCode ?? 1 };
|
|
425
425
|
}
|
|
426
|
-
function matchesSkillDirective(
|
|
427
|
-
|
|
426
|
+
function matchesSkillDirective(hook, skillName) {
|
|
427
|
+
if (hook.skill !== undefined)
|
|
428
|
+
return hook.skill === skillName;
|
|
429
|
+
return hook.command.includes(`Activate ${skillName} skill`);
|
|
428
430
|
}
|
|
429
431
|
async function wireSessionStartHook(settingsPath, skillName, directive) {
|
|
430
432
|
let settings = {};
|
|
@@ -438,12 +440,12 @@ async function wireSessionStartHook(settingsPath, skillName, directive) {
|
|
|
438
440
|
const command = `echo '${directive}'`;
|
|
439
441
|
for (const group of settings.hooks.SessionStart) {
|
|
440
442
|
for (const hook of group.hooks ?? []) {
|
|
441
|
-
if (matchesSkillDirective(hook
|
|
443
|
+
if (matchesSkillDirective(hook, skillName))
|
|
442
444
|
return;
|
|
443
445
|
}
|
|
444
446
|
}
|
|
445
447
|
settings.hooks.SessionStart.push({
|
|
446
|
-
hooks: [{ type: "command", command }]
|
|
448
|
+
hooks: [{ type: "command", command, skill: skillName }]
|
|
447
449
|
});
|
|
448
450
|
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
449
451
|
await Bun.write(settingsPath, JSON.stringify(settings, null, 2) + `
|
|
@@ -457,7 +459,7 @@ async function unwireSessionStartHook(settingsPath, skillName) {
|
|
|
457
459
|
if (!sessionStart)
|
|
458
460
|
return;
|
|
459
461
|
const filteredGroups = sessionStart.map((group) => ({
|
|
460
|
-
hooks: (group.hooks ?? []).filter((h) => !matchesSkillDirective(h
|
|
462
|
+
hooks: (group.hooks ?? []).filter((h) => !matchesSkillDirective(h, skillName))
|
|
461
463
|
})).filter((group) => group.hooks.length > 0);
|
|
462
464
|
if (filteredGroups.length === 0) {
|
|
463
465
|
delete settings.hooks.SessionStart;
|
|
@@ -865,6 +867,10 @@ async function installSkill(cwd, manifest, files, targetTools, activation) {
|
|
|
865
867
|
return { ok: true, installed, skipped };
|
|
866
868
|
}
|
|
867
869
|
|
|
870
|
+
// src/cli/commands/remove.ts
|
|
871
|
+
import { existsSync as existsSync6, statSync as statSync2, unlinkSync as unlinkSync4, rmSync as rmSync4 } from "node:fs";
|
|
872
|
+
import { basename, join as join7 } from "node:path";
|
|
873
|
+
|
|
868
874
|
// src/cli/github.ts
|
|
869
875
|
var REPO2 = "iceinvein/agent-skills";
|
|
870
876
|
var BRANCH2 = "master";
|
|
@@ -969,6 +975,28 @@ function resolveBundlePaths2(entries, bundle) {
|
|
|
969
975
|
}
|
|
970
976
|
|
|
971
977
|
// src/cli/commands/remove.ts
|
|
978
|
+
var SHARED_CONFIG_FILES = new Set([
|
|
979
|
+
".claude/settings.json",
|
|
980
|
+
".claude/settings.local.json",
|
|
981
|
+
".cursor/mcp.json",
|
|
982
|
+
".gemini/settings.json"
|
|
983
|
+
]);
|
|
984
|
+
function isSharedConfigFile(relPath) {
|
|
985
|
+
if (SHARED_CONFIG_FILES.has(relPath))
|
|
986
|
+
return true;
|
|
987
|
+
const parts = relPath.split("/");
|
|
988
|
+
const name = basename(relPath);
|
|
989
|
+
const isConfigName = name === "settings.json" || name === "settings.local.json" || name === "mcp.json";
|
|
990
|
+
return parts.length === 2 && parts[0].startsWith(".") && isConfigName;
|
|
991
|
+
}
|
|
992
|
+
function removeFileOrDir(fullPath) {
|
|
993
|
+
const stat = statSync2(fullPath);
|
|
994
|
+
if (stat.isDirectory()) {
|
|
995
|
+
rmSync4(fullPath, { recursive: true, force: true });
|
|
996
|
+
} else {
|
|
997
|
+
unlinkSync4(fullPath);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
972
1000
|
async function removeSkill(cwd, skillName) {
|
|
973
1001
|
const lockfile = await readLockfile(cwd);
|
|
974
1002
|
const entry = lockfile.skills[skillName];
|
|
@@ -993,18 +1021,28 @@ async function removeSkill(cwd, skillName) {
|
|
|
993
1021
|
});
|
|
994
1022
|
await adapter.remove(cwd, manifestResult.manifest, toolFiles);
|
|
995
1023
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1024
|
+
await removeSkillFromLockfile(cwd, skillName);
|
|
1025
|
+
return { ok: true, removed: entry.files };
|
|
1026
|
+
}
|
|
1027
|
+
const removed = [];
|
|
1028
|
+
const warnings = [];
|
|
1029
|
+
for (const file of entry.files) {
|
|
1030
|
+
if (isSharedConfigFile(file)) {
|
|
1031
|
+
const fullPath2 = join7(cwd, file);
|
|
1032
|
+
if (existsSync6(fullPath2)) {
|
|
1033
|
+
await unwireSessionStartHook(fullPath2, skillName);
|
|
1003
1034
|
}
|
|
1035
|
+
warnings.push(`Manifest unavailable (${manifestResult.error}). Left '${file}' in place, only removing '${skillName}'s SessionStart hook from it; any MCP server entries it owns were not touched.`);
|
|
1036
|
+
continue;
|
|
1004
1037
|
}
|
|
1038
|
+
const fullPath = join7(cwd, file);
|
|
1039
|
+
if (!existsSync6(fullPath))
|
|
1040
|
+
continue;
|
|
1041
|
+
removeFileOrDir(fullPath);
|
|
1042
|
+
removed.push(file);
|
|
1005
1043
|
}
|
|
1006
1044
|
await removeSkillFromLockfile(cwd, skillName);
|
|
1007
|
-
return { ok: true, removed
|
|
1045
|
+
return { ok: true, removed, ...warnings.length > 0 ? { warnings } : {} };
|
|
1008
1046
|
}
|
|
1009
1047
|
|
|
1010
1048
|
// src/cli/commands/list.ts
|
|
@@ -1026,6 +1064,30 @@ async function infoSkill(skillName) {
|
|
|
1026
1064
|
}
|
|
1027
1065
|
|
|
1028
1066
|
// src/cli/commands/remove.ts
|
|
1067
|
+
import { existsSync as existsSync7, statSync as statSync3, unlinkSync as unlinkSync5, rmSync as rmSync5 } from "node:fs";
|
|
1068
|
+
import { basename as basename2, join as join8 } from "node:path";
|
|
1069
|
+
var SHARED_CONFIG_FILES2 = new Set([
|
|
1070
|
+
".claude/settings.json",
|
|
1071
|
+
".claude/settings.local.json",
|
|
1072
|
+
".cursor/mcp.json",
|
|
1073
|
+
".gemini/settings.json"
|
|
1074
|
+
]);
|
|
1075
|
+
function isSharedConfigFile2(relPath) {
|
|
1076
|
+
if (SHARED_CONFIG_FILES2.has(relPath))
|
|
1077
|
+
return true;
|
|
1078
|
+
const parts = relPath.split("/");
|
|
1079
|
+
const name = basename2(relPath);
|
|
1080
|
+
const isConfigName = name === "settings.json" || name === "settings.local.json" || name === "mcp.json";
|
|
1081
|
+
return parts.length === 2 && parts[0].startsWith(".") && isConfigName;
|
|
1082
|
+
}
|
|
1083
|
+
function removeFileOrDir2(fullPath) {
|
|
1084
|
+
const stat = statSync3(fullPath);
|
|
1085
|
+
if (stat.isDirectory()) {
|
|
1086
|
+
rmSync5(fullPath, { recursive: true, force: true });
|
|
1087
|
+
} else {
|
|
1088
|
+
unlinkSync5(fullPath);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1029
1091
|
async function removeSkill2(cwd, skillName) {
|
|
1030
1092
|
const lockfile = await readLockfile(cwd);
|
|
1031
1093
|
const entry = lockfile.skills[skillName];
|
|
@@ -1050,18 +1112,28 @@ async function removeSkill2(cwd, skillName) {
|
|
|
1050
1112
|
});
|
|
1051
1113
|
await adapter.remove(cwd, manifestResult.manifest, toolFiles);
|
|
1052
1114
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1115
|
+
await removeSkillFromLockfile(cwd, skillName);
|
|
1116
|
+
return { ok: true, removed: entry.files };
|
|
1117
|
+
}
|
|
1118
|
+
const removed = [];
|
|
1119
|
+
const warnings = [];
|
|
1120
|
+
for (const file of entry.files) {
|
|
1121
|
+
if (isSharedConfigFile2(file)) {
|
|
1122
|
+
const fullPath2 = join8(cwd, file);
|
|
1123
|
+
if (existsSync7(fullPath2)) {
|
|
1124
|
+
await unwireSessionStartHook(fullPath2, skillName);
|
|
1060
1125
|
}
|
|
1126
|
+
warnings.push(`Manifest unavailable (${manifestResult.error}). Left '${file}' in place, only removing '${skillName}'s SessionStart hook from it; any MCP server entries it owns were not touched.`);
|
|
1127
|
+
continue;
|
|
1061
1128
|
}
|
|
1129
|
+
const fullPath = join8(cwd, file);
|
|
1130
|
+
if (!existsSync7(fullPath))
|
|
1131
|
+
continue;
|
|
1132
|
+
removeFileOrDir2(fullPath);
|
|
1133
|
+
removed.push(file);
|
|
1062
1134
|
}
|
|
1063
1135
|
await removeSkillFromLockfile(cwd, skillName);
|
|
1064
|
-
return { ok: true, removed
|
|
1136
|
+
return { ok: true, removed, ...warnings.length > 0 ? { warnings } : {} };
|
|
1065
1137
|
}
|
|
1066
1138
|
|
|
1067
1139
|
// src/cli/commands/install.ts
|
|
@@ -1131,7 +1203,7 @@ async function updateAllSkills(cwd) {
|
|
|
1131
1203
|
}
|
|
1132
1204
|
|
|
1133
1205
|
// src/cli/commands/bump.ts
|
|
1134
|
-
import { join as
|
|
1206
|
+
import { join as join9 } from "node:path";
|
|
1135
1207
|
|
|
1136
1208
|
// src/cli/semver.ts
|
|
1137
1209
|
function bumpVersion(current, level) {
|
|
@@ -1152,7 +1224,7 @@ function bumpVersion(current, level) {
|
|
|
1152
1224
|
|
|
1153
1225
|
// src/cli/commands/bump.ts
|
|
1154
1226
|
async function bumpSkill(repoRoot, skillName, level) {
|
|
1155
|
-
const manifestPath =
|
|
1227
|
+
const manifestPath = join9(repoRoot, "skills", skillName, "skill.json");
|
|
1156
1228
|
const file = Bun.file(manifestPath);
|
|
1157
1229
|
if (!await file.exists()) {
|
|
1158
1230
|
return { ok: false, error: `Skill '${skillName}' not found at skills/${skillName}/skill.json` };
|
|
@@ -1223,7 +1295,7 @@ async function bumpAllChanged(repoRoot, level, dryRun) {
|
|
|
1223
1295
|
if (skillVersionChanged(repoRoot, skillName, tag))
|
|
1224
1296
|
continue;
|
|
1225
1297
|
if (dryRun) {
|
|
1226
|
-
const manifestPath =
|
|
1298
|
+
const manifestPath = join9(repoRoot, "skills", skillName, "skill.json");
|
|
1227
1299
|
const file = Bun.file(manifestPath);
|
|
1228
1300
|
if (!await file.exists())
|
|
1229
1301
|
continue;
|
|
@@ -1279,10 +1351,10 @@ async function checkForUpdates(cwd) {
|
|
|
1279
1351
|
}
|
|
1280
1352
|
|
|
1281
1353
|
// src/cli/lockfile.ts
|
|
1282
|
-
import { join as
|
|
1354
|
+
import { join as join10 } from "node:path";
|
|
1283
1355
|
var LOCKFILE_NAME2 = ".agent-skills.lock";
|
|
1284
1356
|
async function readLockfile2(cwd) {
|
|
1285
|
-
const path =
|
|
1357
|
+
const path = join10(cwd, LOCKFILE_NAME2);
|
|
1286
1358
|
const file = Bun.file(path);
|
|
1287
1359
|
if (!await file.exists()) {
|
|
1288
1360
|
return { skills: {} };
|
|
@@ -3162,7 +3234,7 @@ async function pickActivation(skillName, modes) {
|
|
|
3162
3234
|
|
|
3163
3235
|
// src/cli/index.ts
|
|
3164
3236
|
import { mkdirSync as mkdirSync4 } from "fs";
|
|
3165
|
-
import { join as
|
|
3237
|
+
import { join as join11 } from "path";
|
|
3166
3238
|
import { homedir } from "os";
|
|
3167
3239
|
async function otherScopeSkillCount(currentDir) {
|
|
3168
3240
|
const home = homedir();
|
|
@@ -3266,7 +3338,7 @@ async function resolveTargetTools(installDir, isGlobal, flags) {
|
|
|
3266
3338
|
for (const tool of picked) {
|
|
3267
3339
|
const dir = TOOL_DIRS[tool];
|
|
3268
3340
|
if (dir)
|
|
3269
|
-
mkdirSync4(
|
|
3341
|
+
mkdirSync4(join11(installDir, dir), { recursive: true });
|
|
3270
3342
|
}
|
|
3271
3343
|
return picked;
|
|
3272
3344
|
}
|
|
@@ -3370,6 +3442,11 @@ Installing ${names.length} skills...
|
|
|
3370
3442
|
console.error(`Error: ${result.error}`);
|
|
3371
3443
|
process.exit(1);
|
|
3372
3444
|
}
|
|
3445
|
+
if (result.warnings) {
|
|
3446
|
+
for (const warning of result.warnings) {
|
|
3447
|
+
console.warn(`\u26A0 ${warning}`);
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3373
3450
|
console.log(`\u2713 Removed '${skillName}'`);
|
|
3374
3451
|
break;
|
|
3375
3452
|
}
|
package/package.json
CHANGED
package/skills/index.json
CHANGED
|
@@ -219,9 +219,15 @@
|
|
|
219
219
|
},
|
|
220
220
|
{
|
|
221
221
|
"name": "magpie",
|
|
222
|
-
"description": "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 (falling back to a Claude second opinion when codex is unavailable), and serves an interactive HTML report for selecting findings to post via gh. Bundles a Bun CLI installed onto PATH via the skill's postinstall step. Use when the user asks to review a GitHub pull request.",
|
|
222
|
+
"description": "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 (falling back to a Claude second opinion when codex is unavailable), and serves an interactive HTML report for selecting findings to post via gh. 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.",
|
|
223
223
|
"type": "prompt",
|
|
224
|
-
"version": "0.
|
|
224
|
+
"version": "0.10.0"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"name": "migrate",
|
|
228
|
+
"description": "Source-agnostic legacy migration mapping. Walks a legacy codebase through probe, enumerate, seam, extract, parity, and queue, building an auditable requirements ledger with mandatory citations and a `migrate check` gate in place of self-reported completeness. Use when the user asks to migrate, re-specify, replatform, or map a legacy system onto a new stack, or to resume, check, or report on a mapping run already under way.",
|
|
229
|
+
"type": "prompt",
|
|
230
|
+
"version": "0.2.0"
|
|
225
231
|
},
|
|
226
232
|
{
|
|
227
233
|
"name": "module-secret-auditor",
|
|
@@ -273,6 +279,12 @@
|
|
|
273
279
|
],
|
|
274
280
|
"quick": false
|
|
275
281
|
},
|
|
282
|
+
{
|
|
283
|
+
"name": "sluice",
|
|
284
|
+
"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 six rules as one-liners in the router and the full treatment in references read only on friction. Claude Code only; conflicts with the superpowers plugin.",
|
|
285
|
+
"type": "prompt",
|
|
286
|
+
"version": "1.0.0"
|
|
287
|
+
},
|
|
276
288
|
{
|
|
277
289
|
"name": "temporal-coupling-detector",
|
|
278
290
|
"description": "Hidden ordering dependency analysis: detect two-phase init, method order dependencies, invisible preconditions, and resource lifecycle violations; fix with types, parameters, and factory patterns",
|
package/skills/magpie/SKILL.md
CHANGED
|
@@ -7,7 +7,7 @@ description: Use when the user asks to review a GitHub pull request (a PR number
|
|
|
7
7
|
|
|
8
8
|
## Prerequisites
|
|
9
9
|
|
|
10
|
-
The skill pre-flights `bun`, `gh`, `git` (required) and `codex` (optional).
|
|
10
|
+
The skill pre-flights `bun`, `gh`, `git` (required) and `codex` (optional). A missing required binary aborts the run with a single install hint line. Without `codex` the run continues and peer review falls back to a Claude second-opinion subagent (setup prints a one-line notice and logs `{stage: preflight, status: done, missingOptional: ["codex"]}`).
|
|
11
11
|
|
|
12
12
|
## Stage walkthrough
|
|
13
13
|
|
|
@@ -15,15 +15,15 @@ Stop reading and follow these steps in order. Do not skip stages. Use the exact
|
|
|
15
15
|
|
|
16
16
|
### 0. Identify the PR
|
|
17
17
|
|
|
18
|
-
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
|
|
18
|
+
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
19
|
|
|
20
|
-
|
|
20
|
+
Before minting a new id, check whether an earlier run on this PR is unfinished:
|
|
21
21
|
|
|
22
22
|
```
|
|
23
23
|
magpie --list-runs
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Each line is `<id>\t<active|archived>\t<path>`. If an `active` id matches `pr-${PR_NUMBER}-*`, that run was interrupted rather than cleaned up. Set `RUN_DIR` to its path and go to "Resuming a crashed run"
|
|
26
|
+
Each line is `<id>\t<active|archived>\t<path>`. If an `active` id matches `pr-${PR_NUMBER}-*`, that run was interrupted rather than cleaned up. Set `RUN_DIR` to its path and go to "Resuming a crashed run"; ask the user first if it is unclear whether they want to resume or start over. (`archived` ids are finished runs, not resumable.)
|
|
27
27
|
|
|
28
28
|
Otherwise compute a fresh run directory:
|
|
29
29
|
|
|
@@ -38,13 +38,30 @@ RUN_DIR="$HOME/.magpie/$RUN_ID"
|
|
|
38
38
|
magpie setup "$RUN_DIR" --pr $PR_NUMBER --repo "$REPO"
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
If exit is non-zero, surface stderr verbatim and stop. The CLI removes the worktree and subdirs on failure
|
|
41
|
+
If exit is non-zero, surface stderr verbatim and stop. The CLI removes the worktree and subdirs on failure, keeping the run directory and `log.jsonl` for diagnostics.
|
|
42
42
|
|
|
43
|
-
Setup
|
|
43
|
+
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
|
+
|
|
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.
|
|
52
|
+
|
|
53
|
+
Setup then splits the filtered diff into shards, writing `$RUN_DIR/shards/manifest.json`
|
|
54
|
+
and, when more than one shard results, `$RUN_DIR/shards/shard-<n>.patch`. `diff.patch`
|
|
55
|
+
itself is never modified: shards are views over it. Re-split with a different budget
|
|
56
|
+
using `magpie shard "$RUN_DIR" --budget <lines> --max-files <n>` (defaults: 6000 patch
|
|
57
|
+
lines, 80 files). Re-splitting invalidates every existing
|
|
58
|
+
`findings/<focus>.shard-<n>.json`, since a shard id then names a different file set:
|
|
59
|
+
delete those files first, or stage 4's resume rule counts a pair as covered that
|
|
60
|
+
nothing reviewed.
|
|
44
61
|
|
|
45
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.
|
|
46
63
|
|
|
47
|
-
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
|
|
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.
|
|
48
65
|
|
|
49
66
|
### 2. Serve
|
|
50
67
|
|
|
@@ -54,9 +71,9 @@ Start the HTML server in the background using the Bash tool with `run_in_backgro
|
|
|
54
71
|
magpie serve "$RUN_DIR"
|
|
55
72
|
```
|
|
56
73
|
|
|
57
|
-
Read `$RUN_DIR/state/server-info` for the URL; the server writes it asynchronously
|
|
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."
|
|
58
75
|
|
|
59
|
-
The server shuts down after 30 minutes
|
|
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.
|
|
60
77
|
|
|
61
78
|
Render the first progress paint:
|
|
62
79
|
|
|
@@ -68,7 +85,7 @@ magpie render "$RUN_DIR" progress
|
|
|
68
85
|
|
|
69
86
|
Append `{stage: context, status: running}` to `$RUN_DIR/log.jsonl` and re-render progress. This stage has two steps and never aborts the run.
|
|
70
87
|
|
|
71
|
-
**Bind probe.** If the `mcp__code-intelligence__*` tools are not in your tool list, skip straight to the scout with `CODE_INTELLIGENCE=unavailable`. Otherwise call `bind_workspace` with `$RUN_DIR/worktree
|
|
88
|
+
**Bind probe.** If the `mcp__code-intelligence__*` tools are not in your tool list, skip straight to the scout with `CODE_INTELLIGENCE=unavailable`. Otherwise call `bind_workspace` with `$RUN_DIR/worktree`: it is a linked git worktree, so an already-indexed base repo seeds its index instead of re-indexing.
|
|
72
89
|
|
|
73
90
|
- `consent_required` means the base repo has never completed an index. **Never call `approve_indexing`**: that is a full GPU pass the user did not ask for. Set `CODE_INTELLIGENCE=unavailable`, and print one line: "Code intelligence is unavailable (the base repo has no index); specialists will review from the diff alone."
|
|
74
91
|
- `indexing_started` or `indexing_in_progress` means the seed took. Poll `get_index_stats` every 5s for at most 60s, then set `CODE_INTELLIGENCE=available` either way. Do not block the pipeline on completion; the specialist contract handles a still-indexing tool.
|
|
@@ -77,17 +94,61 @@ Append `{stage: context, status: running}` to `$RUN_DIR/log.jsonl` and re-render
|
|
|
77
94
|
|
|
78
95
|
**Scout.** Read `references/scout.md` and dispatch one subagent (Agent tool, `general-purpose`) carrying the `magpie-scout` block with `<<RUN_DIR>>`, `<<PR_NUMBER>>`, and `<<CODE_INTELLIGENCE>>` substituted. It writes `$RUN_DIR/brief.json`.
|
|
79
96
|
|
|
80
|
-
Append `{stage: context, status: done, codeIntelligence: true|false}` and re-render progress. If the scout returned without writing `brief.json`, append `{stage: context, status: skipped, codeIntelligence: true|false}` instead and continue: the brief is optional everywhere it is read.
|
|
97
|
+
Append `{stage: context, status: done, codeIntelligence: true|false}` and re-render progress. If the scout returned without writing `brief.json`, append `{stage: context, status: skipped, codeIntelligence: true|false}` instead and continue: the brief is optional everywhere it is read. Both entries carry the probe's result, which is known whatever the scout did.
|
|
81
98
|
|
|
82
99
|
### 4. Specialists
|
|
83
100
|
|
|
84
|
-
Read `references/specialists.md` now, before dispatching anything
|
|
85
|
-
|
|
86
|
-
Append `{stage: specialists, status: running}` to `$RUN_DIR/log.jsonl` and re-render
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
101
|
+
Read `references/specialists.md` now, before dispatching anything: it holds the five focus blocks and the output contract every specialist prompt is built from. Assemble prompts from that file verbatim; written from memory they drift off the JSON contract, and `magpie dedupe` drops findings it cannot parse.
|
|
102
|
+
|
|
103
|
+
Append `{stage: specialists, status: running}` to `$RUN_DIR/log.jsonl` and re-render
|
|
104
|
+
progress, so the served page shows the stage as active rather than "Paused". Then read
|
|
105
|
+
`$RUN_DIR/shards/manifest.json`.
|
|
106
|
+
|
|
107
|
+
**One shard, zero shards, or no manifest** (a diff filtered down to nothing, e.g. a
|
|
108
|
+
lockfile-only PR, yields `shards: []` in an otherwise normal manifest; a run predating
|
|
109
|
+
this feature has no manifest at all): dispatch the five specialists in a single message,
|
|
110
|
+
five parallel Agent calls, one per focus in (security, bugs, performance, code-smells,
|
|
111
|
+
architecture), each carrying the prompt `references/specialists.md` describes with the
|
|
112
|
+
unsharded run header. The shard gate and the wave dispatch below do not apply; the
|
|
113
|
+
logging and the file check at the end of this stage still do.
|
|
114
|
+
|
|
115
|
+
**More than one shard:** each focus reviews every shard, so the run dispatches
|
|
116
|
+
`5 × <shard count>` subagents in total.
|
|
117
|
+
|
|
118
|
+
**More than four shards: stop and ask the user once, before dispatching anything.**
|
|
119
|
+
State the shard count, the resulting agent count, and the three options: proceed as
|
|
120
|
+
sharded; re-shard for fewer, larger chunks with
|
|
121
|
+
`magpie shard "$RUN_DIR" --budget <lines> --max-files <n>`, raising both flags (a PR of
|
|
122
|
+
many small files is split by the 80-file cap, so a larger `--budget` alone changes
|
|
123
|
+
nothing); or review only the highest-risk shards, which means appending
|
|
124
|
+
`{stage: shard-coverage, status: partial, reviewed: [<ids>], skipped: [<ids>]}` to
|
|
125
|
+
`$RUN_DIR/log.jsonl` and telling the user in the terminal which shards go unreviewed.
|
|
126
|
+
That log entry is the only record of the gap: the report has no unreviewed marker.
|
|
127
|
+
Wait for the answer. This is the only interactive gate in the pipeline before the
|
|
128
|
+
report, and it exists so that neither the cost nor a coverage gap is ever silent.
|
|
129
|
+
|
|
130
|
+
Dispatch by wave, one shard per wave, the five focuses in parallel within a wave,
|
|
131
|
+
re-rendering progress between waves. That holds in-flight agents at five and makes a
|
|
132
|
+
crash cheap to resume: only the `(focus, shard)` pairs whose findings file is missing
|
|
133
|
+
need re-dispatching.
|
|
134
|
+
|
|
135
|
+
After each subagent returns, append
|
|
136
|
+
`{stage: specialist, focus: <focus>, shard: <n>, status: done, findings: <count>}` to
|
|
137
|
+
`$RUN_DIR/log.jsonl` and re-render progress. Omit `shard` on the unsharded path (one
|
|
138
|
+
shard, zero shards, or no manifest).
|
|
139
|
+
(Per-focus `specialist` entries are diagnostic; only the aggregate `specialists` entry
|
|
140
|
+
advances `magpie status`.)
|
|
141
|
+
|
|
142
|
+
Before leaving this stage, list `$RUN_DIR/findings` and confirm one file per expected
|
|
143
|
+
`(focus, shard)` pair: `5 × <shard count>` named `findings/<focus>.shard-<n>.json` when
|
|
144
|
+
sharded, five `findings/<focus>.json` otherwise, plus `findings/tests.json` from setup.
|
|
145
|
+
Re-dispatch any pair missing from a shard you meant to review; a shard skipped at the
|
|
146
|
+
gate is expected to have none. `magpie dedupe` re-checks this against the manifest and
|
|
147
|
+
names every missing pair on stdout, as a backstop rather than a substitute.
|
|
148
|
+
|
|
149
|
+
If every specialist fails (no findings files written), log
|
|
150
|
+
`{stage: specialists, status: error}`, rebind code intelligence to `$REPO` if bound
|
|
151
|
+
(stage 10), and stop. Otherwise mark `{stage: specialists, status: done}`.
|
|
91
152
|
|
|
92
153
|
### 5. Dedupe
|
|
93
154
|
|
|
@@ -95,9 +156,9 @@ If all five specialists fail (no findings files written), log `{stage: specialis
|
|
|
95
156
|
magpie dedupe "$RUN_DIR" [--threshold <0-10>]
|
|
96
157
|
```
|
|
97
158
|
|
|
98
|
-
`magpie dedupe` also runs a deterministic evidence check against the worktree: findings whose `file` is missing or whose `line` is out of range are dropped
|
|
159
|
+
`magpie dedupe` also runs a deterministic evidence check against the worktree: findings whose `file` is missing or whose `line` is out of range are dropped, logged, and recorded to `$RUN_DIR/evidence-dropped.json`. The check is skipped when the worktree is gone (archived run replay).
|
|
99
160
|
|
|
100
|
-
Each finding
|
|
161
|
+
Each finding gets a derived 0-10 `score` from its risk fields; those below `--threshold` (default 3) are dropped before the critic LLM runs and recorded to `$RUN_DIR/threshold-dropped.json`. Pass `--threshold 0` to keep everything.
|
|
101
162
|
|
|
102
163
|
Re-render progress.
|
|
103
164
|
|
|
@@ -105,25 +166,38 @@ Re-render progress.
|
|
|
105
166
|
|
|
106
167
|
Read `references/critic.md` and `$RUN_DIR/findings.deduped.json`. Substitute both placeholders in the critic rubric (the compact candidate list including each finding's `onChangedLine`, and the `<<DIFF_EXCERPT>>` hunks for the referenced files), then apply the rubric verbatim (one verdict per finding). Write the kept subset to `$RUN_DIR/findings.kept.json`. Append `{stage: critic, status: done}` and re-render progress.
|
|
107
168
|
|
|
169
|
+
When `findings.deduped.json` holds more than 40 findings, run the rubric in batches of
|
|
170
|
+
30 rather than one prompt: a sharded run can produce more candidates than fit alongside
|
|
171
|
+
their diff excerpts. Apply the same rubric verbatim per batch and concatenate the kept
|
|
172
|
+
subsets into `findings.kept.json`.
|
|
173
|
+
|
|
108
174
|
### 7. Peer review
|
|
109
175
|
|
|
110
|
-
Append `{stage: peer-review, status: running}` to `$RUN_DIR/log.jsonl` and re-render progress. This stage always runs. `codex` is
|
|
176
|
+
Append `{stage: peer-review, status: running}` to `$RUN_DIR/log.jsonl` and re-render progress. This stage always runs. `codex` is preferred because it is a different model from the Claude agents that produced the findings; without it, a Claude second-opinion subagent stands in.
|
|
177
|
+
|
|
178
|
+
Build the peer-review prompt first: read `references/peer-review.md`, take the `magpie-peer-review` block from it, and substitute the placeholders listed in that file's `## Substitute before use` preamble.
|
|
111
179
|
|
|
112
|
-
|
|
180
|
+
One batch carries up to 40 findings; above that, split them 30 at a time, as in stage 6.
|
|
181
|
+
Write each batch's prompt, its `<<KEPT_FINDINGS_COMPACT>>` narrowed to that batch, to
|
|
182
|
+
`$RUN_DIR/peer-prompt-<k>.md`, `<k>` counting from 1. **When there is a single batch, drop `-<k>` throughout** (`peer-prompt.md`,
|
|
183
|
+
`peer.out`), which is the common case. Keep the `add` id counter running across batches
|
|
184
|
+
(`peer-1`, `peer-2`, ...): restarting it per batch produces colliding ids, and every
|
|
185
|
+
finding in `findings.final.json` must have a unique one or the report and post stages
|
|
186
|
+
crash.
|
|
113
187
|
|
|
114
|
-
**Codex path (preferred).** If `codex` is available (setup did not log `missingOptional: ["codex"]` and `command -v codex` succeeds), set `<<PEER_PROVIDER>>` to `codex` and run codex
|
|
188
|
+
**Codex path (preferred).** If `codex` is available (setup did not log `missingOptional: ["codex"]` and `command -v codex` succeeds), set `<<PEER_PROVIDER>>` to `codex` and run codex once per batch, that batch's prompt piped on stdin:
|
|
115
189
|
|
|
116
190
|
```
|
|
117
|
-
codex exec < "$RUN_DIR/peer-prompt
|
|
191
|
+
codex exec < "$RUN_DIR/peer-prompt-<k>.md" > "$RUN_DIR/peer-<k>.out"
|
|
118
192
|
```
|
|
119
193
|
|
|
120
|
-
`peer
|
|
194
|
+
Each `peer-<k>.out` is codex's full transcript; extract the fenced JSON block tagged `review-peer-review` from each. Write the concatenated verdict arrays to `$RUN_DIR/peer.json` once, after the last batch: writing `peer.json` per batch keeps only the last batch's verdicts and silently discards the rest. Then append `{stage: peer-review, status: done, provider: codex}` and apply the verdicts as described below.
|
|
121
195
|
|
|
122
|
-
If codex returns non-zero, do not abort: record `{stage: peer-review, provider: codex, status: fallback, error: "<first line of stderr>"}` and
|
|
196
|
+
If codex returns non-zero on a batch, do not abort: record `{stage: peer-review, provider: codex, status: fallback, batch: <k>, error: "<first line of stderr>"}` and take the Claude path for that batch. (Never log `status: error` for a recoverable codex failure: `magpie status` stops at the first `error` entry and would report the run as poisoned even after the Claude fallback succeeds.)
|
|
123
197
|
|
|
124
|
-
**Claude path (fallback).** When `codex` is unavailable or failed, get the second opinion from a Claude subagent instead. Set `<<PEER_PROVIDER>>` to `claude`, then prepend the `magpie-peer-review-claude-preamble` block from `references/peer-review.md` to
|
|
198
|
+
**Claude path (fallback).** When `codex` is unavailable or failed, get the second opinion from a Claude subagent instead, one per batch. Set `<<PEER_PROVIDER>>` to `claude`, then prepend the `magpie-peer-review-claude-preamble` block from `references/peer-review.md` to each batch's substituted prompt (the preamble forces genuine independence, since the reviewer shares a model family with the primary reviewers). Dispatch one subagent (Agent tool, `general-purpose`) per batch whose entire task is that combined prompt, and instruct it to return only the fenced `review-peer-review` JSON block. Write each output to `$RUN_DIR/peer-<k>.out`, extract each `review-peer-review` block, merge into `$RUN_DIR/peer.json` after the last batch as above, and append `{stage: peer-review, status: done, provider: claude}` (`provider: mixed` if codex handled some batches).
|
|
125
199
|
|
|
126
|
-
**Apply the verdicts (both paths).** Parse the verdicts
|
|
200
|
+
**Apply the verdicts (both paths).** Parse the merged verdicts and apply the `update` / `add` entries (an empty array means no change). Mint each `add`'s `id` as above before merging, since the peer contract does not carry ids. Then write `findings.final.json`. Re-render progress.
|
|
127
201
|
|
|
128
202
|
### 8. Report
|
|
129
203
|
|
|
@@ -139,9 +213,9 @@ End the turn.
|
|
|
139
213
|
|
|
140
214
|
### 9. Post
|
|
141
215
|
|
|
142
|
-
Most users
|
|
216
|
+
Most users tick the checkboxes in the served report and click **Post Selected** (or **Post Recommended**, which takes every `must-fix`/`should-fix` finding and skips the `consider`/`optional` ones); the server posts that batch as one GitHub review with inline threads. The agent posts only when the user types `post` (optionally `post 1,3,7` for indices), which takes the CLI path below: separate inline comments plus a top-level summary comment. Either path records posted ids in `post-status.json`, so the two cannot double-post the same finding.
|
|
143
217
|
|
|
144
|
-
When the user types `post`, read `$RUN_DIR/state/events
|
|
218
|
+
When the user types `post`, read `$RUN_DIR/state/events` and fold them in order, keeping the LAST event per finding id; ids whose last event is `select` are selected. (Not union-minus: the UI emits one event per toggle, so select, deselect, select again resolves to selected.) Merge any explicit indices the user named (1-based, against `findings.final.json` in file order). If nothing is selected, say so and ask rather than posting an empty batch. Then post via the CLI:
|
|
145
219
|
|
|
146
220
|
```
|
|
147
221
|
magpie post "$RUN_DIR" --ids id1,id2,id3
|
|
@@ -149,12 +223,12 @@ magpie post "$RUN_DIR" --ids id1,id2,id3
|
|
|
149
223
|
|
|
150
224
|
That delegates to `runPost`, which:
|
|
151
225
|
|
|
152
|
-
-
|
|
153
|
-
- Falls back to `formatConversationBody` (same shape plus a `Location · <file>:<line>` metaline)
|
|
154
|
-
- When at least one new finding is
|
|
226
|
+
- For a finding with a `line`, picks `formatInlineBody` (severity heading, `<sub>` risk metaline, parsed `Observation`/`Why it matters`/`Suggested direction`/`Needs verification` sections, optional `` ```suggestion `` block, hidden `magpie:finding` marker) and opens an inline review thread via `gh api repos/<owner>/<repo>/pulls/<n>/comments`.
|
|
227
|
+
- Falls back to `formatConversationBody` (same shape plus a `Location · <file>:<line>` metaline) via `gh pr comment <n>` when there is no anchor, or GitHub rejects the inline anchor with 422.
|
|
228
|
+
- When at least one new finding is in the batch (default `auto` mode), prepends one top-level summary comment (verdict line, "Needs Attention" top three, `<details>` risk breakdown) and persists the `__summary__` sentinel in `post-status.json` so re-runs don't duplicate it. Override with `--include-summary always|never`.
|
|
155
229
|
- Appends `{stage: post, ...}` events to `log.jsonl` and updates `$RUN_DIR/post-status.json` per finding id.
|
|
156
230
|
|
|
157
|
-
Pass `--dry-run` to record the would-be gh commands without invoking gh. After posting, append `{stage: post, status: done}` to `$RUN_DIR/log.jsonl` (`runPost` logs per-finding `ok`/`failed` events but not the stage
|
|
231
|
+
Pass `--dry-run` to record the would-be gh commands without invoking gh. After posting, append `{stage: post, status: done}` to `$RUN_DIR/log.jsonl` (`runPost` logs per-finding `ok`/`failed` events but not the stage marker, and `magpie status` counts only `done`), then re-render the report so the badges update:
|
|
158
232
|
|
|
159
233
|
```
|
|
160
234
|
magpie render "$RUN_DIR" findings
|
|
@@ -166,15 +240,15 @@ magpie render "$RUN_DIR" findings
|
|
|
166
240
|
magpie cleanup "$RUN_DIR" --repo "$REPO"
|
|
167
241
|
```
|
|
168
242
|
|
|
169
|
-
If the context stage bound code intelligence, rebind the session
|
|
243
|
+
If the context stage bound code intelligence, rebind the session now: call `bind_workspace` with `$REPO`. Binding is per session with no per-call override, so ending a run without this leaves the session pointed at a worktree `cleanup` just deleted. The daemon prunes the seeded index once the worktree is gone.
|
|
170
244
|
|
|
171
|
-
The run directory is renamed to `<run-dir>.archived-<timestamp>` and the worktree is removed. The CLI prints two lines on success: `archived to <path>` and `view later: magpie open <archived-id>`. Surface that second line
|
|
245
|
+
The run directory is renamed to `<run-dir>.archived-<timestamp>` and the worktree is removed. The CLI prints two lines on success: `archived to <path>` and `view later: magpie open <archived-id>`. Surface that second line verbatim so the user has a one-command path back to the report.
|
|
172
246
|
|
|
173
247
|
The archived `findings.html` is self-contained and auto-switches to read-only "archived" mode when opened, so:
|
|
174
248
|
|
|
175
|
-
- `magpie open` (no args) opens the latest run in the
|
|
249
|
+
- `magpie open` (no args) opens the latest run in the default browser via `open`/`xdg-open`; `--dry-run` prints the command instead of spawning it.
|
|
176
250
|
- `magpie open <id>` opens a specific archived run.
|
|
177
|
-
- `magpie serve <id>` re-spins the Bun server against an archived run
|
|
251
|
+
- `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).
|
|
178
252
|
- `magpie --list-runs` enumerates all runs in `~/.magpie/`.
|
|
179
253
|
|
|
180
254
|
## Resuming a crashed run
|
|
@@ -189,10 +263,14 @@ The JSON output tells you `lastCompleted` and `next`. Resume from `next`:
|
|
|
189
263
|
|
|
190
264
|
- `context` re-runs by redoing the bind probe, then dispatching the scout only if `$RUN_DIR/brief.json` is missing. The seeded index survives a crash, so the rebind is near-instant.
|
|
191
265
|
- Any other stage: run it as written in the walkthrough.
|
|
192
|
-
- If a specialist focus has no findings file but its sibling stages are done,
|
|
266
|
+
- If a specialist focus has no findings file but its sibling stages are done,
|
|
267
|
+
re-dispatch only that focus. On a sharded run the unit is the `(focus, shard)` pair:
|
|
268
|
+
read `shards/manifest.json`, and re-dispatch only the pairs with no
|
|
269
|
+
`findings/<focus>.shard-<n>.json`. Never re-shard mid-run without deleting those
|
|
270
|
+
files first (stage 1): the check would otherwise trust ids that moved.
|
|
193
271
|
- Non-null `error` means the run stopped on a failed stage. Report which stage to the user and confirm before re-running it.
|
|
194
272
|
|
|
195
|
-
The
|
|
273
|
+
The original server is gone. Restart it with `magpie serve "$RUN_DIR"` (step 2) before re-rendering, so the user gets a live URL again.
|
|
196
274
|
|
|
197
275
|
## Aborting
|
|
198
276
|
|