@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
@@ -8,10 +8,14 @@
8
8
  * @source @src/artifacts/types.ts
9
9
  * @tests @test/unit/artifacts/query-engine.test.ts
10
10
  */
11
+ import { refreshFrameworkCapabilities, resolveFrameworkCorpusRoot, isCorpusBundleInstalled } from './framework-capabilities.js';
12
+ import { resolveFrameworkIndexRoot } from './framework-index-paths.js';
13
+ import { ensureRelations, selectPrebuiltRelations, emptyRuleStats } from './relations.js';
11
14
  import { minimatch } from 'minimatch';
15
+ import { inferRuleActivation } from '../rules/activation.mjs';
12
16
  import * as fs from 'node:fs';
13
17
  import * as path from 'node:path';
14
- import { OPERATIONAL_DISCOVERY_TYPES, OPERATIONAL_SHOW_TYPES, isOperationalShowType, loadGlobalGraphConfigs, } from './types.js';
18
+ import { OPERATIONAL_DISCOVERY_TYPES, OPERATIONAL_SHOW_TYPES, isOperationalShowType, loadGlobalGraphConfigs, getGraphIndexDir, } from './types.js';
15
19
  import { loadMetadataIndex, loadGraphIndexFile } from './index-reader.js';
16
20
  import { bm25Rank } from './fulltext.js';
17
21
  import { parseFrontmatter } from './index-builder.js';
@@ -859,21 +863,6 @@ function showHintForDiscoverResult(entry, id) {
859
863
  }
860
864
  return `aiwg show ${id}`;
861
865
  }
862
- async function getAiwgRootForDiscover() {
863
- if (process.env.AIWG_ROOT)
864
- return process.env.AIWG_ROOT;
865
- try {
866
- const mod = await import('../channel/manager.mjs');
867
- if (typeof mod.getFrameworkRoot === 'function') {
868
- const r = await mod.getFrameworkRoot();
869
- return r || null;
870
- }
871
- }
872
- catch {
873
- // channel manager unavailable — fall through
874
- }
875
- return null;
876
- }
877
866
  export async function discoverCapability(cwd, params) {
878
867
  loadGlobalGraphConfigs();
879
868
  const startTime = Date.now();
@@ -888,7 +877,7 @@ export async function discoverCapability(cwd, params) {
888
877
  const limit = params.limit ?? 5;
889
878
  // For framework-graph entries, anchor returned paths to AIWG_ROOT so
890
879
  // they resolve from any project working directory (#1217).
891
- const aiwgRoot = await getAiwgRootForDiscover();
880
+ const aiwgRoot = resolveFrameworkIndexRoot(cwd);
892
881
  // Source: prefer `framework` graph (built post-deploy), fall back to
893
882
  // project / codebase / legacy depending on what's available.
894
883
  let entries = [];
@@ -897,10 +886,24 @@ export async function discoverCapability(cwd, params) {
897
886
  let resourceSource = params.resourceSource ?? 'local';
898
887
  let resolvedWebRelease;
899
888
  let verifiedSourcePaths;
889
+ const prebuiltRelations = resourceSource !== 'web' && (!params.graph || params.graph === 'framework')
890
+ ? selectPrebuiltRelations(cwd).reader : null;
891
+ if ((!prebuiltRelations || backend === 'local') && resourceSource !== 'web' && (!params.graph || params.graph === 'framework')) {
892
+ const root = resolveFrameworkCorpusRoot(cwd) ?? aiwgRoot;
893
+ if (root) {
894
+ try {
895
+ await refreshFrameworkCapabilities(root, backend === 'fortemi-core');
896
+ }
897
+ catch (error) {
898
+ console.error(`Warning: capability index refresh failed: ${error instanceof Error ? error.message : String(error)}`);
899
+ }
900
+ }
901
+ }
900
902
  if (resourceSource === 'auto') {
901
903
  const { loadFortemiCoreMetadataEntries } = await import('./fortemi-core-query-adapter.js');
902
904
  const localGraphs = params.graph ? [params.graph] : defaultCapabilityGraphs(cwd);
903
- const localAvailable = localGraphs.some((graph) => loadFortemiCoreMetadataEntries(cwd, graph).entries.length > 0);
905
+ const localAvailable = localGraphs.some((graph) => loadFortemiCoreMetadataEntries(cwd, graph).entries.length > 0
906
+ || Object.keys(loadGraphIndexFile(cwd, 'metadata.json', graph)?.entries ?? {}).length > 0);
904
907
  resourceSource = localAvailable ? 'local' : 'web';
905
908
  }
906
909
  if (resourceSource === 'web') {
@@ -949,6 +952,16 @@ export async function discoverCapability(cwd, params) {
949
952
  continue;
950
953
  }
951
954
  }
955
+ if (loaded.sourceIndexNewer && resourceSource === 'local') {
956
+ // Do not silently omit a graph whose derived cache is stale. Rank its
957
+ // current source metadata, without consulting scores from the old cache.
958
+ const index = loadGraphIndexFile(cwd, 'metadata.json', graph);
959
+ if (index) {
960
+ console.error(`Warning: ${graph} capability cache is unavailable or stale; using the local ${graph} index. ${loaded.reason}`);
961
+ entries.push(...Object.values(index.entries).map((entry) => withIndexProvenance(entry, graph)));
962
+ continue;
963
+ }
964
+ }
952
965
  if (loaded.reason) {
953
966
  unavailableReason ??= loaded.reason;
954
967
  if (graph === 'framework' && !params.graph) {
@@ -992,6 +1005,7 @@ export async function discoverCapability(cwd, params) {
992
1005
  : {}),
993
1006
  },
994
1007
  results: [],
1008
+ rule_stats: emptyRuleStats(),
995
1009
  total: 0,
996
1010
  hint: unavailableReason,
997
1011
  }, null, 2));
@@ -1011,47 +1025,10 @@ export async function discoverCapability(cwd, params) {
1011
1025
  // discover is EXCLUSIVELY the agentic-capability surface (#1545): source
1012
1026
  // the `framework` graph (deployed AIWG capabilities) + project-local
1013
1027
  // capability artifacts. NEVER `codebase` — source code is not a capability
1014
- // surface. discover leverages indexing transparently: if the framework
1015
- // index is missing but its source is present, build it once (incremental
1016
- // thereafter) so discover Just Works without the operator picking a graph.
1017
- let fwIdx = loadGraphIndexFile(cwd, 'metadata.json', 'framework');
1018
- if (!fwIdx) {
1019
- // The framework graph is shared (XDG) and `defaultBuild: false`, so a bare
1020
- // `aiwg index build` never (re)builds it — only `aiwg use` or an explicit
1021
- // `--graph framework` does. When it's absent, build it from wherever the
1022
- // AIWG source lives: `cwd/agentic/code` when inside the AIWG repo, else
1023
- // `$AIWG_ROOT/agentic/code` for a USER project (#1541 — opencode users sit
1024
- // in their own project, not the AIWG repo, so a cwd-only check left
1025
- // discover unable to find deployed commands like address-issues). buildIndex
1026
- // scans <root>/agentic/code and writes to the cwd-independent XDG dir, so
1027
- // reading back via cwd still resolves the freshly-built shared index.
1028
- const buildRoot = fs.existsSync(path.join(cwd, 'agentic', 'code'))
1029
- ? cwd
1030
- : aiwgRoot && fs.existsSync(path.join(aiwgRoot, 'agentic', 'code'))
1031
- ? aiwgRoot
1032
- : null;
1033
- if (buildRoot) {
1034
- const { buildIndex } = await import('./index-builder.js');
1035
- if (!params.json)
1036
- console.error('Building capability index (first run)…');
1037
- // buildIndex writes progress to stdout. In --json mode that would corrupt
1038
- // the JSON envelope the caller parses, so silence stdout for the duration
1039
- // of the auto-build (the build is incidental to the query).
1040
- const origLog = console.log;
1041
- if (params.json)
1042
- console.log = () => { };
1043
- try {
1044
- await buildIndex(buildRoot, { graph: 'framework', explicit: false });
1045
- }
1046
- catch {
1047
- /* non-fatal — fall through to whatever index is available */
1048
- }
1049
- finally {
1050
- console.log = origLog;
1051
- }
1052
- fwIdx = loadGraphIndexFile(cwd, 'metadata.json', 'framework');
1053
- }
1054
- }
1028
+ // surface. The framework
1029
+ // index is reconciled with its source above, including existing indexes
1030
+ // left behind by an older runtime or a different checkout.
1031
+ const fwIdx = loadGraphIndexFile(cwd, 'metadata.json', 'framework');
1055
1032
  const includeUser = projectAllowsUserIndices(cwd);
1056
1033
  // Project-local operational artifacts authored in this project. The
1057
1034
  // DEFAULT_DISCOVER_TYPES filter keeps non-capability project artifacts
@@ -1082,6 +1059,7 @@ export async function discoverCapability(cwd, params) {
1082
1059
  console.log(JSON.stringify({
1083
1060
  query: { phrase: params.phrase, types },
1084
1061
  results: [],
1062
+ rule_stats: emptyRuleStats(),
1085
1063
  total: 0,
1086
1064
  hint,
1087
1065
  }, null, 2));
@@ -1191,6 +1169,16 @@ export async function discoverCapability(cwd, params) {
1191
1169
  ? `No capability matched "${params.phrase}" among ${entries.length} Fortemi Core static-cache capabilities. Try a broader phrase or a \`--type\` filter. If the local index changed, refresh the cache with \`aiwg index sync ${params.graph ? `--graph ${params.graph}` : '--all'}\`, or pass \`--backend local\` to use the legacy local index.`
1192
1170
  : `No capability matched "${params.phrase}" among ${entries.length} indexed capabilities. Try a broader phrase or a \`--type\` filter. If you just installed or authored capabilities, refresh discovery with \`aiwg use <framework>\` (or \`aiwg index build\`).`
1193
1171
  : null;
1172
+ const relations = resourceSource === 'local' && (!params.graph || params.graph === 'framework')
1173
+ ? prebuiltRelations ?? await ensureRelations(cwd) : undefined;
1174
+ const governing = scored.map(result => compoundGoverningRules(entries, result.entry, relations ?? undefined, params.phrase));
1175
+ const responseRules = new Map();
1176
+ for (const item of governing.flat()) {
1177
+ const key = `${item.rule.type}:${item.rule.path}`, prior = responseRules.get(key);
1178
+ responseRules.set(key, { rule: item.rule, applicability: [...new Set([...(prior?.applicability ?? []), ...item.applicability])] });
1179
+ }
1180
+ const responseFields = governingRuleFields([...responseRules.values()]);
1181
+ const responseRuleStats = governingRuleStats([...responseRules.values()]);
1194
1182
  const includePaths = params.includePaths ?? true;
1195
1183
  const facetActivations = diagnoseFacetActivations(params.phrase);
1196
1184
  const roundDiagnostic = (value) => Math.round(value * 1_000_000) / 1_000_000;
@@ -1228,6 +1216,8 @@ export async function discoverCapability(cwd, params) {
1228
1216
  },
1229
1217
  };
1230
1218
  };
1219
+ const { loadRelations } = await import('./relations.js');
1220
+ const ruleRelations = resourceSource === 'local' ? loadRelations(cwd) ?? (aiwgRoot ? loadRelations(aiwgRoot, cwd) : null) : null;
1231
1221
  if (params.json) {
1232
1222
  const jsonIndent = params.jsonPretty === false ? undefined : 2;
1233
1223
  console.log(JSON.stringify({
@@ -1250,7 +1240,7 @@ export async function discoverCapability(cwd, params) {
1250
1240
  }
1251
1241
  : {}),
1252
1242
  },
1253
- results: scored.map(r => ({
1243
+ results: scored.map((r, resultIndex) => ({
1254
1244
  id: discoveryIdForEntry(r.entry),
1255
1245
  ...(includePaths ? { path: resolvePath(r.entry) } : {}),
1256
1246
  type: r.entry.type,
@@ -1258,9 +1248,14 @@ export async function discoverCapability(cwd, params) {
1258
1248
  title: r.entry.title,
1259
1249
  score: Math.round(r.score * 100) / 100,
1260
1250
  ranking: rankingDiagnostic(r.entry, r.score, r.facetDiagnostics),
1251
+ ...governingRuleFields(governing[resultIndex]),
1261
1252
  triggers: r.entry.triggers ?? [],
1262
1253
  capability: r.entry.capability ?? r.entry.summary,
1263
1254
  kernel: r.entry.kernel ?? false,
1255
+ ...(['skill', 'agent', 'flow', 'command'].includes(r.entry.type) && ruleRelations
1256
+ && ruleRelations.rulesForTarget(r.entry.type, discoveryIdForEntry(r.entry)).length ? {
1257
+ startup_rule_stats: ruleRelations.ruleStats(r.entry.type, discoveryIdForEntry(r.entry)),
1258
+ } : {}),
1264
1259
  provenance: {
1265
1260
  graph: r.entry.indexGraph ?? null,
1266
1261
  scope: r.entry.indexScope ?? null,
@@ -1275,6 +1270,8 @@ export async function discoverCapability(cwd, params) {
1275
1270
  }
1276
1271
  : {}),
1277
1272
  })),
1273
+ rule_stats: responseRuleStats,
1274
+ ...(responseFields.required_rule_checks ? { required_rule_checks: responseFields.required_rule_checks, rule_check_obligation: RULE_CHECK_OBLIGATION } : {}),
1278
1275
  total: scored.length,
1279
1276
  query_time_ms: queryTimeMs,
1280
1277
  diagnostics: {
@@ -1296,6 +1293,7 @@ export async function discoverCapability(cwd, params) {
1296
1293
  }
1297
1294
  const relaxedNote = relaxed ? ' — relaxed match (verbose query)' : '';
1298
1295
  console.log(`Discovery results for "${params.phrase}" (${scored.length} matches, ${queryTimeMs}ms)${relaxedNote}:`);
1296
+ console.log(statsText(responseRuleStats));
1299
1297
  console.log('');
1300
1298
  for (const diagnostic of facetActivations) {
1301
1299
  if (diagnostic.status === 'suppressed' && diagnostic.reason) {
@@ -1339,6 +1337,12 @@ export async function discoverCapability(cwd, params) {
1339
1337
  `scope ${ranking.tie_breakers.scope}`,
1340
1338
  ].filter((part) => Boolean(part));
1341
1339
  console.log(` ranking: ${rankingParts.join('; ')}`);
1340
+ if (governing[i].length) {
1341
+ console.log(` ${statsText(governingRuleStats(governing[i]))}`);
1342
+ console.log(` ${RULE_CHECK_OBLIGATION}`);
1343
+ for (const { rule, applicability } of governing[i])
1344
+ console.log(` - ${rule.name ?? rule.path}: ${rule.ruleSummary ?? ''} (${applicability.join('; ')}) — \`${boundRuleDescriptor(rule).show_command}\``);
1345
+ }
1342
1346
  if (r.entry.script) {
1343
1347
  console.log(` run: ${buildRunHint(r.entry)}`);
1344
1348
  }
@@ -1364,11 +1368,37 @@ function resolveMetadataPath(cwd, aiwgRoot, entry) {
1364
1368
  }
1365
1369
  return path.join(cwd, normalizedPath);
1366
1370
  }
1367
- async function loadShowEntries(cwd, params) {
1368
- const aiwgRoot = await getAiwgRootForDiscover();
1371
+ async function loadShowEntries(cwd, params, compact = false) {
1372
+ const aiwgRoot = resolveFrameworkIndexRoot(cwd);
1369
1373
  const backend = params.backend ?? DEFAULT_ARTIFACT_SEARCH_BACKEND;
1370
1374
  let resourceSource = params.resourceSource ?? 'local';
1371
1375
  let entries = [];
1376
+ let relations;
1377
+ if (compact && resourceSource !== 'web' && (!params.graph || params.graph === 'framework')) {
1378
+ relations = await ensureRelations(cwd) ?? undefined;
1379
+ if (relations?.index.artifacts) {
1380
+ const frameworkEntries = Object.values(relations.index.artifacts).map(entry => withIndexProvenance({
1381
+ phase: '', tags: [], created: '', updated: '', checksum: '', summary: '', dependencies: [], dependents: [], ...entry,
1382
+ }, 'framework'));
1383
+ const localEntries = params.graph ? [] : defaultCapabilityGraphs(cwd).filter(graph => graph !== 'framework')
1384
+ .flatMap(graph => loadLocalCapabilityEntries(cwd, graph));
1385
+ const compactEntries = [...localEntries, ...frameworkEntries];
1386
+ if (findShowMatches(compactEntries, params.typeFilter ?? [...OPERATIONAL_SHOW_TYPES], params.name.trim()).length) {
1387
+ return { entries: compactEntries, aiwgRoot, resourceSource: 'local', relations };
1388
+ }
1389
+ }
1390
+ }
1391
+ if (resourceSource !== 'web' && (!params.graph || params.graph === 'framework')) {
1392
+ const root = resolveFrameworkCorpusRoot(cwd) ?? aiwgRoot;
1393
+ if (root) {
1394
+ try {
1395
+ await refreshFrameworkCapabilities(root, backend === 'fortemi-core' && params.backend !== undefined);
1396
+ }
1397
+ catch (error) {
1398
+ console.error(`Warning: capability index refresh failed: ${error instanceof Error ? error.message : String(error)}`);
1399
+ }
1400
+ }
1401
+ }
1372
1402
  if (resourceSource === 'auto' && backend === 'fortemi-core') {
1373
1403
  const { loadFortemiCoreMetadataEntries } = await import('./fortemi-core-query-adapter.js');
1374
1404
  const localGraphs = params.graph ? [params.graph] : defaultCapabilityGraphs(cwd);
@@ -1451,24 +1481,59 @@ async function loadShowEntries(cwd, params) {
1451
1481
  process.exit(1);
1452
1482
  }
1453
1483
  }
1454
- else if (params.graph) {
1455
- const idx = loadGraphIndexFile(cwd, 'metadata.json', params.graph);
1456
- if (idx)
1457
- entries = Object.values(idx.entries).map((entry) => withIndexProvenance(entry, params.graph));
1458
- }
1459
1484
  else {
1460
- for (const g of defaultCapabilityGraphs(cwd)) {
1461
- const idx = loadGraphIndexFile(cwd, 'metadata.json', g);
1462
- if (idx)
1463
- entries.push(...Object.values(idx.entries).map((entry) => withIndexProvenance(entry, g)));
1485
+ entries = loadLocalCapabilityEntries(cwd, params.graph);
1486
+ }
1487
+ return { entries, aiwgRoot, resourceSource: 'local', relations };
1488
+ }
1489
+ /** Local index inputs shared by show and exact rule-binding validation (#2938). */
1490
+ export function loadLocalCapabilityEntries(cwd, graph, snapshot) {
1491
+ const entries = [];
1492
+ for (const selectedGraph of graph ? [graph] : defaultCapabilityGraphs(cwd)) {
1493
+ const key = snapshot && `${selectedGraph}:${getGraphIndexDir(cwd, selectedGraph)}`;
1494
+ let selected = key ? snapshot.get(key) : undefined;
1495
+ if (!selected) {
1496
+ const index = loadGraphIndexFile(cwd, 'metadata.json', selectedGraph);
1497
+ selected = index ? Object.values(index.entries).map(entry => withIndexProvenance(entry, selectedGraph)) : [];
1498
+ if (key)
1499
+ snapshot.set(key, selected);
1500
+ }
1501
+ entries.push(...selected);
1502
+ }
1503
+ if (!graph && entries.length === 0) {
1504
+ const legacy = loadMetadataIndex(cwd);
1505
+ if (legacy)
1506
+ entries.push(...Object.values(legacy.entries));
1507
+ }
1508
+ return entries;
1509
+ }
1510
+ /** Project/user inputs only; package artifacts come from a validated compact table. */
1511
+ export function loadLocalOverrideCapabilityEntries(cwd) {
1512
+ const local = defaultCapabilityGraphs(cwd).filter(graph => graph !== 'framework')
1513
+ .flatMap(graph => loadLocalCapabilityEntries(cwd, graph));
1514
+ if (local.length)
1515
+ return local;
1516
+ const canonical = (directory) => {
1517
+ try {
1518
+ return fs.realpathSync(directory);
1464
1519
  }
1465
- if (entries.length === 0) {
1466
- const legacy = loadMetadataIndex(cwd);
1467
- if (legacy)
1468
- entries.push(...Object.values(legacy.entries));
1520
+ catch {
1521
+ return path.resolve(directory);
1469
1522
  }
1470
- }
1471
- return { entries, aiwgRoot, resourceSource: 'local' };
1523
+ };
1524
+ const packageRoot = canonical(resolveFrameworkIndexRoot(cwd));
1525
+ // Retain the legacy PROJECT index, never use the package's full index as a fallback.
1526
+ if (canonical(cwd) === packageRoot)
1527
+ return [];
1528
+ const legacy = loadMetadataIndex(cwd);
1529
+ return Object.values(legacy?.entries ?? {}).filter(entry => {
1530
+ const provenance = entry;
1531
+ if (provenance.indexGraph === 'framework' || provenance.indexScope === 'packaged'
1532
+ || normalizeIndexedPath(entry.path).startsWith('agentic/code/'))
1533
+ return false;
1534
+ const relative = path.relative(packageRoot, path.resolve(cwd, entry.path));
1535
+ return relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative);
1536
+ }).map(entry => withIndexProvenance(entry, 'project'));
1472
1537
  }
1473
1538
  function findShowMatches(entries, types, needle) {
1474
1539
  const needleLower = needle.toLowerCase();
@@ -1538,6 +1603,57 @@ function findShowMatches(entries, types, needle) {
1538
1603
  }
1539
1604
  return matches;
1540
1605
  }
1606
+ /** Preselect exact candidates once; the canonical resolver still owns precedence and ambiguity. */
1607
+ export function createRuleBindingTargetResolver(entries) {
1608
+ const candidates = new Map();
1609
+ for (const entry of entries) {
1610
+ if (!['skill', 'agent', 'flow'].includes(entry.type))
1611
+ continue;
1612
+ const normalized = normalizeIndexedPath(entry.path);
1613
+ const basename = path.posix.basename(normalized);
1614
+ const slug = /^(?:SKILL|AGENT|FLOW)\.md$/i.test(basename)
1615
+ ? path.posix.basename(path.posix.dirname(normalized))
1616
+ : basename.replace(/\.[^.]+$/, '');
1617
+ for (const id of new Set([entry.name, discoveryIdForEntry(entry), slug])) {
1618
+ if (typeof id !== 'string')
1619
+ continue;
1620
+ const key = `${entry.type}\0${id}`;
1621
+ const selected = candidates.get(key) ?? [];
1622
+ selected.push(entry);
1623
+ candidates.set(key, selected);
1624
+ }
1625
+ }
1626
+ return (type, id) => resolveRuleBindingTarget(candidates.get(`${type}\0${id}`) ?? [], type, id);
1627
+ }
1628
+ /** Resolve exact rule binding IDs using the same precedence and ambiguity as show.
1629
+ * Canonical layout slugs remain valid when frontmatter names are display names.
1630
+ * Titles and filesystem paths are deliberately excluded (#2938).
1631
+ */
1632
+ export function resolveRuleBindingTarget(entries, type, id) {
1633
+ const exact = entries.filter(entry => {
1634
+ if (entry.type !== type)
1635
+ return false;
1636
+ const normalized = normalizeIndexedPath(entry.path);
1637
+ const basename = path.posix.basename(normalized);
1638
+ const slug = /^(?:SKILL|AGENT|FLOW)\.md$/i.test(basename)
1639
+ ? path.posix.basename(path.posix.dirname(normalized))
1640
+ : basename.replace(/\.[^.]+$/, '');
1641
+ return entry.name === id || discoveryIdForEntry(entry) === id || slug === id;
1642
+ });
1643
+ let matches = findShowMatches(exact, [type], id);
1644
+ // Generated plugin copies (agentic/code/plugins/*) mirror canonical framework
1645
+ // and addon artifacts. Prefer the canonical source when it is unambiguous.
1646
+ if (matches.length > 1) {
1647
+ const canonical = matches.filter(entry => !/(^|\/)agentic\/code\/plugins\//.test(normalizeIndexedPath(entry.path)));
1648
+ if (canonical.length === 1)
1649
+ matches = canonical;
1650
+ }
1651
+ if (matches.length === 0)
1652
+ throw new Error(`Missing ${type} binding target: ${id}`);
1653
+ if (matches.length > 1)
1654
+ throw new Error(`Ambiguous ${type} binding target: ${id}: ${matches.map(entry => entry.path).join(', ')}`);
1655
+ return matches[0];
1656
+ }
1541
1657
  async function fortemiRecordForEntry(cwd, entry, sourcePaths) {
1542
1658
  const graph = (entry.indexGraph ?? 'project');
1543
1659
  const { loadFortemiCoreExport } = await import('./fortemi-core-query-adapter.js');
@@ -1762,6 +1878,178 @@ async function findAgentInCorpus(agentsRoot, name, depth = 0) {
1762
1878
  }
1763
1879
  return null;
1764
1880
  }
1881
+ /** Build an ephemeral reverse lookup from the already-loaded rule metadata. */
1882
+ export function rulesBoundTo(entries, target) {
1883
+ const key = { skill: 'skills', agent: 'agents', flow: 'flows' }[target.type];
1884
+ if (!key)
1885
+ return [];
1886
+ const file = path.basename(normalizeIndexedPath(target.path));
1887
+ const aliases = [target.name, target.title, target.path, discoveryIdForEntry(target),
1888
+ file === 'SKILL.md' ? path.basename(path.dirname(normalizeIndexedPath(target.path))) : file.replace(/\.[^.]+$/, '')]
1889
+ .filter((alias) => !!alias);
1890
+ const seen = new Set();
1891
+ return entries.filter(rule => {
1892
+ if (rule.type !== 'rule' && rule.type !== 'behavior')
1893
+ return false;
1894
+ const patterns = rule.bound_to?.[key];
1895
+ if (!Array.isArray(patterns) || !patterns.some(pattern => typeof pattern === 'string'
1896
+ && aliases.some(alias => alias === pattern || minimatch(alias, pattern, { nonegate: true, nocomment: true }))))
1897
+ return false;
1898
+ const identity = `${rule.type}:${rule.path}`;
1899
+ if (seen.has(identity))
1900
+ return false;
1901
+ seen.add(identity);
1902
+ return true;
1903
+ }).sort((a, b) => (a.name ?? a.path).localeCompare(b.name ?? b.path) || a.path.localeCompare(b.path));
1904
+ }
1905
+ /** Relationship metadata for shipped targets, merged with project/user contracts. */
1906
+ export function relationshipRules(entries, target, relations) {
1907
+ const local = rulesBoundTo(relations ? entries.filter(entry => !normalizeIndexedPath(entry.path).startsWith('agentic/code/')) : entries, target);
1908
+ if (!relations || !['skill', 'agent', 'flow', 'command'].includes(target.type))
1909
+ return local;
1910
+ const asMetadata = (id) => {
1911
+ const rule = relations.index.rules[id];
1912
+ return { path: rule.path, type: rule.type, name: rule.name, activation: rule.activation ?? undefined,
1913
+ bound_to: rule.bound_to, ruleSummary: rule.summary, ruleEnforcement: rule.enforcement ?? undefined, rulePaths: rule.paths, triggers: rule.triggers };
1914
+ };
1915
+ const key = `${target.type}:${discoveryIdForEntry(target)}`;
1916
+ const shipped = Object.hasOwn(relations.index.targets, key)
1917
+ ? relations.rulesForTarget(target.type, discoveryIdForEntry(target)).map(asMetadata)
1918
+ // Project/user overrides keep the old alias/glob behavior using only small records.
1919
+ : rulesBoundTo(Object.keys(relations.index.rules).map(asMetadata), target);
1920
+ const merged = new Map([...local, ...shipped].map(rule => [`${rule.type}:${rule.path}`, rule]));
1921
+ return [...merged.values()].sort((a, b) => (a.name ?? a.path).localeCompare(b.name ?? b.path) || a.path.localeCompare(b.path));
1922
+ }
1923
+ export const RULE_CHECK_OBLIGATION = 'Before governed work, fetch and follow every required rule below; a discovery summary is not the full rule.';
1924
+ function relationMetadata(relations) {
1925
+ return Object.values(relations.index.rules).map(rule => ({ path: rule.path, type: rule.type, name: rule.name,
1926
+ activation: rule.activation ?? undefined, ruleSummary: rule.summary, ruleEnforcement: rule.enforcement ?? undefined,
1927
+ bound_to: rule.bound_to, rulePaths: rule.paths, triggers: rule.triggers }));
1928
+ }
1929
+ /** Post-ranking enrichment: relationship lookup never loads the full metadata index. */
1930
+ export function compoundGoverningRules(entries, target, relations, phrase = '', includeMatches = true, excludeKernel = true) {
1931
+ if (!['skill', 'agent', 'flow', 'command'].includes(target.type))
1932
+ return [];
1933
+ const found = new Map();
1934
+ const add = (rule, why) => {
1935
+ const activation = rule.activation ?? (rule.type === 'behavior' ? 'kernel' : inferRuleActivation(rule.ruleEnforcement));
1936
+ if (excludeKernel && (activation === 'kernel' || rule.kernel || relations?.pinnedRuleIds.has(discoveryIdForEntry(rule))))
1937
+ return;
1938
+ const key = `${rule.type}:${rule.path}`, item = found.get(key) ?? { rule, applicability: [] };
1939
+ if (!item.applicability.includes(why))
1940
+ item.applicability.push(why);
1941
+ found.set(key, item);
1942
+ };
1943
+ for (const rule of relationshipRules(entries, target, relations))
1944
+ add(rule, `bound:${target.type}:${discoveryIdForEntry(target)}`);
1945
+ if (relations && target.type === 'skill' && target.name?.startsWith('projected:')) {
1946
+ for (const id of relations.rulesForTarget('skill', target.name)) {
1947
+ const rule = relationMetadata(relations).find(rule => discoveryIdForEntry(rule) === id);
1948
+ if (rule)
1949
+ add(rule, `projected-flow:${target.name.slice('projected:'.length)}`);
1950
+ }
1951
+ }
1952
+ if (includeMatches) {
1953
+ const query = phrase.toLowerCase();
1954
+ const queryPaths = (phrase.replace(/\\/g, '/').match(/[\w@./*?-]+/g) ?? []).map(value => value.replace(/[.,;:!?]+$/, ''));
1955
+ const corpus = relations ? relationMetadata(relations) : [];
1956
+ const rules = new Map([...corpus, ...entries.filter(entry => ['rule', 'behavior'].includes(entry.type))]
1957
+ .map(rule => [`${rule.type}:${rule.path}`, rule]));
1958
+ for (const rule of rules.values()) {
1959
+ for (const glob of rule.rulePaths ?? [])
1960
+ for (const candidate of [...queryPaths.filter(value => /[./*?]/.test(value) || value === glob), ...(target.governedPaths ?? [])]) {
1961
+ if (minimatch(candidate, glob, { dot: true, nonegate: true, nocomment: true })
1962
+ || ((target.governedPaths ?? []).includes(candidate) && minimatch(glob, candidate, { dot: true, nonegate: true, nocomment: true })))
1963
+ add(rule, `path:${candidate}`);
1964
+ }
1965
+ for (const trigger of rule.triggers ?? []) {
1966
+ const normalized = trigger.trim().toLowerCase();
1967
+ let offset = normalized ? query.indexOf(normalized) : -1;
1968
+ while (offset >= 0) {
1969
+ if ((offset === 0 || !/\w/.test(query[offset - 1]))
1970
+ && (offset + normalized.length === query.length || !/\w/.test(query[offset + normalized.length]))) {
1971
+ add(rule, `trigger:${trigger}`);
1972
+ break;
1973
+ }
1974
+ offset = query.indexOf(normalized, offset + 1);
1975
+ }
1976
+ }
1977
+ }
1978
+ }
1979
+ return [...found.values()].sort((a, b) => (a.rule.name ?? a.rule.path).localeCompare(b.rule.name ?? b.rule.path) || a.rule.path.localeCompare(b.rule.path));
1980
+ }
1981
+ function boundRuleDescriptor(rule) {
1982
+ return { name: rule.name ?? path.basename(rule.path, '.md'), activation: rule.activation ?? null,
1983
+ summary: rule.ruleSummary ?? '', show_command: `aiwg show ${rule.type} ${rule.name ?? path.basename(rule.path, '.md')}` };
1984
+ }
1985
+ /** Counts unique attached contracts; every emitted check is required irrespective of severity. */
1986
+ export function governingRuleStats(governing) {
1987
+ const stats = emptyRuleStats();
1988
+ for (const { rule } of new Map(governing.map(item => [`${item.rule.type}:${item.rule.path}`, item])).values()) {
1989
+ const enforcement = rule.ruleEnforcement?.toLowerCase() ?? 'unknown', activation = rule.activation ?? 'unknown';
1990
+ stats.by_enforcement[enforcement] = (stats.by_enforcement[enforcement] ?? 0) + 1;
1991
+ stats.by_activation[activation] = (stats.by_activation[activation] ?? 0) + 1;
1992
+ stats.required_checks++;
1993
+ }
1994
+ return stats;
1995
+ }
1996
+ export function governingRuleFields(governing) {
1997
+ if (!governing.length)
1998
+ return {};
1999
+ return { bound_rules: governing.map(({ rule }) => boundRuleDescriptor(rule)), rule_stats: governingRuleStats(governing),
2000
+ required_rule_checks: governing.map(({ rule, applicability }) => ({ rule_id: discoveryIdForEntry(rule),
2001
+ applicability, fetch_command: `aiwg show ${rule.type} ${discoveryIdForEntry(rule)}` })), rule_check_obligation: RULE_CHECK_OBLIGATION };
2002
+ }
2003
+ function statsText(stats) {
2004
+ return `rules: ${Object.entries(stats.by_enforcement).map(([level, count]) => `${count} ${level}`).join(', ') || '0'} · activation: ${Object.entries(stats.by_activation).map(([mode, count]) => `${count} ${mode}`).join(', ') || 'none'} · ${stats.required_checks} required checks`;
2005
+ }
2006
+ export function relationshipRuleStats(entries, target, relations) {
2007
+ const shipped = Object.hasOwn(relations.index.targets, `${target.type}:${discoveryIdForEntry(target)}`);
2008
+ const stats = shipped ? relations.ruleStats(target.type, discoveryIdForEntry(target)) : emptyRuleStats();
2009
+ // Shipped counts are precomputed; project/user contracts and overrides are overlays.
2010
+ const overlay = shipped ? rulesBoundTo(entries.filter(entry => !normalizeIndexedPath(entry.path).startsWith('agentic/code/')), target)
2011
+ : relationshipRules(entries, target, relations);
2012
+ for (const rule of overlay) {
2013
+ const enforcement = rule.ruleEnforcement ?? 'unknown';
2014
+ const activation = rule.activation ?? 'unknown';
2015
+ stats.by_enforcement[enforcement] = (stats.by_enforcement[enforcement] ?? 0) + 1;
2016
+ stats.by_activation[activation] = (stats.by_activation[activation] ?? 0) + 1;
2017
+ if (enforcement === 'critical' || enforcement === 'high')
2018
+ stats.required_checks++;
2019
+ }
2020
+ return stats;
2021
+ }
2022
+ async function showBindings(entries, target, content, params, read, relations) {
2023
+ const governing = compoundGoverningRules(entries, target, relations, '', false, false);
2024
+ const rules = governing.map(item => item.rule);
2025
+ const boundRules = await Promise.all(rules.map(async (rule) => ({
2026
+ ...boundRuleDescriptor(rule),
2027
+ ...(params.boundRuleBodies ? { content: await read(rule) } : {}),
2028
+ })));
2029
+ // Parse the selected rule for reverse lookup, including corpus-only rules.
2030
+ const targets = target.type === 'rule' || target.type === 'behavior'
2031
+ ? parseFrontmatter(content).data.bound_to : undefined;
2032
+ const boundTo = targets && typeof targets === 'object' && !Array.isArray(targets)
2033
+ ? Object.fromEntries(['skills', 'agents', 'flows'].flatMap(key => {
2034
+ const values = targets[key];
2035
+ const names = Array.isArray(values) ? [...new Set(values.filter((value) => typeof value === 'string' && !!value.trim()))] : [];
2036
+ return names.length ? [[key, names]] : [];
2037
+ })) : {};
2038
+ let text = '';
2039
+ if (boundRules.length) {
2040
+ text += `\n## Bound rules\n\n${RULE_CHECK_OBLIGATION}\n\n${statsText(governingRuleStats(governing))}\n\n` + boundRules.map(rule => `- **${rule.name}**: ${rule.summary} (\`${rule.show_command}\`)`
2041
+ + ('content' in rule ? `\n\n${rule.content}` : '')).join('\n') + '\n';
2042
+ }
2043
+ if (Object.keys(boundTo).length) {
2044
+ text += '\n## Bound targets\n\n' + Object.entries(boundTo)
2045
+ .map(([key, names]) => `- ${key}: ${names.map(name => `\`${name}\``).join(', ')}`).join('\n') + '\n';
2046
+ }
2047
+ return { fields: {
2048
+ ...governingRuleFields(governing),
2049
+ ...(boundRules.length ? { bound_rules: boundRules } : {}),
2050
+ ...(Object.keys(boundTo).length ? { bound_to: boundTo } : {}),
2051
+ }, text };
2052
+ }
1765
2053
  /**
1766
2054
  * Read and emit the full text of a specific artifact (typically a
1767
2055
  * SKILL.md). Consumers don't need to know where AIWG stores skills —
@@ -1785,7 +2073,7 @@ export async function showArtifact(cwd, params) {
1785
2073
  const types = params.typeFilter && params.typeFilter.length > 0
1786
2074
  ? params.typeFilter
1787
2075
  : [...OPERATIONAL_SHOW_TYPES];
1788
- const { entries, aiwgRoot, resourceSource, webRelease, } = await loadShowEntries(cwd, params);
2076
+ const { entries, aiwgRoot, resourceSource, webRelease, relations, } = await loadShowEntries(cwd, params, true);
1789
2077
  if (entries.length === 0 && params.backend !== 'fortemi-core') {
1790
2078
  console.error('Error: No artifact index found.');
1791
2079
  console.error('Run `aiwg index build --graph framework` (or `aiwg use <framework>`) first.');
@@ -1796,7 +2084,7 @@ export async function showArtifact(cwd, params) {
1796
2084
  if (matches.length === 0) {
1797
2085
  // Corpus fallback (#1221): when the artifact isn't in any indexed graph,
1798
2086
  // scan the AIWG_ROOT corpus for a likely match and render it with an
1799
- // "(uninstalled)" banner. This keeps `aiwg show` useful in workspaces
2087
+ // installation-aware banner. This keeps `aiwg show` useful in workspaces
1800
2088
  // where the operator hasn't run `aiwg use` yet, or where the framework
1801
2089
  // index is stale, rather than exiting with a misleading "not found".
1802
2090
  if (resourceSource === 'local' && aiwgRoot && params.backend !== 'fortemi-core') {
@@ -1811,34 +2099,49 @@ export async function showArtifact(cwd, params) {
1811
2099
  console.error(`Error reading ${corpusMatch.path}: ${e.message ?? String(err)}`);
1812
2100
  process.exit(1);
1813
2101
  }
1814
- const installHint = corpusMatch.bundleKind && corpusMatch.bundleId
1815
- ? `Run \`aiwg use ${corpusMatch.bundleId}\` to install this ${corpusMatch.bundleKind} into the workspace.`
1816
- : 'Run `aiwg index build --graph framework` to refresh the artifact index.';
2102
+ const localPin = corpusMatch.type === 'rule' ? (await import('../rules/pin-state.js')).rulePinState(cwd, aiwgRoot).rules.find(rule => rule.path === corpusMatch.path) : undefined;
2103
+ const installed = await isCorpusBundleInstalled(cwd, aiwgRoot, corpusMatch.bundleKind, corpusMatch.bundleId);
2104
+ const installHint = installed
2105
+ ? 'The installed bundle is missing from this index. Run `aiwg index build --graph framework` to refresh it.'
2106
+ : corpusMatch.bundleKind && corpusMatch.bundleId
2107
+ ? `Run \`aiwg use ${corpusMatch.bundleId}\` to install this ${corpusMatch.bundleKind} into the workspace.`
2108
+ : 'Run `aiwg index build --graph framework` to refresh the artifact index.';
2109
+ const bindings = await showBindings(entries, {
2110
+ path: corpusMatch.path, type: corpusMatch.type, name: needle, title: needle,
2111
+ }, content, params, rule => fs.readFile(resolveMetadataPath(cwd, aiwgRoot, rule), 'utf8'), relations);
1817
2112
  if (params.json) {
1818
2113
  console.log(JSON.stringify({
2114
+ ...bindings.fields,
1819
2115
  path: corpusMatch.path,
1820
2116
  type: corpusMatch.type,
1821
2117
  title: needle,
1822
2118
  kernel: false,
1823
- uninstalled: true,
2119
+ uninstalled: !installed,
2120
+ ...(localPin ? { local_pin: localPin } : {}),
1824
2121
  hint: installHint,
1825
2122
  content,
1826
2123
  }, null, 2));
1827
2124
  return;
1828
2125
  }
1829
- // Plain text mode: prepend a banner so consumers see the artifact is
1830
- // corpus-only, then stream the body.
2126
+ // Plain text mode: distinguish a missing index entry from an
2127
+ // uninstalled bundle, then stream the body.
1831
2128
  const banner = [
1832
- '<!-- AIWG: corpus-only artifact — not in the indexed workspace.',
2129
+ installed
2130
+ ? '<!-- AIWG: installed bundle artifact — not in the selected index.'
2131
+ : '<!-- AIWG: corpus-only artifact — not in the indexed workspace.',
1833
2132
  ` ${installHint}`,
1834
2133
  ' Origin: ' + corpusMatch.path,
1835
2134
  '-->',
1836
2135
  '',
1837
2136
  ].join('\n');
1838
2137
  process.stdout.write(banner);
2138
+ if (localPin)
2139
+ process.stdout.write(`Pinned locally: kernel (canonical ${localPin.canonical_activation}; ${localPin.matches.map(match => match.source).join(', ')}).\n`);
1839
2140
  process.stdout.write(content);
1840
2141
  if (!content.endsWith('\n'))
1841
2142
  process.stdout.write('\n');
2143
+ if (bindings.text)
2144
+ process.stdout.write(bindings.text);
1842
2145
  return;
1843
2146
  }
1844
2147
  }
@@ -1925,6 +2228,20 @@ export async function showArtifact(cwd, params) {
1925
2228
  process.exit(1);
1926
2229
  }
1927
2230
  }
2231
+ const localPin = entry.type === 'rule' ? (await import('../rules/pin-state.js')).rulePinState(cwd, aiwgRoot ?? cwd).rules.find(rule => rule.path === filePath) : undefined;
2232
+ const bindings = await showBindings(entries, entry, content, params, async (rule) => {
2233
+ if (resourceSource === 'local')
2234
+ return fs.readFile(resolvePath(rule), 'utf8');
2235
+ const { logicalIdFromFirstPartyPath, resolveAiwgResourceBytes } = await import('../resources/resolver.js');
2236
+ const logicalId = logicalIdFromFirstPartyPath(rule.path);
2237
+ if (!logicalId || !webRelease)
2238
+ throw new Error(`Cannot resolve bound rule: ${rule.path}`);
2239
+ const resolved = await resolveAiwgResourceBytes(logicalId, {
2240
+ source: 'web', frameworkRoot: aiwgRoot ?? cwd, webRelease,
2241
+ webReleaseOptions: params.webReleaseOptions, offline: params.offline,
2242
+ });
2243
+ return resolved.bytes.toString('utf8');
2244
+ }, relations);
1928
2245
  if (params.json) {
1929
2246
  const providerModels = resourceSource === 'local'
1930
2247
  ? await loadProviderModelMetadata(cwd, aiwgRoot)
@@ -1956,21 +2273,26 @@ export async function showArtifact(cwd, params) {
1956
2273
  ? { executable: true, run_hint: buildRunHint(entry) }
1957
2274
  : {}),
1958
2275
  ...(providerModels ? { providerModels } : {}),
2276
+ ...bindings.fields,
2277
+ ...(localPin ? { local_pin: localPin } : {}),
1959
2278
  content,
1960
2279
  }, null, 2));
1961
2280
  return;
1962
2281
  }
1963
- // Plain mode: stream the file content unmodified so the consumer
1964
- // (agent or operator) sees exactly what the source authored.
2282
+ // Stream the authored content first; append binding guidance only when present.
1965
2283
  // #1227 — for script-bearing skills, prepend a one-line banner so
1966
2284
  // agents reading the body immediately see the skill is executable.
1967
2285
  if (entry.script) {
1968
2286
  const hint = buildRunHint(entry);
1969
2287
  process.stdout.write(`<!-- AIWG: executable skill — run via: ${hint} -->\n`);
1970
2288
  }
2289
+ if (localPin)
2290
+ process.stdout.write(`Pinned locally: kernel (canonical ${localPin.canonical_activation}; ${localPin.matches.map(match => match.source).join(', ')}).\n`);
1971
2291
  process.stdout.write(content);
1972
2292
  if (!content.endsWith('\n'))
1973
2293
  process.stdout.write('\n');
2294
+ if (bindings.text)
2295
+ process.stdout.write(bindings.text);
1974
2296
  }
1975
2297
  export async function showMetadata(cwd, params) {
1976
2298
  loadGlobalGraphConfigs();