@mmerterden/multi-agent-pipeline 20.8.2 → 20.9.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 (37) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/docs/facts.json +1 -1
  3. package/install/_common.mjs +49 -0
  4. package/install/_dev-only-files.mjs +1 -0
  5. package/install/catalog-history.json +1 -0
  6. package/install/claude.mjs +13 -7
  7. package/manifest.json +40 -30
  8. package/package.json +3 -2
  9. package/pipeline/lib/claude-md-links.mjs +328 -0
  10. package/pipeline/lib/owned-path-gate.mjs +699 -0
  11. package/pipeline/lib/repo-profile-derive.mjs +1771 -0
  12. package/pipeline/lib/repo-profile.mjs +780 -0
  13. package/pipeline/lib/stack-detect.sh +59 -19
  14. package/pipeline/lib/unattended.mjs +17 -0
  15. package/pipeline/multi-agent-refs/features/repo-profile.md +96 -0
  16. package/pipeline/multi-agent-refs/features/review-decision.md +18 -13
  17. package/pipeline/multi-agent-refs/features/stack-skill-routing.md +179 -33
  18. package/pipeline/multi-agent-refs/outside-the-pipeline.md +33 -11
  19. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +26 -12
  20. package/pipeline/multi-agent-refs/phases/phase-2-dev.md +24 -13
  21. package/pipeline/multi-agent-refs/phases/phase-3-review.md +16 -4
  22. package/pipeline/multi-agent-refs/phases/phase-4-commit.md +1 -1
  23. package/pipeline/multi-agent-refs/phases/phase-5-report.md +8 -0
  24. package/pipeline/rules/outside-the-pipeline.md +6 -1
  25. package/pipeline/schemas/agent-state.schema.json +66 -2
  26. package/pipeline/schemas/phases.json +4 -4
  27. package/pipeline/schemas/repo-profile.schema.json +1107 -0
  28. package/pipeline/schemas/token-budget.json +4 -4
  29. package/pipeline/scripts/agent-guard.py +30 -0
  30. package/pipeline/scripts/owned-path-gate.mjs +205 -0
  31. package/pipeline/scripts/pre-commit-check.sh +151 -1
  32. package/pipeline/scripts/repo-profile.mjs +244 -0
  33. package/pipeline/scripts/review-decision-gate.mjs +42 -18
  34. package/pipeline/scripts/skill-candidates.mjs +882 -0
  35. package/pipeline/scripts/unattended_policy.py +90 -0
  36. package/pipeline/scripts/usage-report.mjs +36 -6
  37. package/pipeline/skills/.skill-manifest.json +1 -1
package/manifest.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@mmerterden/multi-agent-pipeline",
3
- "version": "20.8.2",
4
- "commit": "cc78721bb6e4e462ca13784dd59f72f398cefdb6",
5
- "generatedAt": "2026-09-27T08:25:32.332Z",
3
+ "version": "20.9.0",
4
+ "commit": "6376813f716c832343373818aec8ff9353e1c2af",
5
+ "generatedAt": "2026-09-27T13:51:52.309Z",
6
6
  "algorithm": "sha256",
7
- "fileCount": 1217,
7
+ "fileCount": 1227,
8
8
  "files": {
9
- "CHANGELOG.md": "1257d760c295813b381c3a7a91886faea510694d5004c23c4c1febcbc5ec3b2d",
9
+ "CHANGELOG.md": "c20ea4d71f35454a3caff76fe8878f22b260c3ed8768c1c21f42603a6ea23d1d",
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": "f926524cabdd39a875a0dd1ee222a401a56d5b55b32eb1c1c3faa4857f969570",
36
+ "docs/facts.json": "519125337473d715b0fd0467944aeefea2cc8e037e86cb1daf752b07f42f0485",
37
37
  "docs/features.md": "6c9f0efb00c683e6f611c440c87847b187f8b0eee59e9e812f3a241695bc2ae1",
38
38
  "docs/performance.md": "bbf2f3922c25dcba0c1a9e69ef420e3871a729af5b35009a49b48a5d3c5cd151",
39
39
  "docs/recovery-guide.md": "59a8f1b66b34cb9ff5507b3d54dac3b9915e184aa044d9c758d18cac818d510e",
@@ -42,16 +42,17 @@
42
42
  "install.js": "b2d71a926a62c96c994c76735f40dff068c429b5a368005fb62e60972da41c2a",
43
43
  "install/_codex-agents.mjs": "983eb1104f84faf0b3b71f6925747e268f9aac110df70a148bfeedcc8759cdf7",
44
44
  "install/_codex-instructions.mjs": "add75ffc0e8d8db0e3bf275b46017a897a4cadb83dc66c7d3affd73bcead20f8",
45
- "install/_common.mjs": "208c71eb135ef3f4d3622096b68210ae22a150941600e60f959a344fd04a1671",
45
+ "install/_common.mjs": "ca09ce68ba019cbffe4be583ecfb74edcd3102824af0decc1cafd5563280f0c7",
46
46
  "install/_copilot-instructions.mjs": "73e4b82b2af0f3e1669f772fa0788dac6b540c131e56d5fe38dcf29be85cc019",
47
- "install/_dev-only-files.mjs": "51285901f3b650ff22a76f8c05b23c69b9ceb6fe3da3333cece9e0caf65065a4",
47
+ "install/_dev-only-files.mjs": "3391819c5e779b9e6d5a330d4937509c591e4c35ac937d25bafd86b37ca95b73",
48
48
  "install/_managed-block.mjs": "7328728a7c82744b66218034389fc295d1384311daa5415408afa94be14303d3",
49
49
  "install/_mcp-register.mjs": "1b1c294c3333c912aab3929ef67dc68b1e24e65d01f2073bf7e23193b3e37c71",
50
50
  "install/_platform-filter.mjs": "1c66acc79da0da0c1aa5a57050989e84198c3153e6437ee03f1d3bacbb12ff2c",
51
51
  "install/_plugin-skills.mjs": "bf287053133f940df0ca434cfc5ee522fd27e80dc7f7e60ccc963ecaabee047a",
52
52
  "install/_telemetry.mjs": "5433d753f909beed2c8789272bba05e245482c472f83d3444f58000c4c9a634b",
53
53
  "install/_unattended-profile.mjs": "e1ed82bde35d19105ee12c2a551eb778912b0e5c93ab227fd15c993098d98de0",
54
- "install/claude.mjs": "8f4a5cd547651744710c6b1682d2ae5e9757cc4b963c3288c6c91a27a67edc69",
54
+ "install/catalog-history.json": "fd545686999f2231b902eb2a614614b8e0b0b2812a94b236fdfa96cd01d31f5b",
55
+ "install/claude.mjs": "c7b0a6eff15e967778e6ab8c463949c79dade05309878b03836f023da704fb3a",
55
56
  "install/codex.mjs": "2c2238cd9dd2f95b4a50a505387e8e49ddc1513ae13998f05f1ed3e3a71b18e7",
56
57
  "install/copilot.mjs": "300a74f17084d1a38f16428ca7477394c29e33cd755156e19b92e457999be234",
57
58
  "install/index.mjs": "07ec26ecdd81cc983f146faf4b7095c98edce39c89a6fd83c24ce43229d2d9e7",
@@ -61,7 +62,7 @@
61
62
  "install/templates/multi-agent-autopilot-awake.plist.template": "ddd25c76c56fcbb38a89de8e35637372d70ace993d6bc487a88ca9cfb702fe1f",
62
63
  "install/templates/multi-agent-autopilot.plist.template": "122c8a61cd927edd99375d6e3da40f1d42bcceed5b6641785c8b28bb930bc77e",
63
64
  "install/unattended-profile-legacy.json": "f52e86b998956f09ad306b1735a6c10b58580d7082d8a55665a4c6bb15c3aa1f",
64
- "package.json": "b606c47b1f00601d119acc868bdb8ed4cd969f03d4a02d0f07c8b5d560b8ac33",
65
+ "package.json": "d11daaab9b0284f5068975a79484016c0c1ef93783255c0fbc29aea8d4e36abb",
65
66
  "pipeline/agents/android-architect.md": "11fb74543cb0ebcf9525ca6654fe95d53da7229e0bf4a1149b31fd9005abc453",
66
67
  "pipeline/agents/backend-architect.md": "b02a606c301e4714131aed70ae6d16eeb77fbc819a001e2752cff2488d4246de",
67
68
  "pipeline/agents/bulk-reader.md": "6597052cd7d205bac2b1c7633f5232fe1a40730664a913b8eb77efd5a786dc25",
@@ -167,6 +168,7 @@
167
168
  "pipeline/lib/autopilot-state.sh": "e97d4cb4fce18be8be3485702be0e6d0948209a14f9725cbc83c2721d293432a",
168
169
  "pipeline/lib/channels-multi-repo.sh": "aef26f888bddbaf7fb0e2e86143d612da776b4a4282bcee8144a090a9050b989",
169
170
  "pipeline/lib/classify-intent.sh": "dab0896bc9707a10ac046d0b31d25a48c363cdde76cb1d720bf847deefc0364c",
171
+ "pipeline/lib/claude-md-links.mjs": "d0fddb740c466c8f31cd28294524094dbae8c6b726038791e11a0605c5cb1c67",
170
172
  "pipeline/lib/confusables.json": "64a31eba5d1f44c6177abf6d9dfddee671201336f76c0db71e96a0d49cb4fdaa",
171
173
  "pipeline/lib/context-link-extractor.sh": "84a154dcabf57136be17199b21d02120d70cc0ca34e8f48fe24bdda74eddda1d",
172
174
  "pipeline/lib/count-lib.sh": "7bafe152b0ffb9ca6fb9296b7ecaaef33deb9410286b3579fefbb37796044f79",
@@ -195,6 +197,7 @@
195
197
  "pipeline/lib/multi-repo-pipeline.sh": "5cca6bcc4fb857448ddadb31add343952be89423a328267e56496c7b7cdc7710",
196
198
  "pipeline/lib/normalize-text.mjs": "c2eeed65ab85cdc27a12ecaadc45dfa00b49a7611250c9ad881b9e93882a51c1",
197
199
  "pipeline/lib/outbound-gate.mjs": "1ebc840e975f19b0da4699b3a37361ecc8a2be1a7a558d2b2fb39b8b64244011",
200
+ "pipeline/lib/owned-path-gate.mjs": "28e75cf021ee43bf8c6369f72d27f426c79e8f4a4ab5ef99a009ac6399669319",
198
201
  "pipeline/lib/parse-complaints.sh": "d787ce870f141cf7f881da80fab4320814044554065e82e94632f061675b4d12",
199
202
  "pipeline/lib/phase-schema.mjs": "8fc0c909f129341295cd8ef2ea9e93cecd0ac493c3a8d83e25879f7ff522f1ab",
200
203
  "pipeline/lib/plan-todos.sh": "4992bed180774a27f84892add8b09e0f7b53e9b8fefb75f60325a04d1af18c56",
@@ -204,16 +207,18 @@
204
207
  "pipeline/lib/regular-file.mjs": "084dd2c98a664a1e4a937757cc5e5e5d7e162b6aa422f8c7cfdb27f7834c6cbc",
205
208
  "pipeline/lib/repo-cache.sh": "82bbdd3d61746042ad5341554a1077098d4b56f15caf6c5cc092377a273c8f45",
206
209
  "pipeline/lib/repo-hygiene.sh": "67f4227d2bc84198d51205f1f16d055e76a07fe11a4e5c2b8103559c44cefa04",
210
+ "pipeline/lib/repo-profile-derive.mjs": "25f22468d6032e0be9b9e7e8f3266cb3b1029654f94706a546593524e4da0e8e",
211
+ "pipeline/lib/repo-profile.mjs": "4da219c9f72f3d74cfe42d9ff8685d4f53a8426c347cef85f0c2a737ac275133",
207
212
  "pipeline/lib/require-jq.sh": "a137b4df202515c2b89d5b93c3e28b04c73c8bc2f7c42be9ca9a22926e042918",
208
213
  "pipeline/lib/review-watch.sh": "89a53f79aab902a39ab8301cabe6eb54c8dc854af4d97c024c6b3b1f3ab38a75",
209
214
  "pipeline/lib/route-state.sh": "8d7555a79be96d824b533382214431436bb95f6d3d21c4f811800f4ab4963f86",
210
215
  "pipeline/lib/run-paths.sh": "2063a1d2f07d6caa391089c1e581fd870a4e9c3caa3b0c25a03f0b4fe4f6d070",
211
216
  "pipeline/lib/shadow-git.sh": "8a36d3942153c27c34762369f7c2716aadca489851e630d4a2a3f324af1e0b93",
212
- "pipeline/lib/stack-detect.sh": "c0dccf0c86316b90b2ed1d4b734e02d0f1e4886b241beff71cec9f15a03adccc",
217
+ "pipeline/lib/stack-detect.sh": "53242b46482547f861625b9cbf44b65790849bce7e42be3bbd56abac67bc3489",
213
218
  "pipeline/lib/submodule-detector.sh": "e2c1a762876c5ae4756818e91f0f0fdd5897cac60decb979651124086e9cab97",
214
219
  "pipeline/lib/unattended-profile-check.mjs": "80a9005a43ed66d7fded8335e73a22c24483854ea532a6d503480512386d0c6d",
215
220
  "pipeline/lib/unattended-settings-location.mjs": "6ea68ecba4cdd28ed744beec0e6f2d140b423b6ad436d7903455e781e0cc01b5",
216
- "pipeline/lib/unattended.mjs": "80f293edcd2bea550a687a62f41a4eb33e2bfe753c61040daf08921f5cc86e2c",
221
+ "pipeline/lib/unattended.mjs": "391231047478c60765a9ce05ed7134bad14f6caf79a91930c24276eb15dcd8bf",
217
222
  "pipeline/lib/unattended.sh": "6160d784bb36b9eaa71fcd097d5b34c093534ced02442bdf16778d66a794dac2",
218
223
  "pipeline/lib/untrusted.mjs": "391f7093658386cac2ae2aabb21767cfc661e8bb04c7572aa2dc15b114bdc078",
219
224
  "pipeline/lib/usage-endpoint.mjs": "9aed3a854e729f82563ca7614c9ab8dbb5af3b42fa0f7c10204b0094627b9906",
@@ -269,8 +274,9 @@
269
274
  "pipeline/multi-agent-refs/features/plan-todos.md": "8780d80b4541daee2b05080759a0694ef7e6270abe9a4621deb92ec7bac94e8e",
270
275
  "pipeline/multi-agent-refs/features/prior-fix-detection.md": "9bd172fa116b0c334576ee3df2dc478810a7e282422410e045be74ac2299253d",
271
276
  "pipeline/multi-agent-refs/features/repo-map.md": "9102f95d5c819c3bce3dc992cc98c67657e83f1fbc77d2e9b3d16b040bfab9f6",
277
+ "pipeline/multi-agent-refs/features/repo-profile.md": "6f9a33c28c37cb9ab8298c282617bbce165481b89cc40683dba884a42bf227b3",
272
278
  "pipeline/multi-agent-refs/features/research.md": "776c9f3b644f8ff355108000b4a7381ff173e38811e8e2a73ef66e756d60c169",
273
- "pipeline/multi-agent-refs/features/review-decision.md": "4154539340df923e564a91d3d807145f3a94d5bd52872068af083fc5345f7010",
279
+ "pipeline/multi-agent-refs/features/review-decision.md": "ac7bf33ec86c311680d9ae8a4d1d0d0b564d0bf0cfe80e270cc98d3abceb65cb",
274
280
  "pipeline/multi-agent-refs/features/review-delta.md": "740cbefbd705a013b708921f2265f12d54d96823f1c46b5c1e644ebbdf7a8f85",
275
281
  "pipeline/multi-agent-refs/features/review-file-set.md": "a751888bce82a8e6970c9abe7f354c5c0704956e5278aeb091b893be0df5c1ef",
276
282
  "pipeline/multi-agent-refs/features/review-multi-repo.md": "f06857b2fa984ee6ca8cdf4360485947b55217056e5e439839c56871f9eb7595",
@@ -280,7 +286,7 @@
280
286
  "pipeline/multi-agent-refs/features/shadow-git.md": "d020c7b99a993eb11398d08e0518207b990b3ec3807fbcf0926c6cb304777ce9",
281
287
  "pipeline/multi-agent-refs/features/skill-conformance.md": "6391c81b2467ca05445c55b70d21f74704c4c62be7d04189196244fa54ef893f",
282
288
  "pipeline/multi-agent-refs/features/stack-adapters.md": "71e23385da872ceed35263f4838f154800aa2da55bb6688ccca74726b70a3ef5",
283
- "pipeline/multi-agent-refs/features/stack-skill-routing.md": "8d45fb7331509411617d2bf5c42e752eaaa7ada24dcf38140f2710b28ab4fc0f",
289
+ "pipeline/multi-agent-refs/features/stack-skill-routing.md": "1989f7f64be6c5ee0a42d8e9962aead7cd7472d795915ab953d89d9382f317e6",
284
290
  "pipeline/multi-agent-refs/features/unattended-gates.md": "46b516cac81a4eacf5d58bc3b1613e2266c26f1559be8dac060d1dc6b7d7ca01",
285
291
  "pipeline/multi-agent-refs/features/unattended-security.md": "eeb8656845ac78ab612b2e3352b241a6ba2d8b8f6d4a6d5c2e7cab1f6592a4d6",
286
292
  "pipeline/multi-agent-refs/features/url-enrichment.md": "72e01a799d9fa54b56c762ba404a9f9236adbbf98320e395a96f6b0f5917e76b",
@@ -294,18 +300,18 @@
294
300
  "pipeline/multi-agent-refs/keychain.md": "8f775488ed846cc11470bfe5a4db1803f4145a16e12e8fa17a3f338380f096a7",
295
301
  "pipeline/multi-agent-refs/knowledge.md": "33f6a4cc0ced930d192f32e0f7b2adf785395562f9dedd82ce939306c3d7a0e5",
296
302
  "pipeline/multi-agent-refs/multi-repo-integration-build.md": "ff4ad08d382d1d31f5c4f3c382cb340058bbe5cb1cffbfef520500cad7b9c05c",
297
- "pipeline/multi-agent-refs/outside-the-pipeline.md": "84213bb29124da8ad74f62276217dce9909a6962a03a9e4b49390df529436b3e",
303
+ "pipeline/multi-agent-refs/outside-the-pipeline.md": "34f9f79b8fcc0ac58dc45905ca8b285477f7604d10391ace1bbde0b249593fbf",
298
304
  "pipeline/multi-agent-refs/payload-contracts.md": "77b10f1d616c20b96c8f83185f9b507c9764fdc69eae1795bff9127ecbc6f37e",
299
305
  "pipeline/multi-agent-refs/phases.md": "730d598b27b8df4776adece9487f51e4835dbdb5a7d33c097e272f3360cbe844",
300
306
  "pipeline/multi-agent-refs/phases/log-format.md": "62d51e57cca8445ab40f3c0dc29615e4b5c8e4743286bf9ef0173610e81397aa",
301
307
  "pipeline/multi-agent-refs/phases/modes.md": "0733e20dce458c09f30343db9149fc93bc5a4c9531670b93952f223811dcb1aa",
302
308
  "pipeline/multi-agent-refs/phases/operations.md": "9506a26684abfff1e332d92c2321e840544fd4f049aff9f118731e198dabca94",
303
309
  "pipeline/multi-agent-refs/phases/phase-0-init.md": "3ec8a7f979818413c82418325e20ff8144a2505bfcdac61e3b8bfb2d8bcf0db8",
304
- "pipeline/multi-agent-refs/phases/phase-1-plan.md": "a83e43d6e41c986f76d6176c222af6f7a8d87d3c74f52a07de86cd52b70e42f0",
305
- "pipeline/multi-agent-refs/phases/phase-2-dev.md": "a7d7ba4cf473da623a4301d1cf01eb334a387d5f40546f2b80970032e15569b6",
306
- "pipeline/multi-agent-refs/phases/phase-3-review.md": "98b910da84bb0b36527d6487a0634a5401b382ccea6672bf7e27abe7c0cf4c3b",
307
- "pipeline/multi-agent-refs/phases/phase-4-commit.md": "b433f1a9147ce2bd6d0d944c643d2bd91f29c12488752c095c3468825f3ee6e5",
308
- "pipeline/multi-agent-refs/phases/phase-5-report.md": "5831563300bbabcfbae96f98c6f95bdbb664e345b853e57391224285532a09fa",
310
+ "pipeline/multi-agent-refs/phases/phase-1-plan.md": "7082a85be2386f10306c6b77fa012505599c39b851af26efb62bd268817be8f2",
311
+ "pipeline/multi-agent-refs/phases/phase-2-dev.md": "724acaba9cd47503adba3ac251c8341cf7fa2137edd6c084be62d4a69c0fcff0",
312
+ "pipeline/multi-agent-refs/phases/phase-3-review.md": "0f620c29ed3f1b6c968718d1c3500abab4e7b8c1c1e3c84c3bbf9087ce43703c",
313
+ "pipeline/multi-agent-refs/phases/phase-4-commit.md": "53df1abe25875881294f1f1a0fcc093eb53f9ab463927a55e1c3964efd7ce733",
314
+ "pipeline/multi-agent-refs/phases/phase-5-report.md": "3d333dc4b072c98eda3f287f884e12de62cda7255b12de7725a72903cefa6016",
309
315
  "pipeline/multi-agent-refs/picker-contract.md": "6e4f77954aec80077da0df0b055894b125bad74bd1a2cd0b2e2adb1072f75637",
310
316
  "pipeline/multi-agent-refs/platform-parity.md": "43a3836efc359c6b7d30af38d66408d8d18661e72e5165b25bb8e15953e6feaf",
311
317
  "pipeline/multi-agent-refs/progress-contract.md": "d1dc3c117aeb880cd44b3439062e9fe7d5e2042ba00c49fd495f70a6bdc1addc",
@@ -333,13 +339,13 @@
333
339
  "pipeline/rules/figma-pipeline.md": "ad300703e42ebf0aa973f56ba7656911b5e78ddbb261e8192a6bd00b8603fdd2",
334
340
  "pipeline/rules/git-conventions.md": "200408fb59e19898096aca998381974f23929760c661741dd76813de902386eb",
335
341
  "pipeline/rules/kotlin-android.md": "2344356125cbdd49f703144f3eb573d40fd1ce2d4b5ef404d44da13c052c9236",
336
- "pipeline/rules/outside-the-pipeline.md": "4e80b1435f8a1d3fcd6ad4112dc3caa08ff3c14afc5a5f8ce94e872aa48c4f96",
342
+ "pipeline/rules/outside-the-pipeline.md": "f5ae881783bb288d803938665489a1135ef7d18e59cd52d85a290c1237450d4a",
337
343
  "pipeline/rules/performance.md": "7cff04b043daebde791f6fb08144c013724f551fabe235e2ca1670ce6a714030",
338
344
  "pipeline/rules/security.md": "54fdea269a4013b50610a129495196c79be04dc10022338491f01dc313b4b53d",
339
345
  "pipeline/rules/swiftui-qa.md": "8c4afe9e72413e7bdab655f3d17588469e80ac34cb7ad2a690901fc5b6534818",
340
346
  "pipeline/rules/tdd.md": "4c1b3114506aa9794c9bb1e1aee1b1bbb4a993d192342e5697cc00bc2f6ea4a2",
341
347
  "pipeline/rules/testing.md": "695be5520b65451f0aa300a9643df2c05e607ff6ad372201ea34798dc5998bd5",
342
- "pipeline/schemas/agent-state.schema.json": "c8a7cbabc8a7bc279f0b769542f2cdc69694aa1efdedd4a11fceb45673f8b015",
348
+ "pipeline/schemas/agent-state.schema.json": "a05ea59a7f59b7b07ffbe91156ffabcb9f46089ec00efeb2f671e953f7daf79a",
343
349
  "pipeline/schemas/analysis-output.schema.json": "41826f4bf7c96778f8cdbcc68531e984087897d438b7b49ac5a93fc29db47916",
344
350
  "pipeline/schemas/analysis-spec.schema.json": "42e4d527c69f952fb6a08bca5bcbbd14dfb75291cf028ce3c653043db70bd622",
345
351
  "pipeline/schemas/answer-request.schema.json": "29d4211a2a05a230e0707b171b73341d2e56f6b83edb178b731905211aab4e23",
@@ -376,7 +382,7 @@
376
382
  "pipeline/schemas/migrations/prefs-2.7.0-to-2.8.0.mjs": "56e3bdd076500825782726eb14f48c1f989c802bbd6374873999a949afee968f",
377
383
  "pipeline/schemas/migrations/state-2.0.0-to-2.1.0.mjs": "b7058a8922dfa85a817616ab130d6e962a1e1b8e9ed42b8700ff82747a0f3fec",
378
384
  "pipeline/schemas/migrations/state-2.1.0-to-2.2.0.mjs": "96c14fcc6375d630681e69c27dc0cad1783596fa93e1426d1a03d6257f1bd053",
379
- "pipeline/schemas/phases.json": "4d10db7471c53d9bb7b9c38bf598f47b95b5b25e065883d1393e54d876ba2f09",
385
+ "pipeline/schemas/phases.json": "f290b26a6606ab3508fd214881a334534a670f90c5f68e281790585b6c131b17",
380
386
  "pipeline/schemas/phases.schema.json": "804eff7978f7cb6ea061627e2730567df03d56bd3d05f8336c20b9787f404ad8",
381
387
  "pipeline/schemas/phone-devices.schema.json": "4dcdd5b1fbd863e1b9528fe038ddec1af6ad88cafb020d0f69b5e12720acfec9",
382
388
  "pipeline/schemas/phone-signed-request.schema.json": "1785de1b523c52943e24f24d3d21f3fda7e670c177872bbe51d62c100db875a8",
@@ -385,6 +391,7 @@
385
391
  "pipeline/schemas/planning-output.schema.json": "bc6eda32ecd435cf84d846078f9d38347f8f741f8a427cff15913dd6c380d4b8",
386
392
  "pipeline/schemas/pr-request.schema.json": "ae75c4856349d62834802b1189af516bb6fb3d36646831b7ff2fa61d938afcd0",
387
393
  "pipeline/schemas/prefs.schema.json": "c327827b65c01cec5c73fdde0b4ad553daf157fb7786c4d0e97a335cfc687282",
394
+ "pipeline/schemas/repo-profile.schema.json": "80d530f82a246c797397ab3f6ad040811e79137a4375d84a3efe9efc885b5d5a",
388
395
  "pipeline/schemas/research-output.schema.json": "54e3ba1b04fdc20fbaa84333b943a8ab95d1395c7b0e0aedc67929184cb49523",
389
396
  "pipeline/schemas/review-file-exclusions.json": "2e8c44b45d21d2c82fc95aa2bf76ad3fbf2d5c68180c7b2ebf89c4a926688d77",
390
397
  "pipeline/schemas/review-file-exclusions.schema.json": "d7310b4328ae14673f0971c9f823a697d3cafe054062a680ef0f020be1867ea3",
@@ -402,7 +409,7 @@
402
409
  "pipeline/schemas/stack-adapters.json": "7bfd46a5eb589dd09fd9e935f7777d380b6803169a0fd7f74dfcee14eeed1c07",
403
410
  "pipeline/schemas/stack-adapters.schema.json": "0d4ca61336d415fe6a2ed03af8b82fffd4d070540f7328fedc85b6625b0e5d34",
404
411
  "pipeline/schemas/test-gap.schema.json": "175a268fca49a5e7a7388693b3dc397de45217c64fe333f7867ea6372610ad03",
405
- "pipeline/schemas/token-budget.json": "54d673b79db352e61e9a2f79a40a2f98d28c37aa06ada6afd1195d3e993909b2",
412
+ "pipeline/schemas/token-budget.json": "b5e84ee6590fbd74894cdc72ed63ccdd3e76eda23efff7cd9efffb8923393f5f",
406
413
  "pipeline/schemas/token-budget.schema.json": "126292faf6a49db0929d1bcb01be888ef7de4a3938cb7d88a0cf7ee786ffb297",
407
414
  "pipeline/schemas/triage-corpus.schema.json": "b919fe3d69443d78e55d875f54717c745320ff94d34c516b0ca32f358ebec2e5",
408
415
  "pipeline/schemas/triage-output.schema.json": "60e329a22cea0f0ed9fa1e8e78f9b512ef283277346b08a194ad491e0bad0995",
@@ -429,7 +436,7 @@
429
436
  "pipeline/scripts/_smoke-root.sh": "dad129959a4e37a0cd0f4f6c04f6d66c5a43752b2bf33989db9c002eb106b832",
430
437
  "pipeline/scripts/_stack-adapter.mjs": "a754c042d237cd03abf11c6909099a7a3ea798cdd13d49b03787797fd3496cd7",
431
438
  "pipeline/scripts/_stack-routing.mjs": "d64421b48eb42efffeae01f1d64ec56182269285de62e80434f3b8b05610e2ab",
432
- "pipeline/scripts/agent-guard.py": "1d214df7992463a0529f9eb2518fcdf6e5817778937a05f6c78efc1af832c9e5",
439
+ "pipeline/scripts/agent-guard.py": "ba8f0c08a80d953d339ea69492efb1fbe8db5d364c2bcb2094a9839cf6dcc081",
433
440
  "pipeline/scripts/agent-guard.sh": "5b57cea687754533c673b66a9dd2281ca06d12ef86bdc0d03fa285fc9e7debf8",
434
441
  "pipeline/scripts/aggregate-metrics.mjs": "c324e349993fb154a885263cf6665af0ff656e9c987b455a263dad8b96cca667",
435
442
  "pipeline/scripts/analysis-story-tree.mjs": "8fd371d3e552a3f60533f18c7699074ae6089016c6663512410c4fa02494548f",
@@ -517,6 +524,7 @@
517
524
  "pipeline/scripts/offload-ref.sh": "a123f7b85de21bdf631916a8721bd07963d7b74833af1a933ec18f4130de14e0",
518
525
  "pipeline/scripts/open-questions-gate.mjs": "8cff43cd552d01466bec540d9565c6225ce101c6716cc8f68f7edc6fc07beb4d",
519
526
  "pipeline/scripts/output-quality-check.sh": "8cfa31afa79fecf6dfe7dc49f32c7fa92bdc6a390075821db4e1cbc330d1b23b",
527
+ "pipeline/scripts/owned-path-gate.mjs": "86b17e7a723d30056fac89026790e33cab891a549ffbda50b9e90d9a7675bf11",
520
528
  "pipeline/scripts/package-manager.mjs": "211046f0e32a05a0ac977a285892a4ad87e1d1e362573b048c8accd45c706d02",
521
529
  "pipeline/scripts/phase-banner.sh": "acca704968d6263810de5804ea50a96b7aab7472ec98a7149d92b78b380d8fa0",
522
530
  "pipeline/scripts/phase-tracker.sh": "bb671c0f3e02a7485d9abe9d5a8b8cd3ffab636a1e2696f838964d9b848a1ed4",
@@ -526,7 +534,7 @@
526
534
  "pipeline/scripts/plan-critique-gate.mjs": "b139394e5f16d11e4997d0bb0b08529f439dbd7fad39781ef4c2475247d8fa97",
527
535
  "pipeline/scripts/plugin-refresh.sh": "687a33199ad963616ab537e32210ec8c967e76ecc42f494ee8a1dc9fe947fc30",
528
536
  "pipeline/scripts/pr-request.mjs": "e6f4ded2366c6599bb345b3011ecc771e13b8a1561afa4c9a50bca4edff17456",
529
- "pipeline/scripts/pre-commit-check.sh": "60c4cf2444981db3d098dced897fe410c93c6a65250e3b2376e48df8428b9b9c",
537
+ "pipeline/scripts/pre-commit-check.sh": "8a2dd546e3712e6915d67729d84b89c0b98c76d48e02348d41773e4a182e7223",
530
538
  "pipeline/scripts/pre-push-check.sh": "e449aff7f0336d7d624eff4c6221f56d8621ba9d010669601755e57fd4ee02c7",
531
539
  "pipeline/scripts/probe-evidence-capability.sh": "ac74252eed6b773fdbc7ec475a63606925f1ba7b5e7c6f911329f46fd920eae7",
532
540
  "pipeline/scripts/prune-logs.sh": "c5587b33104c7253e07be8cce8249b50014c52dfdf232de637811ffdd88e95e4",
@@ -536,9 +544,10 @@
536
544
  "pipeline/scripts/render-cost-summary.sh": "ce9647332ab9a3e518af98f52a6fc70baab9fd3c09ec5f9e85bae2d67078a5e3",
537
545
  "pipeline/scripts/render-work-summary.sh": "ee4db04ebc145cc2fc7fa9a533d42b7da4da191710ef67c883502ca83b2aa67c",
538
546
  "pipeline/scripts/repo-map.mjs": "175dcaeb548e8a018ad36cf661f3bdcb9340d32439fd4d92918fd6b3ab175730",
547
+ "pipeline/scripts/repo-profile.mjs": "5f0cf90c59accdb0bb763fab9512e271e3cc4c8bc930ece137d94baa5c8e08d9",
539
548
  "pipeline/scripts/require-supported-version.sh": "490d681a8c8fb510082bc6f2325965598b3aa3b4720fe5c6e08e371c7c9073f4",
540
549
  "pipeline/scripts/research-gate.mjs": "66b0f7521fea5efa0085b9d4f31b9ce9c5c1719a7e0395ca176252c96ad974c1",
541
- "pipeline/scripts/review-decision-gate.mjs": "44effcca495ca90798ae7fcf65ec98e42967bd229ceafb495c6c0753e7a1bf36",
550
+ "pipeline/scripts/review-decision-gate.mjs": "b54cce49be270a1291571f63563b3a3e11e2417493bd8615c2a58bd7ff41b393",
542
551
  "pipeline/scripts/review-delta.mjs": "14a2de7ac81e7f43a634423f920f9931bb4a27441b8ba3dc8e53ec1f6a6ed54a",
543
552
  "pipeline/scripts/review-file-filter.mjs": "8b1fd9661b8d61b4282a4579ef360f952c827f90520b69ffde4770fdaeaaa05b",
544
553
  "pipeline/scripts/review-scope.mjs": "60be90f1dce0a87071695bbedf17eb4e8600b1c742b97aef8cbea41802315b93",
@@ -553,6 +562,7 @@
553
562
  "pipeline/scripts/scope-check-gate.mjs": "a71b736b1283b6b99fcca23ae2159e9fb134e1e65dbfec1e66d63a5cc20582f4",
554
563
  "pipeline/scripts/search-logs.sh": "af0ce9b75edd359cc93dd678d35b15eb9bb0f14a4b044d8e4d639f8cdb82c76f",
555
564
  "pipeline/scripts/sign-skills.sh": "779355dcf8ce383959e9a3c6093f46660a0875e83a805b7a67983acc8551cb59",
565
+ "pipeline/scripts/skill-candidates.mjs": "800df17b4a4ad8edc700d5d7bfac512ae861b3e20cf27a92a18c9d12729bc6ab",
556
566
  "pipeline/scripts/skill-conformance.mjs": "73c5947ee4a6201f52653d162ba1d6e70757cbff8dd1e90317a7c4b095424b2b",
557
567
  "pipeline/scripts/skill-siblings.mjs": "5f7cbe1e80db64bc246e6fe899e8a8c03647a55a54047387feaa00923c78c29b",
558
568
  "pipeline/scripts/smoke-cross-cli-behavior.sh": "15f29422640eb47c863523cdc3651f9faaf7df0ebe24fff5e59e5b68708a6b78",
@@ -571,13 +581,13 @@
571
581
  "pipeline/scripts/token-budget-report.mjs": "508bbc659df478c535c0ecdd3f8611372af8fef0b0932c7b19a247af2b2b9265",
572
582
  "pipeline/scripts/triage-memory.mjs": "2d8541145f1749593ae5728fb4c9eb3001484edb5e4d37be473aeec34d1dbe7c",
573
583
  "pipeline/scripts/ui-tree-dumper.swift": "a09e42ef069eafa08c05afd8333e05b754ef6a7d8b9687712039b0f2c9801273",
574
- "pipeline/scripts/unattended_policy.py": "695a204f43dd509f04bc4a5eaa9d700f0ea1d3a63a4837b81e0d01fc086e7246",
584
+ "pipeline/scripts/unattended_policy.py": "0f5dc7284421c529368e1dcbf30e3a85e43e73076fd09909ac185cf4ba1eff90",
575
585
  "pipeline/scripts/uninstall.mjs": "880923522ef59fdd98cb7ecb1bdc85c0f15d1e2eed2d79d6d353e88712ca95fa",
576
586
  "pipeline/scripts/update-check.sh": "9c00647e7ca4249414b2e5af08f61705ab5bd2106b6f66ae8ebce8aa143dc957",
577
587
  "pipeline/scripts/update-issue-progress.sh": "ccfef87743e91272e009e396ac07461d2784b2fa84a06c802a582526eeeb9ed9",
578
588
  "pipeline/scripts/usage-identity.mjs": "47c1f907fb1ab47672687ea0cbf2d0737985ae91ec2b48b4fe260e14c3d35d70",
579
589
  "pipeline/scripts/usage-register.mjs": "cc19d7ce5efe57255ce1521d33ad4b9a75b6833385ed6edcc5e339faf3c6e06c",
580
- "pipeline/scripts/usage-report.mjs": "01ef50005c3358d17ca4ef69b80b5b576887ec47cb360c0211adbcc9c1f1e2aa",
590
+ "pipeline/scripts/usage-report.mjs": "93c3f4055592c9cf5007cccf4710d06900576ccc10d1523c631acd13ca68cf10",
581
591
  "pipeline/scripts/validate-analysis-doc.mjs": "ae56b7578ef51e008a99f84eba9def3e0f1dd2bd87a3880f2083aa98b365f7a1",
582
592
  "pipeline/scripts/validate-analysis.mjs": "d2dc9ccf45f6ca2702908761939edd830d18b3b273016a4b449f26fe285d5ea1",
583
593
  "pipeline/scripts/validate-code-graph.mjs": "0ceac2f5b59f641334d54dc30bee5265361fb5fe1fc97e2f9791b546efe79941",
@@ -596,7 +606,7 @@
596
606
  "pipeline/scripts/worktree-prepare.sh": "9391c828375b127f77c86e3c830dbbaf355a61464c6437319969f2f1eecc72b1",
597
607
  "pipeline/scripts/worktrees.mjs": "90dfd09b70fffe9b56c331a4ae2692cc248f4898f4ee6aee4239ef0b20888a90",
598
608
  "pipeline/scripts/write-state.mjs": "e1198bf970eace07d3eff22b394808a526e473df65d3d58aa2d5a12cfdac8b49",
599
- "pipeline/skills/.skill-manifest.json": "a9630d718fa494d75e17f8ea822eb1501b85aa165362e98e53d3789d6e57a051",
609
+ "pipeline/skills/.skill-manifest.json": "449e794ee0d3e0661bd6143f0a2670955ae943b8caae53857a7614417771d1be",
600
610
  "pipeline/skills/.skills-index.json": "042b1090f3e2ca310c6ce70d1bb304e8c7f96bd73133e504b95d83801608e470",
601
611
  "pipeline/skills/shared/README.md": "a144fb39993470b6379d9660e0ee0371bdc858aee3d7c1b375cb6aba03d046db",
602
612
  "pipeline/skills/shared/core/apple-archive-compliance/SKILL.md": "7239fef717ea45b79276bfcc2c3903acd57950b6e00dd6a2a66399c4b09cbd51",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmerterden/multi-agent-pipeline",
3
- "version": "20.8.2",
3
+ "version": "20.9.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,7 +20,7 @@
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/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",
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
24
  "test:unit": "node --test test/*.test.mjs",
25
25
  "test:smoke": "node pipeline/scripts/run-smokes.mjs",
26
26
  "lint:skills": "node pipeline/scripts/lint-skills.mjs",
@@ -94,6 +94,7 @@
94
94
  "!pipeline/scripts/run-smokes.mjs",
95
95
  "!pipeline/scripts/eval-*.mjs",
96
96
  "!pipeline/scripts/check-rewrite-overlap.mjs",
97
+ "!pipeline/scripts/gen-catalog-history.mjs",
97
98
  "!pipeline/scripts/gen-ref-toc.mjs",
98
99
  "!pipeline/scripts/make-manifest.mjs",
99
100
  "!pipeline/scripts/scorecard-snapshot.mjs",
@@ -0,0 +1,328 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * claude-md-links.mjs - the in-repo documents a repo's CLAUDE.md points at.
4
+ *
5
+ * A project CLAUDE.md is often an index: "naming rules are in docs/style.md".
6
+ * Phase 1 treats those linked files as project rules, so it needs them, and it
7
+ * needs them bounded. Three forms count as a reference: a markdown link, a
8
+ * document path written as inline code (`` See `.claude/RULES.md` ``), and a
9
+ * host import line (`@docs/rules.md`). A code path is resolved against the
10
+ * CLAUDE.md's own directory first, then the repo root; a link or import against
11
+ * the file's own directory; a leading `/` means the repo root when its first
12
+ * segment exists there, and is otherwise an absolute filesystem path, which is
13
+ * rejected like `~/`.
14
+ *
15
+ * One hop only (links inside a linked file are not followed), at most
16
+ * MAX_FILES files and MAX_BYTES bytes in total. References on a line that calls
17
+ * the target authoritative ("See X for ...", "X is authoritative", "source of
18
+ * truth") are read first, so the caps drop background reading before rules. The
19
+ * file that crosses the byte budget is truncated at a character boundary and
20
+ * reported in `truncated[]`, not dropped. SKILL.md files are skipped: skills are
21
+ * routed by scripts/skill-candidates.mjs. Nothing that resolves outside the repo
22
+ * root is read - checked on the written path and on the real path, for the
23
+ * CLAUDE.md files themselves too, because a symlink inside the repo can point
24
+ * anywhere.
25
+ *
26
+ * Usage:
27
+ * node claude-md-links.mjs <repo-root> [--no-content]
28
+ *
29
+ * Prints JSON: {files:[{path, from, kind, priority, bytes, content, truncated?,
30
+ * fullBytes?}], rejected:[{link, from, kind, reason}], truncated:[path],
31
+ * totalBytes, sources}. `kind` is "link", "code-path", "import" or, for a
32
+ * rejected CLAUDE.md, "source". Exit 0 always when the root is readable; an
33
+ * absent CLAUDE.md is an empty result, not an error. Exit 2 on usage.
34
+ *
35
+ * @module pipeline/lib/claude-md-links
36
+ */
37
+
38
+ import { existsSync, lstatSync, readFileSync, realpathSync, statSync } from "node:fs";
39
+ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
40
+
41
+ import { invokedDirectly } from "./invoked-directly.mjs";
42
+
43
+ export const SOURCES = ["CLAUDE.md", ".claude/CLAUDE.md"];
44
+ export const MAX_FILES = 10;
45
+ export const MAX_BYTES = 200 * 1024;
46
+
47
+ const SCHEME = /^[a-z][a-z0-9+.-]*:/i;
48
+ const AUTHORITATIVE =
49
+ /\bauthoritative\b|\bsource of truth\b|\bcanonical\b|\bmust (?:read|follow)\b|\bsee\b[^\n]*?\bfor\b|\brules? (?:are|live) in\b/i;
50
+
51
+ function blank(m) {
52
+ return m.replace(/[^\n]/g, " ");
53
+ }
54
+
55
+ function stripFences(md) {
56
+ return md.replace(/^(```|~~~)[\s\S]*?^\1[^\n]*$/gm, blank);
57
+ }
58
+
59
+ function stripCode(md) {
60
+ return stripFences(md).replace(/`[^`\n]*`/g, blank);
61
+ }
62
+
63
+ function cleanTarget(raw) {
64
+ let t = raw.trim().replace(/^<|>$/g, "");
65
+ if (t.startsWith("#")) return null;
66
+ t = t.replace(/[#?].*$/, "");
67
+ if (!t) return null;
68
+ try {
69
+ t = decodeURI(t);
70
+ } catch {
71
+ /* keep the undecoded form */
72
+ }
73
+ return t;
74
+ }
75
+
76
+ function lineAt(text, index) {
77
+ const start = text.lastIndexOf("\n", index - 1) + 1;
78
+ const end = text.indexOf("\n", index);
79
+ return text.slice(start, end < 0 ? text.length : end);
80
+ }
81
+
82
+ function linkHits(md) {
83
+ const text = stripCode(String(md ?? ""));
84
+ const out = [];
85
+ const inline = /!?\[[^\]\n]*\]\(\s*(<[^>\n]*>|[^\s)]+)(?:\s+(?:"[^"\n]*"|'[^'\n]*'))?\s*\)/g;
86
+ const reference = /^[ \t]{0,3}\[[^\]\n]+\]:[ \t]*(<[^>\n]*>|\S+)/gm;
87
+ for (const re of [inline, reference]) {
88
+ for (const m of text.matchAll(re)) {
89
+ const t = cleanTarget(m[1]);
90
+ if (t) out.push({ at: m.index, t });
91
+ }
92
+ }
93
+ return out.sort((a, b) => a.at - b.at);
94
+ }
95
+
96
+ /**
97
+ * Link targets in a markdown document, in document order, fragments and titles
98
+ * removed. Anchor-only links and anything inside code are skipped.
99
+ * @param {string} md
100
+ * @returns {string[]}
101
+ */
102
+ export function extractLinks(md) {
103
+ return linkHits(md).map((x) => x.t);
104
+ }
105
+
106
+ const DOC_PATH =
107
+ /^(?:\.{0,2}\/)?[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*\.(?:md|mdx|markdown|txt|rst)$/i;
108
+
109
+ function codePathHits(md) {
110
+ const text = stripFences(String(md ?? ""));
111
+ const out = [];
112
+ for (const m of text.matchAll(/`([^`\n]+)`/g)) {
113
+ const t = m[1].trim();
114
+ if (DOC_PATH.test(t)) out.push({ at: m.index, t });
115
+ }
116
+ return out;
117
+ }
118
+
119
+ /**
120
+ * Document paths written as inline code, in document order. Only spans that are
121
+ * nothing but a relative-looking path to a text document count, so a command,
122
+ * a type name or a glob is never taken for a reference.
123
+ * @param {string} md
124
+ * @returns {string[]}
125
+ */
126
+ export function extractCodePaths(md) {
127
+ return codePathHits(md).map((x) => x.t);
128
+ }
129
+
130
+ function importHits(md) {
131
+ const text = stripCode(String(md ?? ""));
132
+ const out = [];
133
+ const re = /(^|[\s(])@((?:~|\.{1,2})?\/?[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*)/g;
134
+ for (const m of text.matchAll(re)) {
135
+ const t = m[2].replace(/[.]+$/, "");
136
+ if (/\.[A-Za-z0-9]+$/.test(basename(t))) out.push({ at: m.index + m[1].length, t });
137
+ }
138
+ return out;
139
+ }
140
+
141
+ /**
142
+ * Host import lines (`@path/to/file.md`) outside code, in document order. An
143
+ * `@` inside a word (an e-mail address) is not an import, and a target needs a
144
+ * file extension, so a mention or a package scope is not taken for one.
145
+ * @param {string} md
146
+ * @returns {string[]}
147
+ */
148
+ export function extractImports(md) {
149
+ return importHits(md).map((x) => x.t);
150
+ }
151
+
152
+ function inside(root, p) {
153
+ const rel = relative(root, p);
154
+ return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel));
155
+ }
156
+
157
+ function toPosix(p) {
158
+ return p.split(sep).join("/");
159
+ }
160
+
161
+ function locate(root, ref) {
162
+ const { link } = ref;
163
+ if (SCHEME.test(link) || link.startsWith("//")) return { reason: "url" };
164
+ if (link.startsWith("~")) return { reason: "absolute path" };
165
+ let bases = ref.bases;
166
+ let rel = link;
167
+ if (isAbsolute(link)) {
168
+ const first = link.split("/").find(Boolean);
169
+ if (!first || !existsSync(join(root, first))) return { reason: "absolute path" };
170
+ bases = [root];
171
+ rel = link.replace(/^\/+/, "");
172
+ }
173
+ let sawInside = false;
174
+ for (const b of bases) {
175
+ const target = resolve(b, rel);
176
+ if (!inside(root, target)) continue;
177
+ sawInside = true;
178
+ try {
179
+ lstatSync(target);
180
+ return { target };
181
+ } catch {
182
+ /* try the next base */
183
+ }
184
+ }
185
+ return { reason: sawInside ? "missing" : "outside repo" };
186
+ }
187
+
188
+ /** Largest prefix of `buf` no longer than `limit` that ends on a UTF-8 character boundary. */
189
+ function utf8Cut(buf, limit) {
190
+ let end = Math.min(limit, buf.length);
191
+ while (end > 0 && end < buf.length && (buf[end] & 0xc0) === 0x80) end--;
192
+ return end;
193
+ }
194
+
195
+ /**
196
+ * One reference: a file entry, a rejection reason, or null for a silent skip
197
+ * (a duplicate, or one of the CLAUDE.md files themselves).
198
+ */
199
+ function admit(root, ref, seen, result, { maxFiles, maxBytes, withContent }) {
200
+ const found = locate(root, ref);
201
+ if (found.reason) return found.reason;
202
+ let real;
203
+ try {
204
+ real = realpathSync(found.target);
205
+ } catch {
206
+ return "missing";
207
+ }
208
+ if (!inside(root, real)) return "outside repo";
209
+ if (seen.has(real) || SOURCES.some((s) => join(root, s) === real)) return null;
210
+ if (basename(found.target) === "SKILL.md" || basename(real) === "SKILL.md") return "skill file";
211
+ let st;
212
+ try {
213
+ st = statSync(real);
214
+ } catch {
215
+ return "missing";
216
+ }
217
+ if (!st.isFile()) return "not a regular file";
218
+ if (result.files.length >= maxFiles) return "file cap";
219
+ const room = maxBytes - result.totalBytes;
220
+ if (room <= 0) return "byte budget";
221
+ let buf;
222
+ try {
223
+ buf = readFileSync(real);
224
+ } catch {
225
+ return "unreadable";
226
+ }
227
+ if (buf.includes(0)) return "binary";
228
+ const end = buf.length > room ? utf8Cut(buf, room) : buf.length;
229
+ if (end === 0) return "byte budget";
230
+ seen.add(real);
231
+ result.totalBytes += end;
232
+ const path = toPosix(relative(root, real));
233
+ const entry = {
234
+ path,
235
+ from: null,
236
+ kind: ref.kind,
237
+ priority: ref.priority,
238
+ bytes: end,
239
+ };
240
+ if (end < buf.length) {
241
+ entry.truncated = true;
242
+ entry.fullBytes = buf.length;
243
+ result.truncated.push(path);
244
+ }
245
+ if (withContent) entry.content = buf.subarray(0, end).toString("utf8");
246
+ return entry;
247
+ }
248
+
249
+ function refsOf(text, src, base, root) {
250
+ const tag = (kind, bases) => (h) => ({
251
+ link: h.t,
252
+ kind,
253
+ bases,
254
+ from: src,
255
+ at: h.at,
256
+ priority: AUTHORITATIVE.test(lineAt(text, h.at)) ? "authoritative" : "normal",
257
+ });
258
+ return [
259
+ ...linkHits(text).map(tag("link", [base])),
260
+ ...codePathHits(text).map(tag("code-path", base === root ? [base] : [base, root])),
261
+ ...importHits(text).map(tag("import", [base])),
262
+ ].sort((a, b) => a.at - b.at);
263
+ }
264
+
265
+ /**
266
+ * Follow relative links from the repo's CLAUDE.md files, one hop, bounded.
267
+ * @param {string} repoRoot
268
+ * @param {{maxFiles?: number, maxBytes?: number, content?: boolean}} [opts]
269
+ */
270
+ export function followClaudeMdLinks(repoRoot, opts = {}) {
271
+ const maxFiles = opts.maxFiles ?? MAX_FILES;
272
+ const maxBytes = opts.maxBytes ?? MAX_BYTES;
273
+ const withContent = opts.content !== false;
274
+ const result = { files: [], rejected: [], truncated: [], totalBytes: 0, sources: [] };
275
+
276
+ let root;
277
+ try {
278
+ root = realpathSync(resolve(repoRoot));
279
+ } catch {
280
+ return result;
281
+ }
282
+ const seen = new Set();
283
+ const refs = [];
284
+
285
+ for (const src of SOURCES) {
286
+ const srcPath = join(root, src);
287
+ let text;
288
+ try {
289
+ if (!statSync(srcPath).isFile()) continue;
290
+ if (!inside(root, realpathSync(srcPath))) {
291
+ result.rejected.push({ link: src, from: src, kind: "source", reason: "outside repo" });
292
+ continue;
293
+ }
294
+ text = readFileSync(srcPath, "utf8");
295
+ } catch {
296
+ continue;
297
+ }
298
+ result.sources.push(src);
299
+ refs.push(...refsOf(text, src, dirname(srcPath), root));
300
+ }
301
+
302
+ const ordered = [
303
+ ...refs.filter((r) => r.priority === "authoritative"),
304
+ ...refs.filter((r) => r.priority !== "authoritative"),
305
+ ];
306
+ for (const ref of ordered) {
307
+ const verdict = admit(root, ref, seen, result, { maxFiles, maxBytes, withContent });
308
+ if (verdict === null) continue;
309
+ if (typeof verdict === "string") {
310
+ result.rejected.push({ link: ref.link, from: ref.from, kind: ref.kind, reason: verdict });
311
+ } else {
312
+ verdict.from = ref.from;
313
+ result.files.push(verdict);
314
+ }
315
+ }
316
+ return result;
317
+ }
318
+
319
+ if (invokedDirectly(import.meta.url)) {
320
+ const args = process.argv.slice(2);
321
+ const dir = args.find((a) => !a.startsWith("--"));
322
+ if (!dir) {
323
+ console.error("usage: claude-md-links.mjs <repo-root> [--no-content]");
324
+ process.exit(2);
325
+ }
326
+ const out = followClaudeMdLinks(dir, { content: !args.includes("--no-content") });
327
+ process.stdout.write(`${JSON.stringify(out, null, 2)}\n`);
328
+ }