@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,9 +1,26 @@
1
- import { spawn } from "node:child_process";
1
+ import { readFileSync } from "node:fs";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { safeSpawn } from "./process-utils.js";
5
+ import { maskSensitiveText } from "./runtime/redaction-gateway.js";
6
+
7
+ const packageMetadata = JSON.parse(
8
+ readFileSync(new URL("../package.json", import.meta.url), "utf8"),
9
+ );
10
+
11
+ export function resolveMcpClientVersion(metadata) {
12
+ return typeof metadata?.version === "string" && metadata.version.trim()
13
+ ? metadata.version
14
+ : "0.0.0-dev";
15
+ }
16
+
17
+ export const mcpClientVersion = resolveMcpClientVersion(packageMetadata);
2
18
 
3
19
  const GITMCP_ENDPOINT_PATTERN = /^https:\/\/gitmcp\.io\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
4
20
  const BRIDGE_COMMAND_ENV = "MA_MCP_REMOTE_BRIDGE_CMD";
5
21
  const REQUEST_TIMEOUT_ENV = "MA_MCP_REQUEST_TIMEOUT_MS";
6
22
  const DEFAULT_REQUEST_TIMEOUT_MS = 15000;
23
+ const BRIDGE_POLICY_PATH = path.join("mcp", "bridge.json");
7
24
 
8
25
  function readRequestTimeoutMs() {
9
26
  const raw = process.env[REQUEST_TIMEOUT_ENV]?.trim();
@@ -43,12 +60,46 @@ export function hasConfiguredMcpRemoteBridge() {
43
60
 
44
61
  export function createMcpLiveClient(endpoint) {
45
62
  if (hasConfiguredMcpRemoteBridge()) {
46
- return new McpStdioBridgeClient(endpoint, process.env[BRIDGE_COMMAND_ENV]);
63
+ return new McpStdioBridgeClient(
64
+ endpoint,
65
+ process.env[BRIDGE_COMMAND_ENV],
66
+ safeSpawn,
67
+ getMcpBridgeConfiguration(),
68
+ );
47
69
  }
48
70
 
49
71
  return new McpSseClient(endpoint);
50
72
  }
51
73
 
74
+ export function getMcpBridgeConfiguration(root = process.env.MA_ROOT ?? process.cwd()) {
75
+ const allowedCommands = new Set(
76
+ (process.env.MA_MCP_REMOTE_BRIDGE_ALLOWLIST ?? "")
77
+ .split(",")
78
+ .map((value) => value.trim())
79
+ .filter(Boolean),
80
+ );
81
+ const configPath = path.join(root, BRIDGE_POLICY_PATH);
82
+ try {
83
+ const config = JSON.parse(readFileSync(configPath, "utf8"));
84
+ for (const command of config.allowedCommands ?? []) {
85
+ if (typeof command === "string" && command.trim()) allowedCommands.add(command.trim());
86
+ }
87
+ return {
88
+ configured: true,
89
+ configPath,
90
+ allowedCommands: [...allowedCommands],
91
+ source: "environment+project",
92
+ };
93
+ } catch {
94
+ return {
95
+ configured: true,
96
+ configPath,
97
+ allowedCommands: [...allowedCommands],
98
+ source: allowedCommands.size > 0 ? "environment" : "none",
99
+ };
100
+ }
101
+ }
102
+
52
103
  export class McpSseClient {
53
104
  constructor(baseUrl) {
54
105
  this.baseUrl = baseUrl;
@@ -93,7 +144,7 @@ export class McpSseClient {
93
144
  capabilities: {},
94
145
  clientInfo: {
95
146
  name: "meta-architect",
96
- version: "0.1.13-dev",
147
+ version: mcpClientVersion,
97
148
  },
98
149
  });
99
150
 
@@ -227,10 +278,11 @@ export class McpSseClient {
227
278
  }
228
279
 
229
280
  export class McpStdioBridgeClient {
230
- constructor(endpoint, commandTemplate, spawnImpl = spawn) {
281
+ constructor(endpoint, commandTemplate, spawnImpl = spawn, bridgeConfiguration = {}) {
231
282
  this.endpoint = endpoint;
232
283
  this.commandTemplate = commandTemplate;
233
284
  this.spawnImpl = spawnImpl;
285
+ this.bridgeConfiguration = bridgeConfiguration;
234
286
  this.child = null;
235
287
  this.buffer = "";
236
288
  this.pending = new Map();
@@ -240,10 +292,18 @@ export class McpStdioBridgeClient {
240
292
  }
241
293
 
242
294
  async connect() {
243
- const { command, args } = buildBridgeCommand(this.commandTemplate, this.endpoint);
295
+ const { command, args } = buildBridgeCommand(
296
+ this.commandTemplate,
297
+ this.endpoint,
298
+ this.bridgeConfiguration,
299
+ );
244
300
  this.child = this.spawnImpl(command, args, {
245
301
  stdio: ["pipe", "pipe", "pipe"],
246
- env: process.env,
302
+ env: buildBridgeEnv(),
303
+ });
304
+ await writeBridgeReceipt(this.endpoint, command, args, {
305
+ status: "started",
306
+ pid: this.child.pid ?? null,
247
307
  });
248
308
 
249
309
  this.child.stdout.setEncoding("utf8");
@@ -254,6 +314,10 @@ export class McpStdioBridgeClient {
254
314
  });
255
315
  this.child.on("error", (error) => {
256
316
  this.closed = true;
317
+ void writeBridgeReceipt(this.endpoint, command, args, {
318
+ status: "failed",
319
+ error: error.message,
320
+ });
257
321
  for (const [, handlers] of this.pending) {
258
322
  handlers.reject(new Error(`MCP bridge failed to start: ${error.message}`));
259
323
  }
@@ -261,6 +325,12 @@ export class McpStdioBridgeClient {
261
325
  });
262
326
  this.child.on("exit", (code, signal) => {
263
327
  this.closed = true;
328
+ void writeBridgeReceipt(this.endpoint, command, args, {
329
+ status: "exited",
330
+ code,
331
+ signal,
332
+ stderr: this.stderr,
333
+ });
264
334
  const reason = signal
265
335
  ? `MCP bridge exited with signal ${signal}`
266
336
  : `MCP bridge exited with code ${code}`;
@@ -275,7 +345,7 @@ export class McpStdioBridgeClient {
275
345
  capabilities: {},
276
346
  clientInfo: {
277
347
  name: "meta-architect",
278
- version: "0.1.13-dev",
348
+ version: mcpClientVersion,
279
349
  },
280
350
  });
281
351
 
@@ -373,7 +443,7 @@ export class McpStdioBridgeClient {
373
443
  }
374
444
  }
375
445
 
376
- function buildBridgeCommand(commandTemplate, endpoint) {
446
+ function buildBridgeCommand(commandTemplate, endpoint, bridgeConfiguration = {}) {
377
447
  const template = commandTemplate?.trim();
378
448
  if (!template) {
379
449
  throw new Error(`${BRIDGE_COMMAND_ENV} is empty`);
@@ -390,9 +460,49 @@ function buildBridgeCommand(commandTemplate, endpoint) {
390
460
  }
391
461
 
392
462
  const [command, ...args] = substituted;
463
+ const allowedCommands = new Set(bridgeConfiguration.allowedCommands ?? []);
464
+ const commandName = path.basename(command);
465
+ if (!allowedCommands.has(command) && !allowedCommands.has(commandName)) {
466
+ throw new Error(
467
+ `${BRIDGE_COMMAND_ENV} command is not allowlisted: ${command}. ` +
468
+ "Add its exact command or basename to MA_MCP_REMOTE_BRIDGE_ALLOWLIST or mcp/bridge.json.",
469
+ );
470
+ }
393
471
  return { command, args };
394
472
  }
395
473
 
474
+ async function writeBridgeReceipt(endpoint, command, args, details) {
475
+ const root = process.env.MA_ROOT ?? process.cwd();
476
+ const receiptDir = path.join(root, ".ma", "evidence", "mcp-bridge-receipts");
477
+ const safeError = details.error
478
+ ? maskSensitiveText(String(details.error)).sanitizedText
479
+ : undefined;
480
+ const safeStderr = details.stderr
481
+ ? maskSensitiveText(String(details.stderr)).sanitizedText
482
+ : undefined;
483
+ await fs.mkdir(receiptDir, { recursive: true }).catch(() => {});
484
+ await fs
485
+ .writeFile(
486
+ path.join(receiptDir, `bridge-${Date.now()}-${process.pid}.json`),
487
+ `${JSON.stringify(
488
+ {
489
+ schemaVersion: "0.1.0",
490
+ record_type: "mcp_bridge_receipt",
491
+ generatedAt: new Date().toISOString(),
492
+ endpoint,
493
+ command,
494
+ args,
495
+ ...details,
496
+ ...(safeError ? { error: safeError } : {}),
497
+ ...(safeStderr ? { stderr: safeStderr } : {}),
498
+ },
499
+ null,
500
+ )}\n`,
501
+ { mode: 0o600 },
502
+ )
503
+ .catch(() => {});
504
+ }
505
+
396
506
  async function readResponsePreview(response) {
397
507
  try {
398
508
  return (await response.text()).replace(/\s+/g, " ").trim().slice(0, 240);
@@ -457,6 +567,29 @@ function splitCommand(command) {
457
567
  return parts;
458
568
  }
459
569
 
570
+ function buildBridgeEnv() {
571
+ const passthroughKeys = new Set([
572
+ "PATH",
573
+ "HOME",
574
+ "USERPROFILE",
575
+ "TMPDIR",
576
+ "TMP",
577
+ "TEMP",
578
+ "LANG",
579
+ "LC_ALL",
580
+ "SYSTEMROOT",
581
+ "COMSPEC",
582
+ "MA_ROOT",
583
+ ]);
584
+ const env = {};
585
+ for (const [key, value] of Object.entries(process.env)) {
586
+ if (passthroughKeys.has(key)) {
587
+ env[key] = value;
588
+ }
589
+ }
590
+ return env;
591
+ }
592
+
460
593
  export function parseSseEvent(rawEvent) {
461
594
  const event = { event: "message", data: "" };
462
595
  for (const line of rawEvent.split("\n")) {
package/src/paths.js CHANGED
@@ -1,3 +1,4 @@
1
+ import fs from "node:fs";
1
2
  import path from "node:path";
2
3
  import { fileURLToPath } from "node:url";
3
4
 
@@ -6,7 +7,11 @@ const __dirname = path.dirname(__filename);
6
7
 
7
8
  export const packageRoot = path.resolve(__dirname, "..");
8
9
  export function getRepoRoot() {
9
- return process.env.MA_ROOT ? path.resolve(process.env.MA_ROOT) : process.cwd();
10
+ const root = process.env.MA_ROOT ? path.resolve(process.env.MA_ROOT) : process.cwd();
11
+ if (fs.realpathSync(root) !== root) {
12
+ throw new Error(`Repository root cannot traverse symlinks: ${root}`);
13
+ }
14
+ return root;
10
15
  }
11
16
 
12
17
  export function getRuntimeRoot() {
package/src/policy.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export function validateReleaseOrigin(originBranch) {
2
- if (originBranch === "development") {
2
+ if (originBranch === "dev") {
3
3
  return true;
4
4
  }
5
5
 
@@ -15,7 +15,7 @@ export function rejectsDirectProdPromotion(originBranch) {
15
15
  }
16
16
 
17
17
  export function validateMergeTarget(sourceBranch, targetBranch) {
18
- return sourceBranch.startsWith("feature/") && targetBranch === "development";
18
+ return sourceBranch.startsWith("feature/") && targetBranch === "dev";
19
19
  }
20
20
 
21
21
  export function canMarkBuildDone(releaseState) {
@@ -0,0 +1,223 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { stdin as input, stdout as output } from "node:process";
5
+ import readline from "node:readline/promises";
6
+ import { Agents } from "@jstn-sdk/agents";
7
+ import { agentRegistry, detectInstalled, listAgents } from "./agents.js";
8
+ import {
9
+ createSkillCompatibilityPayload,
10
+ writeSkillCompatibilityExport,
11
+ } from "./runtime/skills-registry-export.js";
12
+ import { ensureSkillsInstalled, ensureSupportBundleInstalled } from "./skill-installer.js";
13
+
14
+ const selectionFile = ".ma/prelaunch.json";
15
+ const projectSignals = {
16
+ codex: [".codex", "AGENTS.md"],
17
+ opencode: [".opencode"],
18
+ "gemini-cli": [".gemini", "GEMINI.md"],
19
+ amp: [".amp"],
20
+ cursor: [".cursor"],
21
+ windsurf: [".windsurf"],
22
+ cline: [".cline"],
23
+ continue: [".continue"],
24
+ roo: [".roo"],
25
+ "kiro-cli": [".kiro"],
26
+ junie: [".junie"],
27
+ };
28
+ const globalSignals = {
29
+ codex: ["~/.codex"],
30
+ opencode: ["~/.config/opencode"],
31
+ "gemini-cli": ["~/.gemini"],
32
+ amp: ["~/.config/agents"],
33
+ cursor: ["~/.cursor"],
34
+ windsurf: ["~/.codeium/windsurf"],
35
+ cline: ["~/.cline"],
36
+ continue: ["~/.continue"],
37
+ roo: ["~/.roo"],
38
+ "kiro-cli": ["~/.kiro"],
39
+ junie: ["~/.junie"],
40
+ };
41
+
42
+ function expandHome(value) {
43
+ return value.startsWith("~/") ? path.join(os.homedir(), value.slice(2)) : value;
44
+ }
45
+
46
+ async function exists(target) {
47
+ try {
48
+ await fs.access(target);
49
+ return true;
50
+ } catch {
51
+ return false;
52
+ }
53
+ }
54
+
55
+ export async function detectPrelaunchTargets(cwd = process.cwd()) {
56
+ const targets = listAgents().map((agent) => {
57
+ const definition = agentRegistry[agent.id];
58
+ const projectPath =
59
+ definition?.isUniversal && agent.id === "universal"
60
+ ? path.join(cwd, ".agents")
61
+ : path.join(cwd, agent.skillsDir);
62
+ const globalPath = expandHome(agent.globalSkillsDir);
63
+ return {
64
+ id: agent.id,
65
+ displayName: agent.displayName,
66
+ surface: agent.surface,
67
+ project: projectPath,
68
+ global: globalPath,
69
+ projectSignals: (projectSignals[agent.id] ?? []).map((signal) => path.join(cwd, signal)),
70
+ globalSignals: (globalSignals[agent.id] ?? []).map(expandHome),
71
+ projectDetected: false,
72
+ globalDetected: false,
73
+ };
74
+ });
75
+
76
+ for (const target of targets) {
77
+ const installed = target.surface === "cli" && detectInstalled(target.id).installed;
78
+ target.projectDetected =
79
+ (target.id === "universal" && (await exists(target.project))) ||
80
+ (await Promise.all(target.projectSignals.map(exists))).some(Boolean);
81
+ target.globalDetected =
82
+ installed ||
83
+ (target.id === "universal" && (await exists(target.global))) ||
84
+ (await Promise.all(target.globalSignals.map(exists))).some(Boolean);
85
+ delete target.projectSignals;
86
+ delete target.globalSignals;
87
+ }
88
+ return targets;
89
+ }
90
+
91
+ async function readSelection(cwd) {
92
+ try {
93
+ const value = JSON.parse(await fs.readFile(path.join(cwd, selectionFile), "utf8"));
94
+ if (
95
+ (value.scope === "project" || value.scope === "global") &&
96
+ Array.isArray(value.targets) &&
97
+ value.targets.every((target) => typeof target === "string" && agentRegistry[target])
98
+ ) {
99
+ return value;
100
+ }
101
+ } catch {
102
+ return null;
103
+ }
104
+ return null;
105
+ }
106
+
107
+ export async function choosePrelaunchInstall({
108
+ cwd = process.cwd(),
109
+ interactive = Boolean(input.isTTY && output.isTTY),
110
+ } = {}) {
111
+ if (!interactive || process.env.MA_SKIP_PRELAUNCH === "1") return null;
112
+ const previous = await readSelection(cwd);
113
+ if (previous) return previous;
114
+
115
+ const detected = await detectPrelaunchTargets(cwd);
116
+ const detectedIds = detected
117
+ .filter((target) => target.projectDetected || target.globalDetected)
118
+ .map((target) => target.id);
119
+ const suggested = detectedIds.length > 0 ? detectedIds : ["codex"];
120
+ console.log("Meta-Architect pre-launch setup");
121
+ console.log("Detected targets:");
122
+ for (const target of detected) {
123
+ if (target.projectDetected || target.globalDetected) {
124
+ console.log(`- ${target.id}: ${target.projectDetected ? "project" : "user"}`);
125
+ }
126
+ }
127
+ const rl = readline.createInterface({ input, output });
128
+ try {
129
+ const answer = await rl.question(
130
+ `Install scope and targets [project:${suggested.join(",")}]: `,
131
+ );
132
+ const value = answer.trim() || `project:${suggested.join(",")}`;
133
+ const [scope, targetValue] = value.split(":", 2);
134
+ const targets = (targetValue || "codex")
135
+ .split(",")
136
+ .map((target) => target.trim())
137
+ .filter((target, index, values) => agentRegistry[target] && values.indexOf(target) === index);
138
+ if (!(scope === "project" || scope === "global") || targets.length === 0) {
139
+ throw new Error("Use project:codex,cursor or global:codex,cursor");
140
+ }
141
+ return { schemaVersion: "0.1.0", scope, targets };
142
+ } finally {
143
+ rl.close();
144
+ }
145
+ }
146
+
147
+ export async function installPrelaunchSelection(selection, cwd = process.cwd()) {
148
+ if (
149
+ !selection ||
150
+ !["project", "global"].includes(selection.scope) ||
151
+ !Array.isArray(selection.targets) ||
152
+ selection.targets.length === 0 ||
153
+ selection.targets.some((target) => typeof target !== "string" || !agentRegistry[target])
154
+ ) {
155
+ throw new Error("Invalid pre-launch selection");
156
+ }
157
+ const payload = createSkillCompatibilityPayload({
158
+ name: "meta-architect",
159
+ description: "Meta-Architect host compatibility entrypoint.",
160
+ capabilities: ["maestro", "agent-compatibility"],
161
+ });
162
+ const results = [];
163
+ if (selection.scope === "project" && selection.targets.includes("cursor")) {
164
+ results.push(await ensureSkillsInstalled({ targetRoot: path.join(cwd, ".agents", "skills") }));
165
+ results.push(
166
+ await Agents.compile(
167
+ {
168
+ version: 1,
169
+ project: { name: path.basename(path.resolve(cwd)) },
170
+ instructions: [
171
+ "Use Meta-Architect lanes for architecture, evidence, review, and gated build work.",
172
+ "Run `ma run '$maestro'` when the host supports shell execution, or invoke /maestro in Cursor Agent.",
173
+ ],
174
+ },
175
+ { targets: ["cursor"], output: cwd },
176
+ ),
177
+ );
178
+ }
179
+ if (selection.scope === "project" && selection.targets.includes("claude-code")) {
180
+ results.push(await ensureSkillsInstalled({ targetRoot: path.join(cwd, ".claude", "skills") }));
181
+ results.push(
182
+ await Agents.compile(
183
+ {
184
+ version: 1,
185
+ project: { name: path.basename(path.resolve(cwd)) },
186
+ instructions: [
187
+ "Use Meta-Architect lanes for architecture, evidence, review, and gated build work.",
188
+ "Invoke /maestro to run the autonomous Meta-Architect manager in Claude Code.",
189
+ ],
190
+ },
191
+ { targets: ["claude-code"], output: cwd },
192
+ ),
193
+ );
194
+ }
195
+ if (selection.targets.includes("codex")) {
196
+ const skillsRoot =
197
+ selection.scope === "global" ? undefined : path.join(cwd, ".agents", "skills");
198
+ const supportRoot =
199
+ selection.scope === "global" ? undefined : path.join(cwd, ".ma", "support-bundle");
200
+ results.push(
201
+ await ensureSkillsInstalled({ targetRoot: skillsRoot }),
202
+ await ensureSupportBundleInstalled({ targetRoot: supportRoot }),
203
+ );
204
+ }
205
+ for (const target of selection.targets) {
206
+ if (selection.scope === "global" && target === "codex") continue;
207
+ results.push(
208
+ await writeSkillCompatibilityExport({
209
+ payload,
210
+ agentType: target,
211
+ renderAgentType: selection.targets.length > 1 ? "generic" : target,
212
+ cwd,
213
+ global: selection.scope === "global",
214
+ agentRootExists: true,
215
+ }),
216
+ );
217
+ }
218
+ await fs.mkdir(path.join(cwd, ".ma"), { recursive: true });
219
+ await fs.writeFile(path.join(cwd, selectionFile), `${JSON.stringify(selection, null, 2)}\n`, {
220
+ mode: 0o600,
221
+ });
222
+ return { selection, results };
223
+ }
@@ -0,0 +1,62 @@
1
+ import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+
4
+ const unsafeExecutable = /[;&|<>`$]/;
5
+
6
+ export function assertSafeExecutable(command, label = "Executable") {
7
+ if (
8
+ typeof command !== "string" ||
9
+ command.trim() === "" ||
10
+ command !== command.trim() ||
11
+ command.includes("\0") ||
12
+ command.includes("\r") ||
13
+ command.includes("\n") ||
14
+ unsafeExecutable.test(command)
15
+ ) {
16
+ throw new Error(`${label} contains an unsafe executable value`);
17
+ }
18
+ return command;
19
+ }
20
+
21
+ function assertArgs(args) {
22
+ if (!Array.isArray(args) || args.some((arg) => typeof arg !== "string" || arg.includes("\0")))
23
+ throw new Error("Process arguments must be an array of strings without NUL bytes");
24
+ }
25
+
26
+ function safeOptions(options = {}) {
27
+ return { ...options, shell: false };
28
+ }
29
+
30
+ export function safeSpawn(command, args = [], options = {}) {
31
+ assertSafeExecutable(command);
32
+ assertArgs(args);
33
+ return spawn(command, args, safeOptions(options));
34
+ }
35
+
36
+ export function safeSpawnSync(command, args = [], options = {}) {
37
+ assertSafeExecutable(command);
38
+ assertArgs(args);
39
+ return spawnSync(command, args, safeOptions(options));
40
+ }
41
+
42
+ export function safeExecFile(command, args = [], options = {}, callback) {
43
+ assertSafeExecutable(command);
44
+ assertArgs(args);
45
+ if (typeof options === "function") {
46
+ callback = options;
47
+ options = {};
48
+ }
49
+ return execFile(command, args, safeOptions(options), callback);
50
+ }
51
+
52
+ export function safeExecFileSync(command, args = [], options = {}) {
53
+ assertSafeExecutable(command);
54
+ assertArgs(args);
55
+ return execFileSync(command, args, safeOptions(options));
56
+ }
57
+
58
+ safeExecFile[promisify.custom] = (command, args = [], options = {}) => {
59
+ assertSafeExecutable(command);
60
+ assertArgs(args);
61
+ return promisify(execFile)(command, args, safeOptions(options));
62
+ };