@aiwg/cli 2026.10.1 → 2026.10.4

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 (361) hide show
  1. package/README.md +2 -2
  2. package/agentic/code/providers/capability-matrix.yaml +270 -0
  3. package/agentic/code/providers/model-capabilities.v1.json +3183 -24
  4. package/bin/aiwg.mjs +135 -47
  5. package/dist/src/a2a/codecs.js +9 -0
  6. package/dist/src/agents/grounding/knowledge-base.js +54 -6
  7. package/dist/src/artifacts/browser-export.js +6 -0
  8. package/dist/src/artifacts/checksum-manifest.js +1 -0
  9. package/dist/src/artifacts/citation-parser.js +173 -92
  10. package/dist/src/artifacts/citation-table-schema.js +59 -0
  11. package/dist/src/artifacts/cli.js +57 -18
  12. package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
  13. package/dist/src/artifacts/corpus-tools/vision-extract.js +107 -15
  14. package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
  15. package/dist/src/artifacts/corpus-views/renderers.js +3 -3
  16. package/dist/src/artifacts/discovery-eval.js +6 -4
  17. package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
  18. package/dist/src/artifacts/fortemi-core-sync.js +166 -40
  19. package/dist/src/artifacts/framework-capabilities.js +171 -0
  20. package/dist/src/artifacts/framework-index-paths.js +73 -0
  21. package/dist/src/artifacts/framework-source-digest.js +75 -0
  22. package/dist/src/artifacts/index-builder.js +285 -131
  23. package/dist/src/artifacts/index-files.js +132 -18
  24. package/dist/src/artifacts/index-preparation.js +65 -0
  25. package/dist/src/artifacts/index-refresh.js +576 -0
  26. package/dist/src/artifacts/index-worker.mjs +9 -0
  27. package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
  28. package/dist/src/artifacts/prebuilt-deployment.js +46 -0
  29. package/dist/src/artifacts/prebuilt-relations.js +80 -0
  30. package/dist/src/artifacts/query-engine.js +410 -88
  31. package/dist/src/artifacts/relations-builder.js +168 -0
  32. package/dist/src/artifacts/relations.js +148 -0
  33. package/dist/src/artifacts/repair.js +9 -4
  34. package/dist/src/artifacts/stats.js +5 -2
  35. package/dist/src/artifacts/types.js +30 -10
  36. package/dist/src/artifacts/watcher.js +1 -1
  37. package/dist/src/auth/credential-store.js +2 -1
  38. package/dist/src/catalog/cli.d.mts +5 -0
  39. package/dist/src/channel/manager.d.mts +20 -0
  40. package/dist/src/channel/manager.mjs +78 -12
  41. package/dist/src/cli/child-process-result.js +40 -0
  42. package/dist/src/cli/command-log.js +2 -3
  43. package/dist/src/cli/env.js +38 -16
  44. package/dist/src/cli/flag-parsing.js +236 -0
  45. package/dist/src/cli/handlers/artifacts.js +3 -0
  46. package/dist/src/cli/handlers/auth.js +3 -0
  47. package/dist/src/cli/handlers/decision.js +218 -5
  48. package/dist/src/cli/handlers/diagnose.js +13 -5
  49. package/dist/src/cli/handlers/effect.js +4 -2
  50. package/dist/src/cli/handlers/evidence.js +3 -0
  51. package/dist/src/cli/handlers/feedback.js +14 -5
  52. package/dist/src/cli/handlers/help.js +1 -0
  53. package/dist/src/cli/handlers/index.js +3 -1
  54. package/dist/src/cli/handlers/init.js +248 -30
  55. package/dist/src/cli/handlers/lint.js +5 -0
  56. package/dist/src/cli/handlers/mc.js +23 -0
  57. package/dist/src/cli/handlers/models.js +169 -10
  58. package/dist/src/cli/handlers/ralph-launcher.js +2 -0
  59. package/dist/src/cli/handlers/ralph.js +3 -0
  60. package/dist/src/cli/handlers/refresh.js +28 -0
  61. package/dist/src/cli/handlers/regenerate.js +14 -0
  62. package/dist/src/cli/handlers/resource-versions.js +3 -0
  63. package/dist/src/cli/handlers/rules.js +195 -0
  64. package/dist/src/cli/handlers/run.js +4 -0
  65. package/dist/src/cli/handlers/runtime-info.js +25 -11
  66. package/dist/src/cli/handlers/script-runner.js +19 -8
  67. package/dist/src/cli/handlers/session.js +406 -62
  68. package/dist/src/cli/handlers/sessions.js +243 -76
  69. package/dist/src/cli/handlers/setup-manifest.js +474 -79
  70. package/dist/src/cli/handlers/setup.js +180 -39
  71. package/dist/src/cli/handlers/steward.js +11 -0
  72. package/dist/src/cli/handlers/subcommands.js +60 -3
  73. package/dist/src/cli/handlers/team.js +18 -8
  74. package/dist/src/cli/handlers/uhp.js +3 -0
  75. package/dist/src/cli/handlers/use.js +307 -55
  76. package/dist/src/cli/handlers/utilities.js +125 -26
  77. package/dist/src/cli/handlers/version.js +8 -0
  78. package/dist/src/cli/prompt-utils.js +72 -0
  79. package/dist/src/cli/provider-resolution.js +11 -0
  80. package/dist/src/cli/router.js +15 -1
  81. package/dist/src/cli/services/deployment-verification.js +19 -9
  82. package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
  83. package/dist/src/cli/skill-usage.js +2 -3
  84. package/dist/src/config/aiwg-config.js +349 -35
  85. package/dist/src/config/cli.js +256 -23
  86. package/dist/src/config/decision-config.js +106 -0
  87. package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
  88. package/dist/src/config/dispatch-thresholds-runtime.mjs +38 -0
  89. package/dist/src/config/gitignore.js +50 -0
  90. package/dist/src/config/index-graph-overrides.js +36 -0
  91. package/dist/src/config/project-artifacts-health.d.mts +37 -0
  92. package/dist/src/config/project-artifacts-health.mjs +14 -3
  93. package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
  94. package/dist/src/config/user-config-dir.d.mts +9 -0
  95. package/dist/src/config/user-config.js +38 -11
  96. package/dist/src/config/user-registry.js +23 -15
  97. package/dist/src/config/user-state.js +7 -0
  98. package/dist/src/decision/adapters/jev.js +75 -27
  99. package/dist/src/decision/ask.js +203 -43
  100. package/dist/src/decision/context-live-qualification.js +2 -2
  101. package/dist/src/decision/driver.js +14 -7
  102. package/dist/src/decision/ensemble/aggregate.js +23 -10
  103. package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
  104. package/dist/src/decision/entry.js +25 -6
  105. package/dist/src/decision/graph-live-qualification.js +1 -1
  106. package/dist/src/decision/graph-run.js +9 -0
  107. package/dist/src/decision/heldout/collector.js +15 -14
  108. package/dist/src/decision/heldout/contract.js +30 -7
  109. package/dist/src/decision/index.js +1 -0
  110. package/dist/src/decision/interop.js +60 -8
  111. package/dist/src/decision/jev-credentials.js +15 -1
  112. package/dist/src/decision/ledger.js +942 -0
  113. package/dist/src/decision/offload-activation.js +137 -0
  114. package/dist/src/decision/offload.js +550 -0
  115. package/dist/src/decision/qualification/load.js +4 -6
  116. package/dist/src/decision/savings.js +28 -0
  117. package/dist/src/decision/validate.js +6 -3
  118. package/dist/src/effects/dispatch-verifier.js +252 -0
  119. package/dist/src/effects/index.js +1 -0
  120. package/dist/src/effects/types.js +5 -1
  121. package/dist/src/effects/verifiers/git.js +2 -1
  122. package/dist/src/effects/verifiers/tracker.js +26 -14
  123. package/dist/src/extensions/commands/definitions.js +19 -0
  124. package/dist/src/extensions/project-local-remove.js +36 -1
  125. package/dist/src/features/installer.js +2 -2
  126. package/dist/src/flow/graph-metadata.js +5 -0
  127. package/dist/src/gates/providers/loader.js +64 -18
  128. package/dist/src/installation/manager-command.d.mts +18 -0
  129. package/dist/src/installation/manager-command.mjs +15 -3
  130. package/dist/src/installation/manager.d.mts +49 -0
  131. package/dist/src/installation/manager.mjs +95 -5
  132. package/dist/src/issues/cli.js +4 -4
  133. package/dist/src/issues/local.js +24 -5
  134. package/dist/src/jobs/runner.js +1 -1
  135. package/dist/src/lint/cli.js +27 -1
  136. package/dist/src/lint/loader.js +2 -1
  137. package/dist/src/lint/reporters.js +25 -1
  138. package/dist/src/lint/rule-activation.js +43 -0
  139. package/dist/src/lint/runner.js +137 -62
  140. package/dist/src/mcp/adapters/codex-runtime.js +366 -56
  141. package/dist/src/mcp/claude-install.d.mts +4 -0
  142. package/dist/src/mcp/claude-install.mjs +41 -0
  143. package/dist/src/mcp/cli.d.mts +1 -0
  144. package/dist/src/mcp/cli.mjs +620 -85
  145. package/dist/src/mcp/config-file.d.mts +28 -0
  146. package/dist/src/mcp/config-file.mjs +279 -0
  147. package/dist/src/mcp/config-layers.d.mts +36 -0
  148. package/dist/src/mcp/config-layers.mjs +348 -0
  149. package/dist/src/mcp/credentials.d.mts +51 -0
  150. package/dist/src/mcp/credentials.mjs +484 -0
  151. package/dist/src/mcp/grok-build-config.d.mts +6 -0
  152. package/dist/src/mcp/grok-build-config.mjs +17 -27
  153. package/dist/src/mcp/helpers.mjs +109 -4
  154. package/dist/src/mcp/muse-native-config.d.mts +13 -0
  155. package/dist/src/mcp/muse-native-config.mjs +1 -0
  156. package/dist/src/mcp/omp-config.d.mts +4 -0
  157. package/dist/src/mcp/omp-config.mjs +50 -29
  158. package/dist/src/mcp/profiles.js +97 -23
  159. package/dist/src/mcp/profiles.mjs +99 -27
  160. package/dist/src/mcp/registry.js +248 -131
  161. package/dist/src/mcp/registry.mjs +169 -100
  162. package/dist/src/mcp/server-config.d.mts +3 -0
  163. package/dist/src/mcp/server-config.mjs +89 -0
  164. package/dist/src/mcp/server.mjs +127 -82
  165. package/dist/src/mcp/test-simple.mjs +2 -1
  166. package/dist/src/mcp/toml-editor.d.mts +4 -0
  167. package/dist/src/mcp/toml-string.d.mts +1 -0
  168. package/dist/src/mcp/toml-string.mjs +9 -0
  169. package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
  170. package/dist/src/mcp/toml-strip-mcp.mjs +135 -0
  171. package/dist/src/mcp/tool-filters.d.mts +46 -0
  172. package/dist/src/mcp/tool-filters.mjs +357 -0
  173. package/dist/src/mcp/tools/agentic-sandbox.mjs +3 -1
  174. package/dist/src/mcp/tools/decision.mjs +22 -3
  175. package/dist/src/mcp/tools/discovery.mjs +62 -55
  176. package/dist/src/mcp/tools/orchestration.mjs +50 -2
  177. package/dist/src/mcp/tools/subsystems.mjs +39 -8
  178. package/dist/src/mcp/tools/worker-invoke.mjs +2856 -0
  179. package/dist/src/mcp/tools/worker-shell-control.mjs +289 -0
  180. package/dist/src/mcp/tools/worker-stdin-runtime.mjs +79 -0
  181. package/dist/src/memory/project-registry.js +2 -2
  182. package/dist/src/mission-protocol/codecs.js +647 -1
  183. package/dist/src/mission-protocol/types.js +110 -0
  184. package/dist/src/mission-protocol/worker-status.js +2 -0
  185. package/dist/src/models/config-loader.js +76 -14
  186. package/dist/src/models/grok-build-models.js +93 -41
  187. package/dist/src/models/model-capabilities.v1.json +3183 -24
  188. package/dist/src/models/model-discovery.js +931 -67
  189. package/dist/src/models/provider-policy.js +719 -11
  190. package/dist/src/models/qualification-digest.d.mts +2 -0
  191. package/dist/src/models/qualification-digest.mjs +17 -0
  192. package/dist/src/models/resolver.js +261 -0
  193. package/dist/src/models/worker-direct-readiness.js +550 -0
  194. package/dist/src/models/worker-dispatch-offer.js +169 -0
  195. package/dist/src/models/worker-health-readiness.js +144 -0
  196. package/dist/src/models/worker-installation.d.mts +39 -0
  197. package/dist/src/models/worker-installation.mjs +986 -0
  198. package/dist/src/models/wrapper-route.js +18 -0
  199. package/dist/src/ops/registry.js +9 -14
  200. package/dist/src/plugins/standalone-packager.js +4 -3
  201. package/dist/src/process/bounded-child-process.d.mts +84 -0
  202. package/dist/src/process/bounded-child-process.mjs +430 -0
  203. package/dist/src/process/cli-command.d.mts +16 -0
  204. package/dist/src/process/cli-command.mjs +75 -0
  205. package/dist/src/process/signal-child.d.mts +6 -0
  206. package/dist/src/process/signal-child.mjs +7 -0
  207. package/dist/src/providers/capability-matrix.js +8 -0
  208. package/dist/src/providers/capability-matrix.yaml +270 -0
  209. package/dist/src/providers/deployed-skill-roots.js +20 -0
  210. package/dist/src/providers/grok-build-acp.js +3 -2
  211. package/dist/src/providers/grok-build-headless.js +53 -23
  212. package/dist/src/providers/grok-build-inspect.js +1 -0
  213. package/dist/src/providers/omp-agent.d.mts +8 -0
  214. package/dist/src/providers/omp-diagnostics.d.mts +1 -0
  215. package/dist/src/providers/omp-paths.d.mts +13 -0
  216. package/dist/src/providers/provider-definitions.js +9 -1
  217. package/dist/src/providers/provider-definitions.mjs +9 -2
  218. package/dist/src/providers/skill-ownership.d.mts +9 -0
  219. package/dist/src/providers/skill-ownership.mjs +88 -0
  220. package/dist/src/rules/activation.d.mts +21 -0
  221. package/dist/src/rules/activation.mjs +62 -0
  222. package/dist/src/rules/inline-budget.d.mts +26 -0
  223. package/dist/src/rules/inline-budget.mjs +66 -0
  224. package/dist/src/rules/inventory.js +129 -0
  225. package/dist/src/rules/pin-state.js +73 -0
  226. package/dist/src/rules/pins.d.mts +14 -0
  227. package/dist/src/rules/pins.mjs +112 -0
  228. package/dist/src/rules/rule-frontmatter.schema.json +197 -0
  229. package/dist/src/serve/executor-registry.js +16 -24
  230. package/dist/src/serve/fleet-mission-conductor.js +9 -0
  231. package/dist/src/serve/mission-conductor.js +47 -12
  232. package/dist/src/serve/shared-host-scheduler.js +2 -0
  233. package/dist/src/serve/worker-invocation.js +558 -0
  234. package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
  235. package/dist/src/serve/worker-stdin-frame.mjs +107 -0
  236. package/dist/src/sessions/adapter-registry.js +49 -0
  237. package/dist/src/sessions/adapters/claude.js +39 -54
  238. package/dist/src/sessions/adapters/codex.js +82 -67
  239. package/dist/src/sessions/adapters/copilot.js +4 -4
  240. package/dist/src/sessions/adapters/cursor.js +40 -57
  241. package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
  242. package/dist/src/sessions/adapters/factory.js +31 -27
  243. package/dist/src/sessions/adapters/generic.js +43 -9
  244. package/dist/src/sessions/adapters/grok-build.js +11 -11
  245. package/dist/src/sessions/adapters/grokbot-locator.js +111 -0
  246. package/dist/src/sessions/adapters/grokbot.js +434 -24
  247. package/dist/src/sessions/adapters/hermes.js +27 -29
  248. package/dist/src/sessions/adapters/muse-workspace-scan.js +128 -0
  249. package/dist/src/sessions/adapters/muse.js +200 -104
  250. package/dist/src/sessions/adapters/omp.js +28 -14
  251. package/dist/src/sessions/adapters/openclaw.js +89 -79
  252. package/dist/src/sessions/adapters/opencode.js +27 -24
  253. package/dist/src/sessions/adapters/openhuman.js +18 -60
  254. package/dist/src/sessions/adapters/pi.js +30 -14
  255. package/dist/src/sessions/adapters/warp.js +3 -3
  256. package/dist/src/sessions/adapters/windsurf.js +19 -48
  257. package/dist/src/sessions/batch-contracts.js +25 -5
  258. package/dist/src/sessions/batch-import.js +72 -51
  259. package/dist/src/sessions/candidates.js +47 -11
  260. package/dist/src/sessions/contracts.js +75 -2
  261. package/dist/src/sessions/coverage-preflight.js +134 -0
  262. package/dist/src/sessions/discovery.js +3 -0
  263. package/dist/src/sessions/export-plan.js +4 -3
  264. package/dist/src/sessions/grok-build-cli.js +67 -5
  265. package/dist/src/sessions/import-exclusions.js +72 -0
  266. package/dist/src/sessions/import-lease.js +34 -18
  267. package/dist/src/sessions/import-limits.js +37 -0
  268. package/dist/src/sessions/importer.js +84 -46
  269. package/dist/src/sessions/index.js +2 -0
  270. package/dist/src/sessions/lifecycle.js +45 -0
  271. package/dist/src/sessions/policy.js +133 -25
  272. package/dist/src/sessions/readers.js +106 -53
  273. package/dist/src/sessions/refresh.js +106 -0
  274. package/dist/src/sessions/repository.js +199 -63
  275. package/dist/src/sessions/workspace-discovery.js +315 -88
  276. package/dist/src/sessions/workspace-matching.js +107 -0
  277. package/dist/src/skills/adapters/clawhub.js +10 -8
  278. package/dist/src/skills/adapters/openclaw.js +10 -7
  279. package/dist/src/skills/importer.js +1 -0
  280. package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
  281. package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
  282. package/dist/src/smiths/context-pipeline/finalization.js +6 -5
  283. package/dist/src/smiths/context-pipeline/generator.js +21 -4
  284. package/dist/src/smiths/context-pipeline/parallelism-section.js +195 -14
  285. package/dist/src/smiths/context-pipeline/workspace-context.js +116 -9
  286. package/dist/src/smiths/hook-bridge/codex-translator.js +6 -5
  287. package/dist/src/smiths/mcpsmith/example.js +1 -1
  288. package/dist/src/smiths/mcpsmith/generator.js +1 -1
  289. package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
  290. package/dist/src/smiths/toolsmith/runtime-discovery.mjs +85 -31
  291. package/dist/src/storage/backends/fs.js +18 -76
  292. package/dist/src/storage/backends/logseq.js +68 -7
  293. package/dist/src/storage/backends/obsidian.js +1 -0
  294. package/dist/src/storage/fortemi-qualification-receipt.js +2 -2
  295. package/dist/src/testing/generators/test-code-generator.js +57 -30
  296. package/dist/src/tracker/forge-http.js +2 -1
  297. package/dist/src/uhp/client.js +11 -1
  298. package/dist/src/uhp/mission.js +5 -0
  299. package/dist/src/update/checker.d.mts +2 -0
  300. package/dist/src/update/checker.mjs +5 -5
  301. package/dist/src/update/notifier.mjs +54 -2
  302. package/dist/src/update/service.d.mts +22 -0
  303. package/dist/src/utils/contained-filesystem.d.mts +37 -0
  304. package/dist/src/utils/contained-filesystem.mjs +683 -0
  305. package/dist/src/utils/temporary-directory.js +36 -0
  306. package/dist/src/writing/example-generator.js +1 -1
  307. package/dist/src/writing/fidelity.js +18 -1
  308. package/package.json +32 -2
  309. package/schemas/mission-v1.schema.json +2429 -0
  310. package/schemas/models/model-catalog.v1.schema.json +301 -0
  311. package/schemas/models/model-config-compatibility.v1.schema.json +281 -0
  312. package/schemas/models/model-policy.v1.schema.json +14 -0
  313. package/schemas/models/provider-model-capabilities.v1.schema.json +1072 -0
  314. package/tools/_resolve-impl.mjs +39 -2
  315. package/tools/agents/deploy-agents.mjs +64 -23
  316. package/tools/agents/providers/base.mjs +153 -166
  317. package/tools/agents/providers/claude.mjs +8 -24
  318. package/tools/agents/providers/codex.mjs +18 -11
  319. package/tools/agents/providers/cursor.mjs +19 -18
  320. package/tools/agents/providers/grok-build.mjs +3 -1
  321. package/tools/agents/providers/grokbot.mjs +11 -12
  322. package/tools/agents/providers/hermes.mjs +62 -49
  323. package/tools/agents/providers/omp.mjs +5 -2
  324. package/tools/agents/providers/openclaw-translator.mjs +19 -5
  325. package/tools/agents/providers/openclaw.mjs +9 -4
  326. package/tools/agents/providers/pi.mjs +5 -0
  327. package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
  328. package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
  329. package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
  330. package/tools/agents/providers/rule-delivery.mjs +1270 -0
  331. package/tools/agents/providers/warp.mjs +20 -26
  332. package/tools/cli/decision-offload-report.mjs +40 -0
  333. package/tools/cli/workspace-status.mjs +862 -0
  334. package/tools/plugin/package-plugins.mjs +186 -8
  335. package/tools/plugin/plugin-links.mjs +114 -0
  336. package/tools/providers/antigravity-transport.mjs +3 -1
  337. package/tools/providers/deepseek-harness-transport.mjs +360 -0
  338. package/tools/providers/omp-transport.mjs +194 -0
  339. package/tools/ralph-external/lib/claude-adapter.mjs +389 -0
  340. package/tools/ralph-external/lib/codex-adapter.mjs +432 -0
  341. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
  342. package/tools/ralph-external/lib/deepseek-harness-adapter.mjs +397 -0
  343. package/tools/ralph-external/lib/factory-adapter.mjs +503 -0
  344. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
  345. package/tools/ralph-external/lib/muse-adapter.mjs +364 -0
  346. package/tools/ralph-external/lib/omp-adapter.mjs +315 -0
  347. package/tools/ralph-external/lib/opencode-adapter.mjs +575 -0
  348. package/tools/ralph-external/lib/openhuman-adapter.mjs +75 -0
  349. package/tools/ralph-external/lib/pi-adapter.mjs +329 -0
  350. package/tools/ralph-external/lib/provider-adapter.mjs +748 -0
  351. package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
  352. package/tools/ralph-external/lib/session-environment.mjs +28 -0
  353. package/tools/ralph-external/lib/systemd-client.mjs +49 -0
  354. package/tools/ralph-external/lib/worker-health.mjs +439 -0
  355. package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
  356. package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
  357. package/tools/ralph-external/process-monitor.mjs +1017 -0
  358. package/tools/ralph-external/session-launcher.mjs +5057 -0
  359. package/tools/scripts/resource-preflight.mjs +541 -0
  360. package/tools/skills/deploy-skills-codex.mjs +2 -1
  361. package/vscode-extension/schemas/aiwg.config.v1.json +2916 -0
@@ -16,13 +16,15 @@ import { load as loadYaml } from 'js-yaml';
16
16
  import { validateDecisionDocument } from '../decision/validate.js';
17
17
  import { parseDecisionJson, parseDecisionYaml } from '../decision/entry.js';
18
18
  import { validateGateDocument } from '../gates/schema.js';
19
- import { DEFAULT_INDEX_EXTENSIONS, INDEX_EXTRACTOR_VERSION, INDEX_VERSION, INDEX_DIR, OPERATIONAL_DISCOVERY_TYPES, PHASE_DIRECTORIES, GRAPH_CONFIGS, TEMPLATE_INDEX_EXTENSIONS, resolveGraphScanDir, loadUserGraphConfigs, loadGlobalGraphConfigs, } from './types.js';
19
+ import { INDEX_EXTRACTOR_VERSION, INDEX_VERSION, INDEX_DIR, OPERATIONAL_DISCOVERY_TYPES, PHASE_DIRECTORIES, GRAPH_CONFIGS, TEMPLATE_INDEX_EXTENSIONS, resolveGraphScanDir, loadUserGraphConfigs, loadGlobalGraphConfigs, resolveGraphBackendType, } from './types.js';
20
20
  import { parseCitationSidecar, citationResultToEdges, buildRefToPathMap } from './citation-parser.js';
21
21
  import { writeIndexFile, resolveIndexDir, loadGraphIndexFile } from './index-reader.js';
22
22
  import { loadManifest, writeManifest, statMatches, makeEntry } from './checksum-manifest.js';
23
+ import { FRAMEWORK_RELATIONS_REVISION } from './framework-source-digest.js';
23
24
  import { normalizeOperationalState } from './operational-state.js';
24
25
  import { DEFAULT_PROJECT_AIWG_DIR, resolveProjectAiwgDir, } from '../config/project-artifacts.js';
25
26
  import { normalizeStateTransferProjection } from './state-transfer.js';
27
+ import { runIndexPreparation } from './index-preparation.js';
26
28
  import { collectGraphIndexFiles, findArtifactFiles, indexPathFor } from './index-files.js';
27
29
  function absoluteEntryPath(cwd, entryPath, graph) {
28
30
  if ((!graph || graph === 'project') && entryPath.startsWith(`${DEFAULT_PROJECT_AIWG_DIR}/`)) {
@@ -56,6 +58,30 @@ export function parseFrontmatter(content) {
56
58
  return { data: {}, body: content };
57
59
  }
58
60
  }
61
+ /** Read the target rule schema tolerantly while older rules remain untagged. */
62
+ export function extractRuleBindings(data, body = '') {
63
+ const bound_to = {};
64
+ const raw = data.bound_to;
65
+ if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
66
+ for (const key of ['skills', 'agents', 'flows']) {
67
+ const targets = raw[key];
68
+ if (Array.isArray(targets)) {
69
+ const names = [...new Set(targets.filter((name) => typeof name === 'string' && name.trim().length > 0))];
70
+ if (names.length)
71
+ bound_to[key] = names;
72
+ }
73
+ }
74
+ }
75
+ const enforcement = typeof data.enforcement === 'string' ? data.enforcement
76
+ : body.match(/^\*\*Enforcement Level\*\*:\s*([A-Za-z]+)/m)?.[1];
77
+ return {
78
+ ...(enforcement ? { ruleEnforcement: enforcement.trim().toLowerCase() } : {}),
79
+ ...(Array.isArray(data.paths) ? { rulePaths: data.paths.filter((value) => typeof value === 'string') } : {}),
80
+ ...(typeof data.activation === 'string' ? { activation: data.activation } : {}),
81
+ ...(typeof data.summary === 'string' ? { ruleSummary: data.summary } : {}),
82
+ ...(Object.keys(bound_to).length ? { bound_to } : {}),
83
+ };
84
+ }
59
85
  /**
60
86
  * Extract @-mention references from content
61
87
  */
@@ -810,29 +836,99 @@ function resolveInstallRootHint() {
810
836
  catch { /* fall through */ }
811
837
  return '<aiwg install root>';
812
838
  }
839
+ /** Shared serial/worker read, checksum and extraction. No index output or global reduction. */
840
+ export function prepareIndexArtifact(job) {
841
+ const { fullPath, relativePath } = job;
842
+ const content = fs.readFileSync(fullPath, 'utf8');
843
+ const checksum = computeChecksum(content);
844
+ if (job.existingChecksum === checksum)
845
+ return { checksum };
846
+ const { data, body } = parseFrontmatter(content);
847
+ // YAML Flow documents (workflow.aiwg.io/v1) are pure YAML, not
848
+ // markdown+frontmatter — detect them up front so they classify and
849
+ // become discoverable (#1540).
850
+ const flow = parseFlowDoc(content, relativePath);
851
+ const decision = flow ? null : parseDecisionDoc(content, relativePath);
852
+ const gatePack = flow || decision ? null : parseGatePackDoc(content, relativePath);
853
+ const inferredType = inferType(data, relativePath);
854
+ const physicalType = inferType({ ...data, type: undefined }, relativePath);
855
+ const runbook = flow || decision || gatePack ? null : parseRunbookDoc(data, body, relativePath);
856
+ const schemaDoc = inferredType === 'schema' ? parseSchemaDoc(content, relativePath) : null;
857
+ const title = flow?.name ?? decision?.name ?? gatePack?.name ?? schemaDoc?.title ?? extractTitle(data, body);
858
+ const phase = typeof data.phase === 'string' ? data.phase : inferPhase(relativePath);
859
+ const type = flow?.type ?? decision?.type ?? gatePack?.type ?? (runbook ? 'runbook' : inferredType);
860
+ const tags = flow?.tags ?? decision?.tags ?? gatePack?.tags ?? (Array.isArray(data.tags) ? data.tags.map(String) : []);
861
+ const summary = flow?.description ?? decision?.description ?? gatePack?.description ?? schemaDoc?.capability ?? runbook?.capability ?? extractSummary(data, body);
862
+ const dependencies = extractMentions(content);
863
+ const markdownLinks = extractMarkdownLinks(content);
864
+ // Discovery metadata (#1214, #1540, #1792) — meaningful for operational
865
+ // AIWG artifact kinds. Kept undefined on document types so the index file
866
+ // stays small for the common case.
867
+ const isDiscoverable = OPERATIONAL_DISCOVERY_TYPES.includes(type);
868
+ // Declarative processes have no trigger phrases — they rely on their
869
+ // capability and structure-aware search terms.
870
+ const triggers = isDiscoverable && !flow ? extractTriggers(body, data) : undefined;
871
+ const capability = flow?.capability ?? decision?.capability ?? gatePack?.capability ?? schemaDoc?.capability ?? runbook?.capability ?? (isDiscoverable ? extractCapability(data, body) : undefined);
872
+ const kind = flow?.kind ?? decision?.kind ?? gatePack?.kind ?? runbook?.kind;
873
+ const sourceType = runbook && physicalType !== 'runbook' ? physicalType : undefined;
874
+ const searchTerms = flow?.searchTerms ?? decision?.searchTerms ?? gatePack?.searchTerms ?? schemaDoc?.searchTerms ?? runbook?.searchTerms;
875
+ const kernel = data.kernel === true || data.kernel === 'true' ? true : undefined;
876
+ // Script entrypoint metadata is meaningful for skills only (#1227).
877
+ const script = type === 'skill' ? extractSkillScript(data) : undefined;
878
+ const operationalState = normalizeOperationalState(data.operational_state);
879
+ const stateTransfer = normalizeStateTransferProjection(data.state_transfer);
880
+ // Canonical short name (#1233) — used by the scorer to floor exact-name
881
+ // queries to 1.0 so hyphenated kernel-skill names like `aiwg-doctor`
882
+ // remain searchable even when the rendered title strips the hyphen.
883
+ const name = flow?.name ?? decision?.name ?? gatePack?.name ?? schemaDoc?.name ?? (isDiscoverable ? extractCanonicalName(data, relativePath) : undefined);
884
+ const entry = {
885
+ path: relativePath,
886
+ type,
887
+ ...(kind ? { kind } : {}),
888
+ ...(sourceType ? { sourceType } : {}),
889
+ phase,
890
+ title,
891
+ tags,
892
+ created: typeof data.created === 'string' ? data.created : new Date(job.birthtimeMs).toISOString(),
893
+ updated: new Date(job.mtimeMs).toISOString(),
894
+ checksum,
895
+ summary,
896
+ dependencies,
897
+ ...(markdownLinks.length > 0 ? { markdownLinks } : {}),
898
+ dependents: [], // Computed after all entries are processed
899
+ ...(name ? { name } : {}),
900
+ ...(triggers && triggers.length > 0 ? { triggers } : {}),
901
+ ...(capability ? { capability } : {}),
902
+ ...(searchTerms && searchTerms.length > 0 ? { searchTerms } : {}),
903
+ ...(kernel ? { kernel } : {}),
904
+ ...(script ? { script } : {}),
905
+ ...(type === 'rule' || type === 'behavior' ? extractRuleBindings(data, body) :
906
+ Array.isArray(data.paths) ? { governedPaths: data.paths.filter((value) => typeof value === 'string') } : {}),
907
+ ...(operationalState ? { operationalState } : {}),
908
+ ...(stateTransfer ? { stateTransfer } : {}),
909
+ };
910
+ return { checksum, entry };
911
+ }
813
912
  export async function buildIndex(cwd, options = {}) {
814
- const { force = false, verbose = false, scope, outputDir, graph, explicit = true } = options;
913
+ const { force = false, verbose = false, scope, includeIgnored = false, outputDir, graph, explicit = true } = options;
815
914
  const startTime = Date.now();
915
+ const log = options.quiet ? (..._args) => { } : console.log;
816
916
  // Ensure user-defined graphs are loaded
817
917
  loadUserGraphConfigs(cwd);
818
918
  loadGlobalGraphConfigs();
819
919
  // Determine scan directories based on graph type
820
920
  const graphConfig = graph ? GRAPH_CONFIGS[graph] : undefined;
821
921
  let scanDirs;
822
- let fileExtensions;
823
922
  if (scope) {
824
923
  // Explicit scope overrides graph config
825
924
  scanDirs = [path.join(cwd, scope)];
826
- fileExtensions = [...DEFAULT_INDEX_EXTENSIONS];
827
925
  }
828
926
  else if (graphConfig) {
829
927
  scanDirs = graphConfig.scanDirs.map(d => resolveGraphScanDir(cwd, d));
830
- fileExtensions = graphConfig.extensions;
831
928
  }
832
929
  else {
833
930
  // Default: scan .aiwg/ (backward compatible)
834
931
  scanDirs = [resolveProjectAiwgDir(cwd)];
835
- fileExtensions = [...DEFAULT_INDEX_EXTENSIONS];
836
932
  }
837
933
  // Verify at least one scan directory exists
838
934
  const existingDirs = scanDirs.filter(d => fs.existsSync(d));
@@ -851,16 +947,16 @@ export async function buildIndex(cwd, options = {}) {
851
947
  // required directories" sends the operator looking in the wrong place (#2530).
852
948
  if (graph === 'framework') {
853
949
  const installRoot = resolveInstallRootHint();
854
- console.log('The framework graph indexes the AIWG corpus and can only be built at the');
855
- console.log('install root, not in a consumer project. Build it there, then sync here:');
856
- console.log('');
857
- console.log(` cd ${installRoot} && aiwg index build --graph framework --force`);
858
- console.log(` cd ${cwd} && aiwg index sync --backend fortemi-core --graph framework`);
859
- console.log('');
860
- console.log("As an immediate workaround, discovery also works with '--backend local'.");
950
+ log('The framework graph indexes the AIWG corpus and can only be built at the');
951
+ log('install root, not in a consumer project. Build it there, then sync here:');
952
+ log('');
953
+ log(` cd ${installRoot} && aiwg index build --graph framework --force`);
954
+ log(` cd ${cwd} && aiwg index sync --backend fortemi-core --graph framework`);
955
+ log('');
956
+ log("As an immediate workaround, discovery also works with '--backend local'.");
861
957
  }
862
958
  else {
863
- console.log('Run this command from a project with the required directories.');
959
+ log('Run this command from a project with the required directories.');
864
960
  }
865
961
  process.exit(1);
866
962
  }
@@ -890,24 +986,21 @@ export async function buildIndex(cwd, options = {}) {
890
986
  verbose,
891
987
  });
892
988
  const buildTimeMs = Date.now() - startTime;
893
- console.log(`Source graph built in ${buildTimeMs}ms`);
894
- console.log(` Output: ${INDEX_DIR}/source/`);
989
+ log(`Source graph built in ${buildTimeMs}ms`);
990
+ log(` Output: ${INDEX_DIR}/source/`);
895
991
  return;
896
992
  }
897
993
  // Load existing index for incremental updates
898
- const existingIndex = force
899
- ? null
900
- : outputDir
901
- ? loadIndexFromDir(indexOutputDir, 'metadata.json')
902
- : loadGraphIndexFile(effectiveOutputCwd, 'metadata.json', graph);
903
- const canReuseExisting = existingIndex?.version === INDEX_VERSION
904
- && existingIndex.extractorVersion === INDEX_EXTRACTOR_VERSION;
905
- const existingEntries = canReuseExisting ? existingIndex.entries : {};
906
- // Load checksum manifest for fast stat-based change detection (#794).
907
- // When --force is set we skip the manifest entirely and rebuild everything.
908
- const manifest = force || !canReuseExisting
994
+ const backendType = resolveGraphBackendType(graph);
995
+ const buildConfigHash = createHash('sha256')
996
+ .update(JSON.stringify({ graphConfig, backendType }))
997
+ .digest('hex');
998
+ const storedManifest = force
909
999
  ? { version: 1, generated: '', entries: {} }
910
1000
  : loadManifest(indexOutputDir);
1001
+ const useFilenameMetadata = graphConfig?.nodeStrategy === 'filename-metadata';
1002
+ const reusable = !useFilenameMetadata && !graphConfig?.metadataSupplements?.length && !graphConfig?.edgeExtraction;
1003
+ const fileStats = !force && reusable ? new Map() : undefined;
911
1004
  const nextManifestEntries = {};
912
1005
  const manifestStats = {
913
1006
  checked: 0,
@@ -917,16 +1010,117 @@ export async function buildIndex(cwd, options = {}) {
917
1010
  pruned: 0,
918
1011
  };
919
1012
  // Collect files from all scan directories
920
- const files = scope
921
- ? existingDirs.flatMap(dir => findArtifactFiles(dir, fileExtensions))
922
- : await collectGraphIndexFiles(cwd, graph);
1013
+ const files = await collectGraphIndexFiles(cwd, graph, { scope, includeIgnored, fileStats });
923
1014
  const entries = {};
924
1015
  const tagIndex = {};
925
1016
  const depGraph = {};
926
1017
  let newCount = 0;
927
1018
  let updatedCount = 0;
928
1019
  let unchangedCount = 0;
929
- const useFilenameMetadata = graphConfig?.nodeStrategy === 'filename-metadata';
1020
+ const reportBuild = (buildTimeMs, total) => {
1021
+ log(`Artifact index built in ${buildTimeMs}ms`);
1022
+ log(` Indexed ${newCount} new, updated ${updatedCount}, unchanged ${unchangedCount}`);
1023
+ log(` Total: ${total} artifacts`);
1024
+ if (!force && manifestStats.checked > 0) {
1025
+ const fastPath = manifestStats.statSkipped;
1026
+ const slowPath = manifestStats.checked - manifestStats.statSkipped;
1027
+ log(` Change detection: ${manifestStats.checked} checked, ${fastPath} skipped via stat, ${slowPath} content-read, ${manifestStats.reindexed} re-indexed`);
1028
+ if (manifestStats.pruned > 0) {
1029
+ log(` Pruned ${manifestStats.pruned} stale manifest entries (files no longer on disk)`);
1030
+ }
1031
+ }
1032
+ const displayDir = graph ? indexOutputDir : `${INDEX_DIR}/`;
1033
+ log(` Output: ${displayDir}`);
1034
+ };
1035
+ const requiredFiles = ['tags.json', 'dependencies.json'];
1036
+ if (backendType === 'sqlite')
1037
+ requiredFiles.push('graph.db');
1038
+ const fileSignature = (filename) => {
1039
+ try {
1040
+ const stat = fs.statSync(path.join(indexOutputDir, filename), { bigint: true });
1041
+ return [stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].join(':');
1042
+ }
1043
+ catch {
1044
+ return '';
1045
+ }
1046
+ };
1047
+ const relationsReceiptMatches = () => {
1048
+ const projection = storedManifest.receipt?.relations;
1049
+ return graph !== 'framework' || Boolean(projection?.revision === FRAMEWORK_RELATIONS_REVISION
1050
+ && projection.signature && projection.signature === fileSignature('relations.json'));
1051
+ };
1052
+ const entriesDigest = (values) => createHash('sha256').update(JSON.stringify(values)).digest('hex');
1053
+ const writeBuildManifest = (values) => {
1054
+ writeManifest(indexOutputDir, { version: 1, generated: new Date().toISOString(), entries: values,
1055
+ ...(reusable ? { receipt: {
1056
+ indexVersion: INDEX_VERSION, extractorVersion: INDEX_EXTRACTOR_VERSION, buildConfigHash,
1057
+ sourceRoot: path.resolve(cwd), metadataSignature: fileSignature('metadata.json'), entriesDigest: entriesDigest(values),
1058
+ ...(graph === 'framework' ? { relations: { revision: FRAMEWORK_RELATIONS_REVISION, signature: fileSignature('relations.json') } } : {}),
1059
+ } } : {}),
1060
+ });
1061
+ };
1062
+ // A successful receipt proves the manifest belongs to this exact metadata
1063
+ // file. Still enumerate/stat every source and verify the complete file set;
1064
+ // unchanged builds need not parse the much larger metadata/dependency data.
1065
+ const receipt = storedManifest.receipt;
1066
+ if (!force && reusable && storedManifest.generated && receipt
1067
+ && receipt.indexVersion === INDEX_VERSION && receipt.extractorVersion === INDEX_EXTRACTOR_VERSION
1068
+ && receipt.buildConfigHash === buildConfigHash && receipt.sourceRoot === path.resolve(cwd)
1069
+ && receipt.metadataSignature && receipt.metadataSignature === fileSignature('metadata.json')
1070
+ && relationsReceiptMatches()
1071
+ && receipt.entriesDigest === entriesDigest(storedManifest.entries)
1072
+ && files.length === Object.keys(storedManifest.entries).length
1073
+ && requiredFiles.every(filename => fs.existsSync(path.join(indexOutputDir, filename)))) {
1074
+ const stats = loadIndexFromDir(indexOutputDir, 'stats.json');
1075
+ if (stats?.version === INDEX_VERSION && stats.extractorVersion === INDEX_EXTRACTOR_VERSION
1076
+ && stats.totalArtifacts === files.length && files.every(file => {
1077
+ try {
1078
+ return statMatches(fileStats?.get(file) ?? fs.statSync(file), storedManifest.entries[indexPathFor(cwd, file, graph)]);
1079
+ }
1080
+ catch {
1081
+ return false;
1082
+ }
1083
+ })) {
1084
+ unchangedCount = files.length;
1085
+ manifestStats.checked = files.length;
1086
+ manifestStats.statSkipped = files.length;
1087
+ const buildTimeMs = Date.now() - startTime;
1088
+ writeIndexFile(effectiveOutputCwd, 'stats.json', { ...stats, builtAt: new Date().toISOString(), buildTimeMs }, indexOutputDir);
1089
+ reportBuild(buildTimeMs, files.length);
1090
+ return;
1091
+ }
1092
+ }
1093
+ const existingIndex = force ? null : outputDir
1094
+ ? loadIndexFromDir(indexOutputDir, 'metadata.json')
1095
+ : loadGraphIndexFile(effectiveOutputCwd, 'metadata.json', graph);
1096
+ const canReuseExisting = existingIndex?.version === INDEX_VERSION
1097
+ && existingIndex.extractorVersion === INDEX_EXTRACTOR_VERSION && existingIndex.buildConfigHash === buildConfigHash;
1098
+ const existingEntries = canReuseExisting ? existingIndex.entries : {};
1099
+ const manifest = canReuseExisting ? storedManifest : { version: 1, generated: '', entries: {} };
1100
+ // Establish the complete changed-file batch before any extraction/reduction.
1101
+ // Warm stat hits never create threads or read source contents.
1102
+ const preparationStats = new Map();
1103
+ const jobs = [];
1104
+ if (!useFilenameMetadata)
1105
+ for (const fullPath of files) {
1106
+ let stat;
1107
+ try {
1108
+ stat = fileStats?.get(fullPath) ?? fs.statSync(fullPath);
1109
+ }
1110
+ catch (error) {
1111
+ if (error.code === 'ENOENT')
1112
+ continue;
1113
+ throw error;
1114
+ }
1115
+ preparationStats.set(fullPath, stat);
1116
+ const relativePath = indexPathFor(cwd, fullPath, graph), previous = manifest.entries[relativePath];
1117
+ if (!force && statMatches(stat, previous) && existingEntries[relativePath]?.checksum === previous?.checksum)
1118
+ continue;
1119
+ jobs.push({ fullPath, relativePath, mtimeMs: stat.mtimeMs, birthtimeMs: stat.birthtimeMs, sizeBytes: stat.size,
1120
+ ...(!force && existingEntries[relativePath] ? { existingChecksum: existingEntries[relativePath].checksum } : {}) });
1121
+ }
1122
+ const preparation = await runIndexPreparation(jobs, prepareIndexArtifact, import.meta.url, options.workers);
1123
+ const preparedArtifacts = new Map(jobs.map((job, index) => [job.fullPath, preparation.results[index]]));
930
1124
  for (const fullPath of files) {
931
1125
  const relativePath = indexPathFor(cwd, fullPath, graph);
932
1126
  let entry;
@@ -944,7 +1138,7 @@ export async function buildIndex(cwd, options = {}) {
944
1138
  entries[relativePath] = existingEntries[relativePath];
945
1139
  unchangedCount++;
946
1140
  if (verbose)
947
- console.log(` unchanged: ${relativePath}`);
1141
+ log(` unchanged: ${relativePath}`);
948
1142
  continue;
949
1143
  }
950
1144
  entry = buildFilenameMetadataEntry(relativePath, fullPath, graphConfig?.filenamePattern);
@@ -956,13 +1150,13 @@ export async function buildIndex(cwd, options = {}) {
956
1150
  // This is the fast path: unchanged files don't touch the filesystem beyond fs.statSync.
957
1151
  let stat;
958
1152
  try {
959
- stat = fs.statSync(fullPath);
1153
+ stat = preparationStats.get(fullPath) ?? fs.statSync(fullPath);
960
1154
  }
961
1155
  catch (err) {
962
1156
  if (err.code === 'ENOENT') {
963
1157
  manifestStats.pruned++;
964
1158
  if (verbose)
965
- console.log(` skipped missing: ${relativePath}`);
1159
+ log(` skipped missing: ${relativePath}`);
966
1160
  continue;
967
1161
  }
968
1162
  throw err;
@@ -976,98 +1170,70 @@ export async function buildIndex(cwd, options = {}) {
976
1170
  unchangedCount++;
977
1171
  manifestStats.statSkipped++;
978
1172
  if (verbose)
979
- console.log(` unchanged (stat): ${relativePath}`);
1173
+ log(` unchanged (stat): ${relativePath}`);
980
1174
  continue;
981
1175
  }
982
- // Phase 2: content read + checksum verification for files that looked changed.
983
- const content = fs.readFileSync(fullPath, 'utf-8');
984
- const checksum = computeChecksum(content);
985
- // Skip unchanged files in incremental mode (checksum matched despite stat drift)
1176
+ const prepared = preparedArtifacts.get(fullPath);
1177
+ const { checksum } = prepared;
986
1178
  if (!force && existingEntries[relativePath]?.checksum === checksum) {
987
1179
  entries[relativePath] = existingEntries[relativePath];
988
- // Update manifest with current stat so Phase 1 succeeds next time.
989
1180
  nextManifestEntries[relativePath] = makeEntry(checksum, stat);
990
1181
  unchangedCount++;
991
1182
  manifestStats.checksumSkipped++;
992
1183
  if (verbose)
993
- console.log(` unchanged (checksum): ${relativePath}`);
1184
+ log(` unchanged (checksum): ${relativePath}`);
994
1185
  continue;
995
1186
  }
996
1187
  manifestStats.reindexed++;
997
1188
  nextManifestEntries[relativePath] = makeEntry(checksum, stat);
998
- const { data, body } = parseFrontmatter(content);
999
- // YAML Flow documents (workflow.aiwg.io/v1) are pure YAML, not
1000
- // markdown+frontmatter — detect them up front so they classify and
1001
- // become discoverable (#1540).
1002
- const flow = parseFlowDoc(content, relativePath);
1003
- const decision = flow ? null : parseDecisionDoc(content, relativePath);
1004
- const gatePack = flow || decision ? null : parseGatePackDoc(content, relativePath);
1005
- const inferredType = inferType(data, relativePath);
1006
- const physicalType = inferType({ ...data, type: undefined }, relativePath);
1007
- const runbook = flow || decision || gatePack ? null : parseRunbookDoc(data, body, relativePath);
1008
- const schemaDoc = inferredType === 'schema' ? parseSchemaDoc(content, relativePath) : null;
1009
- const title = flow?.name ?? decision?.name ?? gatePack?.name ?? schemaDoc?.title ?? extractTitle(data, body);
1010
- const phase = typeof data.phase === 'string' ? data.phase : inferPhase(relativePath);
1011
- const type = flow?.type ?? decision?.type ?? gatePack?.type ?? (runbook ? 'runbook' : inferredType);
1012
- const tags = flow?.tags ?? decision?.tags ?? gatePack?.tags ?? (Array.isArray(data.tags) ? data.tags.map(String) : []);
1013
- const summary = flow?.description ?? decision?.description ?? gatePack?.description ?? schemaDoc?.capability ?? runbook?.capability ?? extractSummary(data, body);
1014
- const dependencies = extractMentions(content);
1015
- const markdownLinks = extractMarkdownLinks(content);
1016
- // Discovery metadata (#1214, #1540, #1792) — meaningful for operational
1017
- // AIWG artifact kinds. Kept undefined on document types so the index file
1018
- // stays small for the common case.
1019
- const isDiscoverable = OPERATIONAL_DISCOVERY_TYPES.includes(type);
1020
- // Declarative processes have no trigger phrases — they rely on their
1021
- // capability and structure-aware search terms.
1022
- const triggers = isDiscoverable && !flow ? extractTriggers(body, data) : undefined;
1023
- const capability = flow?.capability ?? decision?.capability ?? gatePack?.capability ?? schemaDoc?.capability ?? runbook?.capability ?? (isDiscoverable ? extractCapability(data, body) : undefined);
1024
- const kind = flow?.kind ?? decision?.kind ?? gatePack?.kind ?? runbook?.kind;
1025
- const sourceType = runbook && physicalType !== 'runbook' ? physicalType : undefined;
1026
- const searchTerms = flow?.searchTerms ?? decision?.searchTerms ?? gatePack?.searchTerms ?? schemaDoc?.searchTerms ?? runbook?.searchTerms;
1027
- const kernel = data.kernel === true || data.kernel === 'true' ? true : undefined;
1028
- // Script entrypoint metadata is meaningful for skills only (#1227).
1029
- const script = type === 'skill' ? extractSkillScript(data) : undefined;
1030
- const operationalState = normalizeOperationalState(data.operational_state);
1031
- const stateTransfer = normalizeStateTransferProjection(data.state_transfer);
1032
- // Canonical short name (#1233) — used by the scorer to floor exact-name
1033
- // queries to 1.0 so hyphenated kernel-skill names like `aiwg-doctor`
1034
- // remain searchable even when the rendered title strips the hyphen.
1035
- const name = flow?.name ?? decision?.name ?? gatePack?.name ?? schemaDoc?.name ?? (isDiscoverable ? extractCanonicalName(data, relativePath) : undefined);
1036
- entry = {
1037
- path: relativePath,
1038
- type,
1039
- ...(kind ? { kind } : {}),
1040
- ...(sourceType ? { sourceType } : {}),
1041
- phase,
1042
- title,
1043
- tags,
1044
- created: typeof data.created === 'string' ? data.created : stat.birthtime.toISOString(),
1045
- updated: stat.mtime.toISOString(),
1046
- checksum,
1047
- summary,
1048
- dependencies,
1049
- ...(markdownLinks.length > 0 ? { markdownLinks } : {}),
1050
- dependents: [], // Computed after all entries are processed
1051
- ...(name ? { name } : {}),
1052
- ...(triggers && triggers.length > 0 ? { triggers } : {}),
1053
- ...(capability ? { capability } : {}),
1054
- ...(searchTerms && searchTerms.length > 0 ? { searchTerms } : {}),
1055
- ...(kernel ? { kernel } : {}),
1056
- ...(script ? { script } : {}),
1057
- ...(operationalState ? { operationalState } : {}),
1058
- ...(stateTransfer ? { stateTransfer } : {}),
1059
- };
1189
+ entry = prepared.entry;
1060
1190
  }
1061
1191
  entries[relativePath] = entry;
1062
1192
  if (existingEntries[relativePath]) {
1063
1193
  updatedCount++;
1064
1194
  if (verbose)
1065
- console.log(` updated: ${relativePath}`);
1195
+ log(` updated: ${relativePath}`);
1066
1196
  }
1067
1197
  else {
1068
1198
  newCount++;
1069
1199
  if (verbose)
1070
- console.log(` new: ${relativePath}`);
1200
+ log(` new: ${relativePath}`);
1201
+ }
1202
+ }
1203
+ // Reuse derived indexes only when their complete input set is unchanged.
1204
+ // Supplements can live outside the scanned files, so they must still be read.
1205
+ // Configured edge extractors must run to discard stale cached edges even
1206
+ // when source checksums match.
1207
+ const total = Object.keys(entries).length;
1208
+ if (canReuseExisting && !useFilenameMetadata && !graphConfig?.metadataSupplements?.length
1209
+ && !graphConfig?.edgeExtraction
1210
+ && newCount === 0 && updatedCount === 0
1211
+ && total === Object.keys(existingEntries).length
1212
+ && manifest.generated && total === Object.keys(manifest.entries).length
1213
+ && manifestStats.pruned === 0) {
1214
+ const stats = loadIndexFromDir(indexOutputDir, 'stats.json');
1215
+ if (stats?.version === INDEX_VERSION && stats.extractorVersion === INDEX_EXTRACTOR_VERSION
1216
+ && stats.totalArtifacts === total
1217
+ && requiredFiles.every(filename => fs.existsSync(path.join(indexOutputDir, filename)))) {
1218
+ // A timestamp-only source change needs a manifest refresh, but does not
1219
+ // invalidate metadata, tags, dependencies, or the persistent backend.
1220
+ const projectionNeedsRepair = graph === 'framework' && !relationsReceiptMatches();
1221
+ if (graph === 'framework') {
1222
+ const { writeRelations } = await import('./relations-builder.js');
1223
+ await writeRelations(entries, effectiveOutputCwd, indexOutputDir, force || !relationsReceiptMatches());
1224
+ }
1225
+ const buildTimeMs = Date.now() - startTime;
1226
+ writeIndexFile(effectiveOutputCwd, 'stats.json', {
1227
+ ...stats,
1228
+ builtAt: new Date().toISOString(),
1229
+ buildTimeMs,
1230
+ }, indexOutputDir);
1231
+ // Preserve no-op manifest bytes after output-only timestamp drift.
1232
+ // Renew receipts only for changed source stats or repaired projections.
1233
+ if (manifestStats.checksumSkipped > 0 || projectionNeedsRepair)
1234
+ writeBuildManifest(nextManifestEntries);
1235
+ reportBuild(buildTimeMs, total);
1236
+ return;
1071
1237
  }
1072
1238
  }
1073
1239
  // Apply metadata supplements if configured (enriches filename-metadata nodes from sidecars)
@@ -1084,13 +1250,18 @@ export async function buildIndex(cwd, options = {}) {
1084
1250
  }
1085
1251
  // Build dependency graph and compute dependents
1086
1252
  let markdownLinkEdgeCount = 0;
1253
+ const entryPaths = Object.keys(entries);
1254
+ const dependencyPaths = new Map();
1087
1255
  for (const entry of Object.values(entries)) {
1088
1256
  if (!depGraph[entry.path]) {
1089
1257
  depGraph[entry.path] = { upstream: [], downstream: [] };
1090
1258
  }
1091
1259
  for (const dep of entry.dependencies) {
1092
1260
  // Normalize: check if referenced path exists in the index
1093
- const normalizedDep = Object.keys(entries).find(p => p === dep || p.endsWith(dep));
1261
+ if (!dependencyPaths.has(dep)) {
1262
+ dependencyPaths.set(dep, entryPaths.find(p => p === dep || p.endsWith(dep)));
1263
+ }
1264
+ const normalizedDep = dependencyPaths.get(dep);
1094
1265
  if (normalizedDep && normalizedDep !== entry.path) {
1095
1266
  addDependencyEdge(depGraph, entries, entry.path, normalizedDep, 'depends-on');
1096
1267
  }
@@ -1173,18 +1344,23 @@ export async function buildIndex(cwd, options = {}) {
1173
1344
  .filter(edge => !canonicalOutgoing.has(edge)).length,
1174
1345
  };
1175
1346
  if (verbose && citationEdgeCount > 0) {
1176
- console.log(` citation edges: ${citationEdgeCount}`);
1347
+ log(` citation edges: ${citationEdgeCount}`);
1177
1348
  }
1178
1349
  }
1179
1350
  // Deduplicate dependents
1180
1351
  for (const entry of Object.values(entries)) {
1181
1352
  entry.dependents = [...new Set(entry.dependents)];
1182
1353
  }
1354
+ if (graph === 'framework') {
1355
+ const { writeRelations } = await import('./relations-builder.js');
1356
+ await writeRelations(entries, effectiveOutputCwd, indexOutputDir, force || !relationsReceiptMatches());
1357
+ }
1183
1358
  const buildTimeMs = Date.now() - startTime;
1184
1359
  // Write index files
1185
1360
  const index = {
1186
1361
  version: INDEX_VERSION,
1187
1362
  extractorVersion: INDEX_EXTRACTOR_VERSION,
1363
+ buildConfigHash,
1188
1364
  builtAt: new Date().toISOString(),
1189
1365
  buildTimeMs,
1190
1366
  entries,
@@ -1195,8 +1371,6 @@ export async function buildIndex(cwd, options = {}) {
1195
1371
  // Materialize the configured backend and always retain dependencies.json as
1196
1372
  // the stable compatibility/export contract.
1197
1373
  const { createGraphBackend } = await import('./graph-backend.js');
1198
- const { resolveGraphBackendType } = await import('./types.js');
1199
- const backendType = resolveGraphBackendType(graph);
1200
1374
  const persistentPath = backendType === 'sqlite' ? path.join(indexOutputDir, 'graph.db') : undefined;
1201
1375
  let selectedBackend;
1202
1376
  try {
@@ -1210,15 +1384,9 @@ export async function buildIndex(cwd, options = {}) {
1210
1384
  // The next manifest contains entries for every file we processed this build.
1211
1385
  // Files that disappeared from disk are pruned; the resulting manifest is
1212
1386
  // written atomically.
1213
- const nextManifest = {
1214
- version: 1,
1215
- generated: new Date().toISOString(),
1216
- entries: nextManifestEntries,
1217
- };
1218
1387
  manifestStats.pruned = Object.keys(manifest.entries).length - Object.keys(nextManifestEntries).length;
1219
1388
  if (manifestStats.pruned < 0)
1220
1389
  manifestStats.pruned = 0;
1221
- writeManifest(indexOutputDir, nextManifest);
1222
1390
  // Write stats
1223
1391
  const downstreamEdges = Object.values(depGraph).reduce((sum, node) => sum + node.downstream.length, 0);
1224
1392
  const adjacencyEntries = Object.values(depGraph).reduce((sum, node) => sum + node.upstream.length + node.downstream.length, 0);
@@ -1262,22 +1430,8 @@ export async function buildIndex(cwd, options = {}) {
1262
1430
  mostReferenced,
1263
1431
  },
1264
1432
  }, indexOutputDir);
1433
+ writeBuildManifest(nextManifestEntries);
1265
1434
  // Report
1266
- const total = Object.keys(entries).length;
1267
- console.log(`Artifact index built in ${buildTimeMs}ms`);
1268
- console.log(` Indexed ${newCount} new, updated ${updatedCount}, unchanged ${unchangedCount}`);
1269
- console.log(` Total: ${total} artifacts`);
1270
- // Report manifest-driven change detection stats (only meaningful when !force
1271
- // and we actually went through the content-read path for at least one file)
1272
- if (!force && manifestStats.checked > 0) {
1273
- const fastPath = manifestStats.statSkipped;
1274
- const slowPath = manifestStats.checked - manifestStats.statSkipped;
1275
- console.log(` Change detection: ${manifestStats.checked} checked, ${fastPath} skipped via stat, ${slowPath} content-read, ${manifestStats.reindexed} re-indexed`);
1276
- if (manifestStats.pruned > 0) {
1277
- console.log(` Pruned ${manifestStats.pruned} stale manifest entries (files no longer on disk)`);
1278
- }
1279
- }
1280
- const displayDir = graph ? indexOutputDir : `${INDEX_DIR}/`;
1281
- console.log(` Output: ${displayDir}`);
1435
+ reportBuild(buildTimeMs, total);
1282
1436
  }
1283
1437
  //# sourceMappingURL=index-builder.js.map