@aiwg/cli 2026.10.5 → 2026.10.7

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 (263) hide show
  1. package/agentic/code/addons/composition-engine/lib/runtime.mjs +1401 -0
  2. package/agentic/code/addons/composition-engine/lib/validator.mjs +513 -0
  3. package/agentic/code/addons/composition-engine/schemas/flow-graph.schema.json +372 -0
  4. package/agentic/code/providers/model-capabilities.v1.json +74 -50
  5. package/bin/aiwg.mjs +7 -3
  6. package/dist/src/a2a/agent-card.js +11 -0
  7. package/dist/src/api/index.d.ts +1 -0
  8. package/dist/src/api/index.js +1 -0
  9. package/dist/src/artifacts/browser-export.js +1 -0
  10. package/dist/src/artifacts/citation-parser.js +8 -1
  11. package/dist/src/artifacts/cli.js +57 -6
  12. package/dist/src/artifacts/fortemi-core-query-adapter.js +2 -0
  13. package/dist/src/artifacts/fortemi-core-sync.js +26 -8
  14. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/contract.json +103 -0
  15. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-config.schema.json +212 -0
  16. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-set.schema.json +240 -0
  17. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-manifest.schema.json +451 -0
  18. package/dist/src/artifacts/fortemi-shard-export.js +284 -0
  19. package/dist/src/artifacts/graph-query.js +18 -8
  20. package/dist/src/artifacts/index-adapters.js +339 -0
  21. package/dist/src/artifacts/index-builder.js +143 -14
  22. package/dist/src/artifacts/index-files.js +15 -4
  23. package/dist/src/artifacts/index-preparation.js +2 -0
  24. package/dist/src/artifacts/index-reader.js +44 -0
  25. package/dist/src/artifacts/index-refresh.js +114 -55
  26. package/dist/src/artifacts/index-status.js +20 -5
  27. package/dist/src/artifacts/memory-maintenance.js +503 -0
  28. package/dist/src/artifacts/prebuilt-relations.js +3 -1
  29. package/dist/src/artifacts/query-engine.js +263 -108
  30. package/dist/src/artifacts/relations-builder.js +2 -2
  31. package/dist/src/artifacts/source-graph.js +2 -2
  32. package/dist/src/artifacts/types.js +9 -1
  33. package/dist/src/auth/client.js +5 -1
  34. package/dist/src/auth/credential-store.js +5 -1
  35. package/dist/src/auth/sensitive-input.js +151 -0
  36. package/dist/src/channel/manager.mjs +3 -0
  37. package/dist/src/cli/handlers/agentcard.js +47 -25
  38. package/dist/src/cli/handlers/auth.js +22 -2
  39. package/dist/src/cli/handlers/decision.js +22 -3
  40. package/dist/src/cli/handlers/effect.js +4 -84
  41. package/dist/src/cli/handlers/guided-setup.js +470 -0
  42. package/dist/src/cli/handlers/help.js +2 -2
  43. package/dist/src/cli/handlers/housekeeper.js +1110 -0
  44. package/dist/src/cli/handlers/index.js +4 -2
  45. package/dist/src/cli/handlers/init.js +41 -6
  46. package/dist/src/cli/handlers/install.js +6 -0
  47. package/dist/src/cli/handlers/local-executor.js +5 -3
  48. package/dist/src/cli/handlers/marketplace.js +15 -4
  49. package/dist/src/cli/handlers/mc.js +509 -53
  50. package/dist/src/cli/handlers/models.js +32 -2
  51. package/dist/src/cli/handlers/ralph-launcher.js +76 -4
  52. package/dist/src/cli/handlers/rules.js +1 -1
  53. package/dist/src/cli/handlers/script-runner.js +73 -2
  54. package/dist/src/cli/handlers/serve.js +5 -3
  55. package/dist/src/cli/handlers/sessions.js +1 -0
  56. package/dist/src/cli/handlers/setup.js +432 -1
  57. package/dist/src/cli/handlers/skill-lint.js +1 -1
  58. package/dist/src/cli/handlers/subcommands.js +118 -8
  59. package/dist/src/cli/handlers/use.js +335 -107
  60. package/dist/src/cli/handlers/workspace.js +3 -1
  61. package/dist/src/cli/prompt-utils.js +81 -0
  62. package/dist/src/cli/router.js +16 -1
  63. package/dist/src/config/aiwg-config.js +42 -0
  64. package/dist/src/config/cli.js +7 -3
  65. package/dist/src/config/decision-config.js +14 -1
  66. package/dist/src/config/gitignore.js +4 -0
  67. package/dist/src/config/native-sandbox-warnings.js +25 -0
  68. package/dist/src/config/workspace.js +2 -1
  69. package/dist/src/contributors/validation.js +6 -5
  70. package/dist/src/decision/adapters/jev.js +20 -31
  71. package/dist/src/decision/admission.js +7 -3
  72. package/dist/src/decision/ask.js +6 -1
  73. package/dist/src/decision/benchmark.js +482 -0
  74. package/dist/src/decision/ledger.js +22 -3
  75. package/dist/src/decision/pricing.js +147 -0
  76. package/dist/src/decision/qualification/gate-evidence.js +23 -18
  77. package/dist/src/decision/qualification/load-staged.js +576 -0
  78. package/dist/src/decision/qualification/load.js +109 -33
  79. package/dist/src/decision/qualification/runner.js +12 -5
  80. package/dist/src/decision/review/service.js +6 -4
  81. package/dist/src/decision/review/validate.js +23 -2
  82. package/dist/src/decision/savings.js +8 -3
  83. package/dist/src/discovery/index.js +492 -0
  84. package/dist/src/discovery/interview.js +238 -0
  85. package/dist/src/discovery/plan-apply.js +567 -0
  86. package/dist/src/discovery/plan-store.js +171 -0
  87. package/dist/src/discovery/plan-types.js +2 -0
  88. package/dist/src/discovery/plan-validation.js +139 -0
  89. package/dist/src/discovery/types.js +2 -0
  90. package/dist/src/effects/host.js +91 -0
  91. package/dist/src/extensions/commands/definitions.js +49 -9
  92. package/dist/src/extensions/manifest.js +71 -44
  93. package/dist/src/extensions/project-local-discovery.js +1 -1
  94. package/dist/src/extensions/project-local-gitignore.js +2 -0
  95. package/dist/src/extensions/project-quickref.js +18 -17
  96. package/dist/src/extensions/validation.js +32 -29
  97. package/dist/src/housekeeper/daemon-config.js +148 -0
  98. package/dist/src/housekeeper/ipc.js +84 -0
  99. package/dist/src/housekeeper/lifecycle.js +148 -0
  100. package/dist/src/housekeeper/platform.js +35 -0
  101. package/dist/src/housekeeper/status.js +70 -0
  102. package/dist/src/housekeeper/units.js +114 -0
  103. package/dist/src/issues/cli.js +16 -5
  104. package/dist/src/issues/live.js +44 -12
  105. package/dist/src/jobs/flow.js +22 -21
  106. package/dist/src/lint/rule-activation.js +20 -5
  107. package/dist/src/lint/runner.js +1 -1
  108. package/dist/src/marketplace/exchange.js +18 -6
  109. package/dist/src/marketplace/provenance.js +3 -2
  110. package/dist/src/mcp/adapters/codex-runtime.js +46 -76
  111. package/dist/src/mcp/cli.mjs +22 -96
  112. package/dist/src/mcp/config-file.mjs +6 -2
  113. package/dist/src/mcp/config-layers.mjs +23 -7
  114. package/dist/src/mcp/grok-build-config.mjs +2 -1
  115. package/dist/src/mcp/muse-native-config.mjs +2 -1
  116. package/dist/src/mcp/profiles.js +18 -31
  117. package/dist/src/mcp/profiles.mjs +18 -32
  118. package/dist/src/mcp/registry.js +7 -27
  119. package/dist/src/mcp/registry.mjs +7 -28
  120. package/dist/src/mcp/server.mjs +34 -1
  121. package/dist/src/mcp/tools/agentic-sandbox.mjs +8 -7
  122. package/dist/src/mcp/tools/command-run.mjs +3 -2
  123. package/dist/src/mcp/tools/decision.mjs +49 -48
  124. package/dist/src/mcp/tools/discovery.mjs +46 -21
  125. package/dist/src/mcp/tools/orchestration.mjs +22 -22
  126. package/dist/src/mcp/tools/subsystems.mjs +9 -8
  127. package/dist/src/mcp/tools/worker-invoke.mjs +30 -6
  128. package/dist/src/mcp/tools/writer-profiles.mjs +1 -1
  129. package/dist/src/memory/audit.js +719 -0
  130. package/dist/src/memory/canonical-context.js +392 -13
  131. package/dist/src/memory/canonical-maintenance.js +95 -0
  132. package/dist/src/memory/cli-result.js +22 -0
  133. package/dist/src/memory/cli.js +176 -0
  134. package/dist/src/memory/context-pack.js +57 -6
  135. package/dist/src/memory/cost.js +512 -0
  136. package/dist/src/memory/firewall-classification.d.mts +7 -0
  137. package/dist/src/memory/firewall-classification.mjs +32 -0
  138. package/dist/src/memory/index.js +8 -0
  139. package/dist/src/memory/line-maintenance.js +68 -0
  140. package/dist/src/memory/maintenance-cli.js +286 -0
  141. package/dist/src/memory/maintenance-contract.js +136 -0
  142. package/dist/src/memory/maintenance-index.js +18 -0
  143. package/dist/src/memory/maintenance-owner.js +2 -0
  144. package/dist/src/memory/maintenance-record.js +138 -0
  145. package/dist/src/memory/maintenance.js +254 -0
  146. package/dist/src/memory/project-registry.js +2 -1
  147. package/dist/src/memory/provider-memory-adapter.js +154 -0
  148. package/dist/src/memory/recommend-cli.js +83 -0
  149. package/dist/src/memory/recommend.js +269 -0
  150. package/dist/src/memory/scheduled.js +81 -0
  151. package/dist/src/memory/taxonomy.js +44 -0
  152. package/dist/src/mission-protocol/codecs.js +19 -9
  153. package/dist/src/mission-protocol/worker-status.js +91 -1
  154. package/dist/src/models/config-loader.js +11 -1
  155. package/dist/src/models/model-capabilities.v1.json +74 -50
  156. package/dist/src/models/provider-policy.js +125 -62
  157. package/dist/src/models/resolver.js +45 -8
  158. package/dist/src/models/worker-installation.d.mts +8 -1
  159. package/dist/src/models/worker-installation.mjs +231 -38
  160. package/dist/src/models/wrapper-route.js +1 -1
  161. package/dist/src/output-modes/registry.js +8 -7
  162. package/dist/src/packages/adapters/git.js +99 -23
  163. package/dist/src/packages/adapters/gitea.js +8 -27
  164. package/dist/src/packages/adapters/github.js +7 -25
  165. package/dist/src/packages/registry.js +4 -4
  166. package/dist/src/plugin/metadata-validator.js +1 -1
  167. package/dist/src/process/cli-command.d.mts +5 -0
  168. package/dist/src/process/cli-command.mjs +42 -0
  169. package/dist/src/providers/deploy-version.d.mts +1 -0
  170. package/dist/src/providers/deploy-version.mjs +17 -0
  171. package/dist/src/providers/provider-definitions.js +19 -16
  172. package/dist/src/providers/skill-ownership.d.mts +11 -2
  173. package/dist/src/providers/skill-ownership.mjs +35 -17
  174. package/dist/src/rules/activation.d.mts +11 -2
  175. package/dist/src/rules/activation.mjs +82 -6
  176. package/dist/src/rules/inventory.js +1 -1
  177. package/dist/src/rules/rule-frontmatter.schema.json +2 -0
  178. package/dist/src/schema/zod-validation.d.mts +6 -0
  179. package/dist/src/schema/zod-validation.mjs +50 -0
  180. package/dist/src/serve/executor-registry.js +297 -20
  181. package/dist/src/serve/executor-token.d.mts +1 -0
  182. package/dist/src/serve/executor-token.mjs +8 -0
  183. package/dist/src/serve/shared-host-scheduler.js +1102 -31
  184. package/dist/src/serve/worker-invocation.js +124 -15
  185. package/dist/src/sessions/adapters/claude.js +19 -18
  186. package/dist/src/sessions/adapters/codex.js +10 -9
  187. package/dist/src/sessions/adapters/copilot.js +14 -13
  188. package/dist/src/sessions/adapters/cursor.js +19 -18
  189. package/dist/src/sessions/adapters/deepseek-harness.js +9 -8
  190. package/dist/src/sessions/adapters/factory.js +11 -10
  191. package/dist/src/sessions/adapters/generic.js +32 -23
  192. package/dist/src/sessions/adapters/grokbot.js +35 -35
  193. package/dist/src/sessions/adapters/hermes.js +20 -19
  194. package/dist/src/sessions/adapters/muse.js +50 -49
  195. package/dist/src/sessions/adapters/omp.js +5 -4
  196. package/dist/src/sessions/adapters/openclaw.js +30 -29
  197. package/dist/src/sessions/adapters/opencode.js +26 -26
  198. package/dist/src/sessions/adapters/openhuman.js +24 -23
  199. package/dist/src/sessions/adapters/pi.js +5 -4
  200. package/dist/src/sessions/adapters/windsurf.js +8 -8
  201. package/dist/src/sessions/analytics.js +10 -9
  202. package/dist/src/sessions/candidates.js +7 -6
  203. package/dist/src/sessions/contracts.js +63 -53
  204. package/dist/src/sessions/output-registration.js +11 -10
  205. package/dist/src/sessions/workspace-discovery.js +3 -4
  206. package/dist/src/smiths/context-pipeline/aiwg-md.js +18 -1
  207. package/dist/src/smiths/context-pipeline/claude-hook.js +46 -10
  208. package/dist/src/smiths/context-pipeline/context-file-safety.js +330 -9
  209. package/dist/src/smiths/context-pipeline/finalization.js +3 -2
  210. package/dist/src/smiths/context-pipeline/generator.js +7 -19
  211. package/dist/src/smiths/context-pipeline/legacy-inject.js +3 -10
  212. package/dist/src/smiths/context-pipeline/managed-hook.js +5 -5
  213. package/dist/src/smiths/context-pipeline/parallelism-section.js +72 -5
  214. package/dist/src/smiths/context-pipeline/workspace-context.js +110 -22
  215. package/dist/src/tracker/project-inputs.js +2 -0
  216. package/dist/src/update/checker.d.mts +4 -0
  217. package/dist/src/update/checker.mjs +14 -0
  218. package/dist/src/update/notifier.mjs +2 -4
  219. package/dist/src/writing/exemplar-selection.js +6 -6
  220. package/dist/src/writing/voice-evaluation.js +24 -23
  221. package/dist/src/writing/writer-migration.js +14 -13
  222. package/dist/src/writing/writer-profile.js +19 -18
  223. package/dist/src/writing/writing-brief.js +18 -17
  224. package/dist/src/writing/writing-receipt.js +47 -46
  225. package/package.json +5 -2
  226. package/schemas/decision/DecisionReview.schema.json +78 -1
  227. package/schemas/mission-v1.schema.json +3 -0
  228. package/tools/agents/deploy-agents.mjs +67 -39
  229. package/tools/agents/providers/antigravity.mjs +6 -1
  230. package/tools/agents/providers/base.mjs +24 -14
  231. package/tools/agents/providers/claude.mjs +6 -3
  232. package/tools/agents/providers/codex.mjs +54 -57
  233. package/tools/agents/providers/context-write.mjs +28 -0
  234. package/tools/agents/providers/copilot.mjs +13 -4
  235. package/tools/agents/providers/cursor.mjs +1 -1
  236. package/tools/agents/providers/deepseek-harness.mjs +1 -1
  237. package/tools/agents/providers/factory.mjs +7 -1
  238. package/tools/agents/providers/grok-build.mjs +18 -2
  239. package/tools/agents/providers/grokbot.mjs +1 -1
  240. package/tools/agents/providers/hermes.mjs +12 -15
  241. package/tools/agents/providers/muse-hooks.mjs +21 -14
  242. package/tools/agents/providers/muse.mjs +7 -2
  243. package/tools/agents/providers/omp.mjs +31 -5
  244. package/tools/agents/providers/opencode.mjs +14 -5
  245. package/tools/agents/providers/openhuman.mjs +1 -1
  246. package/tools/agents/providers/pi.mjs +1 -1
  247. package/tools/agents/providers/provider-prepare-coordinator.mjs +29 -9
  248. package/tools/agents/providers/rule-delivery.mjs +289 -80
  249. package/tools/agents/providers/warp.mjs +4 -1
  250. package/tools/agents/providers/windsurf.mjs +13 -3
  251. package/tools/cli/workspace-status.mjs +5 -0
  252. package/tools/commands/deploy-prompts-codex.mjs +11 -8
  253. package/tools/ralph-external/lib/codex-adapter.mjs +6 -2
  254. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +26 -0
  255. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +7 -1
  256. package/tools/ralph-external/lib/muse-adapter.mjs +12 -2
  257. package/tools/ralph-external/lib/recovery-probe.mjs +14 -4
  258. package/tools/ralph-external/lib/worker-health.mjs +1 -1
  259. package/tools/ralph-external/lib/worker-isolation.mjs +65 -9
  260. package/tools/ralph-external/process-monitor.mjs +49 -8
  261. package/tools/ralph-external/session-launcher.mjs +405 -35
  262. package/tools/skills/deploy-skills-codex.mjs +11 -7
  263. package/vscode-extension/schemas/aiwg.config.v1.json +11 -1
@@ -1,3 +1,4 @@
1
+ import { importImpl } from '../../_resolve-impl.mjs';
1
2
  /**
2
3
  * OpenAI Codex Provider
3
4
  *
@@ -35,6 +36,7 @@ const staticModelCatalog = _require('../../../agentic/code/providers/model-catal
35
36
  import path from 'path';
36
37
  import os from 'os';
37
38
  import { spawn } from 'child_process';
39
+ import { pathToFileURL } from 'node:url';
38
40
  import { load as loadYaml } from 'js-yaml';
39
41
  import { classifyModelRole, modelForRole } from './model-role.mjs';
40
42
  import {
@@ -66,7 +68,8 @@ import {
66
68
  parseFrontmatter,
67
69
  resolveAiwgRoot
68
70
  } from './base.mjs';
69
- const modelCatalog = loadRuntimeModelCatalog(staticModelCatalog);
71
+
72
+ import { writeContextFileSync } from './context-write.mjs';
70
73
 
71
74
  function resolveCodexHelperRoot(srcRoot) {
72
75
  const resolved = resolveAiwgRoot(srcRoot);
@@ -101,7 +104,6 @@ export const kernelSkillsPath = '.agents/skills/';
101
104
  // Legacy home-dir skills location written by AIWG versions prior to the #766
102
105
  // regression fix. Pruned on every codex skill deploy so codex-rs stops listing
103
106
  // each AIWG skill twice. Never touches non-AIWG (unmarked) skills.
104
- const legacyHomeSkillsDir = path.join(os.homedir(), '.codex', 'skills');
105
107
 
106
108
  export const support = {
107
109
  agents: 'native',
@@ -125,6 +127,7 @@ export const capabilities = {
125
127
  * Map model shorthand to OpenAI/GPT format
126
128
  */
127
129
  export function mapModel(originalModel, modelCfg, modelsConfig) {
130
+ const modelCatalog = modelCfg.modelCatalog || loadRuntimeModelCatalog(staticModelCatalog);
128
131
  const gptModels = {
129
132
  'opus': modelCatalog.providers.codex.roles.reasoning.id,
130
133
  'sonnet': modelCatalog.providers.codex.roles.coding.id,
@@ -204,7 +207,7 @@ export function renderAgentToml(srcPath, content, models) {
204
207
  */
205
208
  export function transformAgent(srcPath, content, opts) {
206
209
  const { reasoningModel, codingModel, efficiencyModel } = opts;
207
- const catalogModels = modelCatalog.providers.codex.roles;
210
+ const catalogModels = (opts.modelCatalog || loadRuntimeModelCatalog(staticModelCatalog)).providers.codex.roles;
208
211
 
209
212
  const models = {
210
213
  reasoning: reasoningModel || catalogModels.reasoning.id,
@@ -239,6 +242,27 @@ export function deployAgents(agentFiles, targetDir, opts) {
239
242
  }, transformAgent);
240
243
  }
241
244
 
245
+ /** Run the helper in this pass; retain process isolation for troubleshooting. */
246
+ async function runCodexDeployer(scriptPath, args, helperRoot, opts) {
247
+ if (process.env.AIWG_DEPLOY_SUBPROCESS !== '1') {
248
+ const deployer = await import(pathToFileURL(scriptPath).href);
249
+ await deployer.main(args, { homeDir: opts.deploymentHome || os.homedir() });
250
+ return;
251
+ }
252
+ await new Promise((resolve, reject) => {
253
+ const child = spawn('node', [scriptPath, ...args], {
254
+ stdio: 'inherit',
255
+ cwd: helperRoot,
256
+ env: opts.deploymentHome ? { ...process.env, HOME: opts.deploymentHome } : process.env,
257
+ });
258
+ child.on('close', code => {
259
+ if (code === 0) resolve();
260
+ else reject(new Error(`${path.basename(scriptPath)} exited with code ${code}`));
261
+ });
262
+ child.on('error', reject);
263
+ });
264
+ }
265
+
242
266
  /**
243
267
  * Deploy commands via external script
244
268
  *
@@ -257,30 +281,16 @@ export async function deployCommands(targetDir, srcRoot, opts) {
257
281
 
258
282
  console.log('Delegating command deployment to deploy-prompts-codex.mjs (~/.codex/prompts/)...');
259
283
 
260
- return new Promise((resolve, reject) => {
261
- // NOTE: Do NOT pass --target - Codex prompts belong in ~/.codex/prompts/ (home)
262
- const args = ['--source', srcRoot];
263
- if (opts.dryRun) args.push('--dry-run');
264
- if (opts.force) args.push('--force');
265
- if (opts.mode) args.push('--mode', opts.mode);
266
- if (opts.copyStandardSkills === true) args.push('--copy-all');
267
- // A copy-all the deployer forced on the caller's behalf (project-local
268
- // bundles) still honors the startup listing cap (#2561).
269
- if (opts.listingBudget === true) args.push('--listing-budget');
270
-
271
- const child = spawn('node', [scriptPath, ...args], {
272
- stdio: 'inherit',
273
- cwd: helperRoot,
274
- env: opts.deploymentHome ? { ...process.env, HOME: opts.deploymentHome } : process.env
275
- });
276
-
277
- child.on('close', (code) => {
278
- if (code === 0) resolve();
279
- else reject(new Error(`deploy-prompts-codex.mjs exited with code ${code}`));
280
- });
281
-
282
- child.on('error', reject);
283
- });
284
+ // NOTE: Do NOT pass --target - Codex prompts belong in ~/.codex/prompts/ (home)
285
+ const args = ['--source', srcRoot];
286
+ if (opts.dryRun) args.push('--dry-run');
287
+ if (opts.force) args.push('--force');
288
+ if (opts.mode) args.push('--mode', opts.mode);
289
+ if (opts.copyStandardSkills === true) args.push('--copy-all');
290
+ // A copy-all the deployer forced on the caller's behalf (project-local
291
+ // bundles) still honors the startup listing cap (#2561).
292
+ if (opts.listingBudget === true) args.push('--listing-budget');
293
+ await runCodexDeployer(scriptPath, args, helperRoot, opts);
284
294
  }
285
295
 
286
296
  /**
@@ -305,37 +315,23 @@ export async function deploySkills(targetDir, srcRoot, opts) {
305
315
  const crossAgentSkillsDir = path.join(targetDir, '.agents', 'skills');
306
316
  console.log(`Deploying skills to ${crossAgentSkillsDir} (.agents/skills — codex-scanned path)...`);
307
317
 
308
- await new Promise((resolve, reject) => {
309
- const args = ['--source', srcRoot, '--target', crossAgentSkillsDir];
310
- // Overflow past Codex's startup listing cap lands here instead of being
311
- // deployed over the cap (#2561); the index still reaches these skills.
312
- args.push('--standard-target', path.join(targetDir, ...paths.skills.split('/').filter(Boolean)));
313
- if (opts.dryRun) args.push('--dry-run');
314
- if (opts.force) args.push('--force');
315
- if (opts.mode) args.push('--mode', opts.mode);
316
- if (opts.copyStandardSkills === true) args.push('--copy-all');
317
- // A copy-all the deployer forced on the caller's behalf (project-local
318
- // bundles) still honors the startup listing cap (#2561).
319
- if (opts.listingBudget === true) args.push('--listing-budget');
320
-
321
- const child = spawn('node', [scriptPath, ...args], {
322
- stdio: 'inherit',
323
- cwd: helperRoot,
324
- env: opts.deploymentHome ? { ...process.env, HOME: opts.deploymentHome } : process.env
325
- });
326
-
327
- child.on('close', (code) => {
328
- if (code === 0) resolve();
329
- else reject(new Error(`deploy-skills-codex.mjs exited with code ${code}`));
330
- });
331
-
332
- child.on('error', reject);
333
- });
318
+ const args = ['--source', srcRoot, '--target', crossAgentSkillsDir];
319
+ // Overflow past Codex's startup listing cap lands here instead of being
320
+ // deployed over the cap (#2561); the index still reaches these skills.
321
+ args.push('--standard-target', path.join(targetDir, ...paths.skills.split('/').filter(Boolean)));
322
+ if (opts.dryRun) args.push('--dry-run');
323
+ if (opts.force) args.push('--force');
324
+ if (opts.mode) args.push('--mode', opts.mode);
325
+ if (opts.copyStandardSkills === true) args.push('--copy-all');
326
+ // A copy-all the deployer forced on the caller's behalf (project-local
327
+ // bundles) still honors the startup listing cap (#2561).
328
+ if (opts.listingBudget === true) args.push('--listing-budget');
329
+ await runCodexDeployer(scriptPath, args, helperRoot, opts);
334
330
 
335
331
  // Self-heal: prune AIWG-managed skill dirs left behind in the legacy
336
332
  // ~/.codex/skills/ home location by pre-fix versions, so codex-rs stops
337
333
  // listing each skill twice.
338
- pruneLegacyCodexSkills(opts, opts.legacyCodexSkills || legacyHomeSkillsDir);
334
+ pruneLegacyCodexSkills(opts, opts.legacyCodexSkills || path.join(os.homedir(), '.codex', 'skills'));
339
335
  }
340
336
 
341
337
  /**
@@ -346,7 +342,7 @@ export async function deploySkills(targetDir, srcRoot, opts) {
346
342
  * the `.aiwg-managed` marker are removed — user-authored skills are never
347
343
  * touched. The now-empty legacy dir is removed if AIWG owned everything in it.
348
344
  */
349
- export function pruneLegacyCodexSkills(opts = {}, legacyDir = legacyHomeSkillsDir) {
345
+ export function pruneLegacyCodexSkills(opts = {}, legacyDir = path.join(os.homedir(), '.codex', 'skills')) {
350
346
  let entries;
351
347
  try {
352
348
  entries = fs.readdirSync(legacyDir, { withFileTypes: true });
@@ -401,6 +397,7 @@ export function deployRules(ruleFiles, targetDir, opts) {
401
397
  * Aggregate agents to single AGENTS.md file
402
398
  */
403
399
  export function aggregateToAgentsMd(agentFiles, destPath, opts) {
400
+ if (opts.contextFiles === false) return 0;
404
401
  const lines = ['# AIWG agent definitions', '', 'Read the selected definition before invoking an agent.', ''];
405
402
  for (const file of agentFiles) {
406
403
  const { metadata } = parseFrontmatter(fs.readFileSync(file, 'utf8'));
@@ -409,7 +406,7 @@ export function aggregateToAgentsMd(agentFiles, destPath, opts) {
409
406
  }
410
407
  const out = `${lines.join('\n')}\n`;
411
408
  if (opts.dryRun) console.log(`[dry-run] write ${destPath}`);
412
- else fs.writeFileSync(destPath, out, 'utf8');
409
+ else writeContextFileSync(destPath, out, 'utf8');
413
410
 
414
411
  }
415
412
 
@@ -518,7 +515,7 @@ export async function postDeploy(targetDir, opts) {
518
515
  initializeFrameworkWorkspace(targetDir, opts.mode, opts.dryRun, opts.srcRoot);
519
516
 
520
517
  if (opts.createAgentsMd) {
521
- createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
518
+ if (opts.contextFiles !== false) createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
522
519
  }
523
520
  }
524
521
 
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Context-file writes for deployment providers (#3037).
3
+ *
4
+ * The journaled writer lives in compiled TypeScript. In source-resolution
5
+ * mode (AIWG_RESOLVE_IMPL_FROM=src without a TS loader) it cannot load, and
6
+ * read-only consumers of provider modules (for example the remotes topology
7
+ * block) must still import cleanly. Writes then fail closed with the original
8
+ * load error, so nothing ever writes context files without the safety layer.
9
+ */
10
+ import { importImpl } from '../../_resolve-impl.mjs';
11
+
12
+ // Mirrors CONTEXT_STAGE_ENV in src/smiths/context-pipeline/context-file-safety.ts.
13
+ export const CONTEXT_STAGE_ENV = 'AIWG_CONTEXT_STAGE_LOG';
14
+
15
+ let loadError = null;
16
+ let writer = null;
17
+ try {
18
+ writer = await importImpl(import.meta.url, 'smiths/context-pipeline/context-file-safety.js');
19
+ } catch (error) {
20
+ loadError = error;
21
+ }
22
+
23
+ export function writeContextFileSync(file, content, encoding) {
24
+ if (!writer) {
25
+ throw new Error(`Context-file safety layer is unavailable; refusing to write ${file}`, { cause: loadError });
26
+ }
27
+ return writer.writeContextFileSync(file, content, encoding);
28
+ }
@@ -1,3 +1,4 @@
1
+ import { importImpl } from '../../_resolve-impl.mjs';
1
2
  /**
2
3
  * GitHub Copilot Provider
3
4
  *
@@ -27,6 +28,7 @@ let fs;
27
28
  try { const gfs = _require('graceful-fs'); gfs.gracefulify(realFs); fs = realFs; } catch { fs = realFs; }
28
29
  import path from 'path';
29
30
  import {
31
+ withRuleDeployment,
30
32
  ensureDir,
31
33
  listMdFiles,
32
34
  listMdFilesRecursive,
@@ -53,6 +55,8 @@ import {
53
55
  deploySoulCompanions,
54
56
  loadRuntimeModelCatalog
55
57
  } from './base.mjs';
58
+
59
+ import { writeContextFileSync } from './context-write.mjs';
56
60
  const modelCatalog = loadRuntimeModelCatalog(staticModelCatalog);
57
61
 
58
62
  // ============================================================================
@@ -429,6 +433,7 @@ export function deploySkills(skillDirs, targetDir, opts) {
429
433
  * Deploy rules to .github/instructions/ as .instructions.md files
430
434
  */
431
435
  export function deployRules(ruleFiles, targetDir, opts) {
436
+ if (opts.ruleDelivery) return [];
432
437
  const destDir = path.join(targetDir, paths.rules);
433
438
  ensureDir(destDir, opts.dryRun);
434
439
  cleanupOldRuleFiles(destDir, opts);
@@ -475,14 +480,14 @@ export function createCopilotInstructions(target, srcRoot, dryRun) {
475
480
  if (dryRun) {
476
481
  console.log(`[dry-run] Would update existing copilot-instructions.md with AIWG section`);
477
482
  } else {
478
- fs.writeFileSync(destPath, combined, 'utf8');
483
+ writeContextFileSync(destPath, combined, 'utf8');
479
484
  console.log('Updated copilot-instructions.md with AIWG SDLC framework section');
480
485
  }
481
486
  } else {
482
487
  if (dryRun) {
483
488
  console.log(`[dry-run] Would create copilot-instructions.md from template`);
484
489
  } else {
485
- fs.writeFileSync(destPath, template, 'utf8');
490
+ writeContextFileSync(destPath, template, 'utf8');
486
491
  console.log('Created copilot-instructions.md from template');
487
492
  }
488
493
  }
@@ -495,8 +500,8 @@ export function createCopilotInstructions(target, srcRoot, dryRun) {
495
500
  export async function postDeploy(targetDir, opts) {
496
501
  initializeFrameworkWorkspace(targetDir, opts.mode, opts.dryRun, opts.srcRoot);
497
502
 
498
- // Create copilot-instructions.md
499
- createCopilotInstructions(targetDir, opts.srcRoot, opts.dryRun);
503
+ // copilot-instructions.md is Copilot's context file: --no-context-files leaves it alone.
504
+ if (opts.contextFiles !== false) createCopilotInstructions(targetDir, opts.srcRoot, opts.dryRun);
500
505
  }
501
506
 
502
507
  // ============================================================================
@@ -517,6 +522,10 @@ export function getFileExtension(type) {
517
522
  // ============================================================================
518
523
 
519
524
  export async function deploy(opts) {
525
+ return withRuleDeployment(opts, { name, paths, kernelSkillsPath }, deploySelected);
526
+ }
527
+
528
+ async function deploySelected(opts) {
520
529
  const {
521
530
  srcRoot,
522
531
  target,
@@ -414,7 +414,7 @@ export async function postDeploy(targetDir, opts) {
414
414
  initializeFrameworkWorkspace(targetDir, opts.mode, opts.dryRun, opts.srcRoot);
415
415
 
416
416
  if (opts.createAgentsMd) {
417
- createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
417
+ if (opts.contextFiles !== false) createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
418
418
  }
419
419
 
420
420
  // PUW-019 (#1120): wire environment.json + worktrees.json templates.
@@ -46,7 +46,7 @@ export function deployCordisPatch(target, opts) {
46
46
  }
47
47
 
48
48
  export async function postDeploy(target, opts) {
49
- if (opts.createAgentsMd || (!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly)) createAgentsMd(target, opts.srcRoot, opts.dryRun);
49
+ if (opts.contextFiles !== false && (opts.createAgentsMd || (!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly))) createAgentsMd(target, opts.srcRoot, opts.dryRun);
50
50
  if (!opts.quiet) console.log('DeepSeek Harness: runtime credentials remain environment-only; use a reviewed ephemeral route patch.');
51
51
  }
52
52
 
@@ -25,6 +25,7 @@ let fs;
25
25
  try { const gfs = _require('graceful-fs'); gfs.gracefulify(realFs); fs = realFs; } catch { fs = realFs; }
26
26
  import path from 'path';
27
27
  import {
28
+ withRuleDeployment,
28
29
  ensureDir,
29
30
  listMdFiles,
30
31
  listMdFilesRecursive,
@@ -576,6 +577,7 @@ export function deploySkills(skillDirs, targetDir, opts) {
576
577
  * Deploy rules to .factory/rules/
577
578
  */
578
579
  export function deployRules(ruleFiles, targetDir, opts) {
580
+ if (opts.ruleDelivery) return [];
579
581
  const destDir = path.join(targetDir, paths.rules);
580
582
  ensureDir(destDir, opts.dryRun);
581
583
  cleanupOldRuleFiles(destDir, opts);
@@ -816,7 +818,7 @@ export async function postDeploy(targetDir, opts) {
816
818
 
817
819
  // Create/update AGENTS.md if requested
818
820
  if (opts.createAgentsMd) {
819
- createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
821
+ if (opts.contextFiles !== false) createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
820
822
  }
821
823
  }
822
824
 
@@ -945,6 +947,10 @@ export function getFileExtension(type) {
945
947
  * Main deployment function for Factory provider
946
948
  */
947
949
  export async function deploy(opts) {
950
+ return withRuleDeployment(opts, { name, paths, kernelSkillsPath, transformSkillMd: transformSkillFrontmatter }, deploySelected);
951
+ }
952
+
953
+ async function deploySelected(opts) {
948
954
  const {
949
955
  srcRoot,
950
956
  target,
@@ -16,6 +16,8 @@ import fs from 'node:fs';
16
16
  import { createHash, randomUUID } from 'node:crypto';
17
17
  import yaml from 'js-yaml';
18
18
  import {
19
+ withRuleDeployment,
20
+ replaceBoundRulesBlock,
19
21
  createAgentsMdFromTemplate,
20
22
  deploySkillsWithKernelRouting,
21
23
  collectFrameworkArtifacts,
@@ -224,7 +226,17 @@ function matchesSkillSource(source, target) {
224
226
  if (relative.endsWith(path.sep)) return true;
225
227
  let content = fs.readFileSync(path.join(source, relative), 'utf8');
226
228
  if (relative === 'SKILL.md') content = injectPlatformInContent(content, 'grok-build');
227
- return content === fs.readFileSync(path.join(target, relative), 'utf8');
229
+ const deployed = fs.readFileSync(path.join(target, relative), 'utf8');
230
+ if (content === deployed) return true;
231
+ if (relative !== 'SKILL.md' || !deployed.includes('<!-- AIWG:bound-rules:start -->')) return false;
232
+ // Activation projection appends generated bindings. Verify the complete
233
+ // deployed bytes against the receipt before comparing the authored body;
234
+ // an operator edit (including inside the binding block) must survive.
235
+ const receipt = JSON.parse(fs.readFileSync(path.join(target, '.aiwg-manifest.json'), 'utf8'));
236
+ const digest = `sha256:${createHash('sha256').update(deployed).digest('hex')}`;
237
+ return receipt.skillSource === path.resolve(source)
238
+ && receipt.managed?.[relative]?.hash === digest
239
+ && replaceBoundRulesBlock(content, []).trimEnd() === replaceBoundRulesBlock(deployed, []).trimEnd();
228
240
  });
229
241
  } catch { return false; }
230
242
  }
@@ -326,7 +338,7 @@ export function uninstall(target, opts = {}) {
326
338
 
327
339
  export async function postDeploy(target, opts = {}) {
328
340
  if (opts.createAgentsMd || (!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly)) {
329
- createAgentsMd(target, opts.srcRoot, opts.dryRun);
341
+ if (opts.contextFiles !== false) createAgentsMd(target, opts.srcRoot, opts.dryRun);
330
342
  }
331
343
  if (!opts.quiet) {
332
344
  const home = resolveGrokHome(opts.env || process.env);
@@ -344,6 +356,10 @@ export function getFileExtension() {
344
356
  }
345
357
 
346
358
  export async function deploy(opts) {
359
+ return withRuleDeployment(opts, { name, paths, kernelSkillsPath }, deploySelected);
360
+ }
361
+
362
+ async function deploySelected(opts) {
347
363
  const mode = normalizeDeploymentMode(opts.mode);
348
364
  if (opts.createAgentsMd || (!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly)) {
349
365
  assertSafeAgentsMd(opts.target);
@@ -171,7 +171,7 @@ export async function postDeploy(targetDir, opts = {}) {
171
171
  opts.createAgentsMd ||
172
172
  (!opts.commandsOnly && !opts.skillsOnly && !opts.rulesOnly)
173
173
  ) {
174
- createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
174
+ if (opts.contextFiles !== false) createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
175
175
  }
176
176
  if (!opts.quiet) {
177
177
  const root = resolveGrokbotSkillsDir(opts.env || process.env);
@@ -1,3 +1,4 @@
1
+ import { importImpl } from '../../_resolve-impl.mjs';
1
2
  /**
2
3
  * Hermes Agent Provider
3
4
  *
@@ -41,6 +42,8 @@ import {
41
42
  stripPlatformsFromContent,
42
43
  } from './base.mjs';
43
44
 
45
+ import { writeContextFileSync } from './context-write.mjs';
46
+
44
47
  // ============================================================================
45
48
  // Hermes home resolution (HERMES_HOME) — #2119
46
49
  // ============================================================================
@@ -224,9 +227,9 @@ export function assertContextBudget(content, name = 'AGENTS.md', rules = []) {
224
227
  * priming on every Hermes turn (#1318 / S7; skill-discovery added #1347).
225
228
  *
226
229
  * The full rule bodies are reachable without MCP via `aiwg discover` and
227
- * `aiwg show rule <name>`; MCP exposes the same path through `rule-list` and
230
+ * `aiwg show rule <name> --body`; MCP exposes the same path through `rule-list` and
228
231
  * `rule-show` when configured (#1320). The bodies exceed Hermes's 20K context
229
- * cap if inlined verbatim (anti-laziness alone is 32K chars), so we inline
232
+ * cap if inlined verbatim (anti-laziness alone was 32K chars; it now ships as a compact core body, #3450), so we inline
230
233
  * compressed directives only and point to CLI/MCP for the full text.
231
234
  *
232
235
  * Selection criteria: rules tagged CRITICAL/HIGH enforcement level whose
@@ -240,7 +243,7 @@ const CRITICAL_RULE_DIRECTIVES = `## CRITICAL Rules (always apply)
240
243
  These are the highest-enforcement AIWG rules. Full bodies are reachable without
241
244
  MCP:
242
245
  - Find rules: \`aiwg discover "rule <topic>" --type rule\`
243
- - Fetch a rule: \`aiwg show rule <name>\`
246
+ - Fetch a rule: \`aiwg show rule <name> --body\`
244
247
 
245
248
  If the optional MCP sidecar is configured, the same surface is available through
246
249
  \`mcp_aiwg_rule_list\` and \`mcp_aiwg_rule_show\`.
@@ -260,13 +263,6 @@ Never add AI-tool attribution to commits, PRs, code, or docs. No \`Co-Authored-B
260
263
  no "Generated with", no "Written by [AI tool]". The AI is a tool; tools don't sign
261
264
  their output. Applies to ALL platforms (Claude, Codex, Copilot, Cursor, etc.).
262
265
 
263
- ### Rule: anti-laziness
264
- Never delete tests to make them pass. Never skip/disable tests. Never remove
265
- features instead of fixing them. Never weaken assertions to be meaningless.
266
- Never suppress CI/pipeline signals (\`continue-on-error\`, \`|| true\`, \`set +e\`).
267
- If stuck after 3 honest attempts: escalate with full context, don't shortcut.
268
- Within scope: leave nothing half-done — code + tests + docs + verification.
269
-
270
266
  ### Rule: citation-policy
271
267
  Never fabricate citations, DOIs, URLs, or page numbers. Only cite sources that
272
268
  exist in the research corpus (.aiwg/research/sources/). Match claim strength
@@ -331,6 +327,7 @@ function mergeHermesContext(file, generated, kind) {
331
327
  }
332
328
 
333
329
  export function generateAgentsMd(agentCount, skillCount, targetDir, opts) {
330
+ if (opts.contextFiles === false) return;
334
331
  const { dryRun } = opts;
335
332
 
336
333
  // Startup is reserved for rule contracts. Workflow routing, artifact catalogs,
@@ -339,7 +336,7 @@ export function generateAgentsMd(agentCount, skillCount, targetDir, opts) {
339
336
  const header = `# AIWG Integration
340
337
 
341
338
  Skills: \`$HERMES_HOME/skills/\` (kernel), \`$HERMES_HOME/skills/.aiwg/\` (standard).
342
- Find: \`aiwg discover "rule <topic>" --type rule\`. Fetch: \`aiwg show rule <name>\`.
339
+ Find: \`aiwg discover "rule <topic>" --type rule\`. Fetch: \`aiwg show rule <name> --body\`.
343
340
  MCP is optional; \`mcp_aiwg_rule_show\` exposes the same rule catalog.
344
341
  `;
345
342
 
@@ -361,7 +358,7 @@ MCP is optional; \`mcp_aiwg_rule_show\` exposes the same rule catalog.
361
358
  if (dryRun) {
362
359
  console.log(`[dry-run] Would write AGENTS.md (${output.length} chars, ${Math.round(output.length / 4)} tokens estimated)`);
363
360
  } else {
364
- fs.writeFileSync(destPath, output, 'utf8');
361
+ writeContextFileSync(destPath, output, 'utf8');
365
362
  const charCount = output.length;
366
363
  const tokenEstimate = Math.round(charCount / 4);
367
364
  let budgetNote;
@@ -403,7 +400,7 @@ when the full AIWG project context is needed.
403
400
  if (dryRun) {
404
401
  console.log(`[dry-run] Would write .hermes.md (${body.length} chars)`);
405
402
  } else {
406
- fs.writeFileSync(destPath, mergeHermesContext(destPath, body, 'routing'), 'utf8');
403
+ writeContextFileSync(destPath, mergeHermesContext(destPath, body, 'routing'), 'utf8');
407
404
  console.log(` Created .hermes.md (${body.length} chars)`);
408
405
  }
409
406
  return 1;
@@ -533,7 +530,7 @@ async function deploySelected(opts) {
533
530
  // Generate AGENTS.md (with inlined CRITICAL rule priming, #1318/#1532)
534
531
  // AND .hermes.md thin pointer (resolves #1319 doc-debt, was claimed in
535
532
  // CHANGELOG but not previously implemented).
536
- if (!skillsOnly && !opts.commandsOnly && !opts.rulesOnly) {
533
+ if (opts.contextFiles !== false && !skillsOnly && !opts.commandsOnly && !opts.rulesOnly) {
537
534
  const artifacts = collectFrameworkArtifacts(srcRoot, normalizedMode, {
538
535
  includeAgents: true,
539
536
  includeCommands: false,
@@ -560,7 +557,7 @@ async function deploySelected(opts) {
560
557
  if (!opts.quiet) {
561
558
  console.log('');
562
559
  console.log(`Skills root: ${kernelSkillsPath}`);
563
- console.log('Rules are in AGENTS.md as compressed directives; full bodies via `aiwg show rule <name>`.');
560
+ console.log('Rules are in AGENTS.md as compressed directives; full bodies via `aiwg show rule <name> --body`.');
564
561
  console.log('Optional: configure config.yaml to connect AIWG MCP server.');
565
562
  console.log('See: docs/integrations/hermes-quickstart.md (optional MCP setup)');
566
563
  }
@@ -43,9 +43,7 @@
43
43
  * Servers inherit the operator's process environment; pass `MUSE_SESSION_ID`
44
44
  * through at runtime instead of hardcoding a session id into `env`.
45
45
  *
46
- * This module is intentionally dependency-free (node builtins +
47
- * muse-paths.mjs only) so provider-policy-style import graphs never pull it
48
- * in transitively.
46
+ * MCP writes use the shared config writer, resolved for source and packaged installs.
49
47
  *
50
48
  * @issue #228
51
49
  */
@@ -53,9 +51,13 @@
53
51
  import fs from 'node:fs';
54
52
  import os from 'node:os';
55
53
  import path from 'node:path';
54
+ import { randomUUID } from 'node:crypto';
55
+ import { importImpl } from '../../_resolve-impl.mjs';
56
56
  import { stripJsonComments } from './base.mjs';
57
57
  import { resolveMuseXdgSkillsDir, museXdgSkillsDirRemediation } from './muse-paths.mjs';
58
58
 
59
+ const { assertConfigDestination, writeConfigAtomic, writeConfigTransaction } = await importImpl(import.meta.url, 'mcp/config-file.mjs');
60
+
59
61
  // ---------------------------------------------------------------------------
60
62
  // Shared JSON helpers
61
63
  // ---------------------------------------------------------------------------
@@ -631,7 +633,7 @@ export function mergeMcpProfile(existingDoc, opts = {}) {
631
633
  * preserving unknown keys, timestamped backup before writing, atomic write,
632
634
  * dry-run diff with zero writes, fail closed on unparseable settings.
633
635
  */
634
- export function deployMuseMcp(opts = {}) {
636
+ export async function deployMuseMcp(opts = {}) {
635
637
  const { dryRun = false, quiet = false, mcp = false, env = process.env, userHome = os.homedir() } = opts;
636
638
  const log = quiet ? () => {} : (msg) => console.log(msg);
637
639
 
@@ -642,6 +644,8 @@ export function deployMuseMcp(opts = {}) {
642
644
 
643
645
  const settingsPath = assertMuseUserSettingsPath(env, userHome);
644
646
  const configDir = path.dirname(settingsPath);
647
+ await assertConfigDestination(settingsPath);
648
+ await assertConfigDestination(path.join(configDir, MUSE_MCP_SIDECAR_NAME));
645
649
  const { doc: existingDoc, raw: existingRaw, existed } = readSettingsDoc(settingsPath);
646
650
  const sidecar = readMcpSidecar(configDir);
647
651
  const profile = aiwgMcpServerProfile();
@@ -680,14 +684,15 @@ export function deployMuseMcp(opts = {}) {
680
684
  return { settingsPath, changed: true, plan, wrote: false, warnings };
681
685
  }
682
686
 
683
- fs.mkdirSync(configDir, { recursive: true });
684
687
  let backupPath = null;
685
688
  if (existed) {
686
- backupPath = `${settingsPath}.aiwg-backup-${new Date().toISOString().replace(/[:.]/g, '-')}`;
687
- fs.copyFileSync(settingsPath, backupPath);
689
+ backupPath = `${settingsPath}.aiwg-backup-${new Date().toISOString().replace(/[:.]/g, '-')}-${randomUUID()}`;
690
+ await writeConfigAtomic(backupPath, existingRaw, { userScope: true });
688
691
  }
689
- atomicWrite(settingsPath, afterText);
690
- atomicWrite(sidecar.path, canonicalJson({ version: 1, servers: ['aiwg'] }));
692
+ await writeConfigTransaction([
693
+ { file: settingsPath, content: afterText, options: { userScope: true } },
694
+ { file: sidecar.path, content: canonicalJson({ version: 1, servers: ['aiwg'] }), options: { userScope: true } },
695
+ ]);
691
696
 
692
697
  log(`Muse MCP: merged AIWG stdio profile into ${settingsPath}`);
693
698
  for (const step of plan) log(` ${step}`);
@@ -700,11 +705,13 @@ export function deployMuseMcp(opts = {}) {
700
705
  * Removal path: drop the AIWG-managed `aiwg` server key (only when recorded
701
706
  * in the sidecar) and delete the sidecar. Operator servers are never touched.
702
707
  */
703
- export function pruneMuseMcp(opts = {}) {
708
+ export async function pruneMuseMcp(opts = {}) {
704
709
  const { dryRun = false, quiet = false, env = process.env, userHome = os.homedir() } = opts;
705
710
  const log = quiet ? () => {} : (msg) => console.log(msg);
706
711
  const settingsPath = assertMuseUserSettingsPath(env, userHome);
707
712
  const configDir = path.dirname(settingsPath);
713
+ await assertConfigDestination(settingsPath);
714
+ await assertConfigDestination(path.join(configDir, MUSE_MCP_SIDECAR_NAME));
708
715
 
709
716
  if (!fs.existsSync(settingsPath)) {
710
717
  log(`Muse MCP: no ${settingsPath}; nothing to prune.`);
@@ -731,10 +738,10 @@ export function pruneMuseMcp(opts = {}) {
731
738
  return { settingsPath, pruned: true, wrote: false };
732
739
  }
733
740
 
734
- const backupPath = `${settingsPath}.aiwg-backup-${new Date().toISOString().replace(/[:.]/g, '-')}`;
735
- fs.copyFileSync(settingsPath, backupPath);
736
- atomicWrite(settingsPath, canonicalJson(merged));
737
- if (fs.existsSync(sidecar.path)) fs.unlinkSync(sidecar.path);
741
+ const backupPath = `${settingsPath}.aiwg-backup-${new Date().toISOString().replace(/[:.]/g, '-')}-${randomUUID()}`;
742
+ await writeConfigAtomic(backupPath, existingRaw, { userScope: true });
743
+ await writeConfigAtomic(settingsPath, canonicalJson(merged), { userScope: true });
744
+ if (await assertConfigDestination(sidecar.path)) fs.unlinkSync(sidecar.path);
738
745
  log(`Muse MCP: removed mcp_servers.aiwg from ${settingsPath} (backup: ${backupPath}).`);
739
746
  return { settingsPath, pruned: true, wrote: true, backupPath };
740
747
  }
@@ -35,6 +35,7 @@ import fs from 'node:fs';
35
35
  import os from 'node:os';
36
36
  import path from 'node:path';
37
37
  import {
38
+ withRuleDeployment,
38
39
  collectFrameworkArtifacts,
39
40
  createAgentsMdFromTemplate,
40
41
  deploySkillsWithKernelRouting,
@@ -226,7 +227,7 @@ export async function postDeploy(targetDir, opts = {}) {
226
227
  // bridge, managed hooks, and an opted-in MCP profile must not be skipped.
227
228
  const fullDeploy = !opts.commandsOnly && !opts.rulesOnly && (!opts.skillsOnly || opts.kernelOnly === true);
228
229
  if (opts.createAgentsMd || fullDeploy) {
229
- createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
230
+ if (opts.contextFiles !== false) createAgentsMd(targetDir, opts.srcRoot, opts.dryRun);
230
231
  }
231
232
 
232
233
  // #228 — managed project hooks (.muse/hooks.json). Default on, mirroring
@@ -240,7 +241,7 @@ export async function postDeploy(targetDir, opts = {}) {
240
241
  // #228 — optional MCP settings profile. Explicit opt-in only (--mcp);
241
242
  // default `aiwg use --provider muse` never touches user settings.
242
243
  if (fullDeploy && opts.mcp === true) {
243
- deployMuseMcp(opts);
244
+ await deployMuseMcp(opts);
244
245
  }
245
246
 
246
247
  if (!opts.quiet) {
@@ -265,6 +266,10 @@ export async function postDeploy(targetDir, opts = {}) {
265
266
  }
266
267
 
267
268
  export async function deploy(opts = {}) {
269
+ return withRuleDeployment(opts, { name, paths, kernelSkillsPath }, deploySelected);
270
+ }
271
+
272
+ async function deploySelected(opts) {
268
273
  const {
269
274
  srcRoot,
270
275
  target,