@mmerterden/multi-agent-pipeline 20.18.0 → 20.19.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/CHANGELOG.md CHANGED
@@ -16,6 +16,12 @@ Internal file-layout changes that don't affect the slash-command surface are sti
16
16
 
17
17
  ## [Unreleased]
18
18
 
19
+ ## [20.19.0] - 2026-10-09
20
+
21
+ ### Changed
22
+
23
+ - `/multi-agent:update` ends with a note of what the update brought, in the user's language: every release between the installed and the new version (not only the last one, and not cut at 40 lines), grouped as new, fixed, changed and removed, plus each stack plugin the refresh moved and what its releases added. The note is written from `release-notes.mjs`, which reads the new release's CHANGELOG and each updated plugin's CHANGELOG and never fails the update; internal-only entries are left out and nothing is added that the CHANGELOGs do not say.
24
+
19
25
  ## [20.18.0] - 2026-10-09
20
26
 
21
27
  ### Added
package/docs/facts.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$comment": "Generated by pipeline/scripts/gen-facts.mjs. Do not hand-edit: the site reads this, and a number edited here instead of at its source is the drift this file removes.",
3
3
  "generatedAt": "2026-10-09",
4
- "version": "20.18.0",
4
+ "version": "20.19.0",
5
5
  "phaseSchema": 2,
6
6
  "phases": [
7
7
  {
package/manifest.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@mmerterden/multi-agent-pipeline",
3
- "version": "20.18.0",
4
- "commit": "896371483bce326d6cb68a1f9609ebb99e7575d6",
5
- "generatedAt": "2026-10-09T05:37:30.195Z",
3
+ "version": "20.19.0",
4
+ "commit": "6db35d97282cfe57fab9bb66a0da8bc6696aca99",
5
+ "generatedAt": "2026-10-09T11:19:00.589Z",
6
6
  "algorithm": "sha256",
7
- "fileCount": 1356,
7
+ "fileCount": 1357,
8
8
  "files": {
9
- "CHANGELOG.md": "76773c616fec7b75d1db838ce716c2f25caebb605762c786c89381dc5aeb6141",
9
+ "CHANGELOG.md": "273977981d9581cd702e05f063930ef2cff624a76b687472cf0fef545e7adad3",
10
10
  "LICENSE": "8d8c51aaf1e1538b180c69965fa038db71be86798cc3e26a143bebfefe16a43e",
11
11
  "README.md": "1336cf131fdc0dbab1dd8b90fcd5614bd19d27b92b4f91ab99c3108d61cb51e1",
12
12
  "README.tr.md": "7979b32e6c72aedc3a6affe85f33b2840e72f056a836e8546566d259b236ff29",
@@ -33,7 +33,7 @@
33
33
  "docs/best-practices.md": "f8b75b0d38b5896a6a852bb30a1edddeb24dab8b5b7b1d9313290e1241c72024",
34
34
  "docs/ecosystem.md": "d504124f421748744599f6974ec71d52664affc47740665651cca5ddc17d0c71",
35
35
  "docs/engineering.md": "550528cd47c7d2c921729c68d0782a00ac8e77a4bdfec479f6749f9c3235d188",
36
- "docs/facts.json": "fd737a1eef03c1cdda33d9d4173556998b39c7bc2d3cfca06c3bcb887a18ce20",
36
+ "docs/facts.json": "80256de517e282de9b5a6e135a270d45a0590ab77ef579fafd171d644ce917b0",
37
37
  "docs/features.md": "87ae163280fe05b36ff9f664b1bf9d98b697cc498731d47298751b439fa7a35c",
38
38
  "docs/performance.md": "bbf2f3922c25dcba0c1a9e69ef420e3871a729af5b35009a49b48a5d3c5cd151",
39
39
  "docs/recovery-guide.md": "59a8f1b66b34cb9ff5507b3d54dac3b9915e184aa044d9c758d18cac818d510e",
@@ -63,7 +63,7 @@
63
63
  "install/templates/multi-agent-autopilot-awake.plist.template": "ddd25c76c56fcbb38a89de8e35637372d70ace993d6bc487a88ca9cfb702fe1f",
64
64
  "install/templates/multi-agent-autopilot.plist.template": "122c8a61cd927edd99375d6e3da40f1d42bcceed5b6641785c8b28bb930bc77e",
65
65
  "install/unattended-profile-legacy.json": "f52e86b998956f09ad306b1735a6c10b58580d7082d8a55665a4c6bb15c3aa1f",
66
- "package.json": "ea701923f2dbd97b1c712d609729f9cea25b3ccbf18eb447d9a604e22fc20e9b",
66
+ "package.json": "c45fc8e778927b72b074d2692f8fd895186b1adc1c752cfa2e18ff41c6822d49",
67
67
  "pipeline/agents/android-architect.md": "11fb74543cb0ebcf9525ca6654fe95d53da7229e0bf4a1149b31fd9005abc453",
68
68
  "pipeline/agents/backend-architect.md": "b02a606c301e4714131aed70ae6d16eeb77fbc819a001e2752cff2488d4246de",
69
69
  "pipeline/agents/bulk-reader.md": "6597052cd7d205bac2b1c7633f5232fe1a40730664a913b8eb77efd5a786dc25",
@@ -142,7 +142,7 @@
142
142
  "pipeline/commands/multi-agent/test/SKILL.md": "5a67be2b1ec0321e79f8b7f35be53676e349e55a4f8ca09a07f0de89dadac8c4",
143
143
  "pipeline/commands/multi-agent/testflight-validation/SKILL.md": "38fa8d3d3c8340d025e100c53a70183cbad22665b7ddccbb6102c05986cce489",
144
144
  "pipeline/commands/multi-agent/uninstall/SKILL.md": "b2124820a6e10cdd72be71df2183aad4f8bd8988ec90248c88599c1514f7d9ab",
145
- "pipeline/commands/multi-agent/update/SKILL.md": "b87d2b9b284860eb14f5973f640b09de2993493cc1e3676bf14bb877927966ed",
145
+ "pipeline/commands/multi-agent/update/SKILL.md": "4b9ca40382eb2bd06f8b1a483d6232d1e9941d6e6288a21e83fa977482a1bae7",
146
146
  "pipeline/commands/sim-test.md": "b09038b9511108c77fae5e0c11f8515b5c64eb8b63a77fe8c2ff5907ddd0313a",
147
147
  "pipeline/contract/CHANGELOG.md": "0813f5737219bafad3eb4daf809b9754937fae8533e07caf3850e30b2685662b",
148
148
  "pipeline/contract/README.md": "6239eb0877d70e70d63db74bae3499599b0425c80c71eceb6bb0a981523eaa89",
@@ -483,7 +483,7 @@
483
483
  "pipeline/schemas/unattended-profile.json": "05bc095b6328ff77b7bee8b008becf9aa20f1dabeab3b0fc174b97cad421e23b",
484
484
  "pipeline/schemas/unattended-profile.schema.json": "a9c0979f0d0742826e410fdbfe1ae1c4a56b05b4e638eec42d4c276d6f48f701",
485
485
  "pipeline/schemas/worktrees.schema.json": "4e15c413c00811814eee734d86734ad9e1d34a37737f7df641b389ce5d8d134b",
486
- "pipeline/scripts/README.md": "937adf6f34ce18fc07d91d6556abcdd39debf2df7819c3f16e009430dc0549a7",
486
+ "pipeline/scripts/README.md": "4cdf7054cddbe5cdcb08460f68782f2347a26c8c343d8ef8418714464672b36c",
487
487
  "pipeline/scripts/_autopilot-config.mjs": "0f42d2791f1d71c4aad5a0735ca56fef1ed27d275b72f99d8ca8f95fcb8aa83a",
488
488
  "pipeline/scripts/_autopilot-ops.mjs": "5e84d628482434c8f09dc69601c73ab70e928af15c2f068d998d2f61bd692d1a",
489
489
  "pipeline/scripts/_autopilot-outcomes.mjs": "5b2423814d979ed5c657473290083b296f7245d40507c866e7931b2771001236",
@@ -617,6 +617,7 @@
617
617
  "pipeline/scripts/prune-logs.sh": "c5587b33104c7253e07be8cce8249b50014c52dfdf232de637811ffdd88e95e4",
618
618
  "pipeline/scripts/purge.sh": "95c60b272ae47209893dba9b17528538592bb5636da47940a02734cd9b4f0ce5",
619
619
  "pipeline/scripts/record-phase.mjs": "041dc262e54f38b66299f45c44db7918812e599a773f5a3dc78cfd09ab5e155e",
620
+ "pipeline/scripts/release-notes.mjs": "05430926cf679534978bcc79c160a6787aaf04831b8b8489d8bbc9fa343a14af",
620
621
  "pipeline/scripts/render-agent-log-cost.sh": "6c58c1f5754ff5754485a0c1b66f4cd713575d2e8ebb1630aedf2f87fe34ef4d",
621
622
  "pipeline/scripts/render-cost-summary.sh": "ce9647332ab9a3e518af98f52a6fc70baab9fd3c09ec5f9e85bae2d67078a5e3",
622
623
  "pipeline/scripts/render-work-summary.sh": "ee4db04ebc145cc2fc7fa9a533d42b7da4da191710ef67c883502ca83b2aa67c",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmerterden/multi-agent-pipeline",
3
- "version": "20.18.0",
3
+ "version": "20.19.0",
4
4
  "description": "6-phase AI development pipeline with full orchestration on Claude Code, Copilot CLI and Codex CLI. Analysis, planning, TDD, CLI-aware parallel review with consensus surfacing + Fable triage, default-FAIL evidence gates, secret + intent guards, per-phase cost ledger, persistent learnings memory, wiki generation, commit automation. Token-preserving uninstall.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -100,9 +100,13 @@ and every later block that needs them starts by sourcing it.
100
100
 
101
101
  4b. **Update the stack plugins** (catalog refresh, then every installed plugin in its own scope):
102
102
  ```bash
103
- bash "$HOME/.claude/scripts/plugin-refresh.sh" {owner}
103
+ REFRESH_LOG="${TMPDIR:-/tmp}/multi-agent-plugin-refresh.log"
104
+ bash "$HOME/.claude/scripts/plugin-refresh.sh" {owner} > "$REFRESH_LOG" 2>&1
105
+ rc=$?
106
+ cat "$REFRESH_LOG"
107
+ echo "plugin-refresh exit: $rc"
104
108
  ```
105
- A non-zero exit means a step failed and the CLI's own error is already on screen. Report it to the user; do not retry silently.
109
+ A non-zero exit means a step failed and the CLI's own error is already on screen. Report it to the user; do not retry silently. The log feeds step 6, which tells the user what each updated plugin brought.
106
110
 
107
111
  4c. **Prune retired adapter files** (Cursor / Antigravity / Copilot Chat were removed in v10.7.0 - the pipeline targets Claude Code, Copilot CLI and Codex CLI):
108
112
  ```bash
@@ -139,19 +143,41 @@ and every later block that needs them starts by sourcing it.
139
143
  or an SSH key on the personal account (the "Personal account" step in
140
144
  `$HOME/.claude/multi-agent-refs/features/usage-reporting.md`).
141
145
 
142
- 6. **Show the new version and its changes** (from the packaged CHANGELOG - there is no git history on this channel):
146
+ 6. **Tell the user what the update brought.** Read the notes, then write the summary yourself:
143
147
  ```bash
144
148
  . "${TMPDIR:-/tmp}/multi-agent-update.env" || { echo "Run step 1 first."; exit 1; }
145
149
  NEW=$(tr -d '[:space:]' < "$HOME/.claude/.pipeline-version" 2>/dev/null)
146
- echo ""
147
- echo "✓ Updated: v$CUR → v$NEW"
148
- echo ""
149
- echo "Changes:"
150
- awk -v new="## [$NEW]" -v cur="## [$CUR]" \
151
- 'index($0,new){on=1} on&&index($0,cur){exit} on' \
152
- "$UPD_DIR/package/CHANGELOG.md" | head -40
150
+ NOTES="$UPD_DIR/package/pipeline/scripts/release-notes.mjs"
151
+ [ -f "$NOTES" ] || NOTES="$HOME/.claude/scripts/release-notes.mjs"
152
+ LOG="$UPD_DIR/package/CHANGELOG.md"
153
+ [ -f "$LOG" ] || LOG=/nonexistent
154
+ node "$NOTES" --changelog "$LOG" --from "$CUR" --to "$NEW" \
155
+ --plugins-dir "$HOME/.claude/plugins/marketplaces/multi-agent-plugins/plugins" \
156
+ --plugin-refresh-log "${TMPDIR:-/tmp}/multi-agent-plugin-refresh.log"
153
157
  ```
154
158
 
159
+ The JSON holds every release between the old and the new version (not only
160
+ the last one) and, for each plugin the refresh moved, that plugin's releases.
161
+ Turn it into a short note in `outputLanguage`, written for the person using
162
+ the pipeline, not for its maintainers:
163
+
164
+ - First line: `✓ Updated: v<CUR> → v<NEW>`, then one line per updated plugin
165
+ (`ai-ios-toolkit 0.20.0 → 0.21.0`).
166
+ - Then up to four groups, each only when it has entries: **New** (added),
167
+ **Fixed**, **Changed**, **Removed** (Turkish: Yeni, Düzeltilen, Değişen,
168
+ Kaldırılan). Put security entries first under Fixed.
169
+ - One plain sentence per item about what the user notices or can now do.
170
+ Merge items that say the same thing; leave out internal-only entries
171
+ (smoke tests, CI, refactors, budgets, docs) unless they change what a user
172
+ sees. At most about eight items per group; when more were dropped, say how
173
+ many and that the full list is in the CHANGELOG.
174
+ - Use only what the JSON says. Never invent a feature or a fix, and never
175
+ guess why something changed.
176
+ - Last line, when anything needs action: what to do (restart Claude Code for
177
+ plugin changes, run a command a release asks for).
178
+
179
+ With empty notes and no plugin updates, print only the version line.
180
+
155
181
  7. **Smoke test (optional)** - the release tarball ships exactly two smokes for this purpose:
156
182
  ```bash
157
183
  . "${TMPDIR:-/tmp}/multi-agent-update.env" || { echo "Run step 1 first."; exit 1; }
@@ -165,18 +191,19 @@ and every later block that needs them starts by sourcing it.
165
191
  ## Output
166
192
 
167
193
  ```
168
- Current: v15.6.0 Latest: v15.6.1
169
- -> npm pack @{npm-scope}/multi-agent-pipeline@15.6.1
194
+ Current: v15.6.0 Latest: v15.7.0
195
+ -> npm pack @{npm-scope}/multi-agent-pipeline@15.7.0
170
196
  -> node install.js --installed (Claude Code; no Copilot CLI or Codex CLI stamp, not installed)
171
197
  -> migrate-prefs.mjs (0 changes - already v2.6.0)
172
198
 
173
- ✓ Updated: v15.6.0 → v15.6.1
199
+ ✓ Updated: v15.6.0 → v15.7.0
200
+ ai-ios-toolkit 0.19.0 → 0.20.0
174
201
 
175
- Changes:
176
- ## [15.6.1] - 2026-08-19
177
- ### Fixed
178
- - Usage report reads the tracker as it is actually written
179
- ...
202
+ New
203
+ - iOS design review: critiques a screen against the HIG from screenshots.
204
+ Fixed
205
+ - The usage report reads the tracker as it is written.
206
+ Restart Claude Code to load the updated plugins.
180
207
 
181
208
  Verification:
182
209
  schema-validation: 27/27 pass
@@ -85,6 +85,7 @@ Shell scripts invoked during pipeline execution.
85
85
  - `github-ssh-setup.sh` - first-run SSH key setup helper
86
86
  - `update-check.sh` - cached registry read (`dist-tags.latest` + `dist-tags.required`); always exit 0
87
87
  - `require-supported-version.sh` - turns the `force` signal into exit 3; fails open offline
88
+ - `release-notes.mjs` - every release between the installed and the new version, grouped by kind, plus each plugin the refresh moved; feeds the update summary
88
89
  - `sync-parity-check.sh` - manual parity verification helper
89
90
 
90
91
  ## Node.js helpers (74 `.mjs` files)
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * release-notes.mjs - what an update brought, grouped so the user can be told.
4
+ *
5
+ * `/multi-agent:update` runs this from the release it just unpacked, against that
6
+ * release's CHANGELOG: every version after the one that was installed, up to the
7
+ * one now installed, with its bullets grouped by kind (added, changed, fixed,
8
+ * removed, ...). A jump across many releases is reported whole, not cut at a line
9
+ * count. The same is done for each stack plugin the plugin refresh moved, read
10
+ * from that plugin's CHANGELOG in the marketplace checkout.
11
+ *
12
+ * The output is data for the update step, which summarizes it for the user in
13
+ * their language. It never fails an update: a missing or unreadable CHANGELOG is
14
+ * empty notes and exit 0.
15
+ *
16
+ * Usage:
17
+ * release-notes.mjs --changelog <CHANGELOG.md> --from <old> --to <new>
18
+ * [--plugins-dir <marketplace>/plugins --plugin-refresh-log <file>]
19
+ *
20
+ * stdout: JSON `{ pipeline: Notes, plugins: [{ name, from, to, notes: Notes }] }`
21
+ * where Notes is `{ from, to, versions: [{ version, date, sections }], counts }`.
22
+ */
23
+
24
+ import { existsSync, readFileSync } from "node:fs";
25
+ import { join } from "node:path";
26
+ import { pathToFileURL } from "node:url";
27
+
28
+ import { runMain } from "../lib/fatal.mjs";
29
+
30
+ const RELEASE_RE = /^## \[(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)\](?:\s*-\s*(.+))?\s*$/;
31
+ const SECTION_RE = /^### (.+?)\s*$/;
32
+
33
+ function cmp(a, b) {
34
+ const [pa, qa = ""] = a.split("-");
35
+ const [pb, qb = ""] = b.split("-");
36
+ const x = pa.split(".").map(Number);
37
+ const y = pb.split(".").map(Number);
38
+ for (let i = 0; i < 3; i++) if (x[i] !== y[i]) return x[i] - y[i];
39
+ if (qa === qb) return 0;
40
+ if (!qa) return 1;
41
+ if (!qb) return -1;
42
+ return qa < qb ? -1 : 1;
43
+ }
44
+
45
+ /**
46
+ * Released versions in a Keep a Changelog file, newest first as written.
47
+ * `## [Unreleased]` is skipped; a bullet's wrapped lines are joined.
48
+ *
49
+ * @param {string} text
50
+ * @returns {{version: string, date: string|null, sections: Record<string, string[]>}[]}
51
+ */
52
+ export function parseChangelog(text) {
53
+ const releases = [];
54
+ let release = null;
55
+ let section = null;
56
+ let bullet = null;
57
+ const flush = () => {
58
+ if (bullet !== null && release && section) release.sections[section].push(bullet.trim());
59
+ bullet = null;
60
+ };
61
+ for (const line of String(text || "").split("\n")) {
62
+ if (line.startsWith("## ")) {
63
+ flush();
64
+ const m = RELEASE_RE.exec(line);
65
+ release = m ? { version: m[1], date: m[2]?.trim() || null, sections: {} } : null;
66
+ if (release) releases.push(release);
67
+ section = null;
68
+ continue;
69
+ }
70
+ if (!release) continue;
71
+ const s = SECTION_RE.exec(line);
72
+ if (s) {
73
+ flush();
74
+ section = s[1].toLowerCase();
75
+ release.sections[section] ||= [];
76
+ continue;
77
+ }
78
+ if (!section) continue;
79
+ if (/^[-*] /.test(line)) {
80
+ flush();
81
+ bullet = line.slice(2);
82
+ } else if (bullet !== null && /^\s+\S/.test(line)) {
83
+ bullet += " " + line.trim();
84
+ } else if (!line.trim()) {
85
+ flush();
86
+ }
87
+ }
88
+ flush();
89
+ return releases;
90
+ }
91
+
92
+ /**
93
+ * Releases after `from` up to and including `to`, newest first, with a count of
94
+ * bullets per section across all of them.
95
+ *
96
+ * @param {string} text
97
+ * @param {{from: string, to: string}} range
98
+ */
99
+ export function releaseNotes(text, { from, to }) {
100
+ const versions = parseChangelog(text)
101
+ .filter((r) => cmp(r.version, from) > 0 && cmp(r.version, to) <= 0)
102
+ .sort((a, b) => cmp(b.version, a.version));
103
+ const counts = {};
104
+ for (const v of versions) {
105
+ for (const [k, items] of Object.entries(v.sections))
106
+ counts[k] = (counts[k] || 0) + items.length;
107
+ }
108
+ return { from, to, versions, counts };
109
+ }
110
+
111
+ /**
112
+ * Plugins the refresh moved, from the CLI's own lines
113
+ * (`Plugin "<name>" updated from <a> to <b> for scope ...`). A plugin updated in
114
+ * several scopes is reported once, from the oldest version any of them had.
115
+ *
116
+ * @param {string} output
117
+ * @returns {{name: string, from: string, to: string}[]}
118
+ */
119
+ export function pluginUpdates(output) {
120
+ const byName = new Map();
121
+ const re = /Plugin "([^"]+)" updated from (\S+) to (\S+)/g;
122
+ for (const m of String(output || "").matchAll(re)) {
123
+ const [, name, from, to] = m;
124
+ const seen = byName.get(name);
125
+ if (!seen) byName.set(name, { name, from, to });
126
+ else {
127
+ if (cmp(from, seen.from) < 0) seen.from = from;
128
+ if (cmp(to, seen.to) > 0) seen.to = to;
129
+ }
130
+ }
131
+ return [...byName.values()];
132
+ }
133
+
134
+ function read(path) {
135
+ try {
136
+ return existsSync(path) ? readFileSync(path, "utf8") : "";
137
+ } catch {
138
+ return "";
139
+ }
140
+ }
141
+
142
+ function arg(argv, name) {
143
+ const i = argv.indexOf(name);
144
+ return i >= 0 ? argv[i + 1] : undefined;
145
+ }
146
+
147
+ export function main(argv = process.argv.slice(2)) {
148
+ const from = arg(argv, "--from") || "0.0.0";
149
+ const to = arg(argv, "--to") || from;
150
+ const pipeline = releaseNotes(read(arg(argv, "--changelog") || ""), { from, to });
151
+
152
+ const pluginsDir = arg(argv, "--plugins-dir");
153
+ const refreshLog = arg(argv, "--plugin-refresh-log");
154
+ const plugins = [];
155
+ if (pluginsDir && refreshLog) {
156
+ for (const u of pluginUpdates(read(refreshLog))) {
157
+ const notes = releaseNotes(read(join(pluginsDir, u.name, "CHANGELOG.md")), u);
158
+ plugins.push({ ...u, notes });
159
+ }
160
+ }
161
+ process.stdout.write(JSON.stringify({ pipeline, plugins }, null, 2) + "\n");
162
+ return 0;
163
+ }
164
+
165
+ if (import.meta.url === pathToFileURL(process.argv[1] || "").href) {
166
+ runMain("release-notes", () => main());
167
+ }