@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
@@ -1,7 +1,13 @@
1
+ import { tomlString } from './toml-string.mjs';
2
+ import { buildServerConfig as renderServerConfig } from './server-config.mjs';
3
+ import { isLowerLayerEntry, loadLayered, lowerEntryQualified, resolveConfigLayers, saveLayerData } from './config-layers.mjs';
4
+ import { assertConfigDestination, assertProjectCredentials, classifyMcpDestination, projectCredentialRemedy, writeConfigAtomic, writeConfigTransaction } from './config-file.mjs';
1
5
  import { manageOmpMcp } from './omp-config.mjs';
2
6
  import { manageGrokBuildMcp } from './grok-build-config.mjs';
3
7
  import { replaceServer } from './toml-editor.mjs';
4
8
  import { resolveOmpPaths } from '../providers/omp-paths.mjs';
9
+ import { applyJsonToolFilterPlan, claudeSettingsPath, prepareClaudePermissions, planToolFilters } from './tool-filters.mjs';
10
+ import { assertCredentialPolicy, resolveCredentialPolicy, validateEnvReferenceName } from './credentials.mjs';
5
11
  /**
6
12
  * MCP Server Registry
7
13
  *
@@ -11,10 +17,12 @@ import { resolveOmpPaths } from '../providers/omp-paths.mjs';
11
17
  *
12
18
  * @implements #554
13
19
  */
14
- import { readFile, writeFile, mkdir } from 'fs/promises';
15
- import { dirname, resolve } from 'path';
20
+ import { readFile } from 'fs/promises';
21
+ import { qualificationDigest } from '../models/qualification-digest.mjs';
22
+ import { qualifyWorkerInvocationProfile, requiredWorkerControls } from '../models/provider-policy.js';
23
+ import { dirname, isAbsolute, resolve } from 'path';
24
+ import { homedir } from 'os';
16
25
  import { resolveConfigDir } from '../config/user-config.js';
17
- import { getProviderDefinition } from '../providers/provider-definitions.js';
18
26
  // ============================================
19
27
  // Registry
20
28
  // ============================================
@@ -33,12 +41,76 @@ function validateCredentialReferences(def) {
33
41
  throw new Error(`Invalid MCP header environment variable reference "${envName}"`);
34
42
  }
35
43
  }
44
+ for (const [key, envName] of Object.entries(def.envFrom ?? {})) {
45
+ if (!ENV_REFERENCE_NAME.test(key))
46
+ throw new Error(`Invalid MCP env-from variable name "${key}"`);
47
+ if (!ENV_REFERENCE_NAME.test(envName)) {
48
+ throw new Error(`Invalid MCP env-from environment variable reference "${envName}"`);
49
+ }
50
+ }
51
+ }
52
+ export function workerServerDigest(server) {
53
+ const { workerQualification, description, injectedProviders, addedAt, updatedAt, ...execution } = server;
54
+ return qualificationDigest(execution);
55
+ }
56
+ function workerQualificationBinding(server, input) {
57
+ if (!Array.isArray(input.requiredControls) || input.requiredControls.some(name => typeof name !== 'string' || !name || !Object.hasOwn(input.adapter.invocationProfiles?.[input.profile]?.controls ?? {}, name))) {
58
+ throw new Error('MCP worker must declare valid required controls');
59
+ }
60
+ if (server.enabled === false || input.mcp.serverRef !== server.name) {
61
+ throw new Error('MCP worker server is disabled or does not match the qualified profile');
62
+ }
63
+ if (input.authorizedServerDigest !== workerServerDigest(server)) {
64
+ throw new Error('MCP server command, endpoint or configuration lacks matching host authorization');
65
+ }
66
+ if (server.type === 'stdio') {
67
+ if (!server.command || !isAbsolute(server.command) || server.command.includes('\0')
68
+ || (server.args ?? []).some(arg => typeof arg !== 'string' || arg.includes('\0'))) {
69
+ throw new Error('MCP worker requires an authorized absolute local command and typed argv');
70
+ }
71
+ }
72
+ else {
73
+ let endpoint;
74
+ try {
75
+ endpoint = new URL(server.url ?? '');
76
+ }
77
+ catch {
78
+ throw new Error('MCP worker endpoint is invalid');
79
+ }
80
+ if (!['http:', 'https:'].includes(endpoint.protocol) || endpoint.username || endpoint.password || endpoint.hash) {
81
+ throw new Error('MCP worker endpoint must be an authorized HTTP URL without credentials or fragment');
82
+ }
83
+ }
84
+ const profile = qualifyWorkerInvocationProfile(input.adapter, { ...input, transport: 'mcp' });
85
+ const installed = input.installationQualification;
86
+ if (installed?.serverDigest !== input.authorizedServerDigest) {
87
+ throw new Error('MCP worker installation does not cover the authorized registration');
88
+ }
89
+ const evidence = profile.installationQualification;
90
+ if (!installed || installed.status !== 'ENABLED' || installed.platform !== 'linux' || installed.cliVersion !== input.cliVersion
91
+ || !evidence || ['scopeDigest', 'fixtureDigest', 'verifiedAt', 'evidenceRef'].some(key => installed[key] !== evidence[key])) {
92
+ throw new Error('MCP worker requires current trusted installation qualification');
93
+ }
94
+ if (Object.keys(server.env ?? {}).some(key => !profile.envAllowlist.includes(key))
95
+ || Object.values(server.headerEnv ?? {}).some(key => !profile.envAllowlist.includes(key))) {
96
+ throw new Error('MCP worker server environment exceeds the qualified profile allowlist');
97
+ }
98
+ return {
99
+ serverDigest: workerServerDigest(server), adapterDigest: qualificationDigest(input.adapter),
100
+ profile: input.profile, cliVersion: input.cliVersion,
101
+ scopeDigest: installed.scopeDigest, fixtureDigest: installed.fixtureDigest,
102
+ requiredControls: requiredWorkerControls(input).sort(),
103
+ };
36
104
  }
37
105
  export class McpServerRegistry {
38
106
  configDir;
39
107
  cache = null;
108
+ /** Configuration layers, lowest precedence first; null for a single directory */
109
+ layers;
110
+ layering = null;
40
111
  constructor(configDirOverride) {
41
- this.configDir = resolveConfigDir(configDirOverride);
112
+ this.layers = resolveConfigLayers(configDirOverride);
113
+ this.configDir = this.layers ? this.layers[this.layers.length - 1] : resolveConfigDir(configDirOverride);
42
114
  }
43
115
  /** Get the registry file path */
44
116
  getPath() {
@@ -48,6 +120,12 @@ export class McpServerRegistry {
48
120
  async load() {
49
121
  if (this.cache)
50
122
  return this.cache;
123
+ if (this.layers) {
124
+ const layered = await loadLayered(this.layers, REGISTRY_FILENAME, 'servers', DEFAULT_REGISTRY);
125
+ this.cache = layered.data;
126
+ this.layering = layered.layering;
127
+ return this.cache;
128
+ }
51
129
  const filePath = this.getPath();
52
130
  try {
53
131
  const content = await readFile(filePath, 'utf-8');
@@ -61,43 +139,67 @@ export class McpServerRegistry {
61
139
  catch {
62
140
  this.cache = { ...DEFAULT_REGISTRY, servers: {} };
63
141
  }
142
+ try {
143
+ resolveCredentialPolicy({ registryPolicy: this.cache.credentialPolicy, env: {} });
144
+ }
145
+ catch (error) {
146
+ this.cache = null;
147
+ throw new Error(`Invalid MCP credential policy in ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
148
+ }
64
149
  return this.cache;
65
150
  }
66
151
  /** Save the registry to disk */
67
- async save() {
68
- if (!this.cache)
152
+ async save(data = this.cache, explicitPolicy = false) {
153
+ if (!data)
69
154
  return;
70
- await mkdir(this.configDir, { recursive: true });
71
- const filePath = this.getPath();
72
- await writeFile(filePath, JSON.stringify(this.cache, null, 2) + '\n', 'utf-8');
155
+ // A fresh instance has no ownership record yet; load it first so inherited
156
+ // entries and inherited policy are never copied into the write layer.
157
+ let layering = this.layering;
158
+ if (this.layers && !layering) {
159
+ await this.load();
160
+ layering = this.layering;
161
+ }
162
+ await saveLayerData(this.getPath(), data, 'servers', this.layers, layering, explicitPolicy);
163
+ this.cache = data;
164
+ if (this.layers) {
165
+ this.clearCache();
166
+ await this.load();
167
+ }
73
168
  }
74
169
  /** Add a new MCP server definition */
75
170
  async add(def) {
76
171
  validateCredentialReferences(def);
77
- const data = await this.load();
172
+ const data = structuredClone(await this.load());
78
173
  if (data.servers[def.name]) {
79
174
  throw new Error(`Server "${def.name}" already exists. Use "update" to modify it.`);
80
175
  }
81
176
  data.servers[def.name] = {
82
177
  ...def,
178
+ workerQualification: undefined,
83
179
  injectedProviders: [],
84
180
  addedAt: new Date().toISOString(),
85
181
  updatedAt: new Date().toISOString(),
86
182
  };
87
- await this.save();
183
+ await this.save(data);
88
184
  }
89
185
  /** Remove an MCP server definition */
90
186
  async remove(name) {
91
- const data = await this.load();
187
+ const data = structuredClone(await this.load());
92
188
  if (!data.servers[name]) {
93
189
  throw new Error(`Server "${name}" not found.`);
94
190
  }
191
+ if (this.layering && isLowerLayerEntry(this.layering, name)) {
192
+ throw new Error(`Server "${name}" is defined in a lower configuration layer; remove it there.`);
193
+ }
194
+ if (this.layering && lowerEntryQualified(this.layering, name)) {
195
+ throw new Error(`Server "${name}" overrides a qualified lower-layer entry; removing the override would silently re-expose its qualification. Remove the lower-layer entry in its own layer instead.`);
196
+ }
95
197
  delete data.servers[name];
96
- await this.save();
198
+ await this.save(data);
97
199
  }
98
200
  /** Update an existing MCP server definition */
99
201
  async update(name, updates) {
100
- const data = await this.load();
202
+ const data = structuredClone(await this.load());
101
203
  if (!data.servers[name]) {
102
204
  throw new Error(`Server "${name}" not found.`);
103
205
  }
@@ -108,8 +210,35 @@ export class McpServerRegistry {
108
210
  updatedAt: new Date().toISOString(),
109
211
  };
110
212
  validateCredentialReferences(next);
213
+ // Ordinary registration edits cannot mint qualification, including a copied binding.
214
+ next.workerQualification = workerServerDigest(next) === workerServerDigest(data.servers[name])
215
+ ? data.servers[name].workerQualification : undefined;
111
216
  data.servers[name] = next;
217
+ await this.save(data);
218
+ }
219
+ /** Bind an already qualified host installation to this exact MCP registration. */
220
+ async recordWorkerQualification(name, input) {
221
+ this.clearCache();
222
+ const server = await this.get(name);
223
+ if (!server)
224
+ throw new Error(`Server "${name}" not found.`);
225
+ const binding = workerQualificationBinding(server, input);
226
+ server.workerQualification = binding;
112
227
  await this.save();
228
+ return structuredClone(binding);
229
+ }
230
+ /** Recheck immediately before use; direct shell invocations do not use this registry. */
231
+ async assertWorkerQualified(name, input) {
232
+ // Other trusted registry writers may have changed the registration since load().
233
+ this.clearCache();
234
+ const server = await this.get(name);
235
+ if (!server)
236
+ throw new Error(`Server "${name}" not found.`);
237
+ const current = workerQualificationBinding(server, input);
238
+ if (!server.workerQualification || qualificationDigest(server.workerQualification) !== qualificationDigest(current)) {
239
+ throw new Error('MCP worker registration qualification is missing or changed');
240
+ }
241
+ return structuredClone(server);
113
242
  }
114
243
  /** Get a specific server definition */
115
244
  async get(name) {
@@ -123,7 +252,7 @@ export class McpServerRegistry {
123
252
  }
124
253
  /** Record that a server was injected into a provider */
125
254
  async recordInjection(name, provider) {
126
- const data = await this.load();
255
+ const data = structuredClone(await this.load());
127
256
  const server = data.servers[name];
128
257
  if (!server)
129
258
  return;
@@ -133,7 +262,7 @@ export class McpServerRegistry {
133
262
  if (!server.injectedProviders.includes(provider)) {
134
263
  server.injectedProviders.push(provider);
135
264
  }
136
- await this.save();
265
+ await this.save(data);
137
266
  }
138
267
  /** Get all providers that have had servers injected */
139
268
  async getInjectedProviders() {
@@ -146,6 +275,33 @@ export class McpServerRegistry {
146
275
  }
147
276
  return [...providers];
148
277
  }
278
+ /** Registry-wide default credential policy, if one is set */
279
+ async getCredentialPolicy() {
280
+ const data = await this.load();
281
+ return data.credentialPolicy;
282
+ }
283
+ /** The enforced organisation floor for this registry, if any layer below the write layer sets one. */
284
+ async getCredentialPolicyFloor() {
285
+ await this.load();
286
+ return this.layering?.lowerPolicy;
287
+ }
288
+ /** The config file of the layer that set the enforced floor, for diagnostics. */
289
+ async getCredentialPolicyFloorSource() {
290
+ await this.load();
291
+ return this.layering?.lowerPolicySource;
292
+ }
293
+ async setCredentialPolicy(policy) {
294
+ const data = structuredClone(await this.load());
295
+ const floorPolicy = this.layering?.lowerPolicy;
296
+ const floorSource = this.layering?.lowerPolicySource;
297
+ const effective = resolveCredentialPolicy({ flag: policy, floorPolicy, floorSource, env: {} });
298
+ if (effective !== policy) {
299
+ const location = floorSource ? ` (set in ${floorSource})` : '';
300
+ throw new Error(`Cannot relax MCP credential policy floor "${effective}"${location} to "${policy}".`);
301
+ }
302
+ data.credentialPolicy = policy;
303
+ await this.save(data, true);
304
+ }
149
305
  /** Clear the in-memory cache */
150
306
  clearCache() {
151
307
  this.cache = null;
@@ -157,110 +313,16 @@ export class McpServerRegistry {
157
313
  /**
158
314
  * Build the MCP config block for a single server in a given provider's format.
159
315
  */
160
- function buildServerConfig(server, provider) {
161
- const adapter = getProviderDefinition(provider)?.adapters.mcpInjection;
162
- switch (adapter) {
163
- case 'antigravity': {
164
- if (server.type === 'stdio') {
165
- return { command: server.command, args: server.args || [], ...(server.env ? { env: server.env } : {}) };
166
- }
167
- return { serverUrl: server.url, ...(server.headers ? { headers: server.headers } : {}) };
168
- }
169
- case 'claude-code': {
170
- if (server.type === 'stdio') {
171
- return {
172
- command: server.command,
173
- args: server.args || [],
174
- ...(server.env ? { env: server.env } : {}),
175
- };
176
- }
177
- // http/sse
178
- return {
179
- url: server.url,
180
- ...(server.headers ? { headers: server.headers } : {}),
181
- };
182
- }
183
- case 'cursor': {
184
- if (server.type === 'stdio') {
185
- return {
186
- command: server.command,
187
- args: server.args || [],
188
- ...(server.env ? { env: server.env } : {}),
189
- };
190
- }
191
- return {
192
- url: server.url,
193
- ...(server.headers ? { headers: server.headers } : {}),
194
- };
195
- }
196
- case 'factory': {
197
- if (server.type === 'stdio') {
198
- return {
199
- type: 'stdio',
200
- command: server.command,
201
- args: server.args || [],
202
- disabled: false,
203
- ...(server.env ? { env: server.env } : {}),
204
- };
205
- }
206
- return {
207
- type: server.type,
208
- url: server.url,
209
- disabled: false,
210
- ...(server.headers ? { headers: server.headers } : {}),
211
- };
212
- }
213
- case 'opencode': {
214
- if (server.type === 'stdio') {
215
- return {
216
- type: 'local',
217
- command: [server.command, ...(server.args || [])],
218
- ...(server.env ? { env: server.env } : {}),
219
- };
220
- }
221
- return {
222
- type: 'remote',
223
- url: server.url,
224
- ...(server.headers ? { headers: server.headers } : {}),
225
- };
226
- }
227
- case 'windsurf':
228
- case 'warp': {
229
- if (server.type === 'stdio') {
230
- return {
231
- command: server.command,
232
- args: server.args || [],
233
- ...(server.env ? { env: server.env } : {}),
234
- };
235
- }
236
- return {
237
- url: server.url,
238
- ...(server.headers ? { headers: server.headers } : {}),
239
- };
240
- }
241
- case 'codex':
242
- // TOML format handled separately
243
- return {};
244
- default:
245
- return {};
246
- }
247
- }
248
- /**
249
- * Build a TOML section for a server (Codex/OpenAI provider).
250
- */
251
- function tomlString(value) {
252
- if (typeof value !== 'string' || [...value].some(char => {
253
- const point = char.codePointAt(0);
254
- return point >= 0xd800 && point <= 0xdfff;
255
- }))
256
- throw new Error('TOML values must be strings containing valid Unicode scalar values');
257
- // JSON escapes align with TOML basic strings except DEL must also be escaped.
258
- return JSON.stringify(value).replace(/\u007f/g, '\\u007f');
316
+ export function buildServerConfig(server, provider) {
317
+ return renderServerConfig(server, provider);
259
318
  }
260
319
  function tomlKey(value) {
261
320
  return typeof value === 'string' && /^[A-Za-z0-9_-]+$/.test(value) ? value : tomlString(value);
262
321
  }
263
- function buildServerToml(server) {
322
+ function tomlInlineTable(values) {
323
+ return `{ ${Object.entries(values).map(([name, value]) => `${tomlKey(name)} = ${tomlString(value)}`).join(', ')} }`;
324
+ }
325
+ export function buildServerToml(server) {
264
326
  const lines = [];
265
327
  lines.push(`[mcp_servers.${tomlKey(server.name)}]`);
266
328
  if (server.type === 'stdio') {
@@ -269,9 +331,27 @@ function buildServerToml(server) {
269
331
  const argsStr = server.args.map(a => tomlString(a)).join(', ');
270
332
  lines.push(`args = [${argsStr}]`);
271
333
  }
334
+ if (server.env && Object.keys(server.env).length > 0)
335
+ lines.push(`env = ${tomlInlineTable(server.env)}`);
336
+ // Codex forwards a variable only under its own name (env_vars).
337
+ const forwarded = Object.entries(server.envFrom ?? {});
338
+ for (const [key, name] of forwarded) {
339
+ validateEnvReferenceName(name);
340
+ if (key !== name) {
341
+ throw new Error(`MCP server "${server.name}": Codex forwards environment variables under their own name only; env-from ${key}=${name} cannot be rendered. Use ${name}=${name}.`);
342
+ }
343
+ }
344
+ if (forwarded.length > 0)
345
+ lines.push(`env_vars = [${forwarded.map(([name]) => tomlString(name)).join(', ')}]`);
272
346
  }
273
347
  else {
274
348
  lines.push(`url = ${tomlString(server.url)}`);
349
+ if (server.headers && Object.keys(server.headers).length > 0)
350
+ lines.push(`http_headers = ${tomlInlineTable(server.headers)}`);
351
+ for (const name of Object.values(server.headerEnv ?? {}))
352
+ validateEnvReferenceName(name);
353
+ if (server.headerEnv && Object.keys(server.headerEnv).length > 0)
354
+ lines.push(`env_http_headers = ${tomlInlineTable(server.headerEnv)}`);
275
355
  }
276
356
  lines.push(`startup_timeout_sec = 10.0`);
277
357
  lines.push(`tool_timeout_sec = 60.0`);
@@ -285,6 +365,9 @@ export function getProviderConfigPath(provider, projectDir = '.', options = {})
285
365
  const homeDir = process.env.HOME || process.env.USERPROFILE || '';
286
366
  return resolve(homeDir, '.gemini/config/mcp_config.json');
287
367
  }
368
+ if ((provider === 'claude-code' || provider === 'claude') && options.scope === 'user') {
369
+ return resolve(process.env.HOME || process.env.USERPROFILE || homedir(), '.claude.json');
370
+ }
288
371
  if ((provider === 'omp' || provider === 'oh-my-pi') && options.scope !== undefined && !['user', 'project'].includes(options.scope))
289
372
  throw new Error('OMP MCP scope must be user or project');
290
373
  if ((provider === 'omp' || provider === 'oh-my-pi') && options.scope === 'user')
@@ -295,12 +378,12 @@ export function getProviderConfigPath(provider, projectDir = '.', options = {})
295
378
  agy: resolve(projectDir, '.agents/mcp_config.json'),
296
379
  omp: resolve(projectDir, '.omp/mcp.json'),
297
380
  'oh-my-pi': resolve(projectDir, '.omp/mcp.json'),
298
- 'claude-code': resolve(projectDir, '.claude/settings.local.json'),
299
- claude: resolve(projectDir, '.claude/settings.local.json'),
381
+ 'claude-code': resolve(projectDir, '.mcp.json'),
382
+ claude: resolve(projectDir, '.mcp.json'),
300
383
  cursor: resolve(projectDir, '.cursor/mcp.json'),
301
384
  factory: resolve(homeDir, '.factory/mcp.json'),
302
- codex: resolve(homeDir, '.codex/config.toml'),
303
- openai: resolve(homeDir, '.codex/config.toml'),
385
+ codex: process.env.CODEX_HOME ? resolve(process.env.CODEX_HOME, 'config.toml') : resolve(homeDir, '.codex/config.toml'),
386
+ openai: process.env.CODEX_HOME ? resolve(process.env.CODEX_HOME, 'config.toml') : resolve(homeDir, '.codex/config.toml'),
304
387
  'grok-build': options.scope === 'user'
305
388
  ? resolve(process.env.GROK_HOME || resolve(homeDir, '.grok'), 'config.toml')
306
389
  : resolve(projectDir, '.grok/config.toml'),
@@ -327,16 +410,37 @@ export async function injectServers(registry, provider, options = {}) {
327
410
  };
328
411
  // Get servers to inject
329
412
  let allServers = await registry.list();
330
- if (serverFilter && serverFilter.length > 0) {
413
+ if (serverFilter !== undefined) {
331
414
  allServers = allServers.filter(s => serverFilter.includes(s.name));
332
415
  }
416
+ const toolPlan = options.toolFilters
417
+ ? planToolFilters(provider, allServers.map(server => server.name), options.toolFilters)
418
+ : null;
419
+ if (toolPlan)
420
+ result.warnings = toolPlan.warnings;
333
421
  if (allServers.length === 0) {
334
422
  result.error = 'No servers to inject. Use "aiwg mcp add" first.';
335
423
  return result;
336
424
  }
425
+ assertCredentialPolicy(allServers, resolveCredentialPolicy({
426
+ flag: options.credentialPolicy, registryPolicy: await registry.getCredentialPolicy(),
427
+ floorPolicy: await registry.getCredentialPolicyFloor(),
428
+ floorSource: await registry.getCredentialPolicyFloorSource(),
429
+ }));
430
+ // The shareable-project decision follows the actual resolved destination,
431
+ // never the requested scope: a `--scope user` request for a provider without
432
+ // a user-scope destination still resolves to the project file and is refused.
433
+ const { userScope, projectRoot } = classifyMcpDestination(provider, options.scope, configPath, projectDir);
434
+ await assertConfigDestination(configPath, projectRoot);
435
+ // Every destination inside the project directory is meant to be committed to
436
+ // version control; home destinations are private, so literals there are not
437
+ // copied into a shared file.
438
+ if (projectRoot) {
439
+ assertProjectCredentials(allServers, configPath, projectCredentialRemedy(provider, projectDir));
440
+ }
337
441
  if (provider === 'omp' || provider === 'oh-my-pi') {
338
442
  try {
339
- const managed = await manageOmpMcp(configPath, allServers, { dryRun });
443
+ const managed = await manageOmpMcp(configPath, allServers, { dryRun, userScope, projectRoot });
340
444
  if (!dryRun)
341
445
  for (const server of allServers)
342
446
  await registry.recordInjection(server.name, 'omp');
@@ -350,6 +454,7 @@ export async function injectServers(registry, provider, options = {}) {
350
454
  try {
351
455
  const managed = await manageGrokBuildMcp(configPath, allServers, {
352
456
  dryRun,
457
+ userScope,
353
458
  root: options.scope === 'user' ? dirname(dirname(configPath)) : resolve(projectDir),
354
459
  });
355
460
  if (!dryRun && !managed.error)
@@ -363,12 +468,12 @@ export async function injectServers(registry, provider, options = {}) {
363
468
  }
364
469
  // Handle TOML-based providers (Codex/OpenAI) separately
365
470
  if (provider === 'codex' || provider === 'openai') {
366
- return injectToml(registry, allServers, configPath, provider, dryRun, result);
471
+ return injectToml(registry, allServers, configPath, provider, dryRun, result, userScope, projectRoot, toolPlan);
367
472
  }
368
473
  // JSON-based providers
369
- return injectJson(registry, allServers, configPath, provider, dryRun, result);
474
+ return injectJson(registry, allServers, configPath, provider, dryRun, result, userScope, projectRoot, toolPlan, { projectDir, scope: options.scope });
370
475
  }
371
- async function injectJson(registry, servers, configPath, provider, dryRun, result) {
476
+ async function injectJson(registry, servers, configPath, provider, dryRun, result, userScope, projectRoot, toolPlan = null, location = {}) {
372
477
  // Load existing config
373
478
  let existing = {};
374
479
  try {
@@ -406,9 +511,21 @@ async function injectJson(registry, servers, configPath, provider, dryRun, resul
406
511
  }
407
512
  // Merge back
408
513
  const merged = { ...existing, [mcpKey]: newServers };
514
+ if (toolPlan) {
515
+ applyJsonToolFilterPlan(merged, mcpKey, toolPlan);
516
+ }
517
+ const isClaude = provider === 'claude-code' || provider === 'claude';
518
+ const settingsPath = isClaude ? claudeSettingsPath(location.projectDir, location.scope) : null;
519
+ const settings = settingsPath ? await prepareClaudePermissions(settingsPath, toolPlan?.claudePermissions || { deny: [], allow: [] }, { userScope, projectRoot, managedDir: dirname(registry.getPath()), mcpPath: configPath }) : null;
520
+ if (settings?.active && settingsPath)
521
+ result.settingsPath = settingsPath;
522
+ if (settings?.warnings.length)
523
+ result.warnings = [...(result.warnings || []), ...settings.warnings];
409
524
  if (!dryRun) {
410
- await mkdir(resolve(configPath, '..'), { recursive: true });
411
- await writeFile(configPath, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
525
+ await writeConfigTransaction([
526
+ { file: configPath, content: JSON.stringify(merged, null, 2) + '\n', options: { userScope, projectRoot } },
527
+ ...(settings?.writes || []),
528
+ ]);
412
529
  // Record injection in registry
413
530
  for (const server of servers) {
414
531
  if ((provider === 'antigravity' || provider === 'agy') && !result.serversInjected.includes(server.name))
@@ -418,7 +535,7 @@ async function injectJson(registry, servers, configPath, provider, dryRun, resul
418
535
  }
419
536
  return result;
420
537
  }
421
- async function injectToml(registry, servers, configPath, provider, dryRun, result) {
538
+ async function injectToml(registry, servers, configPath, provider, dryRun, result, userScope, projectRoot, toolPlan = null) {
422
539
  let existing = '';
423
540
  try {
424
541
  existing = await readFile(configPath, 'utf-8');
@@ -428,15 +545,15 @@ async function injectToml(registry, servers, configPath, provider, dryRun, resul
428
545
  throw error;
429
546
  }
430
547
  for (const server of servers) {
431
- const edited = replaceServer(existing, server.name, buildServerToml(server));
548
+ const filterLines = toolPlan?.tomlLines[server.name] || [];
549
+ const edited = replaceServer(existing, server.name, [buildServerToml(server), ...filterLines].join('\n'));
432
550
  existing = edited.text;
433
551
  if (edited.alreadyPresent)
434
552
  result.alreadyPresent.push(server.name);
435
553
  result.serversInjected.push(server.name);
436
554
  }
437
555
  if (!dryRun) {
438
- await mkdir(resolve(configPath, '..'), { recursive: true });
439
- await writeFile(configPath, existing, 'utf-8');
556
+ await writeConfigAtomic(configPath, existing, { userScope, projectRoot });
440
557
  for (const server of servers) {
441
558
  await registry.recordInjection(server.name, provider);
442
559
  }