@jstn-sdk/ma 0.1.13 → 0.14.1

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 (259) hide show
  1. package/.agents/plugins/marketplace.json +0 -0
  2. package/.codex/agents/Architect.toml +1 -0
  3. package/.codex/agents/Auditor.toml +1 -0
  4. package/.codex/agents/Builder.toml +1 -0
  5. package/.codex/agents/Flow.toml +1 -0
  6. package/.codex/agents/Sage.toml +1 -0
  7. package/.codex/agents/Vibe.toml +1 -0
  8. package/.codex/hooks.json +12 -2
  9. package/.codex/prompts/enforcement.md +0 -0
  10. package/.codex/prompts/onboarding.md +6 -1
  11. package/.codex/prompts/release-rules.md +0 -0
  12. package/.codex/prompts/skill-contract.md +0 -0
  13. package/COVERAGE.md +9 -9
  14. package/DEMO.md +15 -6
  15. package/LICENSE +0 -0
  16. package/README.md +127 -784
  17. package/bin/ma.js +591 -105
  18. package/data/clone-data.ledger.json +0 -0
  19. package/data/clone-data.proof.json +0 -0
  20. package/data/clone-data.rvf +0 -0
  21. package/docs/README.md +8 -2
  22. package/docs/assets/banner.png +0 -0
  23. package/docs/autonomous-tasks.md +46 -0
  24. package/docs/codex-integration.md +16 -0
  25. package/docs/getting-started.md +32 -8
  26. package/docs/installed-sdk.md +0 -0
  27. package/docs/mcp-setup.md +30 -1
  28. package/docs/qa/{release-issue-gates-0.1.13.json → release-issue-gates-0.14.1.json} +77 -77
  29. package/docs/qa/release-readiness-0.1.5.md +0 -0
  30. package/docs/qa/{release-readiness-0.1.13.md → release-readiness-0.14.1.md} +12 -12
  31. package/docs/quality.md +24 -0
  32. package/docs/reference/native-engineering-patterns.md +0 -0
  33. package/docs/reference/native-security-playbooks.md +0 -0
  34. package/docs/reference/native-source-selection.md +0 -0
  35. package/docs/reference/native-style-and-deslop.md +0 -0
  36. package/docs/release-spec.md +27 -14
  37. package/docs/skills.md +1 -1
  38. package/docs/vendor-plugin-publishing.md +49 -0
  39. package/index.js +73 -0
  40. package/mcp/collections.json +0 -0
  41. package/mcp/fallback.json +0 -0
  42. package/mcp/local/code-intel.js +185 -41
  43. package/mcp/local/context.js +180 -0
  44. package/mcp/local/memory.js +17 -6
  45. package/mcp/local/playbooks.js +0 -0
  46. package/mcp/local/quality.js +39 -0
  47. package/mcp/local/state.js +7 -3
  48. package/mcp/local/team-run.js +8 -3
  49. package/mcp/local/trace.js +0 -0
  50. package/mcp/local-capabilities.json +9 -0
  51. package/mcp/native-playbooks.json +0 -0
  52. package/mcp/servers.json +0 -0
  53. package/package.json +37 -6
  54. package/plugins/meta-architect/.app.json +1 -1
  55. package/plugins/meta-architect/.claude-plugin/plugin.json +12 -0
  56. package/plugins/meta-architect/.codex-plugin/plugin.json +1 -1
  57. package/plugins/meta-architect/.mcp.json +1 -1
  58. package/plugins/meta-architect/README.md +14 -2
  59. package/plugins/meta-architect/obsidian/main.js +137 -4
  60. package/plugins/meta-architect/obsidian/manifest.json +1 -1
  61. package/plugins/meta-architect/obsidian/styles.css +0 -0
  62. package/plugins/meta-architect/skills/align/SKILL.md +0 -0
  63. package/plugins/meta-architect/skills/align/agents/openai.yaml +0 -0
  64. package/plugins/meta-architect/skills/align/references/shared-language.md +0 -0
  65. package/plugins/meta-architect/skills/arch/SKILL.md +0 -0
  66. package/plugins/meta-architect/skills/arch/agents/openai.yaml +0 -0
  67. package/plugins/meta-architect/skills/build/SKILL.md +0 -0
  68. package/plugins/meta-architect/skills/build/agents/openai.yaml +0 -0
  69. package/plugins/meta-architect/skills/cleanup/SKILL.md +0 -0
  70. package/plugins/meta-architect/skills/cleanup/agents/openai.yaml +0 -0
  71. package/plugins/meta-architect/skills/cleanup/references/style-and-deslop.md +0 -0
  72. package/plugins/meta-architect/skills/diagnose/SKILL.md +0 -0
  73. package/plugins/meta-architect/skills/diagnose/agents/openai.yaml +0 -0
  74. package/plugins/meta-architect/skills/flow/SKILL.md +0 -0
  75. package/plugins/meta-architect/skills/flow/agents/openai.yaml +0 -0
  76. package/plugins/meta-architect/skills/maestro/SKILL.md +17 -1
  77. package/plugins/meta-architect/skills/maestro/agents/openai.yaml +0 -0
  78. package/plugins/meta-architect/skills/maestro/references/core-release-rules.md +2 -2
  79. package/plugins/meta-architect/skills/maestro/references/native-ingest-map.md +0 -0
  80. package/plugins/meta-architect/skills/sage/SKILL.md +0 -0
  81. package/plugins/meta-architect/skills/sage/agents/openai.yaml +0 -0
  82. package/plugins/meta-architect/skills/sage/references/source-selection.md +0 -0
  83. package/plugins/meta-architect/skills/tdd/SKILL.md +0 -0
  84. package/plugins/meta-architect/skills/tdd/agents/openai.yaml +0 -0
  85. package/plugins/meta-architect/skills/vet/SKILL.md +0 -0
  86. package/plugins/meta-architect/skills/vet/agents/openai.yaml +0 -0
  87. package/plugins/meta-architect/skills/vet/references/security-playbooks.md +0 -0
  88. package/plugins/meta-architect/skills/vibe/SKILL.md +0 -0
  89. package/plugins/meta-architect/skills/vibe/agents/openai.yaml +0 -0
  90. package/schemas/autonomous-task-queue.schema.json +58 -0
  91. package/schemas/handoff-packet.schema.json +36 -0
  92. package/schemas/setup-receipt.schema.json +28 -0
  93. package/schemas/skill-frontmatter.schema.json +20 -0
  94. package/schemas/support-bundle.schema.json +37 -0
  95. package/schemas/task-contract.schema.json +31 -0
  96. package/scripts/active-autonomy-hook.mjs +141 -11
  97. package/scripts/biome-staged.js +0 -0
  98. package/scripts/build-linux-packages.mjs +7 -3
  99. package/scripts/cleanup-test-fixtures.sh +55 -0
  100. package/scripts/context-hydration-hook.mjs +89 -0
  101. package/scripts/demo-smoke.js +285 -0
  102. package/scripts/doctor.js +10 -8
  103. package/scripts/install.sh +33 -4
  104. package/scripts/install.sh.sha256 +1 -0
  105. package/scripts/linux-package-lib.mjs +0 -0
  106. package/scripts/linux-package-smoke.mjs +2 -2
  107. package/scripts/package-size-check.mjs +26 -0
  108. package/scripts/plugin-build.js +314 -0
  109. package/scripts/plugin-publish.js +77 -0
  110. package/scripts/plugin-sync.js +83 -17
  111. package/scripts/prepack.js +9 -0
  112. package/scripts/ralph/prompt.md +0 -0
  113. package/scripts/release-metadata.js +0 -0
  114. package/scripts/release-sync.js +83 -7
  115. package/scripts/release-verify.js +131 -7
  116. package/scripts/setup-npmrc.js +48 -23
  117. package/scripts/skills-install.js +9 -2
  118. package/scripts/skills-manifest.js +2 -21
  119. package/scripts/skills-pack.js +2 -2
  120. package/scripts/skills-validate.js +5 -55
  121. package/skills/align/SKILL.md +0 -0
  122. package/skills/align/agents/openai.yaml +0 -0
  123. package/skills/align/references/shared-language.md +0 -0
  124. package/skills/arch/SKILL.md +0 -0
  125. package/skills/arch/agents/openai.yaml +0 -0
  126. package/skills/build/SKILL.md +0 -0
  127. package/skills/build/agents/openai.yaml +0 -0
  128. package/skills/cleanup/SKILL.md +0 -0
  129. package/skills/cleanup/agents/openai.yaml +0 -0
  130. package/skills/cleanup/references/style-and-deslop.md +0 -0
  131. package/skills/diagnose/SKILL.md +0 -0
  132. package/skills/diagnose/agents/openai.yaml +0 -0
  133. package/skills/flow/SKILL.md +0 -0
  134. package/skills/flow/agents/openai.yaml +0 -0
  135. package/skills/index.json +0 -0
  136. package/skills/maestro/SKILL.md +17 -1
  137. package/skills/maestro/agents/openai.yaml +0 -0
  138. package/skills/maestro/references/core-release-rules.md +2 -2
  139. package/skills/maestro/references/native-ingest-map.md +0 -0
  140. package/skills/sage/SKILL.md +0 -0
  141. package/skills/sage/agents/openai.yaml +0 -0
  142. package/skills/sage/references/source-selection.md +0 -0
  143. package/skills/tdd/SKILL.md +0 -0
  144. package/skills/tdd/agents/openai.yaml +0 -0
  145. package/skills/vet/SKILL.md +0 -0
  146. package/skills/vet/agents/openai.yaml +0 -0
  147. package/skills/vet/references/security-playbooks.md +0 -0
  148. package/skills/vibe/SKILL.md +0 -0
  149. package/skills/vibe/agents/openai.yaml +0 -0
  150. package/sprint/00-idea.md +0 -0
  151. package/sprint/01-architecture.md +0 -0
  152. package/sprint/02-oss-evidence.md +0 -0
  153. package/sprint/03-logic.md +0 -0
  154. package/sprint/04-security.md +0 -0
  155. package/sprint/05-dx-ux.md +0 -0
  156. package/sprint/06-build-plan.md +0 -0
  157. package/sprint/07-release.md +2 -2
  158. package/src/agents.js +281 -0
  159. package/src/bootstrap.js +272 -65
  160. package/src/build-gate.js +0 -0
  161. package/src/codex-app-server.js +291 -0
  162. package/src/decision-log.js +0 -0
  163. package/src/fs-utils.js +175 -4
  164. package/src/launcher.js +26 -23
  165. package/src/mcp-config.js +9 -1
  166. package/src/mcp-live-client.js +141 -8
  167. package/src/paths.js +6 -1
  168. package/src/policy.js +2 -2
  169. package/src/prelaunch.js +223 -0
  170. package/src/process-utils.js +62 -0
  171. package/src/quality/ai-quality-orchestrator.js +328 -0
  172. package/src/release-issue-gates.js +62 -0
  173. package/src/release-operations.js +72 -0
  174. package/src/release-state.js +0 -0
  175. package/src/runtime/active-autonomy-core.js +0 -0
  176. package/src/runtime/agent-compat.js +31 -0
  177. package/src/runtime/alignment-sentinel.js +0 -0
  178. package/src/runtime/architect-review.js +249 -11
  179. package/src/runtime/autonomous-tasks.js +585 -0
  180. package/src/runtime/build-readiness.js +0 -0
  181. package/src/runtime/code-graph-rehearse.js +0 -0
  182. package/src/runtime/codeburn-core.js +118 -0
  183. package/src/runtime/context-authority.js +40 -0
  184. package/src/runtime/context-economy-core.js +40 -0
  185. package/src/runtime/continuity-graph.js +201 -0
  186. package/src/runtime/continuity-notes.js +71 -26
  187. package/src/runtime/core-source-ingest.js +2 -2
  188. package/src/runtime/detached-provider.js +90 -13
  189. package/src/runtime/doctor-report.js +18 -0
  190. package/src/runtime/environment-awareness-core.js +0 -0
  191. package/src/runtime/exposure-catalog.js +0 -0
  192. package/src/runtime/graphify-core.js +167 -0
  193. package/src/runtime/guidance-stack.js +9 -2
  194. package/src/runtime/handoff-packets.js +92 -0
  195. package/src/runtime/headroom-core.js +104 -0
  196. package/src/runtime/helper-orchestration-core.js +0 -0
  197. package/src/runtime/hook-profiles.js +20 -0
  198. package/src/runtime/learning-loop-core.js +110 -4
  199. package/src/runtime/live-agent-verification.js +122 -0
  200. package/src/runtime/maestro-events.js +0 -0
  201. package/src/runtime/maestro-manager.js +0 -0
  202. package/src/runtime/maestro-output.js +58 -0
  203. package/src/runtime/maestro-state.js +0 -0
  204. package/src/runtime/managed-markdown.js +29 -0
  205. package/src/runtime/mcp-authority.js +147 -0
  206. package/src/runtime/mcp-policy.js +0 -0
  207. package/src/runtime/obsidian-integration-core.js +255 -17
  208. package/src/runtime/obsidian-plugin-bridge.js +344 -38
  209. package/src/runtime/orchestrator.js +0 -0
  210. package/src/runtime/pi-maestro-core.js +89 -0
  211. package/src/runtime/preferences.js +79 -0
  212. package/src/runtime/project-context.js +636 -0
  213. package/src/runtime/prompt-strategy-core.js +0 -0
  214. package/src/runtime/quorum-review.js +0 -0
  215. package/src/runtime/ralph-execution-core.js +0 -0
  216. package/src/runtime/redaction-gateway.js +218 -19
  217. package/src/runtime/runtime-state.js +94 -2
  218. package/src/runtime/schema-migrations.js +162 -0
  219. package/src/runtime/semantic-recording-core.js +0 -0
  220. package/src/runtime/signal-hooks.js +13 -0
  221. package/src/runtime/skills-registry-export.js +96 -8
  222. package/src/runtime/startup-path.js +0 -0
  223. package/src/runtime/task-contracts.js +83 -0
  224. package/src/runtime/universal-plugin-broker-core.js +373 -50
  225. package/src/runtime/workspace-intelligence-runtime.js +0 -0
  226. package/src/runtime/workspace-virtualizer.js +0 -0
  227. package/src/runtime/workspaces.js +0 -0
  228. package/src/runtime-artifacts.js +0 -0
  229. package/src/setup-lifecycle.js +359 -0
  230. package/src/skill-frontmatter.js +78 -0
  231. package/src/skill-installer.js +226 -71
  232. package/src/skills.js +309 -31
  233. package/src/state-sync.js +0 -0
  234. package/src/test-fixtures.js +543 -0
  235. package/src/tui/grid.js +67 -0
  236. package/src/tui/status-grid.js +23 -0
  237. package/support-bundle.json +114 -0
  238. package/templates/AGENTS.md +6 -2
  239. package/templates/catalog-manifest.json +0 -0
  240. package/templates/model-instructions/core.md +0 -0
  241. package/templates/model-instructions/release.md +0 -0
  242. package/templates/model-instructions/security.md +0 -0
  243. package/templates/quality/ai-quality-rules.yml +37 -0
  244. package/docs/assets/image/Screenshot(1).png +0 -0
  245. package/docs/assets/image/Screenshot(2).png +0 -0
  246. package/docs/assets/image/Screenshot(3).png +0 -0
  247. package/docs/assets/image/Screenshot(4).png +0 -0
  248. package/docs/assets/image/Screenshot(5).png +0 -0
  249. package/docs/assets/image/Screenshot(6).png +0 -0
  250. package/docs/assets/image/Screenshot(7).png +0 -0
  251. package/docs/assets/image/Screenshot(8).png +0 -0
  252. package/docs/assets/image/Screenshot(9).png +0 -0
  253. package/docs/assets/meta-architect-logo.png +0 -0
  254. package/docs/assets/meta-architect-logo.svg +0 -8
  255. package/docs/onboarding.md +0 -81
  256. package/docs/prompt-guidance-contract.md +0 -17
  257. package/docs/prompt-guidance-fragments/active-autonomy-core.md +0 -7
  258. package/docs/skills-publishing.md +0 -255
  259. package/scripts/postinstall.js +0 -23
@@ -1,8 +1,19 @@
1
- import { spawnSync } from "node:child_process";
1
+ import { readFileSync } from "node:fs";
2
2
  import fs from "node:fs/promises";
3
+ import path from "node:path";
3
4
  import { readJson, writeFileIfMissing, writeJson } from "../fs-utils.js";
4
5
  import { getRuntimeStatePath } from "../paths.js";
5
- import { redactProviderBoundText, seedRedactionVault } from "./redaction-gateway.js";
6
+ import { safeSpawnSync } from "../process-utils.js";
7
+ import {
8
+ maskSensitiveText,
9
+ redactProviderBoundPayload,
10
+ redactProviderBoundText,
11
+ seedRedactionVault,
12
+ } from "./redaction-gateway.js";
13
+
14
+ const REVIEW_POLICY_PATH = path.join(".ma", "architect-review-policy.json");
15
+ const DEFAULT_REVIEW_TIMEOUT_MS = 120000;
16
+ const MAX_REVIEW_OUTPUT_BYTES = 1024 * 1024;
6
17
 
7
18
  export function getArchitectReviewPath() {
8
19
  return getRuntimeStatePath("architect-review.json");
@@ -57,32 +68,259 @@ export async function runExternalArchitectReview({ prompt = "" } = {}) {
57
68
  const outputPath = getRuntimeStatePath("architect-review.output.json");
58
69
  await fs.writeFile(promptPath, `${redactedPrompt.sanitizedText.trimEnd()}\n`, "utf8");
59
70
 
60
- const result = spawnSync("/bin/sh", ["-lc", command], {
71
+ const { command: binary, args } = parseTrustedCommand(
72
+ command,
73
+ redactedPrompt.sanitizedText,
74
+ getArchitectReviewConfiguration(),
75
+ );
76
+ const timeout = readReviewTimeoutMs();
77
+ const result = safeSpawnSync(binary, args, {
61
78
  cwd: process.cwd(),
62
- env: {
63
- ...process.env,
79
+ env: buildArchitectReviewEnv({
64
80
  MA_ARCHITECT_REVIEW_PROMPT: redactedPrompt.sanitizedText,
65
81
  MA_ARCHITECT_REVIEW_PROMPT_FILE: promptPath,
66
82
  MA_ARCHITECT_REVIEW_OUTPUT: outputPath,
67
- },
83
+ }),
68
84
  encoding: "utf8",
85
+ shell: false,
86
+ timeout,
87
+ maxBuffer: MAX_REVIEW_OUTPUT_BYTES,
88
+ killSignal: "SIGKILL",
69
89
  });
70
90
 
91
+ if (result.error) {
92
+ await writeReviewReceipt({
93
+ status: "failed",
94
+ command: binary,
95
+ timeout,
96
+ error: result.error.message,
97
+ });
98
+ throw new Error(`External architect review failed: ${result.error.message}`);
99
+ }
100
+
71
101
  if (result.status !== 0) {
102
+ await writeReviewReceipt({
103
+ status: "failed",
104
+ command: binary,
105
+ timeout,
106
+ code: result.status,
107
+ stderr: result.stderr,
108
+ });
72
109
  throw new Error(
73
- result.stderr?.trim() || result.stdout?.trim() || "External architect review failed",
110
+ maskSensitiveText(
111
+ result.stderr?.trim() || result.stdout?.trim() || "External architect review failed",
112
+ ).sanitizedText,
74
113
  );
75
114
  }
76
115
 
116
+ let outputStat;
117
+ try {
118
+ outputStat = await fs.stat(outputPath);
119
+ } catch {
120
+ await writeReviewReceipt({
121
+ status: "failed",
122
+ command: binary,
123
+ timeout,
124
+ error: "reviewer did not produce the configured JSON output",
125
+ });
126
+ throw new Error("External architect review did not produce JSON output");
127
+ }
128
+ if (outputStat.size > MAX_REVIEW_OUTPUT_BYTES) {
129
+ await writeReviewReceipt({
130
+ status: "failed",
131
+ command: binary,
132
+ timeout,
133
+ error: `review output exceeds ${MAX_REVIEW_OUTPUT_BYTES} bytes`,
134
+ });
135
+ throw new Error(`External architect review output exceeds ${MAX_REVIEW_OUTPUT_BYTES} bytes`);
136
+ }
77
137
  const parsed = JSON.parse(await fs.readFile(outputPath, "utf8"));
138
+ const safeOutput = await redactProviderBoundPayload(parsed, {
139
+ kind: "architect-review-output",
140
+ });
141
+ const sanitized = safeOutput.sanitizedPayload;
78
142
  const review = {
79
143
  schemaVersion: "0.1.0",
80
- verdict: parsed.verdict ?? "UNKNOWN",
81
- reviewer: parsed.reviewer ?? "external",
82
- summary: parsed.summary ?? null,
83
- findings: Array.isArray(parsed.findings) ? parsed.findings : [],
144
+ verdict: sanitized.verdict ?? "UNKNOWN",
145
+ reviewer: sanitized.reviewer ?? "external",
146
+ summary: sanitized.summary ?? null,
147
+ findings: Array.isArray(sanitized.findings) ? sanitized.findings : [],
84
148
  reviewedAt: new Date().toISOString(),
85
149
  };
86
150
  await saveArchitectReview(review);
151
+ await writeReviewReceipt({
152
+ status: "completed",
153
+ command: binary,
154
+ timeout,
155
+ verdict: review.verdict,
156
+ });
87
157
  return review;
88
158
  }
159
+
160
+ export function getArchitectReviewConfiguration(root = process.env.MA_ROOT ?? process.cwd()) {
161
+ const allowedCommands = new Set(
162
+ (process.env.MA_ARCHITECT_REVIEW_ALLOWLIST ?? "")
163
+ .split(",")
164
+ .map((value) => value.trim())
165
+ .filter(Boolean),
166
+ );
167
+ const policyPath = path.join(root, REVIEW_POLICY_PATH);
168
+ try {
169
+ const policy = JSON.parse(readFileSync(policyPath, "utf8"));
170
+ for (const value of policy.allowedCommands ?? []) {
171
+ if (typeof value === "string" && value.trim()) allowedCommands.add(value.trim());
172
+ }
173
+ return { allowedCommands: [...allowedCommands], policyPath, source: "environment+project" };
174
+ } catch {
175
+ return {
176
+ allowedCommands: [...allowedCommands],
177
+ policyPath,
178
+ source: allowedCommands.size > 0 ? "environment" : "none",
179
+ };
180
+ }
181
+ }
182
+
183
+ function parseTrustedCommand(command, promptText, configuration = {}) {
184
+ if (/[;&|`<>]/.test(command)) {
185
+ throw new Error("MA_ARCHITECT_REVIEW_CMD must not contain shell metacharacters");
186
+ }
187
+
188
+ const parts = splitCommand(command);
189
+ if (parts.length === 0) {
190
+ throw new Error("MA_ARCHITECT_REVIEW_CMD is empty");
191
+ }
192
+
193
+ if (parts[0].includes("=") && !parts[0].includes("/")) {
194
+ throw new Error("MA_ARCHITECT_REVIEW_CMD must start with an executable");
195
+ }
196
+
197
+ const substituted = parts.map((part) => part.replaceAll("{prompt}", promptText));
198
+ const configured = new Set(configuration.allowedCommands ?? []);
199
+ const commandName = path.basename(substituted[0]);
200
+ if (!configured.has(substituted[0]) && !configured.has(commandName)) {
201
+ throw new Error(
202
+ `MA_ARCHITECT_REVIEW_CMD is not allowlisted: ${substituted[0]}. ` +
203
+ "Add its exact command or basename to MA_ARCHITECT_REVIEW_ALLOWLIST or .ma/architect-review-policy.json.",
204
+ );
205
+ }
206
+ return {
207
+ command: substituted[0],
208
+ args: substituted.slice(1),
209
+ };
210
+ }
211
+
212
+ function readReviewTimeoutMs() {
213
+ const parsed = Number(process.env.MA_ARCHITECT_REVIEW_TIMEOUT_MS);
214
+ if (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_REVIEW_TIMEOUT_MS;
215
+ return Math.min(120000, Math.max(1000, Math.floor(parsed)));
216
+ }
217
+
218
+ async function writeReviewReceipt(details) {
219
+ const receiptDir = path.join(
220
+ process.env.MA_ROOT ?? process.cwd(),
221
+ ".ma",
222
+ "evidence",
223
+ "architect-review-receipts",
224
+ );
225
+ const safeDetails = { ...details };
226
+ for (const key of ["error", "stderr"]) {
227
+ if (safeDetails[key])
228
+ safeDetails[key] = maskSensitiveText(String(safeDetails[key])).sanitizedText;
229
+ }
230
+ await fs.mkdir(receiptDir, { recursive: true }).catch(() => {});
231
+ await fs
232
+ .writeFile(
233
+ path.join(receiptDir, `review-${Date.now()}-${process.pid}.json`),
234
+ `${JSON.stringify(
235
+ {
236
+ schemaVersion: "0.1.0",
237
+ record_type: "architect_review_receipt",
238
+ generatedAt: new Date().toISOString(),
239
+ ...safeDetails,
240
+ },
241
+ null,
242
+ 2,
243
+ )}\n`,
244
+ { mode: 0o600 },
245
+ )
246
+ .catch(() => {});
247
+ }
248
+
249
+ function buildArchitectReviewEnv(extraEnv = {}) {
250
+ const passthroughKeys = new Set([
251
+ "PATH",
252
+ "HOME",
253
+ "USERPROFILE",
254
+ "TMPDIR",
255
+ "TMP",
256
+ "TEMP",
257
+ "LANG",
258
+ "LC_ALL",
259
+ "SYSTEMROOT",
260
+ "COMSPEC",
261
+ "MA_ROOT",
262
+ ]);
263
+ const env = {};
264
+ for (const [key, value] of Object.entries(process.env)) {
265
+ if (passthroughKeys.has(key)) {
266
+ env[key] = value;
267
+ }
268
+ }
269
+ return { ...env, ...extraEnv };
270
+ }
271
+
272
+ function splitCommand(command) {
273
+ const parts = [];
274
+ let current = "";
275
+ let quote = null;
276
+ let escaping = false;
277
+
278
+ for (const char of command) {
279
+ if (escaping) {
280
+ current += char;
281
+ escaping = false;
282
+ continue;
283
+ }
284
+
285
+ if (char === "\\") {
286
+ escaping = true;
287
+ continue;
288
+ }
289
+
290
+ if (quote) {
291
+ if (char === quote) {
292
+ quote = null;
293
+ } else {
294
+ current += char;
295
+ }
296
+ continue;
297
+ }
298
+
299
+ if (char === "'" || char === '"') {
300
+ quote = char;
301
+ continue;
302
+ }
303
+
304
+ if (/\s/.test(char)) {
305
+ if (current) {
306
+ parts.push(current);
307
+ current = "";
308
+ }
309
+ continue;
310
+ }
311
+
312
+ current += char;
313
+ }
314
+
315
+ if (escaping) {
316
+ current += "\\";
317
+ }
318
+ if (quote) {
319
+ throw new Error("MA_ARCHITECT_REVIEW_CMD has an unterminated quote");
320
+ }
321
+ if (current) {
322
+ parts.push(current);
323
+ }
324
+
325
+ return parts;
326
+ }