@mmerterden/multi-agent-pipeline 12.3.0 → 12.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/package.json +1 -1
  3. package/pipeline/commands/multi-agent/forget/SKILL.md +29 -0
  4. package/pipeline/commands/multi-agent/help/SKILL.md +18 -0
  5. package/pipeline/commands/multi-agent/routines/SKILL.md +30 -0
  6. package/pipeline/commands/multi-agent/save/SKILL.md +46 -0
  7. package/pipeline/commands/multi-agent/sync/SKILL.md +4 -4
  8. package/pipeline/lib/extract-conventions.sh +1 -0
  9. package/pipeline/lib/repo-cache.sh +1 -0
  10. package/pipeline/lib/shadow-git.sh +8 -0
  11. package/pipeline/multi-agent-refs/cross-cli-contract.md +4 -3
  12. package/pipeline/multi-agent-refs/phases/phase-0-init.md +9 -1
  13. package/pipeline/multi-agent-refs/phases/phase-4-review.md +3 -2
  14. package/pipeline/multi-agent-refs/phases/phase-5-test.md +10 -0
  15. package/pipeline/multi-agent-refs/phases.md +2 -0
  16. package/pipeline/multi-agent-refs/prompt-assembly.md +31 -0
  17. package/pipeline/schemas/learnings-ledger.schema.json +4 -0
  18. package/pipeline/schemas/migrations/prefs-2.3.0-to-2.4.0.mjs +32 -0
  19. package/pipeline/schemas/prefs.schema.json +36 -1
  20. package/pipeline/schemas/token-budget.json +2 -2
  21. package/pipeline/schemas/triage-corpus.schema.json +5 -1
  22. package/pipeline/scripts/eval-mine-corpus.mjs +19 -5
  23. package/pipeline/scripts/fixtures/install-layout.tsv +7 -7
  24. package/pipeline/scripts/gc-worktrees.sh +23 -1
  25. package/pipeline/scripts/learning-curve.mjs +167 -0
  26. package/pipeline/scripts/learnings-ledger.mjs +9 -2
  27. package/pipeline/scripts/migrate-prefs.mjs +8 -1
  28. package/pipeline/scripts/repo-map.mjs +1 -1
  29. package/pipeline/scripts/routine-registry.mjs +226 -0
  30. package/pipeline/scripts/smoke-generate-issue.sh +3 -3
  31. package/pipeline/scripts/smoke-learning-curve.sh +61 -0
  32. package/pipeline/scripts/smoke-migrate-state.sh +3 -3
  33. package/pipeline/scripts/smoke-pref-migration.sh +8 -6
  34. package/pipeline/scripts/smoke-review-readiness.sh +1 -1
  35. package/pipeline/scripts/smoke-routines.sh +84 -0
  36. package/pipeline/scripts/triage-memory.mjs +48 -10
  37. package/pipeline/skills/shared/core/multi-agent-forget/SKILL.md +22 -0
  38. package/pipeline/skills/shared/core/multi-agent-routines/SKILL.md +20 -0
  39. package/pipeline/skills/shared/core/multi-agent-save/SKILL.md +27 -0
  40. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +4 -4
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env bash
2
+ # smoke-routines.sh - contract for routine-registry.mjs, the backend of the
3
+ # user-defined routine commands (/multi-agent:save, :routines, :forget).
4
+
5
+ set -uo pipefail
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
8
+ RR="$SCRIPT_DIR/routine-registry.mjs"
9
+
10
+ PASS=0
11
+ FAIL=0
12
+ record_pass() { PASS=$((PASS + 1)); printf ' \342\234\223 %s\n' "$1"; }
13
+ record_fail() { FAIL=$((FAIL + 1)); printf ' \342\234\227 %s\n' "$1"; }
14
+
15
+ printf '\342\206\222 smoke-routines: routine-registry backend contract\n'
16
+
17
+ tmp=$(mktemp -d "${TMPDIR:-/tmp}/routines.XXXXXX")
18
+ trap 'rm -rf "$tmp"' EXIT
19
+ H="$tmp/home"
20
+ mkdir -p "$H/.claude/commands/multi-agent"
21
+ printf '{"global":{}}\n' > "$H/.claude/multi-agent-preferences.json"
22
+ printf 'Run steps 1-6 of the checkin service migration.\n' > "$tmp/body.md"
23
+
24
+ # 1. add -> writes a local-only command dir + registry entry
25
+ node "$RR" add --name checkin-services --description "Checkin servis kontrolu" \
26
+ --source "CLAUDE.md" --body-file "$tmp/body.md" --home "$H" >/dev/null 2>&1
27
+ skill="$H/.claude/commands/multi-agent/checkin-services/SKILL.md"
28
+ if [ -f "$skill" ] && grep -qx "local-only: true" "$skill"; then
29
+ record_pass "add writes a local-only command SKILL.md"
30
+ else
31
+ record_fail "add did not write a local-only routine command"
32
+ fi
33
+ if node "$RR" list --json --home "$H" | grep -q '"name": "checkin-services"'; then
34
+ record_pass "registry records the routine"
35
+ else
36
+ record_fail "registry missing the routine"
37
+ fi
38
+
39
+ # 2. add is name-collision safe (same name rejected)
40
+ node "$RR" add --name checkin-services --description x --source y --body-file "$tmp/body.md" --home "$H" >/dev/null 2>&1
41
+ [ "$?" -eq 1 ] && record_pass "duplicate name rejected" || record_fail "duplicate name should reject"
42
+
43
+ # 3. invalid name rejected
44
+ node "$RR" add --name "Bad Name" --description x --source y --body-file "$tmp/body.md" --home "$H" >/dev/null 2>&1
45
+ [ "$?" -eq 1 ] && record_pass "invalid name rejected" || record_fail "invalid name should reject"
46
+
47
+ # 4. shipped-command guard: never remove a non-local-only command
48
+ mkdir -p "$H/.claude/commands/multi-agent/status"
49
+ printf -- '---\ndescription: "shipped"\n---\nbody\n' > "$H/.claude/commands/multi-agent/status/SKILL.md"
50
+ node "$RR" remove --name status --home "$H" >/dev/null 2>&1
51
+ if [ "$?" -eq 1 ] && [ -d "$H/.claude/commands/multi-agent/status" ]; then
52
+ record_pass "refuses to remove a shipped command"
53
+ else
54
+ record_fail "shipped command must be protected"
55
+ fi
56
+
57
+ # 5. sync backstop proxy: the generated routine carries local-only:true, which is
58
+ # exactly what sync/SKILL.md greps to keep it out of the public repo.
59
+ if grep -qx "local-only: true" "$skill"; then
60
+ record_pass "routine is sync-excluded (local-only marker present)"
61
+ else
62
+ record_fail "routine missing local-only marker (would leak on sync)"
63
+ fi
64
+
65
+ # 6. remove -> deletes dir + registry entry
66
+ node "$RR" remove --name checkin-services --home "$H" >/dev/null 2>&1
67
+ if [ ! -d "$H/.claude/commands/multi-agent/checkin-services" ] \
68
+ && ! node "$RR" list --json --home "$H" | grep -q "checkin-services"; then
69
+ record_pass "remove deletes dir + registry entry"
70
+ else
71
+ record_fail "remove left dir or registry entry behind"
72
+ fi
73
+
74
+ # 7. install preservation proxy: snapshotLocalOnlyWrappers keys on local-only:true.
75
+ # Re-add, then confirm the marker survives a simulated namespace re-read.
76
+ node "$RR" add --name my-routine --description d --source s --body-file "$tmp/body.md" --home "$H" >/dev/null 2>&1
77
+ if grep -qx "local-only: true" "$H/.claude/commands/multi-agent/my-routine/SKILL.md"; then
78
+ record_pass "re-added routine keeps the install-preserving marker"
79
+ else
80
+ record_fail "re-added routine lost its local-only marker"
81
+ fi
82
+
83
+ printf '\n\342\225\220\342\225\220 smoke-routines: %d passed, %d failed \342\225\220\342\225\220\n' "$PASS" "$FAIL"
84
+ [ "$FAIL" -eq 0 ]
@@ -84,6 +84,33 @@ function ensureDir(p) {
84
84
  mkdirSync(dirname(p), { recursive: true });
85
85
  }
86
86
 
87
+ // Freshness gate (P1): stamp each finding with the git blob SHA of its file at
88
+ // ingest time; on query, if the file's current content hash differs, the hit is
89
+ // flagged stale so a reused finding is verified, not trusted blindly.
90
+ function repoTop() {
91
+ try {
92
+ return execFileSync("git", ["rev-parse", "--show-toplevel"], {
93
+ encoding: "utf8",
94
+ stdio: ["ignore", "pipe", "ignore"],
95
+ }).trim() || null;
96
+ } catch {
97
+ return null;
98
+ }
99
+ }
100
+
101
+ function fileSha(file, top) {
102
+ if (!file || !top) return null;
103
+ try {
104
+ return execFileSync("git", ["hash-object", "--", file], {
105
+ cwd: top,
106
+ encoding: "utf8",
107
+ stdio: ["ignore", "pipe", "ignore"],
108
+ }).trim() || null;
109
+ } catch {
110
+ return null;
111
+ }
112
+ }
113
+
87
114
  const STOP = new Set([
88
115
  "the","a","an","and","or","but","of","in","on","at","to","for","with","by","is","are",
89
116
  "be","been","being","this","that","these","those","it","its","as","not","no","do","does",
@@ -157,6 +184,7 @@ function ingest() {
157
184
  catch (e) { die(`cannot parse triage JSON: ${e.message}`, 1); }
158
185
 
159
186
  const ts = new Date().toISOString();
187
+ const top = repoTop();
160
188
  const corpus = readCorpus(slug);
161
189
  const seen = new Set(corpus.map((r) => `${r.task_id}::${r.classification}::${r.file}::${r.issue}`));
162
190
 
@@ -188,6 +216,7 @@ function ingest() {
188
216
  fix: f.fix || null,
189
217
  reason: f._reason || null,
190
218
  reviewer: f.reviewer || null,
219
+ file_sha: fileSha(file, top),
191
220
  };
192
221
  writeRow(slug, row);
193
222
  seen.add(key);
@@ -212,21 +241,30 @@ function query() {
212
241
  const qTokens = tokenize(issueText);
213
242
  const fileRe = fileGlob ? globToRegExp(fileGlob) : null;
214
243
  const filtered = fileRe ? corpus.filter((r) => fileRe.test(r.file || "")) : corpus.slice();
244
+ const top = repoTop();
215
245
  const ranked = filtered
216
246
  .map((r) => ({ row: r, score: score(qTokens, r) }))
217
247
  .filter((x) => x.score > 0 || qTokens.size === 0)
218
248
  .sort((a, b) => b.score - a.score)
219
249
  .slice(0, topN)
220
- .map(({ row, score }) => ({
221
- taskId: row.task_id,
222
- classification: row.classification,
223
- severity: row.severity,
224
- file: row.file,
225
- line: row.line,
226
- issue: row.issue,
227
- reason: row.reason,
228
- score: Math.round(score * 1000) / 1000,
229
- }));
250
+ .map(({ row, score }) => {
251
+ // Freshness: null when we cannot compare (no stored sha, or file gone /
252
+ // not in a git repo); true means the file changed since the finding was
253
+ // recorded, so verify before reusing.
254
+ const cur = row.file_sha ? fileSha(row.file, top) : null;
255
+ const stale = row.file_sha && cur ? cur !== row.file_sha : null;
256
+ return {
257
+ taskId: row.task_id,
258
+ classification: row.classification,
259
+ severity: row.severity,
260
+ file: row.file,
261
+ line: row.line,
262
+ issue: row.issue,
263
+ reason: row.reason,
264
+ stale,
265
+ score: Math.round(score * 1000) / 1000,
266
+ };
267
+ });
230
268
  process.stdout.write(JSON.stringify({ ok: true, slug, total: filtered.length, hits: ranked }) + "\n");
231
269
  process.exit(0);
232
270
  }
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: multi-agent-forget
3
+ language: en
4
+ description: "Remove a saved /multi-agent routine (created by /multi-agent:save): deletes its local-only command and its registry entry. Asks which one and confirms."
5
+ user-invocable: true
6
+ argument-hint: "[name]"
7
+ ---
8
+
9
+ # multi-agent-forget - Remove a Routine
10
+
11
+ Delete a routine saved via `/multi-agent:save`. Backed by `$HOME/.copilot/scripts/routine-registry.mjs`.
12
+
13
+ ## Steps
14
+
15
+ 1. Resolve which routine: from `$ARGUMENTS`, else `routine-registry.mjs list --json` + `AskUserQuestion` (in `outputLanguage`). None -> nothing to forget.
16
+ 2. Confirm via `AskUserQuestion`: "Remove /multi-agent:<name>?" (Remove / Cancel). Anything but Remove -> stop.
17
+ 3. `node "$HOME/.copilot/scripts/routine-registry.mjs" remove --name "<name>"` (backend refuses to remove a shipped command; only a registered `local-only: true` dir).
18
+ 4. Report removed; note the autocomplete entry clears on the next session reload.
19
+
20
+ ## Notes
21
+
22
+ - Only user-saved routines can be forgotten. See `/multi-agent:routines` for the list.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: multi-agent-routines
3
+ language: en
4
+ description: "List your saved /multi-agent routines (from /multi-agent:save) with what each one does, rendered in outputLanguage."
5
+ user-invocable: true
6
+ ---
7
+
8
+ # multi-agent-routines - List Saved Routines
9
+
10
+ Show every routine saved via `/multi-agent:save`. Read-only. Backed by `$HOME/.copilot/scripts/routine-registry.mjs`.
11
+
12
+ ## Steps
13
+
14
+ 1. `node "$HOME/.copilot/scripts/routine-registry.mjs" list --json`
15
+ 2. Render in `outputLanguage`: empty -> say there are none yet and point to `/multi-agent:save`; otherwise a compact table of `/multi-agent:<name>` + description + created date.
16
+ 3. Flag any registry entry with no matching command dir (or vice versa) as `needs repair`; never modify anything.
17
+
18
+ ## Notes
19
+
20
+ - Add with `/multi-agent:save`, remove with `/multi-agent:forget <name>`. Routines are local-only and never synced.
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: multi-agent-save
3
+ language: en
4
+ description: "Save a recurring job as a reusable /multi-agent:<name> command. Reviews the conversation + your CLAUDE.md for candidate routines, you pick one and name it; stored local-only, never synced."
5
+ user-invocable: true
6
+ argument-hint: "[name]"
7
+ ---
8
+
9
+ # multi-agent-save - Save a Routine
10
+
11
+ **Input**: $ARGUMENTS (optional routine name)
12
+
13
+ Turn a recurring procedure into a reusable `/multi-agent:<name>` command. The routine is stored local-only (a `local-only: true` command + a `prefs.global.routines` entry) and is never synced. Backed by `$HOME/.copilot/scripts/routine-registry.mjs`.
14
+
15
+ ## Steps
16
+
17
+ 1. Distill candidates from what was just done (primary): group the tasks executed this session into coherent, repeatable jobs (suggested name + one-line summary + ordered steps). Then add named end-to-end procedures from `$HOME/.claude/CLAUDE.md`.
18
+ 2. `AskUserQuestion` with `multiSelect: true` (in `outputLanguage`): one option per candidate + an always-present `Other` free-text. One selected -> that routine; several selected -> combine into ONE routine (steps concatenated in shown order, single name); `Other` -> user types it. Empty is not consent.
19
+ 3. Name the command (from `$ARGUMENTS` or ask); validate `^[a-z0-9][a-z0-9-]*$`, <= 40 chars; the backend rejects collisions.
20
+ 4. Write the full procedure body to a temp file (single candidate: embed its steps, reference the CLAUDE.md section by name when that is the source; combined: concatenate the selected candidates' steps in order under numbered phase headings). Do not invent steps.
21
+ 5. Save: `node "$HOME/.copilot/scripts/routine-registry.mjs" add --name "<name>" --description "<summary>" --source "<source>" --body-file "/tmp/routine-<name>.md"`.
22
+ 6. Report it is available as `/multi-agent:<name>` after a session reload; offer to run it now.
23
+
24
+ ## Notes
25
+
26
+ - Never writes under a repo `pipeline/` tree; routines live only under the CLI home.
27
+ - List with `/multi-agent:routines`; remove with `/multi-agent:forget <name>`.
@@ -29,7 +29,7 @@ Run all steps automatically:
29
29
 
30
30
  ```
31
31
  Step 1: DETECT Compare timestamps, find stale targets
32
- Step 2: COPILOT Claude Code -> Copilot CLI (instructions + 38 sub-command skills)
32
+ Step 2: COPILOT Claude Code -> Copilot CLI (instructions + 41 sub-command skills)
33
33
  Step 3: REPO Claude Code -> pipeline repo (genericized, personal data scrub)
34
34
  Step 4: WEBSITE Version + phase/model counts -> {website-host} (i18n + projects.ts)
35
35
  Step 5: Commit Commit + push all changed repos
@@ -136,14 +136,14 @@ When invoked with the `release` argument:
136
136
  |-------------|-------------|
137
137
  | `~/.claude/commands/multi-agent/{cmd}.md` | `~/.copilot/skills/multi-agent-{cmd}/SKILL.md` |
138
138
 
139
- **38 commands are synced** (canonical inventory - must match `cross-cli-contract.md` section 1; drift = contract violation):
139
+ **41 commands are synced** (canonical inventory - must match `cross-cli-contract.md` section 1; drift = contract violation):
140
140
 
141
141
  ```
142
142
  analysis, analysis-resolve, autopilot, build-optimize, channels, create-jira, dev,
143
- dev-autopilot, dev-local, dev-local-autopilot, diff-explain, finish, garbage-collect,
143
+ dev-autopilot, dev-local, dev-local-autopilot, diff-explain, finish, forget, garbage-collect,
144
144
  help, issue, jira, kill, language, local,
145
145
  local-autopilot, log, manual-test, prune-logs, purge, refactor, resume, review, review-issue, review-jira,
146
- scan, search, setup, stack, status, sync, test, uninstall, update
146
+ routines, save, scan, search, setup, stack, status, sync, test, uninstall, update
147
147
  ```
148
148
 
149
149
  **NOT synced**: `refs/*` - Lazy-load references, Claude Code specific