@aiwg/cli 2026.9.20 → 2026.9.24

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 (275) hide show
  1. package/agentic/code/providers/capability-matrix.yaml +58 -6
  2. package/agentic/code/providers/model-capabilities.v1.json +34 -0
  3. package/agentic/code/providers/model-catalog.v1.json +55 -13
  4. package/bin/aiwg.mjs +2 -0
  5. package/dist/src/api/index.d.ts +1 -0
  6. package/dist/src/api/index.js +1 -0
  7. package/dist/src/artifacts/cli.js +23 -18
  8. package/dist/src/artifacts/enrichment/cli.js +1 -1
  9. package/dist/src/artifacts/fortemi-core-query-adapter.js +6 -2
  10. package/dist/src/artifacts/query-engine.js +14 -4
  11. package/dist/src/artifacts/views/cli.js +1 -1
  12. package/dist/src/audit/operator-decision.js +10 -0
  13. package/dist/src/auth/credential-store.js +127 -27
  14. package/dist/src/catalog/builtin-models.json +272 -40
  15. package/dist/src/catalog/cli.js +1 -1
  16. package/dist/src/catalog/cli.mjs +1 -1
  17. package/dist/src/cli/handlers/effect.js +872 -0
  18. package/dist/src/cli/handlers/help.js +2 -0
  19. package/dist/src/cli/handlers/index.js +4 -2
  20. package/dist/src/cli/handlers/models.js +41 -10
  21. package/dist/src/cli/handlers/ralph.js +1 -1
  22. package/dist/src/cli/handlers/sessions.js +113 -37
  23. package/dist/src/cli/handlers/subcommands.js +31 -0
  24. package/dist/src/cli/handlers/use.js +46 -9
  25. package/dist/src/cli/handlers/utilities.js +10 -0
  26. package/dist/src/cli/handlers/workspace.js +3 -1
  27. package/dist/src/cli/scope-resolver.js +112 -9
  28. package/dist/src/cli/services/deployment-verification.js +5 -0
  29. package/dist/src/config/aiwg-config.js +3 -0
  30. package/dist/src/decision/acceptance-rollout.js +93 -0
  31. package/dist/src/decision/acceptance.js +175 -0
  32. package/dist/src/decision/adapters/jev.js +234 -13
  33. package/dist/src/decision/adapters/llm-subagent.js +60 -12
  34. package/dist/src/decision/admission.js +550 -0
  35. package/dist/src/decision/batch-benchmark.js +47 -0
  36. package/dist/src/decision/batch-receipts/accounting.js +75 -0
  37. package/dist/src/decision/batch-receipts/export.js +40 -0
  38. package/dist/src/decision/batch-receipts/index.js +9 -0
  39. package/dist/src/decision/batch-receipts/protection.js +40 -0
  40. package/dist/src/decision/batch-receipts/receipt.js +44 -0
  41. package/dist/src/decision/batch-receipts/result-store.js +341 -0
  42. package/dist/src/decision/batch-receipts/store.js +362 -0
  43. package/dist/src/decision/batch-receipts/types.js +2 -0
  44. package/dist/src/decision/batch-receipts/validate.js +127 -0
  45. package/dist/src/decision/batch.js +68 -0
  46. package/dist/src/decision/calibration/index.js +4 -0
  47. package/dist/src/decision/calibration/receipts.js +130 -0
  48. package/dist/src/decision/calibration/registry.js +258 -0
  49. package/dist/src/decision/calibration/types.js +2 -0
  50. package/dist/src/decision/compile-cache/benchmark.js +62 -0
  51. package/dist/src/decision/compile-cache/compatibility.js +65 -0
  52. package/dist/src/decision/compile-cache/file-store.js +280 -0
  53. package/dist/src/decision/compile-cache/identity.js +26 -0
  54. package/dist/src/decision/compile-cache/index.js +9 -0
  55. package/dist/src/decision/compile-cache/prefix.js +21 -0
  56. package/dist/src/decision/compile-cache/records.js +86 -0
  57. package/dist/src/decision/compile-cache/runtime.js +68 -0
  58. package/dist/src/decision/compile-cache/store.js +108 -0
  59. package/dist/src/decision/compile-cache/types.js +2 -0
  60. package/dist/src/decision/context-plan.js +234 -0
  61. package/dist/src/decision/context-qualification.js +60 -0
  62. package/dist/src/decision/ensemble/aggregate.js +215 -0
  63. package/dist/src/decision/ensemble/contract.js +316 -0
  64. package/dist/src/decision/ensemble/drift.js +51 -0
  65. package/dist/src/decision/ensemble/index.js +6 -0
  66. package/dist/src/decision/ensemble/report.js +71 -0
  67. package/dist/src/decision/ensemble/types.js +2 -0
  68. package/dist/src/decision/evaluate.js +1024 -78
  69. package/dist/src/decision/file-lifecycle-store.js +168 -0
  70. package/dist/src/decision/graph-beam.js +40 -0
  71. package/dist/src/decision/graph-budget.js +110 -0
  72. package/dist/src/decision/graph-decision-bridge.js +93 -0
  73. package/dist/src/decision/graph-evidence.js +123 -0
  74. package/dist/src/decision/graph-flow-adapter.js +56 -0
  75. package/dist/src/decision/graph-flow.js +105 -0
  76. package/dist/src/decision/graph-parallel.js +79 -0
  77. package/dist/src/decision/graph-patterns.js +20 -0
  78. package/dist/src/decision/graph-run-store.js +66 -0
  79. package/dist/src/decision/graph-run.js +166 -0
  80. package/dist/src/decision/graph-skill-bridge.js +91 -0
  81. package/dist/src/decision/graph-templates.js +56 -0
  82. package/dist/src/decision/graph.js +115 -0
  83. package/dist/src/decision/index.js +35 -0
  84. package/dist/src/decision/job-accounting.js +93 -0
  85. package/dist/src/decision/job-contract.js +186 -0
  86. package/dist/src/decision/job-effects.js +95 -0
  87. package/dist/src/decision/job-evaluate.js +80 -0
  88. package/dist/src/decision/job-gateway.js +121 -0
  89. package/dist/src/decision/job-http.js +194 -0
  90. package/dist/src/decision/job-payload-store.js +258 -0
  91. package/dist/src/decision/job-poll-limiter.js +92 -0
  92. package/dist/src/decision/job-quota.js +158 -0
  93. package/dist/src/decision/job-runtime.js +240 -0
  94. package/dist/src/decision/job-scheduler.js +26 -0
  95. package/dist/src/decision/job-service.js +117 -0
  96. package/dist/src/decision/job-store.js +264 -0
  97. package/dist/src/decision/job-worker.js +141 -0
  98. package/dist/src/decision/lifecycle.js +148 -0
  99. package/dist/src/decision/patterns/artifacts.js +173 -0
  100. package/dist/src/decision/patterns/catalog.js +108 -0
  101. package/dist/src/decision/patterns/durable-review.js +237 -0
  102. package/dist/src/decision/patterns/index.js +8 -0
  103. package/dist/src/decision/patterns/playground.js +401 -0
  104. package/dist/src/decision/patterns/recorded.js +43 -0
  105. package/dist/src/decision/patterns/specs.js +174 -0
  106. package/dist/src/decision/patterns/types.js +2 -0
  107. package/dist/src/decision/portable-secrets.js +53 -0
  108. package/dist/src/decision/projection.js +210 -0
  109. package/dist/src/decision/qualification/capture.js +67 -0
  110. package/dist/src/decision/qualification/drift.js +68 -0
  111. package/dist/src/decision/qualification/gate-evidence.js +172 -0
  112. package/dist/src/decision/qualification/gates.js +83 -0
  113. package/dist/src/decision/qualification/index.js +12 -0
  114. package/dist/src/decision/qualification/load.js +282 -0
  115. package/dist/src/decision/qualification/manifest.js +37 -0
  116. package/dist/src/decision/qualification/privacy.js +41 -0
  117. package/dist/src/decision/qualification/quality.js +213 -0
  118. package/dist/src/decision/qualification/release.js +116 -0
  119. package/dist/src/decision/qualification/runner.js +408 -0
  120. package/dist/src/decision/qualification/types.js +2 -0
  121. package/dist/src/decision/receipts.js +30 -7
  122. package/dist/src/decision/result-cache/index.js +6 -0
  123. package/dist/src/decision/result-cache/integrity.js +28 -0
  124. package/dist/src/decision/result-cache/key.js +28 -0
  125. package/dist/src/decision/result-cache/service.js +92 -0
  126. package/dist/src/decision/result-cache/store.js +327 -0
  127. package/dist/src/decision/result-cache/types.js +3 -0
  128. package/dist/src/decision/review/authorization.js +110 -0
  129. package/dist/src/decision/review/effect-ledger-adapter.js +321 -0
  130. package/dist/src/decision/review/index.js +12 -0
  131. package/dist/src/decision/review/migrate.js +34 -0
  132. package/dist/src/decision/review/operator-audit.js +59 -0
  133. package/dist/src/decision/review/recovery-adapters.js +252 -0
  134. package/dist/src/decision/review/recovery.js +33 -0
  135. package/dist/src/decision/review/service.js +498 -0
  136. package/dist/src/decision/review/store.js +219 -0
  137. package/dist/src/decision/review/types.js +2 -0
  138. package/dist/src/decision/review/validate.js +205 -0
  139. package/dist/src/decision/scheduler.js +115 -0
  140. package/dist/src/decision/telemetry/context.js +42 -0
  141. package/dist/src/decision/telemetry/debug-sidecar.js +152 -0
  142. package/dist/src/decision/telemetry/exporter.js +77 -0
  143. package/dist/src/decision/telemetry/file-debug-backend.js +112 -0
  144. package/dist/src/decision/telemetry/index.js +13 -0
  145. package/dist/src/decision/telemetry/mapping.js +146 -0
  146. package/dist/src/decision/telemetry/metrics.js +164 -0
  147. package/dist/src/decision/telemetry/otlp-http.js +183 -0
  148. package/dist/src/decision/telemetry/redaction.js +93 -0
  149. package/dist/src/decision/telemetry/retention.js +109 -0
  150. package/dist/src/decision/telemetry/runtime.js +310 -0
  151. package/dist/src/decision/telemetry/trace.js +158 -0
  152. package/dist/src/decision/telemetry/types.js +3 -0
  153. package/dist/src/decision/validate.js +135 -5
  154. package/dist/src/effects/checkpoint-sinks.js +94 -0
  155. package/dist/src/effects/checkpoint.js +76 -0
  156. package/dist/src/effects/decision-links.js +54 -0
  157. package/dist/src/effects/errors.js +48 -0
  158. package/dist/src/effects/identity.js +178 -0
  159. package/dist/src/effects/index.js +25 -0
  160. package/dist/src/effects/keyring.js +160 -0
  161. package/dist/src/effects/keys.js +130 -0
  162. package/dist/src/effects/ledger.js +435 -0
  163. package/dist/src/effects/lifecycle.js +110 -0
  164. package/dist/src/effects/lock-recovery.js +269 -0
  165. package/dist/src/effects/reader.js +103 -0
  166. package/dist/src/effects/records.js +114 -0
  167. package/dist/src/effects/redaction.js +51 -0
  168. package/dist/src/effects/schema.js +73 -0
  169. package/dist/src/effects/store.js +239 -0
  170. package/dist/src/effects/types.js +49 -0
  171. package/dist/src/effects/verifiers/decision.js +168 -0
  172. package/dist/src/effects/verifiers/file.js +106 -0
  173. package/dist/src/effects/verifiers/git.js +206 -0
  174. package/dist/src/effects/verifiers/index.js +90 -0
  175. package/dist/src/effects/verifiers/registry.js +148 -0
  176. package/dist/src/effects/verifiers/review.js +123 -0
  177. package/dist/src/effects/verifiers/tracker.js +299 -0
  178. package/dist/src/effects/verifiers/types.js +21 -0
  179. package/dist/src/effects/verify.js +295 -0
  180. package/dist/src/evidence/bundle.js +2 -0
  181. package/dist/src/extensions/commands/definitions.js +28 -0
  182. package/dist/src/extensions/manifest.js +1 -0
  183. package/dist/src/marketplace/provenance.js +25 -27
  184. package/dist/src/mcp/cli.mjs +11 -1
  185. package/dist/src/mcp/grok-build-config.mjs +189 -0
  186. package/dist/src/mcp/registry.js +21 -1
  187. package/dist/src/mcp/registry.mjs +20 -1
  188. package/dist/src/models/grok-build-models.js +356 -0
  189. package/dist/src/models/index.js +1 -0
  190. package/dist/src/models/model-capabilities.v1.json +34 -0
  191. package/dist/src/models/model-catalog.v1.json +55 -13
  192. package/dist/src/models/model-discovery.js +41 -5
  193. package/dist/src/models/provider-policy.js +3 -2
  194. package/dist/src/plugin/skill-command-translator.js +1 -0
  195. package/dist/src/providers/capability-matrix.yaml +58 -6
  196. package/dist/src/providers/grok-build-acp.js +192 -0
  197. package/dist/src/providers/grok-build-dispatch.js +251 -0
  198. package/dist/src/providers/grok-build-headless.js +170 -0
  199. package/dist/src/providers/muse-paths.js +105 -0
  200. package/dist/src/providers/provider-definitions.js +93 -7
  201. package/dist/src/providers/provider-definitions.mjs +21 -1
  202. package/dist/src/providers/provider-inventory.js +3 -0
  203. package/dist/src/security/artifact-attestation.js +4 -8
  204. package/dist/src/security/signing.js +119 -0
  205. package/dist/src/sessions/adapters/grok-build.js +187 -0
  206. package/dist/src/sessions/adapters/muse.js +412 -0
  207. package/dist/src/sessions/contracts.js +1 -1
  208. package/dist/src/sessions/grok-build-cli.js +196 -0
  209. package/dist/src/sessions/index.js +3 -0
  210. package/dist/src/sessions/workspace-discovery.js +9 -5
  211. package/dist/src/skills/deployer.js +33 -5
  212. package/dist/src/smiths/context-pipeline/claude-hook.js +2 -0
  213. package/dist/src/smiths/context-pipeline/context-file-safety.js +18 -0
  214. package/dist/src/smiths/context-pipeline/finalization.js +1 -32
  215. package/dist/src/smiths/context-pipeline/generator.js +12 -0
  216. package/dist/src/smiths/context-pipeline/index.js +1 -1
  217. package/dist/src/smiths/context-pipeline/managed-hook.js +2 -0
  218. package/dist/src/smiths/context-pipeline/muse-bridge.js +50 -0
  219. package/dist/src/smiths/context-pipeline/provider-policy.js +5 -0
  220. package/dist/src/smiths/context-pipeline/workspace-context.js +22 -16
  221. package/dist/src/smiths/hook-bridge/grok-build-translator.js +64 -0
  222. package/dist/src/smiths/hook-bridge/index.js +3 -0
  223. package/dist/src/smiths/hook-bridge/shim.js +2 -0
  224. package/dist/src/smiths/hook-bridge/types.js +5 -0
  225. package/dist/src/smiths/skillsmith/collision-detector.js +1 -0
  226. package/dist/src/storage/protected-files.js +102 -0
  227. package/dist/src/tracker/forge-http.js +371 -0
  228. package/dist/src/tracker/project-inputs.js +41 -0
  229. package/package.json +2 -1
  230. package/schemas/decision/BatchReceipt.v1.schema.json +50 -0
  231. package/schemas/decision/CalibrationArtifact.v1.schema.json +76 -0
  232. package/schemas/decision/CalibrationCompatibility.v1.schema.json +18 -0
  233. package/schemas/decision/CalibrationGovernanceReceipt.v1.schema.json +44 -0
  234. package/schemas/decision/DecisionAdmissionEvidence.v1.schema.json +43 -0
  235. package/schemas/decision/DecisionBinding.schema.json +309 -0
  236. package/schemas/decision/DecisionBinding.v1alpha2.schema.json +369 -0
  237. package/schemas/decision/DecisionCacheTelemetry.v1.schema.json +20 -0
  238. package/schemas/decision/DecisionChampionChallenger.v1.schema.json +100 -0
  239. package/schemas/decision/DecisionCompileCacheEntry.v1.schema.json +35 -0
  240. package/schemas/decision/DecisionCompileCacheIdentity.v1.schema.json +26 -0
  241. package/schemas/decision/DecisionCompileCacheTombstone.v1.schema.json +31 -0
  242. package/schemas/decision/DecisionContextEvidence.v1.schema.json +136 -0
  243. package/schemas/decision/DecisionDefinition.schema.json +159 -0
  244. package/schemas/decision/DecisionDefinition.v1alpha2.schema.json +203 -0
  245. package/schemas/decision/DecisionDriftResponse.v1.schema.json +55 -0
  246. package/schemas/decision/DecisionEnsembleAggregate.v1.schema.json +86 -0
  247. package/schemas/decision/DecisionEnsembleIntegrityReport.v1.schema.json +64 -0
  248. package/schemas/decision/DecisionEnsemblePolicy.v1.schema.json +130 -0
  249. package/schemas/decision/DecisionGraph.v1.schema.json +77 -0
  250. package/schemas/decision/DecisionJob.v1.schema.json +71 -0
  251. package/schemas/decision/DecisionLifecyclePolicy.v1.schema.json +52 -0
  252. package/schemas/decision/DecisionProjectionPolicy.v1.schema.json +65 -0
  253. package/schemas/decision/DecisionProviderPrefixCompatibility.v1.schema.json +45 -0
  254. package/schemas/decision/DecisionProviderPrefixEvidence.v1.schema.json +17 -0
  255. package/schemas/decision/DecisionProviderPrefixIdentity.v1.schema.json +36 -0
  256. package/schemas/decision/DecisionResult.schema.json +524 -0
  257. package/schemas/decision/DecisionResult.v1alpha2.schema.json +619 -0
  258. package/schemas/decision/DecisionResultCacheEntry.v1.schema.json +53 -0
  259. package/schemas/decision/DecisionReview.schema.json +29 -0
  260. package/schemas/decision/DecisionRuleset.schema.json +313 -0
  261. package/schemas/decision/DecisionRuleset.v1alpha2.schema.json +313 -0
  262. package/schemas/decision/DecisionTelemetryTrace.v1.schema.json +95 -0
  263. package/schemas/decision/RulesetResult.schema.json +721 -0
  264. package/schemas/decision/RulesetResult.v1alpha2.schema.json +891 -0
  265. package/schemas/security/pr-evidence-receipt.v1.schema.json +77 -0
  266. package/schemas/security/threat-assessment-report.v1.schema.json +71 -1
  267. package/tools/agents/deploy-agents.mjs +21 -6
  268. package/tools/agents/providers/base.mjs +7 -4
  269. package/tools/agents/providers/codex.mjs +1 -1
  270. package/tools/agents/providers/grok-build.d.mts +7 -0
  271. package/tools/agents/providers/grok-build.mjs +249 -9
  272. package/tools/agents/providers/model-role.mjs +6 -3
  273. package/tools/agents/providers/muse-hooks.mjs +785 -0
  274. package/tools/agents/providers/muse-paths.mjs +112 -0
  275. package/tools/agents/providers/muse.mjs +336 -0
@@ -123,6 +123,7 @@ export const ProviderConfigSchema = z.object({
123
123
  'factory',
124
124
  'grokbot',
125
125
  'hermes',
126
+ 'muse',
126
127
  'opencode',
127
128
  'openclaw',
128
129
  'openhuman',
@@ -12,6 +12,7 @@ import { createHash, createPrivateKey, createPublicKey, sign as cryptoSign, veri
12
12
  import { lstat, readFile, readdir } from 'node:fs/promises';
13
13
  import path from 'node:path';
14
14
  import { promisify } from 'node:util';
15
+ import { createCanonicalSigner } from '../security/signing.js';
15
16
  import { MARKETPLACE_ENVELOPE_SCHEMA, MARKETPLACE_LOCK_SCHEMA, MARKETPLACE_RECEIPT_SCHEMA, } from './provenance-types.js';
16
17
  const execFileAsync = promisify(execFile);
17
18
  const SHA256 = /^[a-f0-9]{64}$/;
@@ -55,7 +56,20 @@ function stable(value) {
55
56
  }
56
57
  return value;
57
58
  }
58
- /** RFC-8785-style deterministic JSON for the JSON-native protocol values. */
59
+ /**
60
+ * RFC-8785-style deterministic JSON for the JSON-native protocol values.
61
+ *
62
+ * For JSON-native values (no `undefined`, no non-finite numbers, no `toJSON`,
63
+ * no array-index keys such as "9" or "10") this is byte-identical to the
64
+ * RFC 8785 `artifact-trust.canonicalJson`: both sort keys by UTF-16 code unit
65
+ * and serialize scalars with `JSON.stringify`. It is kept because existing
66
+ * marketplace signatures depend on its handling of the other values:
67
+ * `undefined` object members are dropped, `undefined` array entries and
68
+ * non-finite numbers become `null` (RFC 8785 throws), and array-index keys are
69
+ * emitted first in numeric order because the sorted object is rebuilt.
70
+ * test/unit/security/canonical-json-parity.test.ts pins the parity and the
71
+ * differences.
72
+ */
59
73
  export function canonicalJson(value) {
60
74
  return JSON.stringify(stable(value));
61
75
  }
@@ -411,40 +425,24 @@ export function envelopeSigningPayload(envelope) {
411
425
  const { signatures: _signatures, ...payload } = envelope;
412
426
  return payload;
413
427
  }
414
- function publicKeyDer(key) {
415
- const object = typeof key === 'string'
416
- ? createPublicKey(key)
417
- : key.type === 'public' ? key : createPublicKey(key);
418
- if (object.asymmetricKeyType !== 'ed25519')
419
- throw new Error('Marketplace signing keys must use Ed25519');
420
- return object.export({ format: 'der', type: 'spki' });
421
- }
428
+ // Marketplace signatures are made over this module's `canonicalJson`, which is
429
+ // kept (see its comment) so existing envelopes and receipts verify unchanged.
430
+ // The helpers themselves live in src/security/signing.ts.
431
+ const marketplaceSigner = createCanonicalSigner({
432
+ canonicalize: canonicalJson,
433
+ keyTypeErrorMessage: 'Marketplace signing keys must use Ed25519',
434
+ });
422
435
  function publicKeyFromBase64(value) {
423
436
  return createPublicKey({ key: Buffer.from(value, 'base64'), format: 'der', type: 'spki' });
424
437
  }
425
438
  export function signCanonicalDocument(document, privateKeyPem, options = {}) {
426
- const privateKey = createPrivateKey(privateKeyPem);
427
- if (privateKey.asymmetricKeyType !== 'ed25519')
428
- throw new Error('Marketplace signing keys must use Ed25519');
429
- const publicKey = options.publicKeyPem ? createPublicKey(options.publicKeyPem) : createPublicKey(privateKey);
430
- const payload = canonicalJson(document);
431
- return {
432
- keyId: options.keyId ?? signingKeyId(publicKey),
433
- algorithm: 'ed25519',
434
- publicKey: publicKeyDer(publicKey).toString('base64'),
435
- signedAt: options.signedAt ?? new Date().toISOString(),
436
- payloadSha256: sha256(payload),
437
- signature: cryptoSign(null, Buffer.from(payload), privateKey).toString('base64'),
438
- };
439
+ return marketplaceSigner.signCanonicalDocument(document, privateKeyPem, options);
439
440
  }
440
441
  export function verifyCanonicalSignature(document, signature) {
441
- const payload = canonicalJson(document);
442
- return signature.algorithm === 'ed25519'
443
- && signature.payloadSha256 === sha256(payload)
444
- && cryptoVerify(null, Buffer.from(payload), publicKeyFromBase64(signature.publicKey), Buffer.from(signature.signature, 'base64'));
442
+ return marketplaceSigner.verifyCanonicalSignature(document, signature);
445
443
  }
446
444
  export function signingKeyId(publicKey) {
447
- return `ed25519:${sha256(publicKeyDer(publicKey)).slice(0, 32)}`;
445
+ return marketplaceSigner.signingKeyId(publicKey);
448
446
  }
449
447
  export function signProvenanceEnvelope(envelope, privateKeyPem, options = {}) {
450
448
  validateProvenanceEnvelope(envelope);
@@ -19,6 +19,7 @@ import {
19
19
  import { McpProfileRegistry } from './profiles.mjs';
20
20
  import { getMcpInjectionDefinition } from '../providers/provider-definitions.mjs';
21
21
  import { manageOmpMcp } from './omp-config.mjs';
22
+ import { unmanageGrokBuildMcp } from './grok-build-config.mjs';
22
23
 
23
24
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
24
25
 
@@ -1202,12 +1203,21 @@ export async function main(args = process.argv.slice(2)) {
1202
1203
 
1203
1204
  case 'uninject': {
1204
1205
  const provider = parseFlag(subArgs, '--provider');
1205
- if (!['omp', 'oh-my-pi'].includes(provider)) throw new Error('uninject currently supports --provider omp');
1206
+ if (!['omp', 'oh-my-pi', 'grok-build'].includes(provider)) throw new Error('uninject supports --provider omp or grok-build');
1206
1207
  const scope = parseFlag(subArgs, '--scope') || 'project';
1207
1208
  if (!['project', 'user'].includes(scope)) throw new Error('Scope must be project or user');
1208
1209
  const remove = (parseFlag(subArgs, '--servers') || '').split(',').map(s => s.trim()).filter(Boolean);
1209
1210
  if (!remove.length) throw new Error('uninject requires --servers name[,name]');
1210
1211
  const configPath = getProviderConfigPath(provider, parseFlag(subArgs, '--project') || '.', { scope });
1212
+ if (provider === 'grok-build') {
1213
+ const projectDir = parseFlag(subArgs, '--project') || '.';
1214
+ const result = await unmanageGrokBuildMcp(configPath, remove, {
1215
+ dryRun: subArgs.includes('--dry-run'),
1216
+ root: scope === 'user' ? path.dirname(path.dirname(configPath)) : path.resolve(projectDir),
1217
+ });
1218
+ console.log(JSON.stringify(result, null, 2));
1219
+ break;
1220
+ }
1211
1221
  const result = await manageOmpMcp(configPath, [], { remove, dryRun: subArgs.includes('--dry-run') });
1212
1222
  console.log(JSON.stringify(result, null, 2));
1213
1223
  break;
@@ -0,0 +1,189 @@
1
+ import { constants } from 'node:fs';
2
+ import { access, lstat, mkdir, open, readFile, rename, rm, writeFile } from 'node:fs/promises';
3
+ import { dirname, relative, resolve } from 'node:path';
4
+ import { spawnSync } from 'node:child_process';
5
+ import { replaceServer } from './toml-editor.mjs';
6
+
7
+ const START = name => `# >>> AIWG-managed Grok MCP: ${name}`;
8
+ const END = name => `# <<< AIWG-managed Grok MCP: ${name}`;
9
+ const ENV_REF = /^(?:[^$]|\$\{[A-Za-z_][A-Za-z0-9_]*(?::-[^}]*)?\})*$/;
10
+
11
+ function string(value) {
12
+ if (typeof value !== 'string') throw new Error('Grok MCP string fields must be strings');
13
+ return JSON.stringify(value).replace(/\u007f/g, '\\u007f');
14
+ }
15
+
16
+ function key(value) {
17
+ return /^[A-Za-z0-9_-]+$/.test(value) ? value : string(value);
18
+ }
19
+
20
+ function inline(values = {}) {
21
+ return `{ ${Object.entries(values).map(([name, value]) => `${key(name)} = ${string(value)}`).join(', ')} }`;
22
+ }
23
+
24
+ export function renderGrokMcpServer(server) {
25
+ if (!server?.name) throw new Error('Grok MCP server name is required');
26
+ const lines = [`[mcp_servers.${key(server.name)}]`];
27
+ if (server.type === 'stdio') {
28
+ if (!server.command) throw new Error(`Grok MCP stdio server "${server.name}" requires command`);
29
+ lines.push(`command = ${string(server.command)}`);
30
+ if (server.args?.length) lines.push(`args = [${server.args.map(string).join(', ')}]`);
31
+ if (server.env && Object.keys(server.env).length) lines.push(`env = ${inline(server.env)}`);
32
+ } else {
33
+ if (!server.url) throw new Error(`Grok MCP HTTP server "${server.name}" requires url`);
34
+ lines.push(`url = ${string(server.url)}`);
35
+ const headers = { ...(server.headers || {}) };
36
+ for (const [header, envName] of Object.entries(server.headerEnv || {})) {
37
+ headers[header] = `\${${envName}}`;
38
+ }
39
+ if (Object.keys(headers).length) lines.push(`headers = ${inline(headers)}`);
40
+ }
41
+ if (server.enabled === false) lines.push('enabled = false');
42
+ for (const value of [server.command, server.url, ...(server.args || []), ...Object.values(server.env || {}), ...Object.values(server.headers || {})]) {
43
+ if (typeof value === 'string' && value.includes('$') && !ENV_REF.test(value)) {
44
+ throw new Error(`Grok MCP server "${server.name}" contains an invalid environment reference`);
45
+ }
46
+ }
47
+ lines.push('startup_timeout_sec = 30', 'tool_timeout_sec = 6000');
48
+ return lines.join('\n');
49
+ }
50
+
51
+ function managedRange(text, name) {
52
+ let start = text.indexOf(START(name));
53
+ const endMarker = END(name);
54
+ const end = text.indexOf(endMarker);
55
+ if (start < 0 && end < 0) return null;
56
+ if (start < 0 || end < start) throw new Error(`Malformed AIWG ownership markers for Grok MCP server "${name}"`);
57
+ // Deployment owns the single blank separator it adds before its marker.
58
+ if (start > 0 && text.slice(start - 2, start) === '\n\n') start -= 1;
59
+ return [start, end + endMarker.length + (text[end + endMarker.length] === '\n' ? 1 : 0)];
60
+ }
61
+
62
+ export function mergeGrokMcpServers(text, servers) {
63
+ let output = text;
64
+ const seen = new Set();
65
+ for (const server of servers) {
66
+ if (seen.has(server.name)) throw new Error(`Duplicate Grok MCP server name "${server.name}"`);
67
+ seen.add(server.name);
68
+ const section = renderGrokMcpServer(server);
69
+ const range = managedRange(output, server.name);
70
+ const probeText = range ? output.slice(0, range[0]) + output.slice(range[1]) : output;
71
+ const probe = replaceServer(probeText, server.name, section);
72
+ if (!range && probe.alreadyPresent) {
73
+ throw new Error(`Grok MCP server "${server.name}" is operator-managed; refusing to replace it`);
74
+ }
75
+ const prefix = range ? output.slice(0, range[0]) : `${output}${output && !output.endsWith('\n') ? '\n' : ''}`;
76
+ const block = `${prefix ? '\n' : ''}${START(server.name)}\n${section}\n${END(server.name)}\n`;
77
+ output = range ? prefix + block + output.slice(range[1]) : prefix + block;
78
+ // Parse and validate the completed document without exposing its content.
79
+ replaceServer(output, '__aiwg_validation_probe__', '[mcp_servers.__aiwg_validation_probe__]\ncommand = "true"');
80
+ }
81
+ return output;
82
+ }
83
+
84
+ export function removeGrokMcpServers(text, names) {
85
+ let output = text;
86
+ const removed = [];
87
+ for (const name of names) {
88
+ const range = managedRange(output, name);
89
+ if (!range) continue;
90
+ output = output.slice(0, range[0]) + output.slice(range[1]);
91
+ removed.push(name);
92
+ }
93
+ if (output) replaceServer(output, '__aiwg_validation_probe__', '[mcp_servers.__aiwg_validation_probe__]\ncommand = "true"');
94
+ return { text: output, removed };
95
+ }
96
+
97
+ async function assertSafeTarget(configPath, root) {
98
+ const absolute = resolve(configPath);
99
+ const base = resolve(root);
100
+ const rel = relative(base, absolute);
101
+ if (!rel || rel.startsWith('..') || rel.startsWith('/') || rel.includes('\0')) {
102
+ throw new Error('Grok MCP config path must be a file beneath the selected configuration root');
103
+ }
104
+ for (let current = dirname(absolute); current !== base; current = dirname(current)) {
105
+ try {
106
+ if ((await lstat(current)).isSymbolicLink()) throw new Error('Refusing Grok MCP path through a symbolic link');
107
+ } catch (error) {
108
+ if (error?.code !== 'ENOENT') throw error;
109
+ }
110
+ }
111
+ try {
112
+ const info = await lstat(absolute);
113
+ if (info.isSymbolicLink() || !info.isFile()) throw new Error('Refusing unsafe Grok MCP config target');
114
+ } catch (error) {
115
+ if (error?.code !== 'ENOENT') throw error;
116
+ }
117
+ }
118
+
119
+ async function atomicWrite(file, content) {
120
+ await mkdir(dirname(file), { recursive: true });
121
+ const temp = `${file}.aiwg-${process.pid}-${Date.now()}.tmp`;
122
+ try {
123
+ const handle = await open(temp, 'wx', 0o600);
124
+ await handle.writeFile(content, 'utf8');
125
+ await handle.sync();
126
+ await handle.close();
127
+ await rename(temp, file);
128
+ } catch (error) {
129
+ await rm(temp, { force: true });
130
+ throw error;
131
+ }
132
+ }
133
+
134
+ export async function manageGrokBuildMcp(configPath, servers, options = {}) {
135
+ const root = options.root || dirname(dirname(configPath));
136
+ if (options.blockedByPolicy) return { configPath, state: 'blocked-by-policy', serversInjected: [], alreadyPresent: [], error: options.blockedByPolicy };
137
+ await assertSafeTarget(configPath, root);
138
+ let before = '';
139
+ try { before = await readFile(configPath, 'utf8'); } catch (error) { if (error?.code !== 'ENOENT') throw error; }
140
+ const alreadyPresent = servers.filter(server => managedRange(before, server.name)).map(server => server.name);
141
+ const after = mergeGrokMcpServers(before, servers);
142
+ if (!options.dryRun && after !== before) await atomicWrite(configPath, after);
143
+ return { configPath, state: options.disabled ? 'disabled' : 'configured', serversInjected: servers.map(server => server.name), alreadyPresent };
144
+ }
145
+
146
+ export async function unmanageGrokBuildMcp(configPath, names, options = {}) {
147
+ const root = options.root || dirname(dirname(configPath));
148
+ await assertSafeTarget(configPath, root);
149
+ let before;
150
+ try { before = await readFile(configPath, 'utf8'); } catch (error) { if (error?.code === 'ENOENT') return { configPath, state: 'absent', removed: [] }; throw error; }
151
+ const result = removeGrokMcpServers(before, names);
152
+ if (!options.dryRun && result.text !== before) await atomicWrite(configPath, result.text);
153
+ return { configPath, state: result.removed.length ? 'configured' : 'absent', removed: result.removed };
154
+ }
155
+
156
+ function runJson(binary, args, cwd, env) {
157
+ const run = spawnSync(binary, args, { cwd, env, encoding: 'utf8', timeout: 30_000 });
158
+ if (run.error || run.status !== 0) return { ok: false, detail: run.error?.message || run.stderr || `exit ${run.status}` };
159
+ try { return { ok: true, value: JSON.parse(run.stdout || '{}') }; }
160
+ catch { return { ok: false, detail: 'Grok returned malformed JSON' }; }
161
+ }
162
+
163
+ export async function inspectGrokBuildNative(options = {}) {
164
+ const configPath = options.configPath || resolve(options.projectDir || '.', '.grok/config.toml');
165
+ let config = '';
166
+ try { config = await readFile(configPath, 'utf8'); } catch (error) { if (error?.code === 'ENOENT') return { mcp: 'absent', hooks: 'absent', diagnostics: [] }; throw error; }
167
+ const disabled = /\benabled\s*=\s*false\b/.test(config);
168
+ const names = [...config.matchAll(/# >>> AIWG-managed Grok MCP: ([^\n]+)/g)].map(match => match[1]);
169
+ let mcp = names.length ? (disabled ? 'disabled' : 'configured') : 'absent';
170
+ const diagnostics = [];
171
+ if (options.blockedByPolicy) mcp = 'blocked-by-policy';
172
+ if (options.binary && names.length) {
173
+ const inspected = runJson(options.binary, ['inspect', '--json'], options.projectDir || '.', options.env || process.env);
174
+ if (!inspected.ok || !names.every(name => JSON.stringify(inspected.value || {}).includes(name))) {
175
+ mcp = 'unhealthy';
176
+ diagnostics.push(inspected.ok ? 'grok inspect did not expose every AIWG-managed MCP server' : inspected.detail);
177
+ }
178
+ const listed = runJson(options.binary, ['mcp', 'list', '--json'], options.projectDir || '.', options.env || process.env);
179
+ if (!listed.ok) { mcp = 'unhealthy'; diagnostics.push(listed.detail); }
180
+ for (const name of names) {
181
+ const doctor = runJson(options.binary, ['mcp', 'doctor', name, '--json'], options.projectDir || '.', options.env || process.env);
182
+ if (!doctor.ok) { mcp = 'unhealthy'; diagnostics.push(`${name}: ${doctor.detail}`); }
183
+ }
184
+ }
185
+ const hooksDir = resolve(options.projectDir || '.', '.grok/hooks');
186
+ let hooks = 'absent';
187
+ try { await access(hooksDir, constants.R_OK); hooks = options.trusted === true ? 'configured' : 'untrusted'; } catch { /* absent */ }
188
+ return { mcp, hooks, servers: names, diagnostics };
189
+ }
@@ -1,4 +1,5 @@
1
1
  import { manageOmpMcp } from './omp-config.mjs';
2
+ import { manageGrokBuildMcp } from './grok-build-config.mjs';
2
3
  import { replaceServer } from './toml-editor.mjs';
3
4
  import { resolveOmpPaths } from '../providers/omp-paths.mjs';
4
5
  /**
@@ -11,7 +12,7 @@ import { resolveOmpPaths } from '../providers/omp-paths.mjs';
11
12
  * @implements #554
12
13
  */
13
14
  import { readFile, writeFile, mkdir } from 'fs/promises';
14
- import { resolve } from 'path';
15
+ import { dirname, resolve } from 'path';
15
16
  import { resolveConfigDir } from '../config/user-config.js';
16
17
  import { getProviderDefinition } from '../providers/provider-definitions.js';
17
18
  // ============================================
@@ -300,6 +301,9 @@ export function getProviderConfigPath(provider, projectDir = '.', options = {})
300
301
  factory: resolve(homeDir, '.factory/mcp.json'),
301
302
  codex: resolve(homeDir, '.codex/config.toml'),
302
303
  openai: resolve(homeDir, '.codex/config.toml'),
304
+ 'grok-build': options.scope === 'user'
305
+ ? resolve(process.env.GROK_HOME || resolve(homeDir, '.grok'), 'config.toml')
306
+ : resolve(projectDir, '.grok/config.toml'),
303
307
  opencode: resolve(projectDir, 'opencode.json'),
304
308
  windsurf: resolve(homeDir, '.codeium/windsurf/mcp_config.json'),
305
309
  warp: resolve(homeDir, '.warp/mcp.json'),
@@ -342,6 +346,21 @@ export async function injectServers(registry, provider, options = {}) {
342
346
  return { ...result, error: error instanceof Error ? error.message : String(error) };
343
347
  }
344
348
  }
349
+ if (provider === 'grok-build') {
350
+ try {
351
+ const managed = await manageGrokBuildMcp(configPath, allServers, {
352
+ dryRun,
353
+ root: options.scope === 'user' ? dirname(dirname(configPath)) : resolve(projectDir),
354
+ });
355
+ if (!dryRun && !managed.error)
356
+ for (const server of allServers)
357
+ await registry.recordInjection(server.name, 'grok-build');
358
+ return { ...result, ...managed };
359
+ }
360
+ catch (error) {
361
+ return { ...result, error: error instanceof Error ? error.message : String(error) };
362
+ }
363
+ }
345
364
  // Handle TOML-based providers (Codex/OpenAI) separately
346
365
  if (provider === 'codex' || provider === 'openai') {
347
366
  return injectToml(registry, allServers, configPath, provider, dryRun, result);
@@ -432,6 +451,7 @@ export const SUPPORTED_PROVIDERS = [
432
451
  'cursor',
433
452
  'factory',
434
453
  'codex',
454
+ 'grok-build',
435
455
  'opencode',
436
456
  'windsurf',
437
457
  'warp',
@@ -1,4 +1,5 @@
1
1
  import { manageOmpMcp } from './omp-config.mjs';
2
+ import { manageGrokBuildMcp } from './grok-build-config.mjs';
2
3
  import { replaceServer } from './toml-editor.mjs';
3
4
  import { resolveOmpPaths } from '../providers/omp-paths.mjs';
4
5
  /**
@@ -15,7 +16,7 @@ import { resolveOmpPaths } from '../providers/omp-paths.mjs';
15
16
  */
16
17
 
17
18
  import { readFile, writeFile, mkdir } from 'fs/promises';
18
- import { resolve } from 'path';
19
+ import { dirname, resolve } from 'path';
19
20
  import { homedir } from 'os';
20
21
  import { existsSync } from 'fs';
21
22
  import {
@@ -345,6 +346,24 @@ export async function injectServers(registry, provider, options = {}) {
345
346
  }
346
347
  }
347
348
 
349
+ if (normalizedProvider === 'grok-build') {
350
+ try {
351
+ const managed = await manageGrokBuildMcp(configPath, allServers, {
352
+ dryRun,
353
+ root: options.scope === 'user'
354
+ ? dirname(dirname(configPath))
355
+ : resolve(projectDir),
356
+ blockedByPolicy: options.blockedByPolicy,
357
+ });
358
+ if (!dryRun && !managed.error) {
359
+ for (const server of allServers) await registry.recordInjection(server.name, 'grok-build');
360
+ }
361
+ return { ...result, ...managed };
362
+ } catch (error) {
363
+ return { ...result, error: error instanceof Error ? error.message : String(error) };
364
+ }
365
+ }
366
+
348
367
  if (mcpDefinition?.configFormat === 'toml') {
349
368
  return injectToml(registry, allServers, configPath, provider, dryRun, result);
350
369
  }