@mmerterden/multi-agent-pipeline 20.11.0 → 20.12.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 +22 -0
- package/docs/facts.json +2 -2
- package/install/catalog-history.json +1 -1
- package/install/claude.mjs +9 -0
- package/manifest.json +26 -25
- package/package.json +5 -5
- package/pipeline/agents/code-reviewer.md +1 -1
- package/pipeline/commands/multi-agent/model/SKILL.md +7 -0
- package/pipeline/commands/multi-agent/store-ready/SKILL.md +4 -4
- package/pipeline/commands/multi-agent/sync/SKILL.md +5 -6
- package/pipeline/lib/agent-model-sync.mjs +95 -0
- package/pipeline/lib/model-rung.sh +7 -0
- package/pipeline/multi-agent-refs/features/doctor.md +10 -0
- package/pipeline/multi-agent-refs/features/model-fallback.md +10 -0
- package/pipeline/multi-agent-refs/features/phone-api.md +1 -1
- package/pipeline/multi-agent-refs/features/unattended-security.md +2 -2
- package/pipeline/multi-agent-refs/features/usage-reporting.md +13 -0
- package/pipeline/scripts/doctor.mjs +48 -0
- package/pipeline/scripts/migrate-prefs.mjs +14 -0
- package/pipeline/scripts/unattended_policy.py +12 -5
- package/pipeline/scripts/usage-report.mjs +84 -9
- package/pipeline/skills/.skill-manifest.json +2 -2
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +4 -0
package/manifest.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mmerterden/multi-agent-pipeline",
|
|
3
|
-
"version": "20.
|
|
4
|
-
"commit": "
|
|
5
|
-
"generatedAt": "2026-
|
|
3
|
+
"version": "20.12.0",
|
|
4
|
+
"commit": "67faa4d431ba4b6d942630d6acbcfedbec220c9a",
|
|
5
|
+
"generatedAt": "2026-10-01T18:29:41.758Z",
|
|
6
6
|
"algorithm": "sha256",
|
|
7
|
-
"fileCount":
|
|
7
|
+
"fileCount": 1231,
|
|
8
8
|
"files": {
|
|
9
|
-
"CHANGELOG.md": "
|
|
9
|
+
"CHANGELOG.md": "787a8bd3432c3fdbbf572f8d8ef13c548291f0a46691abba28811943d516eaef",
|
|
10
10
|
"LICENSE": "8d8c51aaf1e1538b180c69965fa038db71be86798cc3e26a143bebfefe16a43e",
|
|
11
11
|
"README.md": "0613eebb0b38062bd94c44043595d1950b5c9b470874dd8e402c052fd20960cf",
|
|
12
12
|
"README.tr.md": "0c6b5e6943d6c3d3bf3a7ed99c705fb9a78d15d7722e6252f58404d3f6d3524d",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"docs/best-practices.md": "f8b75b0d38b5896a6a852bb30a1edddeb24dab8b5b7b1d9313290e1241c72024",
|
|
34
34
|
"docs/ecosystem.md": "e6c00f30e95b5e178f77bdfbefd686c8a4164f39c03865a89ab23465d1a37acf",
|
|
35
35
|
"docs/engineering.md": "550528cd47c7d2c921729c68d0782a00ac8e77a4bdfec479f6749f9c3235d188",
|
|
36
|
-
"docs/facts.json": "
|
|
36
|
+
"docs/facts.json": "8f5a257e848433f3c3deca22a452e5407d7853913b5c7f60a304ff5a5aab4fee",
|
|
37
37
|
"docs/features.md": "6c9f0efb00c683e6f611c440c87847b187f8b0eee59e9e812f3a241695bc2ae1",
|
|
38
38
|
"docs/performance.md": "bbf2f3922c25dcba0c1a9e69ef420e3871a729af5b35009a49b48a5d3c5cd151",
|
|
39
39
|
"docs/recovery-guide.md": "59a8f1b66b34cb9ff5507b3d54dac3b9915e184aa044d9c758d18cac818d510e",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"install/_plugin-skills.mjs": "bf287053133f940df0ca434cfc5ee522fd27e80dc7f7e60ccc963ecaabee047a",
|
|
52
52
|
"install/_telemetry.mjs": "5433d753f909beed2c8789272bba05e245482c472f83d3444f58000c4c9a634b",
|
|
53
53
|
"install/_unattended-profile.mjs": "e1ed82bde35d19105ee12c2a551eb778912b0e5c93ab227fd15c993098d98de0",
|
|
54
|
-
"install/catalog-history.json": "
|
|
55
|
-
"install/claude.mjs": "
|
|
54
|
+
"install/catalog-history.json": "15422563f83ca98278f94ae3e34730112e65888262e1da0442768352f46a75d4",
|
|
55
|
+
"install/claude.mjs": "6af22bb9b2594aee502c36550020d2c9c4c3222061ed8a88a0885687cd91ebec",
|
|
56
56
|
"install/codex.mjs": "2c2238cd9dd2f95b4a50a505387e8e49ddc1513ae13998f05f1ed3e3a71b18e7",
|
|
57
57
|
"install/copilot.mjs": "300a74f17084d1a38f16428ca7477394c29e33cd755156e19b92e457999be234",
|
|
58
58
|
"install/index.mjs": "07ec26ecdd81cc983f146faf4b7095c98edce39c89a6fd83c24ce43229d2d9e7",
|
|
@@ -62,11 +62,11 @@
|
|
|
62
62
|
"install/templates/multi-agent-autopilot-awake.plist.template": "ddd25c76c56fcbb38a89de8e35637372d70ace993d6bc487a88ca9cfb702fe1f",
|
|
63
63
|
"install/templates/multi-agent-autopilot.plist.template": "122c8a61cd927edd99375d6e3da40f1d42bcceed5b6641785c8b28bb930bc77e",
|
|
64
64
|
"install/unattended-profile-legacy.json": "f52e86b998956f09ad306b1735a6c10b58580d7082d8a55665a4c6bb15c3aa1f",
|
|
65
|
-
"package.json": "
|
|
65
|
+
"package.json": "64ce800c06e8882db62e426c03d3f729ec4344f44d9c6f50602d539c319ec054",
|
|
66
66
|
"pipeline/agents/android-architect.md": "11fb74543cb0ebcf9525ca6654fe95d53da7229e0bf4a1149b31fd9005abc453",
|
|
67
67
|
"pipeline/agents/backend-architect.md": "b02a606c301e4714131aed70ae6d16eeb77fbc819a001e2752cff2488d4246de",
|
|
68
68
|
"pipeline/agents/bulk-reader.md": "6597052cd7d205bac2b1c7633f5232fe1a40730664a913b8eb77efd5a786dc25",
|
|
69
|
-
"pipeline/agents/code-reviewer.md": "
|
|
69
|
+
"pipeline/agents/code-reviewer.md": "213798685e9bff10a08f0774f57bcfbab42ee15993095ec3a2c84e13a3453725",
|
|
70
70
|
"pipeline/agents/dev-critic.md": "33843216b79977ce2926fc9c54f8c9eef30b91632a11ef624647229b61a9b36a",
|
|
71
71
|
"pipeline/agents/explorer.md": "12564aec29d4581684bf84313b86a4add5a6989d3b8db5c35769674cab5649c6",
|
|
72
72
|
"pipeline/agents/ios-architect.md": "e1fe0c034a4dfa76794ed04ba9f4bf85d5cdd3a5d6b948cc0635e8dda8baeef9",
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
"pipeline/commands/multi-agent/language/SKILL.md": "4b58b25bda9449a52df005f8d3417ace2f8b3a18d84524dc021a9561f1d16b9d",
|
|
105
105
|
"pipeline/commands/multi-agent/log/SKILL.md": "033bafcf4c9b8d117a892f50a3039fd11d753b4db070ed14c50e8388613482fa",
|
|
106
106
|
"pipeline/commands/multi-agent/manual-test/SKILL.md": "7b81b1ff572fb4865495283c39f93d5c803fff2e8ce9008bf1a2ab51046c3d38",
|
|
107
|
-
"pipeline/commands/multi-agent/model/SKILL.md": "
|
|
107
|
+
"pipeline/commands/multi-agent/model/SKILL.md": "56b636f8ab42c24defd3d1260f1a5e4dd332f4556d9b6d1a700322cbfa6f4f3d",
|
|
108
108
|
"pipeline/commands/multi-agent/prune-logs/SKILL.md": "a7b22228ec338623c386cfd3744092488eed6871c65b531bdb6a0eb8dc8c2e9b",
|
|
109
109
|
"pipeline/commands/multi-agent/prune-prompts/SKILL.md": "1aefd2b737b34aaff18f3fd12090322c04dbc41c981200622d7989c9ad45e9c2",
|
|
110
110
|
"pipeline/commands/multi-agent/purge/SKILL.md": "c881805f80d7db909bcf09f1a5c980ebaf0c31d9a8ee9cae4f729387ff4ca19d",
|
|
@@ -129,8 +129,8 @@
|
|
|
129
129
|
"pipeline/commands/multi-agent/stack/SKILL.md": "6d64e96b9a2d41d25ff077afa84300172cac9bf9dd78ec1d9fbbaf45df34f5dd",
|
|
130
130
|
"pipeline/commands/multi-agent/status/SKILL.md": "a2ed316434aca49f692aafe11f95873822b9b955558a53f4f6a9667ee32a3659",
|
|
131
131
|
"pipeline/commands/multi-agent/steer/SKILL.md": "07e738c228ba328f717f86e97e7af207df090b16433e28e613d430c3ff0aee3e",
|
|
132
|
-
"pipeline/commands/multi-agent/store-ready/SKILL.md": "
|
|
133
|
-
"pipeline/commands/multi-agent/sync/SKILL.md": "
|
|
132
|
+
"pipeline/commands/multi-agent/store-ready/SKILL.md": "2fd7a908ee84669361bded693e7af53a17eb811e27cb4aa94237c0fce80591fa",
|
|
133
|
+
"pipeline/commands/multi-agent/sync/SKILL.md": "8d2a1d86a97cb27c4af5feba0eaa64bc937f979c3cba1e38dc1821557c243300",
|
|
134
134
|
"pipeline/commands/multi-agent/test-accessibility/SKILL.md": "d62992541303a0776ca088d5f3d5475108fd3fba5e25af04efb3ea4bbbd1fe21",
|
|
135
135
|
"pipeline/commands/multi-agent/test-dark-mode/SKILL.md": "6bfe158c94f76fc6f4d06454978d0c5cad6ec00645ab48c1d1cee15ad93ae095",
|
|
136
136
|
"pipeline/commands/multi-agent/test-dynamic-type/SKILL.md": "c093fe1a78a4266ad4cf1e48b3cc233d9abd7e1c524d0b8e5db0c81d8b4d58fd",
|
|
@@ -162,6 +162,7 @@
|
|
|
162
162
|
"pipeline/contract/types/index.d.ts": "5b738dd173c4946f8dae9cb926ad63b8adac68b77000085895608bfd87ac1aa9",
|
|
163
163
|
"pipeline/lib/_jira-auth.sh": "fc468736057f8841d824eb1a4db527442562f8979fea333ebaef5527f4369e9f",
|
|
164
164
|
"pipeline/lib/account-resolver.sh": "f7bef9beba3f839268509361651be0b1e1f319f2f23821f90ba466fdf185a8f6",
|
|
165
|
+
"pipeline/lib/agent-model-sync.mjs": "2e0264ca8d8fef761563db219d588fed9067847043f960f68f055c186c20b668",
|
|
165
166
|
"pipeline/lib/analysis-jira-write.sh": "477e7b4e2aaaa8289ab0cdb315f04c3a9b22beb8f075bbb73b6fa174d6679197",
|
|
166
167
|
"pipeline/lib/ask-choice.sh": "b77282645cf64d016cdebf43d1ee299874a1411f5c51b6ae3b6c580d1bbfe78a",
|
|
167
168
|
"pipeline/lib/autopilot-activation.sh": "29102f7936ea8b8238fac4e9c557bdbb39e2ea5afdc3b6cdd20622593d36da9b",
|
|
@@ -193,7 +194,7 @@
|
|
|
193
194
|
"pipeline/lib/json-file-lock.mjs": "6ff8d8d7b890fc549e07287eca1e8261b69f930ec523d08fb73786677aa0a45a",
|
|
194
195
|
"pipeline/lib/md2confluence-v3.py": "3677ca47931e4b3d0fd2ce3e11299fc5f6d24799eb8332e37dd60b6c3c4653a2",
|
|
195
196
|
"pipeline/lib/model-dispatch.sh": "7edd5c92d41ee3bf74972e0827ccbb671853d5410e7fb0af0b7cb020f95e05c5",
|
|
196
|
-
"pipeline/lib/model-rung.sh": "
|
|
197
|
+
"pipeline/lib/model-rung.sh": "8cecb4d390f306270713bd162e311dee879995e393970405c9a8f9af11da1727",
|
|
197
198
|
"pipeline/lib/multi-repo-pipeline.sh": "5cca6bcc4fb857448ddadb31add343952be89423a328267e56496c7b7cdc7710",
|
|
198
199
|
"pipeline/lib/normalize-text.mjs": "c2eeed65ab85cdc27a12ecaadc45dfa00b49a7611250c9ad881b9e93882a51c1",
|
|
199
200
|
"pipeline/lib/outbound-gate.mjs": "1ebc840e975f19b0da4699b3a37361ecc8a2be1a7a558d2b2fb39b8b64244011",
|
|
@@ -263,13 +264,13 @@
|
|
|
263
264
|
"pipeline/multi-agent-refs/features/cost-analysis.md": "42f1b90b41ef454f29ccfd3f93c959920f0e74cbca65e140695e9a30d6d90b87",
|
|
264
265
|
"pipeline/multi-agent-refs/features/design-conformance.md": "a88ca595827cafa0c87c2facfcf9190dac69485e0596e5c198c6fc1613880452",
|
|
265
266
|
"pipeline/multi-agent-refs/features/dev-critic.md": "08e01edef04b5ac216f8f43332126966d33da7b95625e47a8a6cd8ac105b263f",
|
|
266
|
-
"pipeline/multi-agent-refs/features/doctor.md": "
|
|
267
|
+
"pipeline/multi-agent-refs/features/doctor.md": "708aba37c507bf7559af816e6328a11ba0acdc6d61d023bc10afc727d068876a",
|
|
267
268
|
"pipeline/multi-agent-refs/features/external-context-injection.md": "fe0373f27c3e9aae660a55a3ba9a8163df329b4d95c727d6023b6b3afaf71d66",
|
|
268
269
|
"pipeline/multi-agent-refs/features/jira-context.md": "7d14802ed9ad1747e1a06dec26f22ff9e8959e03c47d7a018af45b8274aea47c",
|
|
269
270
|
"pipeline/multi-agent-refs/features/maturity-followup.md": "e663e5bcf0b3b4d4d2a67f4516dbb9fb12520dfbdedb47a9c5f57c13a9a0633e",
|
|
270
|
-
"pipeline/multi-agent-refs/features/model-fallback.md": "
|
|
271
|
+
"pipeline/multi-agent-refs/features/model-fallback.md": "e6b9635124f3557013dfbfa71d414a9d9abe9ddc89f2228a5496086c8e3b0c72",
|
|
271
272
|
"pipeline/multi-agent-refs/features/package-manager.md": "b9e52f798f5dcba43abff1fe5cad8d5d5374058c9a6847c993975c665acab05a",
|
|
272
|
-
"pipeline/multi-agent-refs/features/phone-api.md": "
|
|
273
|
+
"pipeline/multi-agent-refs/features/phone-api.md": "817eeaffd3772b0a80811b0cd17dee3498f08f6eccf8fd7e56deb0bc24c1c434",
|
|
273
274
|
"pipeline/multi-agent-refs/features/plan-critic.md": "d218c8599d893211480a412d46efdbba4aa48e3b18e8bb0ebfd66782f3b8ebc5",
|
|
274
275
|
"pipeline/multi-agent-refs/features/plan-todos.md": "8780d80b4541daee2b05080759a0694ef7e6270abe9a4621deb92ec7bac94e8e",
|
|
275
276
|
"pipeline/multi-agent-refs/features/prior-fix-detection.md": "9bd172fa116b0c334576ee3df2dc478810a7e282422410e045be74ac2299253d",
|
|
@@ -288,9 +289,9 @@
|
|
|
288
289
|
"pipeline/multi-agent-refs/features/stack-adapters.md": "71e23385da872ceed35263f4838f154800aa2da55bb6688ccca74726b70a3ef5",
|
|
289
290
|
"pipeline/multi-agent-refs/features/stack-skill-routing.md": "1989f7f64be6c5ee0a42d8e9962aead7cd7472d795915ab953d89d9382f317e6",
|
|
290
291
|
"pipeline/multi-agent-refs/features/unattended-gates.md": "46b516cac81a4eacf5d58bc3b1613e2266c26f1559be8dac060d1dc6b7d7ca01",
|
|
291
|
-
"pipeline/multi-agent-refs/features/unattended-security.md": "
|
|
292
|
+
"pipeline/multi-agent-refs/features/unattended-security.md": "18da3d51c175a42ab6312051a50b9d0339524dad2cc34e0350de62fbf0cece91",
|
|
292
293
|
"pipeline/multi-agent-refs/features/url-enrichment.md": "72e01a799d9fa54b56c762ba404a9f9236adbbf98320e395a96f6b0f5917e76b",
|
|
293
|
-
"pipeline/multi-agent-refs/features/usage-reporting.md": "
|
|
294
|
+
"pipeline/multi-agent-refs/features/usage-reporting.md": "095d86b839c1289c1023919da3972e561c1bbd4a32a1fc9f8e694abe887005ac",
|
|
294
295
|
"pipeline/multi-agent-refs/features/verify-by-test.md": "b0735adaa7ead797167b8d9ea48a10851a138083c6e638738cc96c66b02a1e37",
|
|
295
296
|
"pipeline/multi-agent-refs/features/verify.md": "1a6db5e7239eac01e51100353013abd16bf785a7fce88b40139fca27f4166be4",
|
|
296
297
|
"pipeline/multi-agent-refs/features/visual-evidence.md": "2b6aa2e4ff05c7a1fbe9e0fedfa4b28b9ad01c93a64f1ca6f6cd3e02293866aa",
|
|
@@ -481,7 +482,7 @@
|
|
|
481
482
|
"pipeline/scripts/crush-json.mjs": "303382670f8f170c8c6710202853a2de2915625813cd157185f9be4e2b27258e",
|
|
482
483
|
"pipeline/scripts/diff-explain.mjs": "194d0635639607cb1af3de064453162e01c0d59dc0c2e187d21d45bae3bc559c",
|
|
483
484
|
"pipeline/scripts/diff-risk-score.mjs": "b3e8da42fea1258162e40f4eb7809074831339b7563874527f7e825f7aa795ca",
|
|
484
|
-
"pipeline/scripts/doctor.mjs": "
|
|
485
|
+
"pipeline/scripts/doctor.mjs": "0ac54a2630fc00b39bdf430f9466f91e6b52296a23dca8694ac1a1bd894bcfea",
|
|
485
486
|
"pipeline/scripts/evidence-gate.mjs": "347a8d3fa1d17d996de345585c216bd7071f4c1d687999daf6e48881720ea5df",
|
|
486
487
|
"pipeline/scripts/feedback-send.mjs": "d3ed66e9dacdfb4b5eacf1b244bf66752304fb4fcb144531441b458f3ab806be",
|
|
487
488
|
"pipeline/scripts/finding-fingerprint.mjs": "fa12ab10701db5d2bfff014e41d1cd4c0b2b772aee5fc69205de14b2761b0c83",
|
|
@@ -518,7 +519,7 @@
|
|
|
518
519
|
"pipeline/scripts/maturity-followup.mjs": "531eb9578a71ea5bb2510bb24a48f264c36aaf7adceca7b4ec3dccd1c63ae412",
|
|
519
520
|
"pipeline/scripts/memory-load.sh": "c6a1976372ed033c9f3b55e757e22d05b4ef0d9774bfe185453d8847525b4a89",
|
|
520
521
|
"pipeline/scripts/memory-save.sh": "ab6b4c3d78707e0ac66fe56726308782568f247463d4c7edf2316b2dd399ee6b",
|
|
521
|
-
"pipeline/scripts/migrate-prefs.mjs": "
|
|
522
|
+
"pipeline/scripts/migrate-prefs.mjs": "9d38ef7dac14c48bf1c6c8053680999728d3179c598e6c9e53f5a66290fc323e",
|
|
522
523
|
"pipeline/scripts/migrate-state.mjs": "b6fb42f56ea56ab10c00e947f013e3aecaeef1aa0d819493eedbef7cdf7d4370",
|
|
523
524
|
"pipeline/scripts/note-session.sh": "1466d93f401c66074705635117a052a3f811ca22f928defa1ec2e4c804c19fc1",
|
|
524
525
|
"pipeline/scripts/observations.mjs": "edef08a8ab8f967030da0575d83f501eb6256460c0b72c2ba377c629b3ee0674",
|
|
@@ -583,13 +584,13 @@
|
|
|
583
584
|
"pipeline/scripts/token-budget-report.mjs": "508bbc659df478c535c0ecdd3f8611372af8fef0b0932c7b19a247af2b2b9265",
|
|
584
585
|
"pipeline/scripts/triage-memory.mjs": "2d8541145f1749593ae5728fb4c9eb3001484edb5e4d37be473aeec34d1dbe7c",
|
|
585
586
|
"pipeline/scripts/ui-tree-dumper.swift": "a09e42ef069eafa08c05afd8333e05b754ef6a7d8b9687712039b0f2c9801273",
|
|
586
|
-
"pipeline/scripts/unattended_policy.py": "
|
|
587
|
+
"pipeline/scripts/unattended_policy.py": "f1df6470c798bd7d4bc9f8338eaabf98bdcfab00198d98b7e2e654aab84b709c",
|
|
587
588
|
"pipeline/scripts/uninstall.mjs": "880923522ef59fdd98cb7ecb1bdc85c0f15d1e2eed2d79d6d353e88712ca95fa",
|
|
588
589
|
"pipeline/scripts/update-check.sh": "9c00647e7ca4249414b2e5af08f61705ab5bd2106b6f66ae8ebce8aa143dc957",
|
|
589
590
|
"pipeline/scripts/update-issue-progress.sh": "ccfef87743e91272e009e396ac07461d2784b2fa84a06c802a582526eeeb9ed9",
|
|
590
591
|
"pipeline/scripts/usage-identity.mjs": "47c1f907fb1ab47672687ea0cbf2d0737985ae91ec2b48b4fe260e14c3d35d70",
|
|
591
592
|
"pipeline/scripts/usage-register.mjs": "cc19d7ce5efe57255ce1521d33ad4b9a75b6833385ed6edcc5e339faf3c6e06c",
|
|
592
|
-
"pipeline/scripts/usage-report.mjs": "
|
|
593
|
+
"pipeline/scripts/usage-report.mjs": "af1672206b1fa7d6612fa70aa46fd63372552619fdba866d89819a12ba943549",
|
|
593
594
|
"pipeline/scripts/validate-analysis-doc.mjs": "ae56b7578ef51e008a99f84eba9def3e0f1dd2bd87a3880f2083aa98b365f7a1",
|
|
594
595
|
"pipeline/scripts/validate-analysis.mjs": "d2dc9ccf45f6ca2702908761939edd830d18b3b273016a4b449f26fe285d5ea1",
|
|
595
596
|
"pipeline/scripts/validate-code-graph.mjs": "0ceac2f5b59f641334d54dc30bee5265361fb5fe1fc97e2f9791b546efe79941",
|
|
@@ -609,7 +610,7 @@
|
|
|
609
610
|
"pipeline/scripts/worktrees.mjs": "90dfd09b70fffe9b56c331a4ae2692cc248f4898f4ee6aee4239ef0b20888a90",
|
|
610
611
|
"pipeline/scripts/write-state.mjs": "e1198bf970eace07d3eff22b394808a526e473df65d3d58aa2d5a12cfdac8b49",
|
|
611
612
|
"pipeline/scripts/write-store-config.mjs": "b994a061f6febb2ca3d396074d8b51d2c1c1f991d3072d321703380da3639a56",
|
|
612
|
-
"pipeline/skills/.skill-manifest.json": "
|
|
613
|
+
"pipeline/skills/.skill-manifest.json": "9e1b32d035cb49c4d56121c2d422121a7dda643717168c78b484527b0c168e1f",
|
|
613
614
|
"pipeline/skills/.skills-index.json": "042b1090f3e2ca310c6ce70d1bb304e8c7f96bd73133e504b95d83801608e470",
|
|
614
615
|
"pipeline/skills/shared/README.md": "a144fb39993470b6379d9660e0ee0371bdc858aee3d7c1b375cb6aba03d046db",
|
|
615
616
|
"pipeline/skills/shared/core/apple-archive-compliance/SKILL.md": "7239fef717ea45b79276bfcc2c3903acd57950b6e00dd6a2a66399c4b09cbd51",
|
|
@@ -1001,7 +1002,7 @@
|
|
|
1001
1002
|
"pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md": "2adea2a5e6180eb4c4562870ee8ba94fbc0bb9d1fcafaaec0fbf9b16a20d5243",
|
|
1002
1003
|
"pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md": "9e110f42e060d32b96947433c63702a426a8ad6c7ad52c3969e0cdcbac237e3d",
|
|
1003
1004
|
"pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md": "f9d50d1af854ada5d1ac3f50ba25a598b3ae8d55fc82242b5a62ed9582712ba2",
|
|
1004
|
-
"pipeline/skills/shared/external/swift-architecture/SKILL.md": "
|
|
1005
|
+
"pipeline/skills/shared/external/swift-architecture/SKILL.md": "295f6ce1488a93f64e5e78228b90863924fe21911769307844cc73f621c04ab1",
|
|
1005
1006
|
"pipeline/skills/shared/external/swift-architecture/evals/evals.json": "05c57f42d5f442c213890b78e841e85ff101e348f4c52f2fed06cc5d4d3e5c08",
|
|
1006
1007
|
"pipeline/skills/shared/external/swift-charts/SKILL.md": "4500143e373e617ab71e6292e1958bb2b34346ff9c5794ef3f2446bdd7e6de4a",
|
|
1007
1008
|
"pipeline/skills/shared/external/swift-charts/evals/evals.json": "4edf3a32dab0849858a87ec117edccd54becfe7c15f4a27e3049ae4eca884304",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mmerterden/multi-agent-pipeline",
|
|
3
|
-
"version": "20.
|
|
3
|
+
"version": "20.12.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",
|
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
22
|
"start": "node index.js",
|
|
23
|
-
"test": "npm run format:check && npm run lint && node --test test/*.test.mjs && node pipeline/scripts/run-smokes.mjs && node pipeline/scripts/lint-skills.mjs && node pipeline/scripts/gen-skills-index.mjs --check && node pipeline/scripts/gen-catalog-history.mjs --check && node pipeline/scripts/lint-personas.mjs && node pipeline/scripts/lint-mcp-refs.mjs && node pipeline/scripts/eval-triage.mjs && node pipeline/scripts/eval-golden-tasks.mjs && node pipeline/scripts/eval-intent.mjs && node pipeline/scripts/eval-recall.mjs && node pipeline/scripts/eval-guard-robustness.mjs && node pipeline/scripts/validate-schemas.mjs && node pipeline/scripts/validate-prefs.mjs && node pipeline/scripts/scorecard.mjs",
|
|
24
|
-
"test:unit": "node --test test/*.test.mjs",
|
|
23
|
+
"test": "npm run format:check && npm run lint && node --test test/*.test.mjs && node --test test/*.serial.mjs && node pipeline/scripts/run-smokes.mjs && node pipeline/scripts/lint-skills.mjs && node pipeline/scripts/gen-skills-index.mjs --check && node pipeline/scripts/gen-catalog-history.mjs --check && node pipeline/scripts/lint-personas.mjs && node pipeline/scripts/lint-mcp-refs.mjs && node pipeline/scripts/eval-triage.mjs && node pipeline/scripts/eval-golden-tasks.mjs && node pipeline/scripts/eval-intent.mjs && node pipeline/scripts/eval-recall.mjs && node pipeline/scripts/eval-guard-robustness.mjs && node pipeline/scripts/validate-schemas.mjs && node pipeline/scripts/validate-prefs.mjs && node pipeline/scripts/scorecard.mjs",
|
|
24
|
+
"test:unit": "node --test test/*.test.mjs && node --test test/*.serial.mjs",
|
|
25
25
|
"test:smoke": "node pipeline/scripts/run-smokes.mjs",
|
|
26
26
|
"lint:skills": "node pipeline/scripts/lint-skills.mjs",
|
|
27
|
-
"test:quick": "node --test test/*.test.mjs && node pipeline/scripts/lint-skills.mjs && node pipeline/scripts/lint-personas.mjs && node pipeline/scripts/lint-mcp-refs.mjs",
|
|
28
|
-
"test:coverage": "c8 --clean --reporter=none node --test test/*.test.mjs && c8 --clean=false --check-coverage --reporter=text --reporter=lcov node pipeline/scripts/run-smokes.mjs",
|
|
27
|
+
"test:quick": "node --test test/*.test.mjs && node --test test/*.serial.mjs && node pipeline/scripts/lint-skills.mjs && node pipeline/scripts/lint-personas.mjs && node pipeline/scripts/lint-mcp-refs.mjs",
|
|
28
|
+
"test:coverage": "c8 --clean --reporter=none node --test test/*.test.mjs && c8 --clean=false --reporter=none node --test test/*.serial.mjs && c8 --clean=false --check-coverage --reporter=text --reporter=lcov node pipeline/scripts/run-smokes.mjs",
|
|
29
29
|
"lint": "eslint .",
|
|
30
30
|
"lint:fix": "eslint . --fix",
|
|
31
31
|
"format": "prettier --write \"**/*.{js,mjs,json,yml}\" --ignore-path .gitignore --ignore-path .prettierignore",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: code-reviewer
|
|
3
|
-
description: "Code reviewer for multi-agent Phase 3 - security, architecture, quality, performance.
|
|
3
|
+
description: "Code reviewer for multi-agent Phase 3 - security, architecture, quality, performance. Model follows the fable switch (fable when on, opus when off); Phase 3 orchestrator overrides to sonnet for Reviewer 3."
|
|
4
4
|
model: fable
|
|
5
5
|
preferredModel: fable
|
|
6
6
|
modelRationale: "Reviewer 1 tier - deep security + architecture review runs on fable, the top tier (opus is the first fallback). Phase 3 orchestrator overrides to sonnet for Reviewer 3 (quality/correctness focus) via CLAUDE_CODE_SUBAGENT_MODEL before dispatch. Copilot CLI adds Reviewer 2 on gpt-5.4 for cross-model diversity."
|
|
@@ -38,6 +38,13 @@ This command sets both, together:
|
|
|
38
38
|
Flipping one without the other is the defect this closes, so the pair moves as
|
|
39
39
|
one write or not at all.
|
|
40
40
|
|
|
41
|
+
The same step re-applies the installed agents (`lib/agent-model-sync.mjs`): every
|
|
42
|
+
agent under `~/.claude/agents` whose `preferredModel` is `fable` gets
|
|
43
|
+
`model: fable` while the switch is on and `model: opus` while it is off. An agent
|
|
44
|
+
spawned without an explicit model runs on that line, so without this the switch
|
|
45
|
+
would cover routed dispatches only. The installer applies the same rule on every
|
|
46
|
+
install and update, so a sync never puts the agents back on a rung that is off.
|
|
47
|
+
|
|
41
48
|
## What it reports with no argument
|
|
42
49
|
|
|
43
50
|
The current rung, the pricing model, and **what the switch means on this host** -
|
|
@@ -295,9 +295,9 @@ project. Without the credential, skip this line and say why.
|
|
|
295
295
|
`bundletool validate` is Gate 1 in every case; reporting it here would fake a
|
|
296
296
|
cleared gate.
|
|
297
297
|
|
|
298
|
-
|
|
299
|
-
`versionCode
|
|
300
|
-
|
|
298
|
+
Google does not document whether an upload into a discarded edit reserves its
|
|
299
|
+
`versionCode`; fastlane's `validate_only` runs the same sequence as a pre-release
|
|
300
|
+
check. If the real upload is later refused as "already used", raise `versionCode`.
|
|
301
301
|
|
|
302
302
|
## Step 6 - Gate 3, policy review
|
|
303
303
|
|
|
@@ -363,7 +363,7 @@ Not run
|
|
|
363
363
|
Gate 2: no local Play validator - Google Play not onboarded; the authoritative check is server-side only
|
|
364
364
|
|
|
365
365
|
Open questions
|
|
366
|
-
Gate 2 (Android):
|
|
366
|
+
Gate 2 (Android): versionCode <n> went to a discarded edit; Google does not document whether that reserves it
|
|
367
367
|
|
|
368
368
|
Before rollout (human inputs, not verified)
|
|
369
369
|
Phased rollout: <1% -> 10% -> 50% -> 100% | full>
|
|
@@ -134,12 +134,10 @@ Step 0 gate rules and why: `features/doctor.md`.
|
|
|
134
134
|
~/multi-agent-pipeline/pipeline/commands/ 2>/dev/null \
|
|
135
135
|
&& { echo "ABORT: corporate reference leaked into pipeline/commands"; exit 1; } || true
|
|
136
136
|
```
|
|
137
|
-
- **
|
|
138
|
-
`
|
|
139
|
-
`
|
|
140
|
-
|
|
141
|
-
after copying commands into `pipeline/commands/`, normalize the copy and
|
|
142
|
-
verify no sidecar or Turkish description leaked:
|
|
137
|
+
- **Installed-tree artifacts.** Picker descriptions are Turkish with a
|
|
138
|
+
`description-en:` sidecar when `outputLanguage=tr`, and fable-tier agents
|
|
139
|
+
carry `model: opus` while `fableEnabled` is off. The repo always gets the
|
|
140
|
+
English form and `model: fable`, so normalize after copying:
|
|
143
141
|
```bash
|
|
144
142
|
# normalize: restore English descriptions, strip description-en sidecars
|
|
145
143
|
node ~/multi-agent-pipeline/pipeline/scripts/localize-commands.mjs restore \
|
|
@@ -149,6 +147,7 @@ Step 0 gate rules and why: `features/doctor.md`.
|
|
|
149
147
|
# backstop: repo description lines must be English (Turkish-diacritics proxy)
|
|
150
148
|
grep -rn '^description:.*[çğıöşüÇĞİÖŞÜ]' ~/multi-agent-pipeline/pipeline/commands/ 2>/dev/null \
|
|
151
149
|
&& { echo "ABORT: localized description leaked into pipeline/commands"; exit 1; } || true
|
|
150
|
+
node ~/multi-agent-pipeline/pipeline/lib/agent-model-sync.mjs --dir ~/multi-agent-pipeline/pipeline/agents --fable-on
|
|
152
151
|
```
|
|
153
152
|
|
|
154
153
|
6. **Cross-platform smoke gate** (final step of the REPO sync, before push):
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-model-sync.mjs - installed agents follow the fable switch.
|
|
3
|
+
*
|
|
4
|
+
* prefs.global.modelFallback.fableEnabled is the one switch for the fable rung.
|
|
5
|
+
* The router honours it for every dispatch it resolves, but an agent spawned
|
|
6
|
+
* without an explicit model runs on its frontmatter `model:`. So every installed
|
|
7
|
+
* agent whose `preferredModel` is fable has that line written from the switch:
|
|
8
|
+
* fable while it is on, opus while it is off. Sources keep `model: fable`; only
|
|
9
|
+
* installed copies change. Called by model-rung.sh on every flip and by the
|
|
10
|
+
* installer after it lays the agents down.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* node agent-model-sync.mjs [--dir <agents dir>] [--prefs <prefs file>] [--fable-on]
|
|
14
|
+
*
|
|
15
|
+
* --fable-on ignores the switch and writes fable: the sync step uses it to put
|
|
16
|
+
* the repo copy back on its source form.
|
|
17
|
+
*
|
|
18
|
+
* @module pipeline/lib/agent-model-sync
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { existsSync, lstatSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
22
|
+
import { homedir } from "node:os";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
import { pathToFileURL } from "node:url";
|
|
25
|
+
|
|
26
|
+
const FRONTMATTER = /^---\n([\s\S]*?)\n---\n/;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {string} dir - an installed agents directory
|
|
30
|
+
* @param {boolean} fableOn
|
|
31
|
+
* @returns {string[]} the file names whose model line changed
|
|
32
|
+
*/
|
|
33
|
+
export function applyFableRung(dir, fableOn) {
|
|
34
|
+
if (!existsSync(dir)) return [];
|
|
35
|
+
// A linked agents directory is the source tree itself (a maintainer's
|
|
36
|
+
// --symlink install); writing there would edit the shipped personas.
|
|
37
|
+
if (lstatSync(dir).isSymbolicLink()) return [];
|
|
38
|
+
const want = fableOn ? "fable" : "opus";
|
|
39
|
+
const changed = [];
|
|
40
|
+
for (const name of readdirSync(dir).sort()) {
|
|
41
|
+
if (!name.endsWith(".md")) continue;
|
|
42
|
+
const path = join(dir, name);
|
|
43
|
+
let text;
|
|
44
|
+
try {
|
|
45
|
+
text = readFileSync(path, "utf8");
|
|
46
|
+
} catch {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const front = FRONTMATTER.exec(text);
|
|
50
|
+
if (!front || !/^preferredModel:\s*fable\s*$/m.test(front[1])) continue;
|
|
51
|
+
const nextFront = front[1].replace(/^model:.*$/m, `model: ${want}`);
|
|
52
|
+
if (nextFront === front[1]) continue;
|
|
53
|
+
const next = text.replace(front[1], nextFront);
|
|
54
|
+
if (lstatSync(path).isSymbolicLink()) rmSync(path);
|
|
55
|
+
writeFileSync(path, next);
|
|
56
|
+
changed.push(name);
|
|
57
|
+
}
|
|
58
|
+
return changed;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @param {string} prefsPath
|
|
63
|
+
* @returns {boolean}
|
|
64
|
+
*/
|
|
65
|
+
export function fableEnabled(prefsPath) {
|
|
66
|
+
try {
|
|
67
|
+
const prefs = JSON.parse(readFileSync(prefsPath, "utf8"));
|
|
68
|
+
return prefs?.global?.modelFallback?.fableEnabled === true;
|
|
69
|
+
} catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function arg(argv, flag) {
|
|
75
|
+
const i = argv.indexOf(flag);
|
|
76
|
+
return i >= 0 ? argv[i + 1] : undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const invokedDirectly = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
80
|
+
if (invokedDirectly) {
|
|
81
|
+
const argv = process.argv.slice(2);
|
|
82
|
+
const claude = join(homedir(), ".claude");
|
|
83
|
+
const dir = arg(argv, "--dir") ?? join(claude, "agents");
|
|
84
|
+
const prefs =
|
|
85
|
+
arg(argv, "--prefs") ??
|
|
86
|
+
(process.env.MULTI_AGENT_PREFS || undefined) ??
|
|
87
|
+
join(claude, "multi-agent-preferences.json");
|
|
88
|
+
const on = argv.includes("--fable-on") || fableEnabled(prefs);
|
|
89
|
+
const changed = applyFableRung(dir, on);
|
|
90
|
+
if (changed.length) {
|
|
91
|
+
process.stdout.write(
|
|
92
|
+
`agents on ${on ? "fable" : "opus"}: ${changed.map((n) => n.replace(/\.md$/, "")).join(", ")}\n`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -128,6 +128,13 @@ fi
|
|
|
128
128
|
mv "$TMP" "$PREFS"
|
|
129
129
|
trap - EXIT
|
|
130
130
|
|
|
131
|
+
# Agents spawned without an explicit model run on their frontmatter, so the
|
|
132
|
+
# installed fable-tier agents follow the switch in the same step.
|
|
133
|
+
if command -v node >/dev/null 2>&1 && [ -f "$_MA_MR_HERE/agent-model-sync.mjs" ]; then
|
|
134
|
+
node "$_MA_MR_HERE/agent-model-sync.mjs" --prefs "$PREFS" ||
|
|
135
|
+
echo "model-rung: installed agents could not be updated; run node $_MA_MR_HERE/agent-model-sync.mjs" >&2
|
|
136
|
+
fi
|
|
137
|
+
|
|
131
138
|
echo "fable rung: $CUR -> $WANT"
|
|
132
139
|
echo "cost pricing: $PRICING -> $WANT_PRICING"
|
|
133
140
|
echo ""
|
|
@@ -293,6 +293,16 @@ readiness, not correctness: a machine that fails all four still runs the
|
|
|
293
293
|
pipeline perfectly well with someone watching it. What they catch is the
|
|
294
294
|
failure mode of an unwatched run - stopping without saying so.
|
|
295
295
|
|
|
296
|
+
|
|
297
|
+
### usage-report
|
|
298
|
+
|
|
299
|
+
The outcome `usage-report.mjs` recorded for its last send attempt, read from
|
|
300
|
+
`logs/multi-agent/usage-last-send.json`. SKIP when `usageLog.enabled` is off or
|
|
301
|
+
nothing has been sent yet; OK when the last send was accepted; WARN for every
|
|
302
|
+
other outcome (`redirected`, `rejected`, `not-accepted`, `failed`, `skipped`),
|
|
303
|
+
with the status code or reason and the step that fixes it. The emitter runs
|
|
304
|
+
detached from phase-tracker with its output discarded, so this record is the
|
|
305
|
+
only surface on which a reporting path that stopped working shows up.
|
|
296
306
|
### unattended-contract
|
|
297
307
|
|
|
298
308
|
Is `multi-agent-refs/unattended-contract.md` installed. It is the only place
|
|
@@ -132,6 +132,16 @@ evidence, and there is now one fewer of them. Triage also runs on `opus`, which
|
|
|
132
132
|
makes it the same model as Reviewer 1; the Step 3 anonymisation requirement
|
|
133
133
|
already covers that case and is not optional here.
|
|
134
134
|
|
|
135
|
+
**Installed agents follow the switch.** Five personas declare
|
|
136
|
+
`preferredModel: fable` (code-reviewer, plan-critic, and the iOS, Android and
|
|
137
|
+
backend architects). A dispatch the router resolves passes its model explicitly,
|
|
138
|
+
but an agent spawned without one runs on its frontmatter `model:`. So the
|
|
139
|
+
installed copies are written from `fableEnabled`: `fable` while it is on, `opus`
|
|
140
|
+
while it is off. `model-rung.sh` re-applies them on every flip and the installer
|
|
141
|
+
on every install; sources keep `model: fable`, so `lint-personas.mjs` still holds
|
|
142
|
+
`model === preferredModel` there. A `--symlink` install links the source tree and
|
|
143
|
+
is left alone.
|
|
144
|
+
|
|
135
145
|
**Commands without Phase 0 resolve it themselves.** `/multi-agent:review`,
|
|
136
146
|
`/multi-agent:review-analysis` and the untracked-branch tail of
|
|
137
147
|
`/multi-agent:resume` dispatch reviewers and triage without a Phase 0 Step 0, so
|
|
@@ -303,4 +303,4 @@ Scripts: `contract-server.mjs` (phone branch, `isPhonePath`), `_phone-auth.mjs`
|
|
|
303
303
|
`unattended-policy.json` (`publisherScripts`). Tests:
|
|
304
304
|
`test/phone-api.test.mjs`, `test/contract-server.test.mjs`,
|
|
305
305
|
`test/launch-request.test.mjs`, `test/redact.test.mjs`,
|
|
306
|
-
`test/contract-kit.test.mjs`, `test/unattended-guard.
|
|
306
|
+
`test/contract-kit.test.mjs`, `test/unattended-guard.serial.mjs`.
|
|
@@ -35,7 +35,7 @@ the variable the same way.
|
|
|
35
35
|
|
|
36
36
|
With the variable unset every hook behaves as it did without this feature:
|
|
37
37
|
`smoke-unattended-redteam.sh` runs the same inputs both ways, and
|
|
38
|
-
`test/unattended-guard.
|
|
38
|
+
`test/unattended-guard.serial.mjs` holds the attended verdicts.
|
|
39
39
|
|
|
40
40
|
## The guard
|
|
41
41
|
|
|
@@ -722,7 +722,7 @@ Scripts: `agent-guard.sh`, `agent-guard.py`, `unattended_policy.py`,
|
|
|
722
722
|
`lib/outbound-gate.mjs`, `lib/unattended-settings-location.mjs`,
|
|
723
723
|
`install/_unattended-profile.mjs`. Data:
|
|
724
724
|
`schemas/unattended-policy.json`, `schemas/unattended-profile.json`,
|
|
725
|
-
`schemas/pr-request.schema.json`. Tests: `test/unattended-guard.
|
|
725
|
+
`schemas/pr-request.schema.json`. Tests: `test/unattended-guard.serial.mjs`,
|
|
726
726
|
`test/pr-request.test.mjs`, `test/autopilot-publish.test.mjs`,
|
|
727
727
|
the runner's tests, `test/outbound-gate.test.mjs`,
|
|
728
728
|
`test/unattended-profile.test.mjs`, `test/agent-guard.test.mjs`; smokes
|
|
@@ -147,6 +147,19 @@ reason, `unavailable` with the cause, `declined`, `registered`, `enabled`, `dry-
|
|
|
147
147
|
"you turned it off" and "we could not reach the endpoint" are different facts
|
|
148
148
|
about the same empty panel.
|
|
149
149
|
|
|
150
|
+
Every send attempt past the on-switch overwrites
|
|
151
|
+
`~/.claude/logs/multi-agent/usage-last-send.json` with its outcome: `accepted`,
|
|
152
|
+
`not-accepted` (the server answered but did not keep the record), `rejected`
|
|
153
|
+
(with the status code), `redirected` (with the host it pointed to; a redirect is
|
|
154
|
+
never followed, because the token would ride along), `failed` (network or
|
|
155
|
+
timeout), or `skipped` (`no-token`, `no-login`, `endpoint-not-allowed`). It
|
|
156
|
+
carries the run id, the event and the endpoint host, never the token. The
|
|
157
|
+
phase-tracker ping discards the emitter's output, so `/multi-agent:doctor`
|
|
158
|
+
reads this file (check `usage-report`) and turns anything but `accepted` into a
|
|
159
|
+
WARN with one step. `migrate-prefs.mjs` rewrites an endpoint on the platform
|
|
160
|
+
host to the site's domain, so a stored redirecting endpoint does not outlive an
|
|
161
|
+
update.
|
|
162
|
+
|
|
150
163
|
`prefs.global.usageLog.endpoint` overrides where both calls go - the register URL
|
|
151
164
|
is derived from it, so a self-hosted ingest gets its own registration rather than
|
|
152
165
|
this one's. Absent means the shipped default, and every shipped default names the
|
|
@@ -99,6 +99,7 @@ const CHECK_IDS = [
|
|
|
99
99
|
"mcp-surface",
|
|
100
100
|
"disk-space",
|
|
101
101
|
"worktree-residue",
|
|
102
|
+
"usage-report",
|
|
102
103
|
];
|
|
103
104
|
|
|
104
105
|
// Only under `--profile server`. Kept in their own list so `--list-checks`
|
|
@@ -883,6 +884,52 @@ function checkMcpSurface() {
|
|
|
883
884
|
);
|
|
884
885
|
}
|
|
885
886
|
|
|
887
|
+
// usage-report.mjs runs detached with its output discarded, so the record it
|
|
888
|
+
// writes after every send attempt is the one place a broken reporting path
|
|
889
|
+
// becomes visible.
|
|
890
|
+
const USAGE_STEPS = {
|
|
891
|
+
"no-login": () => `run node ${join(HERE, "usage-identity.mjs")} --refresh`,
|
|
892
|
+
"no-token": () => `run node ${join(HERE, "usage-register.mjs")}`,
|
|
893
|
+
"endpoint-not-allowed": () => "set usageLog.endpoint to an https address",
|
|
894
|
+
rejected: () => `run node ${join(HERE, "usage-register.mjs")}`,
|
|
895
|
+
"not-accepted": () => `run node ${join(HERE, "usage-identity.mjs")} --refresh`,
|
|
896
|
+
redirected: (r) =>
|
|
897
|
+
r.location
|
|
898
|
+
? `set usageLog.endpoint to the address on ${r.location} it redirects to`
|
|
899
|
+
: `run node ${join(HERE, "migrate-prefs.mjs")}`,
|
|
900
|
+
failed: () => "re-run a phase once the reporting endpoint is reachable",
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
function checkUsageReport() {
|
|
904
|
+
const prefs = readJson(join(CLAUDE, "multi-agent-preferences.json"));
|
|
905
|
+
const usage = prefs?.global?.usageLog;
|
|
906
|
+
if (usage?.enabled !== true || usage?.optOut === true) {
|
|
907
|
+
skip("usage-report", "usage reporting is off");
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
const last = readJson(join(CLAUDE, "logs", "multi-agent", "usage-last-send.json"));
|
|
911
|
+
if (!last || typeof last.outcome !== "string") {
|
|
912
|
+
skip("usage-report", "no report has been sent from this machine yet");
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
if (last.outcome === "accepted") {
|
|
916
|
+
ok("usage-report");
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
const key = last.outcome === "skipped" ? last.reason : last.outcome;
|
|
920
|
+
const stepFor = USAGE_STEPS[key] ?? USAGE_STEPS.failed;
|
|
921
|
+
const detail = [last.status, last.reason, last.location && `to ${last.location}`]
|
|
922
|
+
.filter(Boolean)
|
|
923
|
+
.join(", ");
|
|
924
|
+
const when = last.t ? ` at ${last.t}` : "";
|
|
925
|
+
report(
|
|
926
|
+
"usage-report",
|
|
927
|
+
"WARN",
|
|
928
|
+
`the last usage report${last.id ? ` for ${last.id}` : ""} was ${last.outcome}${detail ? ` (${detail})` : ""}${when}, so runs are missing from the usage panel`,
|
|
929
|
+
stepFor(last),
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
|
|
886
933
|
function checkDiskSpace() {
|
|
887
934
|
let freeGb = null;
|
|
888
935
|
try {
|
|
@@ -1175,6 +1222,7 @@ function main() {
|
|
|
1175
1222
|
checkMcpSurface();
|
|
1176
1223
|
checkDiskSpace();
|
|
1177
1224
|
checkWorktreeResidue();
|
|
1225
|
+
checkUsageReport();
|
|
1178
1226
|
if (PROFILE === "server") checkServerProfile(resolved);
|
|
1179
1227
|
|
|
1180
1228
|
const blocked = results.filter((r) => r.severity === "BLOCK");
|
|
@@ -15,6 +15,7 @@ import path from "node:path";
|
|
|
15
15
|
import os from "node:os";
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
17
17
|
import { runMain } from "../lib/fatal.mjs";
|
|
18
|
+
import { canonicalEndpoint } from "../lib/usage-endpoint.mjs";
|
|
18
19
|
import { lockTimeoutFrom, withFileLockSync, writeJsonAtomicSync } from "../lib/json-file-lock.mjs";
|
|
19
20
|
|
|
20
21
|
const DEFAULT_FILE = path.join(os.homedir(), ".claude", "multi-agent-preferences.json");
|
|
@@ -445,9 +446,22 @@ function migrate(prefs) {
|
|
|
445
446
|
// reportContent / wikiScope structure. Idempotent - safe to re-run.
|
|
446
447
|
migrateReportChannels(out, changes);
|
|
447
448
|
|
|
449
|
+
// 13. usageLog.endpoint on the platform host: it answers every request with a
|
|
450
|
+
// redirect, and the usage clients never follow one with a credential attached.
|
|
451
|
+
migrateUsageEndpoint(out, changes);
|
|
452
|
+
|
|
448
453
|
return { out, changes, alreadyMigrated: changes.length === 0 };
|
|
449
454
|
}
|
|
450
455
|
|
|
456
|
+
function migrateUsageEndpoint(out, changes) {
|
|
457
|
+
const usage = out.global.usageLog;
|
|
458
|
+
if (!usage || typeof usage !== "object" || typeof usage.endpoint !== "string") return;
|
|
459
|
+
const next = canonicalEndpoint(usage.endpoint);
|
|
460
|
+
if (next === usage.endpoint) return;
|
|
461
|
+
changes.push(`usageLog.endpoint: ${usage.endpoint} → ${next}`);
|
|
462
|
+
usage.endpoint = next;
|
|
463
|
+
}
|
|
464
|
+
|
|
451
465
|
// v5.7+ sub-migration: legacy jiraCommentDefault / confluenceDefault / wikiDefault booleans
|
|
452
466
|
// → new reportChannels {pr,jira,confluence,wiki} object + wikiScope array. Also initializes
|
|
453
467
|
// reportContent + autopilotReportTimeoutSeconds with schema defaults. Old fields are removed
|
|
@@ -570,15 +570,22 @@ def _run_base_sha(env=None):
|
|
|
570
570
|
return sha if re.match(r"^[0-9a-f]{7,40}$", sha) else None
|
|
571
571
|
|
|
572
572
|
|
|
573
|
+
_TOP_CACHE = {}
|
|
574
|
+
|
|
575
|
+
|
|
573
576
|
def _worktree_top(d):
|
|
577
|
+
if d in _TOP_CACHE:
|
|
578
|
+
return _TOP_CACHE[d]
|
|
574
579
|
try:
|
|
575
580
|
import subprocess
|
|
576
581
|
|
|
577
582
|
r = subprocess.run(["git", "-C", d, "rev-parse", "--show-toplevel"],
|
|
578
583
|
capture_output=True, text=True, timeout=3)
|
|
579
|
-
|
|
584
|
+
top = os.path.realpath(r.stdout.strip()) if r.returncode == 0 and r.stdout.strip() else None
|
|
580
585
|
except Exception:
|
|
581
|
-
|
|
586
|
+
top = None
|
|
587
|
+
_TOP_CACHE[d] = top
|
|
588
|
+
return top
|
|
582
589
|
|
|
583
590
|
|
|
584
591
|
def _tracked_clean(path, env=None, cwd=None):
|
|
@@ -609,11 +616,11 @@ def _tracked_clean(path, env=None, cwd=None):
|
|
|
609
616
|
|
|
610
617
|
# The file's path relative to the worktree root, so the base lookup
|
|
611
618
|
# names it correctly and a file outside this worktree is rejected.
|
|
612
|
-
top =
|
|
619
|
+
top = _worktree_top(d)
|
|
613
620
|
ls = g("ls-files", "--error-unmatch", "--", name)
|
|
614
621
|
st = g("status", "--porcelain", "--untracked-files=all", "--", name)
|
|
615
|
-
in_worktree = top
|
|
616
|
-
rel = os.path.relpath(p,
|
|
622
|
+
in_worktree = top is not None
|
|
623
|
+
rel = os.path.relpath(p, top) if in_worktree else None
|
|
617
624
|
at_base = (
|
|
618
625
|
g("cat-file", "-e", f"{base}:{rel}").returncode == 0
|
|
619
626
|
and g("diff", "--quiet", base, "--", name).returncode == 0
|