@aiwg/cli 2026.9.19 → 2026.9.21

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 (272) 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/enrichment/cli.js +1 -1
  8. package/dist/src/artifacts/views/cli.js +1 -1
  9. package/dist/src/audit/operator-decision.js +10 -0
  10. package/dist/src/auth/credential-store.js +127 -27
  11. package/dist/src/catalog/builtin-models.json +272 -40
  12. package/dist/src/catalog/cli.js +1 -1
  13. package/dist/src/catalog/cli.mjs +1 -1
  14. package/dist/src/cli/handlers/effect.js +872 -0
  15. package/dist/src/cli/handlers/help.js +2 -0
  16. package/dist/src/cli/handlers/index.js +4 -2
  17. package/dist/src/cli/handlers/models.js +41 -10
  18. package/dist/src/cli/handlers/ralph.js +1 -1
  19. package/dist/src/cli/handlers/sessions.js +113 -37
  20. package/dist/src/cli/handlers/subcommands.js +31 -0
  21. package/dist/src/cli/handlers/use.js +46 -9
  22. package/dist/src/cli/handlers/utilities.js +10 -0
  23. package/dist/src/cli/handlers/workspace.js +3 -1
  24. package/dist/src/cli/scope-resolver.js +112 -9
  25. package/dist/src/cli/services/deployment-verification.js +5 -0
  26. package/dist/src/config/aiwg-config.js +3 -0
  27. package/dist/src/decision/acceptance-rollout.js +93 -0
  28. package/dist/src/decision/acceptance.js +175 -0
  29. package/dist/src/decision/adapters/jev.js +234 -13
  30. package/dist/src/decision/adapters/llm-subagent.js +60 -12
  31. package/dist/src/decision/admission.js +550 -0
  32. package/dist/src/decision/batch-benchmark.js +47 -0
  33. package/dist/src/decision/batch-receipts/accounting.js +75 -0
  34. package/dist/src/decision/batch-receipts/export.js +40 -0
  35. package/dist/src/decision/batch-receipts/index.js +9 -0
  36. package/dist/src/decision/batch-receipts/protection.js +40 -0
  37. package/dist/src/decision/batch-receipts/receipt.js +44 -0
  38. package/dist/src/decision/batch-receipts/result-store.js +341 -0
  39. package/dist/src/decision/batch-receipts/store.js +362 -0
  40. package/dist/src/decision/batch-receipts/types.js +2 -0
  41. package/dist/src/decision/batch-receipts/validate.js +127 -0
  42. package/dist/src/decision/batch.js +68 -0
  43. package/dist/src/decision/calibration/index.js +4 -0
  44. package/dist/src/decision/calibration/receipts.js +130 -0
  45. package/dist/src/decision/calibration/registry.js +258 -0
  46. package/dist/src/decision/calibration/types.js +2 -0
  47. package/dist/src/decision/compile-cache/benchmark.js +62 -0
  48. package/dist/src/decision/compile-cache/compatibility.js +65 -0
  49. package/dist/src/decision/compile-cache/file-store.js +280 -0
  50. package/dist/src/decision/compile-cache/identity.js +26 -0
  51. package/dist/src/decision/compile-cache/index.js +9 -0
  52. package/dist/src/decision/compile-cache/prefix.js +21 -0
  53. package/dist/src/decision/compile-cache/records.js +86 -0
  54. package/dist/src/decision/compile-cache/runtime.js +68 -0
  55. package/dist/src/decision/compile-cache/store.js +108 -0
  56. package/dist/src/decision/compile-cache/types.js +2 -0
  57. package/dist/src/decision/context-plan.js +234 -0
  58. package/dist/src/decision/context-qualification.js +60 -0
  59. package/dist/src/decision/ensemble/aggregate.js +215 -0
  60. package/dist/src/decision/ensemble/contract.js +316 -0
  61. package/dist/src/decision/ensemble/drift.js +51 -0
  62. package/dist/src/decision/ensemble/index.js +6 -0
  63. package/dist/src/decision/ensemble/report.js +71 -0
  64. package/dist/src/decision/ensemble/types.js +2 -0
  65. package/dist/src/decision/evaluate.js +1024 -78
  66. package/dist/src/decision/file-lifecycle-store.js +168 -0
  67. package/dist/src/decision/graph-beam.js +40 -0
  68. package/dist/src/decision/graph-budget.js +110 -0
  69. package/dist/src/decision/graph-decision-bridge.js +93 -0
  70. package/dist/src/decision/graph-evidence.js +123 -0
  71. package/dist/src/decision/graph-flow-adapter.js +56 -0
  72. package/dist/src/decision/graph-flow.js +105 -0
  73. package/dist/src/decision/graph-parallel.js +79 -0
  74. package/dist/src/decision/graph-patterns.js +20 -0
  75. package/dist/src/decision/graph-run-store.js +66 -0
  76. package/dist/src/decision/graph-run.js +166 -0
  77. package/dist/src/decision/graph-skill-bridge.js +91 -0
  78. package/dist/src/decision/graph-templates.js +56 -0
  79. package/dist/src/decision/graph.js +115 -0
  80. package/dist/src/decision/index.js +35 -0
  81. package/dist/src/decision/job-accounting.js +93 -0
  82. package/dist/src/decision/job-contract.js +186 -0
  83. package/dist/src/decision/job-effects.js +95 -0
  84. package/dist/src/decision/job-evaluate.js +80 -0
  85. package/dist/src/decision/job-gateway.js +121 -0
  86. package/dist/src/decision/job-http.js +194 -0
  87. package/dist/src/decision/job-payload-store.js +258 -0
  88. package/dist/src/decision/job-poll-limiter.js +92 -0
  89. package/dist/src/decision/job-quota.js +158 -0
  90. package/dist/src/decision/job-runtime.js +240 -0
  91. package/dist/src/decision/job-scheduler.js +26 -0
  92. package/dist/src/decision/job-service.js +117 -0
  93. package/dist/src/decision/job-store.js +264 -0
  94. package/dist/src/decision/job-worker.js +141 -0
  95. package/dist/src/decision/lifecycle.js +148 -0
  96. package/dist/src/decision/patterns/artifacts.js +173 -0
  97. package/dist/src/decision/patterns/catalog.js +108 -0
  98. package/dist/src/decision/patterns/durable-review.js +237 -0
  99. package/dist/src/decision/patterns/index.js +8 -0
  100. package/dist/src/decision/patterns/playground.js +401 -0
  101. package/dist/src/decision/patterns/recorded.js +43 -0
  102. package/dist/src/decision/patterns/specs.js +174 -0
  103. package/dist/src/decision/patterns/types.js +2 -0
  104. package/dist/src/decision/portable-secrets.js +53 -0
  105. package/dist/src/decision/projection.js +210 -0
  106. package/dist/src/decision/qualification/capture.js +67 -0
  107. package/dist/src/decision/qualification/drift.js +68 -0
  108. package/dist/src/decision/qualification/gate-evidence.js +172 -0
  109. package/dist/src/decision/qualification/gates.js +83 -0
  110. package/dist/src/decision/qualification/index.js +12 -0
  111. package/dist/src/decision/qualification/load.js +275 -0
  112. package/dist/src/decision/qualification/manifest.js +37 -0
  113. package/dist/src/decision/qualification/privacy.js +41 -0
  114. package/dist/src/decision/qualification/quality.js +213 -0
  115. package/dist/src/decision/qualification/release.js +116 -0
  116. package/dist/src/decision/qualification/runner.js +408 -0
  117. package/dist/src/decision/qualification/types.js +2 -0
  118. package/dist/src/decision/receipts.js +30 -7
  119. package/dist/src/decision/result-cache/index.js +6 -0
  120. package/dist/src/decision/result-cache/integrity.js +28 -0
  121. package/dist/src/decision/result-cache/key.js +28 -0
  122. package/dist/src/decision/result-cache/service.js +92 -0
  123. package/dist/src/decision/result-cache/store.js +327 -0
  124. package/dist/src/decision/result-cache/types.js +3 -0
  125. package/dist/src/decision/review/authorization.js +110 -0
  126. package/dist/src/decision/review/effect-ledger-adapter.js +321 -0
  127. package/dist/src/decision/review/index.js +12 -0
  128. package/dist/src/decision/review/migrate.js +34 -0
  129. package/dist/src/decision/review/operator-audit.js +59 -0
  130. package/dist/src/decision/review/recovery-adapters.js +252 -0
  131. package/dist/src/decision/review/recovery.js +33 -0
  132. package/dist/src/decision/review/service.js +498 -0
  133. package/dist/src/decision/review/store.js +219 -0
  134. package/dist/src/decision/review/types.js +2 -0
  135. package/dist/src/decision/review/validate.js +205 -0
  136. package/dist/src/decision/scheduler.js +115 -0
  137. package/dist/src/decision/telemetry/context.js +42 -0
  138. package/dist/src/decision/telemetry/debug-sidecar.js +152 -0
  139. package/dist/src/decision/telemetry/exporter.js +77 -0
  140. package/dist/src/decision/telemetry/file-debug-backend.js +112 -0
  141. package/dist/src/decision/telemetry/index.js +13 -0
  142. package/dist/src/decision/telemetry/mapping.js +146 -0
  143. package/dist/src/decision/telemetry/metrics.js +164 -0
  144. package/dist/src/decision/telemetry/otlp-http.js +183 -0
  145. package/dist/src/decision/telemetry/redaction.js +93 -0
  146. package/dist/src/decision/telemetry/retention.js +109 -0
  147. package/dist/src/decision/telemetry/runtime.js +310 -0
  148. package/dist/src/decision/telemetry/trace.js +158 -0
  149. package/dist/src/decision/telemetry/types.js +3 -0
  150. package/dist/src/decision/validate.js +135 -5
  151. package/dist/src/effects/checkpoint-sinks.js +94 -0
  152. package/dist/src/effects/checkpoint.js +76 -0
  153. package/dist/src/effects/decision-links.js +54 -0
  154. package/dist/src/effects/errors.js +48 -0
  155. package/dist/src/effects/identity.js +178 -0
  156. package/dist/src/effects/index.js +25 -0
  157. package/dist/src/effects/keyring.js +160 -0
  158. package/dist/src/effects/keys.js +130 -0
  159. package/dist/src/effects/ledger.js +435 -0
  160. package/dist/src/effects/lifecycle.js +110 -0
  161. package/dist/src/effects/lock-recovery.js +269 -0
  162. package/dist/src/effects/reader.js +103 -0
  163. package/dist/src/effects/records.js +114 -0
  164. package/dist/src/effects/redaction.js +51 -0
  165. package/dist/src/effects/schema.js +73 -0
  166. package/dist/src/effects/store.js +239 -0
  167. package/dist/src/effects/types.js +49 -0
  168. package/dist/src/effects/verifiers/decision.js +168 -0
  169. package/dist/src/effects/verifiers/file.js +106 -0
  170. package/dist/src/effects/verifiers/git.js +206 -0
  171. package/dist/src/effects/verifiers/index.js +90 -0
  172. package/dist/src/effects/verifiers/registry.js +148 -0
  173. package/dist/src/effects/verifiers/review.js +123 -0
  174. package/dist/src/effects/verifiers/tracker.js +299 -0
  175. package/dist/src/effects/verifiers/types.js +21 -0
  176. package/dist/src/effects/verify.js +295 -0
  177. package/dist/src/evidence/bundle.js +2 -0
  178. package/dist/src/extensions/commands/definitions.js +28 -0
  179. package/dist/src/extensions/manifest.js +1 -0
  180. package/dist/src/marketplace/provenance.js +25 -27
  181. package/dist/src/mcp/cli.mjs +11 -1
  182. package/dist/src/mcp/grok-build-config.mjs +189 -0
  183. package/dist/src/mcp/registry.js +21 -1
  184. package/dist/src/mcp/registry.mjs +20 -1
  185. package/dist/src/models/grok-build-models.js +356 -0
  186. package/dist/src/models/index.js +1 -0
  187. package/dist/src/models/model-capabilities.v1.json +34 -0
  188. package/dist/src/models/model-catalog.v1.json +55 -13
  189. package/dist/src/models/model-discovery.js +41 -5
  190. package/dist/src/models/provider-policy.js +3 -2
  191. package/dist/src/plugin/skill-command-translator.js +1 -0
  192. package/dist/src/providers/capability-matrix.yaml +58 -6
  193. package/dist/src/providers/grok-build-acp.js +192 -0
  194. package/dist/src/providers/grok-build-dispatch.js +251 -0
  195. package/dist/src/providers/grok-build-headless.js +170 -0
  196. package/dist/src/providers/muse-paths.js +105 -0
  197. package/dist/src/providers/provider-definitions.js +93 -7
  198. package/dist/src/providers/provider-definitions.mjs +21 -1
  199. package/dist/src/providers/provider-inventory.js +3 -0
  200. package/dist/src/security/artifact-attestation.js +4 -8
  201. package/dist/src/security/signing.js +119 -0
  202. package/dist/src/sessions/adapters/grok-build.js +187 -0
  203. package/dist/src/sessions/adapters/muse.js +412 -0
  204. package/dist/src/sessions/contracts.js +1 -1
  205. package/dist/src/sessions/grok-build-cli.js +196 -0
  206. package/dist/src/sessions/index.js +3 -0
  207. package/dist/src/sessions/workspace-discovery.js +9 -5
  208. package/dist/src/skills/deployer.js +33 -5
  209. package/dist/src/smiths/context-pipeline/claude-hook.js +2 -0
  210. package/dist/src/smiths/context-pipeline/context-file-safety.js +18 -0
  211. package/dist/src/smiths/context-pipeline/finalization.js +1 -32
  212. package/dist/src/smiths/context-pipeline/generator.js +12 -0
  213. package/dist/src/smiths/context-pipeline/index.js +1 -1
  214. package/dist/src/smiths/context-pipeline/managed-hook.js +2 -0
  215. package/dist/src/smiths/context-pipeline/muse-bridge.js +50 -0
  216. package/dist/src/smiths/context-pipeline/provider-policy.js +5 -0
  217. package/dist/src/smiths/context-pipeline/workspace-context.js +22 -16
  218. package/dist/src/smiths/hook-bridge/grok-build-translator.js +64 -0
  219. package/dist/src/smiths/hook-bridge/index.js +3 -0
  220. package/dist/src/smiths/hook-bridge/shim.js +2 -0
  221. package/dist/src/smiths/hook-bridge/types.js +5 -0
  222. package/dist/src/smiths/skillsmith/collision-detector.js +1 -0
  223. package/dist/src/storage/protected-files.js +102 -0
  224. package/dist/src/tracker/forge-http.js +371 -0
  225. package/dist/src/tracker/project-inputs.js +41 -0
  226. package/package.json +2 -1
  227. package/schemas/decision/BatchReceipt.v1.schema.json +50 -0
  228. package/schemas/decision/CalibrationArtifact.v1.schema.json +76 -0
  229. package/schemas/decision/CalibrationCompatibility.v1.schema.json +18 -0
  230. package/schemas/decision/CalibrationGovernanceReceipt.v1.schema.json +44 -0
  231. package/schemas/decision/DecisionAdmissionEvidence.v1.schema.json +43 -0
  232. package/schemas/decision/DecisionBinding.schema.json +309 -0
  233. package/schemas/decision/DecisionBinding.v1alpha2.schema.json +369 -0
  234. package/schemas/decision/DecisionCacheTelemetry.v1.schema.json +20 -0
  235. package/schemas/decision/DecisionChampionChallenger.v1.schema.json +100 -0
  236. package/schemas/decision/DecisionCompileCacheEntry.v1.schema.json +35 -0
  237. package/schemas/decision/DecisionCompileCacheIdentity.v1.schema.json +26 -0
  238. package/schemas/decision/DecisionCompileCacheTombstone.v1.schema.json +31 -0
  239. package/schemas/decision/DecisionContextEvidence.v1.schema.json +136 -0
  240. package/schemas/decision/DecisionDefinition.schema.json +159 -0
  241. package/schemas/decision/DecisionDefinition.v1alpha2.schema.json +203 -0
  242. package/schemas/decision/DecisionDriftResponse.v1.schema.json +55 -0
  243. package/schemas/decision/DecisionEnsembleAggregate.v1.schema.json +86 -0
  244. package/schemas/decision/DecisionEnsembleIntegrityReport.v1.schema.json +64 -0
  245. package/schemas/decision/DecisionEnsemblePolicy.v1.schema.json +130 -0
  246. package/schemas/decision/DecisionGraph.v1.schema.json +77 -0
  247. package/schemas/decision/DecisionJob.v1.schema.json +71 -0
  248. package/schemas/decision/DecisionLifecyclePolicy.v1.schema.json +52 -0
  249. package/schemas/decision/DecisionProjectionPolicy.v1.schema.json +65 -0
  250. package/schemas/decision/DecisionProviderPrefixCompatibility.v1.schema.json +45 -0
  251. package/schemas/decision/DecisionProviderPrefixEvidence.v1.schema.json +17 -0
  252. package/schemas/decision/DecisionProviderPrefixIdentity.v1.schema.json +36 -0
  253. package/schemas/decision/DecisionResult.schema.json +524 -0
  254. package/schemas/decision/DecisionResult.v1alpha2.schema.json +619 -0
  255. package/schemas/decision/DecisionResultCacheEntry.v1.schema.json +53 -0
  256. package/schemas/decision/DecisionReview.schema.json +29 -0
  257. package/schemas/decision/DecisionRuleset.schema.json +313 -0
  258. package/schemas/decision/DecisionRuleset.v1alpha2.schema.json +313 -0
  259. package/schemas/decision/DecisionTelemetryTrace.v1.schema.json +95 -0
  260. package/schemas/decision/RulesetResult.schema.json +721 -0
  261. package/schemas/decision/RulesetResult.v1alpha2.schema.json +891 -0
  262. package/schemas/security/pr-evidence-receipt.v1.schema.json +77 -0
  263. package/schemas/security/threat-assessment-report.v1.schema.json +71 -1
  264. package/tools/agents/deploy-agents.mjs +21 -6
  265. package/tools/agents/providers/base.mjs +7 -4
  266. package/tools/agents/providers/codex.mjs +1 -1
  267. package/tools/agents/providers/grok-build.d.mts +7 -0
  268. package/tools/agents/providers/grok-build.mjs +249 -9
  269. package/tools/agents/providers/model-role.mjs +6 -3
  270. package/tools/agents/providers/muse-hooks.mjs +785 -0
  271. package/tools/agents/providers/muse-paths.mjs +112 -0
  272. package/tools/agents/providers/muse.mjs +336 -0
@@ -0,0 +1,196 @@
1
+ import { execFile as execFileCallback } from 'node:child_process';
2
+ import { constants as fsConstants } from 'node:fs';
3
+ import { lstat, open, realpath, writeFile } from 'node:fs/promises';
4
+ import { dirname, isAbsolute, join, parse, resolve } from 'node:path';
5
+ import { promisify } from 'node:util';
6
+ import { resolveGrokHomeResult } from '../providers/grok-build-paths.js';
7
+ import { SessionContractError } from './contracts.js';
8
+ import { validateGrokBuildMarkdown } from './adapters/grok-build.js';
9
+ const execFile = promisify(execFileCallback);
10
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
11
+ const MAX_OUTPUT = 2_000_000;
12
+ const TIMEOUT_MS = 15_000;
13
+ export async function grokBuildList(options, limit = 20) {
14
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 500) {
15
+ throw new SessionContractError('INVALID_ARGUMENT', 'Grok Build list limit must be 1..500');
16
+ }
17
+ const stdout = await runGrok(options, ['sessions', 'list', '--limit', String(limit)]);
18
+ return parseSessionIds(stdout, 'list');
19
+ }
20
+ export async function grokBuildSearch(options, query, limit = 20) {
21
+ if (!query.trim() || query.length > 500 || /[\x00-\x1f\x7f]/.test(query)) {
22
+ throw new SessionContractError('INVALID_SEARCH_QUERY', 'Grok Build search query must be nonempty plain text under 500 characters');
23
+ }
24
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 500) {
25
+ throw new SessionContractError('INVALID_ARGUMENT', 'Grok Build search limit must be 1..500');
26
+ }
27
+ const stdout = await runGrok(options, ['sessions', 'search', query, '--limit', String(limit)]);
28
+ return parseSessionIds(stdout, 'search');
29
+ }
30
+ export async function grokBuildExport(options, sessionId, outputDirectory) {
31
+ const id = assertGrokSessionId(sessionId);
32
+ const directory = await safeDirectory(outputDirectory);
33
+ const stdout = await runGrok(options, ['export', id]);
34
+ if (Buffer.byteLength(stdout) > MAX_OUTPUT) {
35
+ throw new SessionContractError('SCHEMA_DRIFT', 'Grok Build export is not a bounded documented Markdown transcript');
36
+ }
37
+ validateGrokBuildMarkdown(stdout);
38
+ const destination = join(directory, `${id}.md`);
39
+ try {
40
+ await writeFile(destination, stdout.endsWith('\n') ? stdout : `${stdout}\n`, { flag: 'wx', mode: 0o600 });
41
+ }
42
+ catch (error) {
43
+ if (error.code === 'EEXIST') {
44
+ throw new SessionContractError('IMPORT_CONFLICT', 'Grok Build export already exists; select a new output directory');
45
+ }
46
+ throw error;
47
+ }
48
+ return destination;
49
+ }
50
+ export function assertGrokSessionId(value) {
51
+ if (!UUID.test(value)) {
52
+ throw new SessionContractError('INVALID_ARGUMENT', 'Grok Build session ID must be a UUID');
53
+ }
54
+ return value.toLowerCase();
55
+ }
56
+ export function parseGrokHeadlessSessionId(output, format) {
57
+ if (Buffer.byteLength(output) > MAX_OUTPUT) {
58
+ throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Grok Build headless output exceeds 2 MB');
59
+ }
60
+ const lines = format === 'json' ? [output] : output.trim().split(/\r?\n/);
61
+ const ids = new Set();
62
+ for (const line of lines) {
63
+ let value;
64
+ try {
65
+ value = JSON.parse(line);
66
+ }
67
+ catch {
68
+ throw new SessionContractError('SCHEMA_DRIFT', 'Grok Build headless output is not valid JSON');
69
+ }
70
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
71
+ throw new SessionContractError('SCHEMA_DRIFT', 'Grok Build headless event must be an object');
72
+ }
73
+ const id = value.sessionId;
74
+ if (id !== undefined) {
75
+ if (typeof id !== 'string' || !UUID.test(id)) {
76
+ throw new SessionContractError('SCHEMA_DRIFT', 'Grok Build headless event has an invalid sessionId');
77
+ }
78
+ ids.add(id.toLowerCase());
79
+ }
80
+ }
81
+ if (ids.size !== 1) {
82
+ throw new SessionContractError('SCHEMA_DRIFT', 'Grok Build headless output must expose exactly one consistent sessionId');
83
+ }
84
+ return [...ids][0];
85
+ }
86
+ export async function grokHeadlessSessionIdFromFile(input, format) {
87
+ const path = resolve(input);
88
+ const parent = await safeDirectory(dirname(path));
89
+ if (dirname(path) !== parent) {
90
+ throw new SessionContractError('SOURCE_OUTSIDE_ALLOWED_ROOT', 'Grok Build headless output escapes its authorized directory');
91
+ }
92
+ let handle;
93
+ try {
94
+ handle = await open(path, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
95
+ const item = await handle.stat();
96
+ if (!item.isFile() || item.size > MAX_OUTPUT) {
97
+ throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'Grok Build headless output must be a bounded regular file');
98
+ }
99
+ return parseGrokHeadlessSessionId(await handle.readFile('utf8'), format);
100
+ }
101
+ catch (error) {
102
+ if (error.code === 'ELOOP') {
103
+ throw new SessionContractError('SOURCE_SYMLINK', 'Grok Build headless output cannot be a symlink');
104
+ }
105
+ throw error;
106
+ }
107
+ finally {
108
+ await handle?.close();
109
+ }
110
+ }
111
+ async function runGrok(options, args) {
112
+ const cwd = await safeDirectory(options.cwd);
113
+ const env = options.env ?? process.env;
114
+ const home = resolveGrokHomeResult(env);
115
+ if (!home.ok)
116
+ throw new SessionContractError('SOURCE_NOT_AUTHORIZED', home.message);
117
+ await rejectSymlinkComponents(home.path);
118
+ const binary = options.binary ?? 'grok';
119
+ if (binary !== 'grok' && !isAbsolute(binary)) {
120
+ throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'Grok Build binary override must be absolute');
121
+ }
122
+ const runner = options.runner ?? execFile;
123
+ try {
124
+ const result = await runner(binary, ['--no-auto-update', ...args], {
125
+ cwd, env: { ...env, GROK_HOME: home.path, NO_COLOR: '1' },
126
+ timeout: TIMEOUT_MS, maxBuffer: MAX_OUTPUT,
127
+ });
128
+ if (Buffer.byteLength(result.stdout) > MAX_OUTPUT) {
129
+ throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Grok Build CLI output exceeds 2 MB');
130
+ }
131
+ return result.stdout;
132
+ }
133
+ catch (error) {
134
+ if (error instanceof SessionContractError)
135
+ throw error;
136
+ if (error.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') {
137
+ throw new SessionContractError('RESOURCE_LIMIT_EXCEEDED', 'Grok Build CLI output exceeds 2 MB');
138
+ }
139
+ throw new SessionContractError('UNSUPPORTED_OPERATION', 'Grok Build CLI failed or timed out; check the local `grok` installation, authentication, and selected workspace.');
140
+ }
141
+ }
142
+ function parseSessionIds(stdout, mode) {
143
+ const ids = [];
144
+ let recognized = false;
145
+ for (const line of stdout.split(/\r?\n/)) {
146
+ if (!line.trim())
147
+ continue;
148
+ if (line === 'No sessions found.' || line.startsWith('SESSION ID ') || line.startsWith('Label: ')
149
+ || line === '(no label)' || /^Total: \d+$/.test(line)) {
150
+ recognized = true;
151
+ continue;
152
+ }
153
+ const id = /^([0-9a-f-]{36})(?:\s|$)/i.exec(line)?.[1];
154
+ if (id && UUID.test(id)) {
155
+ ids.push(id.toLowerCase());
156
+ recognized = true;
157
+ continue;
158
+ }
159
+ if (mode === 'search' && /^ \S/.test(line) && recognized)
160
+ continue;
161
+ throw new SessionContractError('SCHEMA_DRIFT', `unrecognized Grok Build ${mode} output; check CLI version`);
162
+ }
163
+ if (!recognized) {
164
+ throw new SessionContractError('SCHEMA_DRIFT', `empty Grok Build ${mode} output; check CLI version`);
165
+ }
166
+ return [...new Set(ids)];
167
+ }
168
+ async function safeDirectory(input) {
169
+ if (!isAbsolute(input) || parse(resolve(input)).root === resolve(input)) {
170
+ throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'Grok Build directory must be an absolute non-root path');
171
+ }
172
+ await rejectSymlinkComponents(input);
173
+ const canonical = await realpath(input);
174
+ if (!(await lstat(canonical)).isDirectory()) {
175
+ throw new SessionContractError('SOURCE_NOT_AUTHORIZED', 'Grok Build directory must exist and be a directory');
176
+ }
177
+ return canonical;
178
+ }
179
+ async function rejectSymlinkComponents(input) {
180
+ let current = resolve(input);
181
+ while (current !== parse(current).root) {
182
+ try {
183
+ if ((await lstat(current)).isSymbolicLink()) {
184
+ throw new SessionContractError('SOURCE_SYMLINK', 'Grok Build path contains a symlink');
185
+ }
186
+ }
187
+ catch (error) {
188
+ if (error instanceof SessionContractError)
189
+ throw error;
190
+ if (error.code !== 'ENOENT')
191
+ throw error;
192
+ }
193
+ current = dirname(current);
194
+ }
195
+ }
196
+ //# sourceMappingURL=grok-build-cli.js.map
@@ -34,9 +34,12 @@ export * from './adapters/opencode.js';
34
34
  export * from './adapters/openclaw.js';
35
35
  export * from './adapters/openhuman.js';
36
36
  export * from './adapters/grokbot.js';
37
+ export * from './adapters/grok-build.js';
38
+ export * from './grok-build-cli.js';
37
39
  export * from './adapters/pi.js';
38
40
  export * from './adapters/deepseek-harness.js';
39
41
  export * from './adapters/warp.js';
40
42
  export * from './adapters/windsurf.js';
43
+ export * from './adapters/muse.js';
41
44
  export * from './adapters/omp.js';
42
45
  //# sourceMappingURL=index.js.map
@@ -16,8 +16,8 @@ import { redactSourceLocator } from './discovery.js';
16
16
  import { fingerprintSourceFile } from './readers.js';
17
17
  export const DISCOVERY_MANIFEST_VERSION = '1.0.0';
18
18
  const MANUAL_EXPORT_PROVIDERS = new Set([
19
- 'copilot', 'hermes', 'opencode', 'openclaw', 'openhuman', 'grokbot',
20
- 'warp', 'devin-desktop', 'generic',
19
+ 'copilot', 'hermes', 'opencode', 'openclaw', 'openhuman', 'grokbot', 'grok-build',
20
+ 'warp', 'devin-desktop', 'muse', 'generic',
21
21
  ]);
22
22
  export async function discoverWorkspaceHistories(options) {
23
23
  const workspacePath = await canonicalPath(options.workspace);
@@ -127,9 +127,13 @@ export async function discoverWorkspaceHistories(options) {
127
127
  if (reports.has(provider))
128
128
  continue;
129
129
  const manual = MANUAL_EXPORT_PROVIDERS.has(provider);
130
- reports.set(provider, providerReport(provider, manual ? 'export-required' : 'not-checked', manual ? 'manual-export' : 'unsupported', [], manual ? 'EXPLICIT_EXPORT_REQUIRED' : 'PROVIDER_NOT_CHECKED', manual
131
- ? `Select and authorize a supported ${provider} export before importing it.`
132
- : `No discovery strategy is registered for ${provider}.`));
130
+ const grokBuild = provider === 'grok-build';
131
+ reports.set(provider, providerReport(provider, manual ? 'export-required' : 'not-checked', manual ? 'manual-export' : 'unsupported', [], grokBuild ? 'CLI_EXPORT_REQUIRED'
132
+ : manual ? 'EXPLICIT_EXPORT_REQUIRED' : 'PROVIDER_NOT_CHECKED', grokBuild
133
+ ? 'Run `grok sessions list` or `grok sessions search`, then export an authorized session with `grok export <id> <id>.md`; Grok Build resolves $GROK_HOME itself.'
134
+ : manual
135
+ ? `Select and authorize a supported ${provider} export before importing it.`
136
+ : `No discovery strategy is registered for ${provider}.`));
133
137
  }
134
138
  const sources = candidates.sort(compareSources);
135
139
  const providers = [...reports.values()].sort((a, b) => a.provider.localeCompare(b.provider));
@@ -16,6 +16,7 @@ import { AGENT_SKILLS_SIDECAR_SCHEMA, AIWG_SKILL_CONTROL_FIELDS, createAgentSkil
16
16
  import { getImportedAgentSkill } from './importer.js';
17
17
  import { validateAgentSkillContent } from './validator.js';
18
18
  import { resolveHermesHomePath } from '../providers/hermes-home.js';
19
+ import { museXdgSkillsDirRemediation, resolveMuseXdgSkillsDir, } from '../providers/muse-paths.js';
19
20
  import { GROKBOT_SKILLS_DIR_ENV, grokbotMissingRootRemediation, resolveGrokbotSkillsDir, } from '../providers/grokbot-paths.js';
20
21
  export const AGENT_SKILL_MANAGED_MARKER = '.aiwg-managed';
21
22
  export const AGENT_SKILL_DEPLOYMENT_SIDECAR = '.aiwg-agent-skill.json';
@@ -101,6 +102,33 @@ function resolvePolicy(target, options) {
101
102
  }
102
103
  reasons.push('uses the active HERMES_HOME skills surface with strict managed ownership markers');
103
104
  break;
105
+ case 'muse': {
106
+ // ADR scope rule 1: the default projection is the project
107
+ // <repo>/.agents/skills surface shared with antigravity, codex, and
108
+ // deepseek-harness. Muse reads both that root and its XDG user root,
109
+ // so writing the user root by default would list a skill twice.
110
+ // #234: an explicit user scope resolves the documented XDG root at
111
+ // deploy time — never ~/.muse — and bad XDG metadata fails closed.
112
+ if (options.scope === 'user') {
113
+ const home = resolvedHome(options);
114
+ const configured = resolveMuseXdgSkillsDir(process.env, home);
115
+ if (!configured) {
116
+ root = '';
117
+ status = 'unsupported';
118
+ supported = false;
119
+ reasons.push(museXdgSkillsDirRemediation(process.env, home));
120
+ warnings.push('Agent Skills deploy for muse requires a valid absolute XDG_CONFIG_HOME; no filesystem root was invented');
121
+ }
122
+ else {
123
+ root = configured;
124
+ reasons.push('uses the Muse XDG user skills root ($XDG_CONFIG_HOME/muse/skills, default ~/.config/muse/skills) with strict managed ownership markers');
125
+ }
126
+ }
127
+ else {
128
+ reasons.push('uses the project .agents/skills surface shared with antigravity, codex, and deepseek-harness');
129
+ }
130
+ break;
131
+ }
104
132
  case 'openhuman':
105
133
  status = 'projected';
106
134
  reasons.push('uses the verified global one-level ~/.openhuman/skills layout');
@@ -307,13 +335,13 @@ function readDeploymentSidecar(targetPath, expectedName, expectedProvider) {
307
335
  }
308
336
  }
309
337
  /**
310
- * Codex, Antigravity, and DeepSeek Harness intentionally consume the same
311
- * portable project skill surface. A projection written for any member is
312
- * managed ownership for the others when the desired payload is otherwise
313
- * byte-identical.
338
+ * Codex, Antigravity, DeepSeek Harness, and Muse intentionally consume the
339
+ * same portable project skill surface (<repo>/.agents/skills). A projection
340
+ * written for any member is managed ownership for the others when the
341
+ * desired payload is otherwise byte-identical.
314
342
  */
315
343
  function providersShareProjectionSurface(actual, expected) {
316
- const shared = new Set(['antigravity', 'codex', 'deepseek-harness']);
344
+ const shared = new Set(['antigravity', 'codex', 'deepseek-harness', 'muse']);
317
345
  return actual === expected || (shared.has(actual) && shared.has(expected));
318
346
  }
319
347
  function buildProjectionPlan(record, options) {
@@ -23,6 +23,7 @@ import * as fs from 'fs/promises';
23
23
  import * as path from 'path';
24
24
  import { buildProviderBootstrapBlock, PROVIDER_BOOTSTRAP_START, PROVIDER_BOOTSTRAP_END, } from './workspace-context.js';
25
25
  import { dominantLineEnding, withLineEnding } from './line-endings.js';
26
+ import { assertSafeContextFile } from './context-file-safety.js';
26
27
  export const CLAUDE_HOOK_START = '<!-- AIWG:claude-md-hook:start -->';
27
28
  export const CLAUDE_HOOK_END = '<!-- AIWG:claude-md-hook:end -->';
28
29
  function buildClaudeArtifactOutputPolicy(policy = {}) {
@@ -65,6 +66,7 @@ export function buildClaudeHookBlock(policy = {}) {
65
66
  */
66
67
  export async function ensureClaudeMdHook(projectPath, opts = {}) {
67
68
  const claudeMdPath = path.join(projectPath, 'CLAUDE.md');
69
+ await assertSafeContextFile(claudeMdPath);
68
70
  const result = {
69
71
  claudeMdPath,
70
72
  action: 'skipped',
@@ -0,0 +1,18 @@
1
+ import { lstat } from 'node:fs/promises';
2
+ import { basename } from 'node:path';
3
+ /** Never read or update a provider instruction file through a filesystem link. */
4
+ export async function assertSafeContextFile(filePath) {
5
+ let stat;
6
+ try {
7
+ stat = await lstat(filePath);
8
+ }
9
+ catch (error) {
10
+ if (error.code === 'ENOENT')
11
+ return;
12
+ throw error;
13
+ }
14
+ if (stat.isSymbolicLink() || !stat.isFile()) {
15
+ throw new Error(`Refusing unsafe ${basename(filePath)} target: ${filePath}`);
16
+ }
17
+ }
18
+ //# sourceMappingURL=context-file-safety.js.map
@@ -7,47 +7,16 @@
7
7
  */
8
8
  import { promises as fs } from 'node:fs';
9
9
  import * as path from 'node:path';
10
- import { execFile } from 'node:child_process';
11
- import { promisify } from 'node:util';
12
- import { readAiwgConfig } from '../../config/aiwg-config.js';
13
10
  import { projectAiwgPath, projectControlPath, resolveProjectAiwgDir, } from '../../config/project-artifacts.js';
14
11
  import { renderTrackerProtocol, resolveTrackerAuthority } from '../../tracker/capability-protocol.js';
12
+ import { readConfig, readGitRemoteUrls } from '../../tracker/project-inputs.js';
15
13
  import { buildExternalLinksSection, replaceOrAppendExternalLinksBlock, } from './external-links-section.js';
16
14
  export const FINALIZATION_START = '<!-- aiwg-context-finalization:START -->';
17
15
  export const FINALIZATION_END = '<!-- aiwg-context-finalization:END -->';
18
16
  const AIWG_SIGNATURE_COMMENT = '<!-- aiwg-managed -->';
19
- const execFileAsync = promisify(execFile);
20
17
  function formatList(values) {
21
18
  return values.length > 0 ? values.join(', ') : 'none recorded';
22
19
  }
23
- async function readConfig(projectPath) {
24
- try {
25
- return await readAiwgConfig(projectPath);
26
- }
27
- catch {
28
- return null;
29
- }
30
- }
31
- async function readGitRemoteUrls(projectPath) {
32
- try {
33
- const { stdout } = await execFileAsync('git', ['-C', projectPath, 'remote', '-v'], {
34
- maxBuffer: 1024 * 1024,
35
- });
36
- const urls = {};
37
- for (const line of stdout.split(/\r?\n/)) {
38
- const match = line.match(/^(\S+)\s+(\S+)\s+\((fetch|push)\)$/);
39
- if (!match)
40
- continue;
41
- const [, name, url, direction] = match;
42
- if (direction === 'fetch' || !urls[name])
43
- urls[name] = url;
44
- }
45
- return urls;
46
- }
47
- catch {
48
- return {};
49
- }
50
- }
51
20
  function displayProjectPath(projectPath, targetPath) {
52
21
  const relative = path.relative(projectPath, targetPath).replace(/\\/g, '/');
53
22
  if (relative && !relative.startsWith('..') && !path.isAbsolute(relative))
@@ -20,6 +20,7 @@ import { writeNormalizedAiwgMd } from './finalization.js';
20
20
  import { shouldEmitAgentsMd, shouldEmitAiwgMd, shouldEmitClaudeMdHook } from './provider-policy.js';
21
21
  import { ensureClaudeMdHook } from './claude-hook.js';
22
22
  import { ensureManagedHook } from './managed-hook.js';
23
+ import { assertSafeContextFile } from './context-file-safety.js';
23
24
  import { buildProviderBootstrapBlock, ensureWorkspaceContext, registerProviderContext, } from './workspace-context.js';
24
25
  import { projectControlPath } from '../../config/project-artifacts.js';
25
26
  const SECTION_TITLES = {
@@ -344,6 +345,17 @@ async function writeSpilloverBlock(projectPath, spilloverMarkdown, result) {
344
345
  * is present; otherwise it is skipped with a warning.
345
346
  */
346
347
  export async function generate(opts) {
348
+ // Check every startup/context output before writing the first one. A linked
349
+ // AGENTS.md or CLAUDE.md must not become an input to generated context, even
350
+ // when an additive hook or forced backup would otherwise read it.
351
+ const plannedFiles = [
352
+ ...(!opts.skip?.workspaceMd ? ['WORKSPACE.md'] : []),
353
+ ...(!opts.skip?.agentsMd && shouldEmitAgentsMd(opts.provider) ? ['AGENTS.md'] : []),
354
+ ...(!opts.skip?.aiwgMd && shouldEmitAiwgMd(opts.provider) ? ['AIWG.md'] : []),
355
+ ...(shouldEmitClaudeMdHook(opts.provider) ? ['CLAUDE.md'] : []),
356
+ ];
357
+ for (const file of plannedFiles)
358
+ await assertSafeContextFile(path.join(opts.projectPath, file));
347
359
  const result = {
348
360
  workspaceMdPath: '',
349
361
  aiwgMdPath: '',
@@ -22,7 +22,7 @@ export { generate, buildAgentsMd, renderEntry, renderSection, isOverwriteSafe, }
22
22
  export { sanitizeDescription, sanitizeTag, sanitizeTags } from './sanitizer.js';
23
23
  export { checkPathAllowed } from './allowlist.js';
24
24
  export { discoverDeployedArtifacts, discoverSection, } from './discovery.js';
25
- export { AGENTS_MD_PROVIDERS, shouldEmitContextFiles, shouldEmitAiwgMd, shouldEmitAgentsMd, shouldEmitClaudeMdHook, } from './provider-policy.js';
25
+ export { AGENTS_MD_PROVIDERS, shouldEmitContextFiles, shouldEmitAiwgMd, shouldEmitAgentsMd, shouldEmitClaudeMdHook, buildMuseBridgeText, } from './provider-policy.js';
26
26
  export { generateAiwgMd } from './aiwg-md.js';
27
27
  export { CLAUDE_HOOK_START, CLAUDE_HOOK_END, buildClaudeHookBlock, ensureClaudeMdHook, } from './claude-hook.js';
28
28
  export { FINALIZATION_START, FINALIZATION_END, buildContextFinalizationBlock, buildNormalizedAiwgMd, replaceOrAppendFinalizationBlock, writeNormalizedAiwgMd, } from './finalization.js';
@@ -15,6 +15,7 @@ import * as fs from 'fs/promises';
15
15
  import * as path from 'path';
16
16
  import { buildProviderBootstrapBlock } from './workspace-context.js';
17
17
  import { dominantLineEnding, withLineEnding } from './line-endings.js';
18
+ import { assertSafeContextFile } from './context-file-safety.js';
18
19
  export const CONTEXT_HOOK_START = '<!-- AIWG:context-hook:start -->';
19
20
  export const CONTEXT_HOOK_END = '<!-- AIWG:context-hook:end -->';
20
21
  /** The managed block — loads canonical workspace context before framework context. */
@@ -42,6 +43,7 @@ export function hasContextHook(content) {
42
43
  * operator-authored content. Additive by default (no `--force` needed).
43
44
  */
44
45
  export async function ensureManagedHook(filePath, opts = {}) {
46
+ await assertSafeContextFile(filePath);
45
47
  const base = path.basename(filePath);
46
48
  let block = buildContextHookBlock(opts.provider);
47
49
  const result = { path: filePath, action: 'skipped', warnings: [] };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Muse-native discover-first bridge text for the AGENTS.md managed section
3
+ * (#227).
4
+ *
5
+ * Muse Code reads project AGENTS.md natively — but only after the workspace
6
+ * is explicitly trusted (first-run trust prompt). The bridge therefore leads
7
+ * with the trust-gated load, follows the Muse-native instruction order
8
+ * (this AGENTS.md first, then WORKSPACE.md, then AIWG.md), and states the
9
+ * discover-first protocol (`aiwg discover` / `aiwg show`) with no auto-load
10
+ * claims for other AIWG paths. Artifact references and one-sentence
11
+ * summaries only — never full bodies — per
12
+ * docs/architecture/adr-muse-provider-target.md. No CLAUDE.md shim, no
13
+ * foreign-provider context file.
14
+ *
15
+ * Deterministic: static text only — no timestamps, no randomness — so
16
+ * `aiwg regenerate --provider muse` produces byte-identical output.
17
+ *
18
+ * This module is intentionally dependency-free: the bridge text is consumed
19
+ * by `buildProviderBootstrapBlock` (workspace-context.ts), which must not
20
+ * pull provider-policy.ts's module-evaluation side effects into every
21
+ * import graph.
22
+ *
23
+ * @issue #227
24
+ */
25
+ export function buildMuseBridgeText() {
26
+ return [
27
+ 'This file is the discover-first AIWG bridge for Muse Code. Muse loads',
28
+ 'project AGENTS.md natively — but only after the workspace is explicitly',
29
+ 'trusted (first-run trust prompt). Until the workspace is trusted, this',
30
+ 'file does not load: trust the workspace when prompted, then start a new',
31
+ 'Muse session so the bridge is read.',
32
+ '',
33
+ 'Instruction order: this AGENTS.md first, then [WORKSPACE.md](./WORKSPACE.md)',
34
+ 'for project/operator context, then [AIWG.md](./AIWG.md) (canonical source:',
35
+ '`.aiwg/AIWG.md`) for AIWG discovery, quickrefs, and framework routing.',
36
+ 'Plain Markdown links are explicit reading instructions; AIWG does not',
37
+ 'claim Muse auto-loads any other AIWG path on its own.',
38
+ '',
39
+ 'Before improvising on any AIWG capability: run `aiwg discover "<intent>"`,',
40
+ 'then `aiwg show <type> <name>` for the artifact you need. Prefer indexed',
41
+ 'agents, skills, rules, and commands over guessing. Store artifact',
42
+ 'references and one-sentence summaries only — never paste full artifact',
43
+ 'bodies into context or memory.',
44
+ '',
45
+ 'After `aiwg regenerate` or a redeploy, trust the workspace again if',
46
+ 'prompted and start a new Muse session to re-read this file. Changes are',
47
+ 'not picked up by reloading an IDE window.',
48
+ ].join('\n');
49
+ }
50
+ //# sourceMappingURL=muse-bridge.js.map
@@ -61,4 +61,9 @@ export function shouldEmitAgentsMd(provider) {
61
61
  export function shouldEmitClaudeMdHook(provider) {
62
62
  return getProviderDefinition(provider)?.paths.contextFiles.claudeMdHook ?? false;
63
63
  }
64
+ // The Muse discover-first bridge text lives in ./muse-bridge.js (kept
65
+ // dependency-free so consumers like buildProviderBootstrapBlock do not pull
66
+ // this module's evaluation side effects into every import graph). Re-export
67
+ // it here so the provider-policy surface stays the canonical policy home.
68
+ export { buildMuseBridgeText } from './muse-bridge.js';
64
69
  //# sourceMappingURL=provider-policy.js.map
@@ -12,6 +12,7 @@ import * as path from 'node:path';
12
12
  import { buildAiwgMdContent } from './aiwg-md.js';
13
13
  import { buildNormalizedAiwgMd } from './finalization.js';
14
14
  import { getProviderDefinition, listProviderDefinitions, } from '../../providers/provider-definitions.js';
15
+ import { buildMuseBridgeText } from './muse-bridge.js';
15
16
  import { readAiwgConfig } from '../../config/aiwg-config.js';
16
17
  import { projectAiwgPath, projectControlPath, resolveProjectAiwgDir, } from '../../config/project-artifacts.js';
17
18
  import { dominantLineEnding, withLineEnding } from './line-endings.js';
@@ -490,24 +491,29 @@ export function buildProviderBootstrapBlock(provider) {
490
491
  'guidance. Platform capability and safety constraints remain absolute; see WORKSPACE.md',
491
492
  '"Precedence" for the capability-versus-directive distinction.',
492
493
  ];
493
- const loading = contract.loadMode === 'native-include'
494
- ? [
495
- 'Load the canonical project context first, then the generated AIWG framework context:',
496
- '',
497
- provider === 'omp' ? '@../WORKSPACE.md' : '@WORKSPACE.md',
498
- provider === 'omp' ? '@../AIWG.md' : '@AIWG.md',
499
- ]
500
- : contract.loadMode === 'config-registration'
494
+ // #227: Muse gets its own discover-first bridge text — Muse-native
495
+ // instruction order (this AGENTS.md first, then WORKSPACE.md, then AIWG.md)
496
+ // plus the trust-gated load caveat — instead of the generic prose-directive.
497
+ const loading = provider === 'muse'
498
+ ? [buildMuseBridgeText()]
499
+ : contract.loadMode === 'native-include'
501
500
  ? [
502
- 'WORKSPACE.md and AIWG.md are registered through the provider configuration.',
503
- 'Read and follow WORKSPACE.md first; use AIWG.md second for AIWG discovery and routing.',
504
- ]
505
- : [
506
- 'Read and follow [WORKSPACE.md](./WORKSPACE.md) first.',
507
- 'Then read [AIWG.md](./AIWG.md) for AIWG discovery, quickrefs, and framework routing.',
501
+ 'Load the canonical project context first, then the generated AIWG framework context:',
508
502
  '',
509
- 'These are explicit reading instructions. Plain Markdown links are not claimed to auto-load.',
510
- ];
503
+ provider === 'omp' ? '@../WORKSPACE.md' : '@WORKSPACE.md',
504
+ provider === 'omp' ? '@../AIWG.md' : '@AIWG.md',
505
+ ]
506
+ : contract.loadMode === 'config-registration'
507
+ ? [
508
+ 'WORKSPACE.md and AIWG.md are registered through the provider configuration.',
509
+ 'Read and follow WORKSPACE.md first; use AIWG.md second for AIWG discovery and routing.',
510
+ ]
511
+ : [
512
+ 'Read and follow [WORKSPACE.md](./WORKSPACE.md) first.',
513
+ 'Then read [AIWG.md](./AIWG.md) for AIWG discovery, quickrefs, and framework routing.',
514
+ '',
515
+ 'These are explicit reading instructions. Plain Markdown links are not claimed to auto-load.',
516
+ ];
511
517
  return [
512
518
  PROVIDER_BOOTSTRAP_START,
513
519
  '',
@@ -0,0 +1,64 @@
1
+ /** Grok Build hook translator. Deployment writes project JSON only; trust remains an operator action. */
2
+ import { promises as fs } from 'node:fs';
3
+ import * as path from 'node:path';
4
+ export const GROK_EVENT_MAP = {
5
+ SessionStart: 'SessionStart',
6
+ SessionEnd: 'SessionEnd',
7
+ UserPromptSubmit: 'UserPromptSubmit',
8
+ PreToolUse: 'PreToolUse',
9
+ PostToolUse: 'PostToolUse',
10
+ Stop: 'Stop',
11
+ };
12
+ function safeId(id) {
13
+ return /^[a-z0-9][a-z0-9-]{0,62}$/.test(id) ? id : null;
14
+ }
15
+ export async function translateForGrokBuild(source, options) {
16
+ const result = { provider: 'grok-build', emittedPaths: [], warnings: [], skipped: false };
17
+ if (source.degradeOn?.includes('grok-build'))
18
+ return { ...result, skipped: true, skipReason: 'degrade-on declared grok-build' };
19
+ const id = safeId(source.id);
20
+ if (!id)
21
+ return { ...result, skipped: true, skipReason: 'hook id is not a safe Grok file name' };
22
+ const mapped = source.events.map(event => GROK_EVENT_MAP[event]).filter((event) => Boolean(event));
23
+ const unsupported = source.events.filter(event => !GROK_EVENT_MAP[event]);
24
+ if (unsupported.length)
25
+ result.warnings.push(`Unsupported Grok hook events: ${unsupported.join(', ')}`);
26
+ if (!mapped.length)
27
+ return { ...result, skipped: true, skipReason: 'no equivalent Grok hook events' };
28
+ const hooksDir = path.resolve(options.projectPath, '.grok', 'hooks');
29
+ const filePath = path.resolve(hooksDir, `aiwg-${id}.json`);
30
+ if (!filePath.startsWith(`${hooksDir}${path.sep}`))
31
+ return { ...result, skipped: true, skipReason: 'hook path escapes project hook root' };
32
+ const hooks = Object.fromEntries(mapped.map(event => [event, [{ hooks: [{ type: 'command', command: [source.command, ...(source.args || [])].join(' '), timeout: 10 }] }]]));
33
+ const document = { _aiwg_managed: true, _aiwg_id: id, hooks };
34
+ result.warnings.push('Project hook emitted without granting Grok trust; use /hooks-trust separately after review.');
35
+ if (source.events.includes('PreToolUse')) {
36
+ result.warnings.push('Only explicit PreToolUse JSON denial blocks; crashes, timeouts, malformed output, and all passive hook failures fail open.');
37
+ }
38
+ if (options.dryRun) {
39
+ result.emittedPaths.push(`${filePath} (dry-run)`);
40
+ return result;
41
+ }
42
+ await fs.mkdir(hooksDir, { recursive: true });
43
+ const dirInfo = await fs.lstat(hooksDir);
44
+ if (dirInfo.isSymbolicLink())
45
+ throw new Error('Refusing to write Grok hooks through a symbolic link');
46
+ try {
47
+ const info = await fs.lstat(filePath);
48
+ if (info.isSymbolicLink() || !info.isFile())
49
+ throw new Error('Refusing unsafe Grok hook target');
50
+ const current = JSON.parse(await fs.readFile(filePath, 'utf8'));
51
+ if (current._aiwg_managed !== true || current._aiwg_id !== id)
52
+ throw new Error(`Refusing to replace operator-managed Grok hook ${filePath}`);
53
+ }
54
+ catch (error) {
55
+ if (error.code !== 'ENOENT')
56
+ throw error;
57
+ }
58
+ const temp = `${filePath}.tmp-${process.pid}`;
59
+ await fs.writeFile(temp, `${JSON.stringify(document, null, 2)}\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
60
+ await fs.rename(temp, filePath);
61
+ result.emittedPaths.push(filePath);
62
+ return result;
63
+ }
64
+ //# sourceMappingURL=grok-build-translator.js.map
@@ -13,6 +13,7 @@ import { translateForCodex } from './codex-translator.js';
13
13
  import { translateForCopilot } from './copilot-translator.js';
14
14
  import { translateForFactory } from './factory-translator.js';
15
15
  import { translateForHermes } from './hermes-translator.js';
16
+ import { translateForGrokBuild } from './grok-build-translator.js';
16
17
  import { getProviderDefinition } from '../../providers/provider-definitions.js';
17
18
  export { AIWG_ENV_VARS, NATIVE_ENV_VAR_MAP } from './types.js';
18
19
  export { substituteEnvVars, generateShimBash, EXIT_CODE_MAP } from './shim.js';
@@ -20,6 +21,7 @@ export { translateForCodex, renderCodexHookToml, injectHookBlock as injectCodexH
20
21
  export { translateForCopilot } from './copilot-translator.js';
21
22
  export { translateForFactory } from './factory-translator.js';
22
23
  export { translateForHermes } from './hermes-translator.js';
24
+ export { translateForGrokBuild, GROK_EVENT_MAP } from './grok-build-translator.js';
23
25
  export { loadHookSources } from './loader.js';
24
26
  /**
25
27
  * Provider id → translator function. Adding a new provider requires:
@@ -34,6 +36,7 @@ export const TRANSLATORS = {
34
36
  copilot: translateForCopilot,
35
37
  factory: translateForFactory,
36
38
  hermes: translateForHermes,
39
+ 'grok-build': translateForGrokBuild,
37
40
  };
38
41
  function resolveHookTranslator(providerId) {
39
42
  const adapterKey = getProviderDefinition(providerId)?.adapters.hookBridge ?? providerId;