@jstn-sdk/ma 0.1.11 → 0.1.13

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 (159) hide show
  1. package/.codex/agents/Architect.toml +0 -2
  2. package/.codex/agents/Auditor.toml +0 -2
  3. package/.codex/agents/Builder.toml +0 -2
  4. package/.codex/agents/Flow.toml +0 -2
  5. package/.codex/agents/Sage.toml +0 -2
  6. package/.codex/agents/Vibe.toml +0 -2
  7. package/.codex/hooks.json +14 -13
  8. package/.codex/prompts/enforcement.md +4 -0
  9. package/.codex/prompts/skill-contract.md +4 -0
  10. package/COVERAGE.md +211 -0
  11. package/DEMO.md +265 -0
  12. package/README.md +287 -37
  13. package/bin/ma.js +232 -71
  14. package/data/clone-data.ledger.json +41 -0
  15. package/data/clone-data.proof.json +50 -0
  16. package/data/clone-data.rvf +37 -0
  17. package/docs/README.md +7 -1
  18. package/docs/getting-started.md +108 -37
  19. package/docs/installed-sdk.md +23 -0
  20. package/docs/mcp-setup.md +65 -1
  21. package/docs/onboarding.md +18 -2
  22. package/docs/prompt-guidance-contract.md +17 -0
  23. package/docs/prompt-guidance-fragments/active-autonomy-core.md +7 -0
  24. package/docs/qa/release-issue-gates-0.1.13.json +644 -0
  25. package/docs/qa/release-readiness-0.1.13.md +116 -0
  26. package/docs/qa/release-readiness-0.1.5.md +1 -1
  27. package/docs/reference/native-engineering-patterns.md +35 -0
  28. package/docs/reference/native-security-playbooks.md +27 -0
  29. package/docs/reference/native-source-selection.md +27 -0
  30. package/docs/reference/native-style-and-deslop.md +20 -0
  31. package/docs/release-spec.md +37 -10
  32. package/docs/skills-publishing.md +25 -1
  33. package/docs/skills.md +37 -12
  34. package/index.js +222 -2
  35. package/mcp/collections.json +23 -6
  36. package/mcp/local/code-intel.js +113 -0
  37. package/mcp/local/memory.js +46 -0
  38. package/mcp/local/playbooks.js +168 -0
  39. package/mcp/local/state.js +71 -0
  40. package/mcp/local/team-run.js +113 -0
  41. package/mcp/local/trace.js +60 -0
  42. package/mcp/local-capabilities.json +56 -0
  43. package/mcp/native-playbooks.json +117 -0
  44. package/mcp/servers.json +34 -0
  45. package/package.json +8 -2
  46. package/plugins/meta-architect/.app.json +1 -1
  47. package/plugins/meta-architect/.codex-plugin/plugin.json +4 -4
  48. package/plugins/meta-architect/.mcp.json +1 -1
  49. package/plugins/meta-architect/README.md +34 -1
  50. package/plugins/meta-architect/obsidian/main.js +401 -0
  51. package/plugins/meta-architect/obsidian/manifest.json +9 -0
  52. package/plugins/meta-architect/obsidian/styles.css +7 -0
  53. package/plugins/meta-architect/skills/align/SKILL.md +24 -0
  54. package/plugins/meta-architect/skills/align/agents/openai.yaml +4 -0
  55. package/plugins/meta-architect/skills/align/references/shared-language.md +24 -0
  56. package/plugins/meta-architect/skills/arch/SKILL.md +2 -0
  57. package/plugins/meta-architect/skills/build/SKILL.md +3 -0
  58. package/plugins/meta-architect/skills/cleanup/SKILL.md +23 -0
  59. package/plugins/meta-architect/skills/cleanup/agents/openai.yaml +4 -0
  60. package/plugins/meta-architect/skills/cleanup/references/style-and-deslop.md +18 -0
  61. package/plugins/meta-architect/skills/diagnose/SKILL.md +24 -0
  62. package/plugins/meta-architect/skills/diagnose/agents/openai.yaml +4 -0
  63. package/plugins/meta-architect/skills/flow/SKILL.md +2 -0
  64. package/plugins/meta-architect/skills/maestro/SKILL.md +36 -3
  65. package/plugins/meta-architect/skills/maestro/agents/openai.yaml +2 -2
  66. package/plugins/meta-architect/skills/maestro/references/native-ingest-map.md +44 -0
  67. package/plugins/meta-architect/skills/sage/SKILL.md +5 -1
  68. package/plugins/meta-architect/skills/sage/references/source-selection.md +36 -0
  69. package/plugins/meta-architect/skills/tdd/SKILL.md +24 -0
  70. package/plugins/meta-architect/skills/tdd/agents/openai.yaml +4 -0
  71. package/plugins/meta-architect/skills/vet/SKILL.md +5 -1
  72. package/plugins/meta-architect/skills/vet/references/security-playbooks.md +30 -0
  73. package/plugins/meta-architect/skills/vibe/SKILL.md +2 -0
  74. package/scripts/active-autonomy-hook.mjs +96 -0
  75. package/scripts/build-linux-packages.mjs +328 -0
  76. package/scripts/doctor.js +36 -4
  77. package/scripts/install.sh +28 -0
  78. package/scripts/linux-package-lib.mjs +40 -0
  79. package/scripts/linux-package-smoke.mjs +103 -0
  80. package/scripts/ralph/prompt.md +35 -0
  81. package/scripts/release-sync.js +13 -6
  82. package/scripts/release-verify.js +166 -1
  83. package/skills/align/SKILL.md +24 -0
  84. package/skills/align/agents/openai.yaml +4 -0
  85. package/skills/align/references/shared-language.md +24 -0
  86. package/skills/arch/SKILL.md +2 -0
  87. package/skills/build/SKILL.md +3 -0
  88. package/skills/cleanup/SKILL.md +23 -0
  89. package/skills/cleanup/agents/openai.yaml +4 -0
  90. package/skills/cleanup/references/style-and-deslop.md +18 -0
  91. package/skills/diagnose/SKILL.md +24 -0
  92. package/skills/diagnose/agents/openai.yaml +4 -0
  93. package/skills/flow/SKILL.md +2 -0
  94. package/skills/index.json +21 -6
  95. package/skills/maestro/SKILL.md +36 -3
  96. package/skills/maestro/agents/openai.yaml +2 -2
  97. package/skills/maestro/references/native-ingest-map.md +44 -0
  98. package/skills/sage/SKILL.md +5 -1
  99. package/skills/sage/references/source-selection.md +36 -0
  100. package/skills/tdd/SKILL.md +24 -0
  101. package/skills/tdd/agents/openai.yaml +4 -0
  102. package/skills/vet/SKILL.md +5 -1
  103. package/skills/vet/references/security-playbooks.md +30 -0
  104. package/skills/vibe/SKILL.md +2 -0
  105. package/src/bootstrap.js +141 -24
  106. package/src/build-gate.js +2 -2
  107. package/src/decision-log.js +12 -9
  108. package/src/launcher.js +4 -0
  109. package/src/mcp-config.js +130 -8
  110. package/src/mcp-live-client.js +289 -3
  111. package/src/paths.js +37 -1
  112. package/src/policy.js +1 -1
  113. package/src/release-issue-gates.js +190 -0
  114. package/src/release-state.js +30 -1
  115. package/src/runtime/active-autonomy-core.js +208 -0
  116. package/src/runtime/alignment-sentinel.js +165 -0
  117. package/src/runtime/architect-review.js +88 -0
  118. package/src/runtime/build-readiness.js +62 -0
  119. package/src/runtime/code-graph-rehearse.js +113 -0
  120. package/src/runtime/context-economy-core.js +276 -0
  121. package/src/runtime/continuity-notes.js +79 -0
  122. package/src/runtime/core-source-ingest.js +379 -0
  123. package/src/runtime/detached-provider.js +74 -0
  124. package/src/runtime/environment-awareness-core.js +460 -0
  125. package/src/runtime/exposure-catalog.js +276 -0
  126. package/src/runtime/guidance-stack.js +42 -0
  127. package/src/runtime/helper-orchestration-core.js +307 -0
  128. package/src/runtime/learning-loop-core.js +238 -0
  129. package/src/runtime/maestro-events.js +18 -0
  130. package/src/runtime/maestro-manager.js +605 -0
  131. package/src/runtime/maestro-state.js +125 -0
  132. package/src/runtime/mcp-policy.js +192 -0
  133. package/src/runtime/obsidian-integration-core.js +851 -0
  134. package/src/runtime/obsidian-plugin-bridge.js +739 -0
  135. package/src/runtime/orchestrator.js +158 -0
  136. package/src/runtime/prompt-strategy-core.js +230 -0
  137. package/src/runtime/quorum-review.js +90 -0
  138. package/src/runtime/ralph-execution-core.js +217 -0
  139. package/src/runtime/redaction-gateway.js +174 -0
  140. package/src/runtime/runtime-state.js +1223 -0
  141. package/src/runtime/semantic-recording-core.js +147 -0
  142. package/src/runtime/signal-hooks.js +67 -0
  143. package/src/runtime/skills-registry-export.js +670 -0
  144. package/src/runtime/startup-path.js +14 -0
  145. package/src/runtime/universal-plugin-broker-core.js +575 -0
  146. package/src/runtime/workspace-intelligence-runtime.js +674 -0
  147. package/src/runtime/workspace-virtualizer.js +102 -0
  148. package/src/runtime/workspaces.js +25 -0
  149. package/src/runtime-artifacts.js +407 -84
  150. package/src/skill-installer.js +53 -5
  151. package/src/skills.js +1454 -78
  152. package/src/state-sync.js +51 -8
  153. package/docs/qa/release-readiness-0.1.11.md +0 -79
  154. package/plugins/meta-architect/skills/meta-architect/SKILL.md +0 -32
  155. package/plugins/meta-architect/skills/meta-architect/agents/openai.yaml +0 -4
  156. package/skills/meta-architect/SKILL.md +0 -32
  157. package/skills/meta-architect/agents/openai.yaml +0 -4
  158. /package/plugins/meta-architect/skills/{meta-architect → maestro}/references/core-release-rules.md +0 -0
  159. /package/skills/{meta-architect → maestro}/references/core-release-rules.md +0 -0
@@ -1,5 +1,6 @@
1
1
  import { readJson, writeJson } from "./fs-utils.js";
2
2
  import { getRuntimeReadPath, getRuntimeWritePath } from "./paths.js";
3
+ import { guardLeaderMutation } from "./runtime/runtime-state.js";
3
4
 
4
5
  function validateDecisionShape(entry) {
5
6
  const required = ["decision", "status", "evidence", "blockers", "next_allowed_triggers"];
@@ -20,7 +21,16 @@ export async function loadDecisionLog() {
20
21
  return parsed;
21
22
  }
22
23
 
23
- export async function appendDecision(entry) {
24
+ export async function appendDecision(entry, options = {}) {
25
+ const guard = await guardLeaderMutation({
26
+ actor: options.actor,
27
+ kind: "decision-append",
28
+ payload: entry,
29
+ });
30
+ if (!guard.allowed) {
31
+ return { proposed: true, proposalPath: guard.proposalPath };
32
+ }
33
+
24
34
  validateDecisionShape(entry);
25
35
  const log = await loadDecisionLog();
26
36
  log.decisions.push({
@@ -29,12 +39,5 @@ export async function appendDecision(entry) {
29
39
  });
30
40
 
31
41
  await writeJson(getRuntimeWritePath("decisions.json"), log);
32
- }
33
-
34
- export async function updateDecisionStatuses(statusUpdates) {
35
- const log = await loadDecisionLog();
36
- for (const [field, value] of Object.entries(statusUpdates)) {
37
- log[field] = value;
38
- }
39
- await writeJson(getRuntimeWritePath("decisions.json"), log);
42
+ return { proposed: false, proposalPath: null };
40
43
  }
package/src/launcher.js CHANGED
@@ -8,8 +8,12 @@ const nativeCommands = new Set([
8
8
  "init",
9
9
  "idea",
10
10
  "skills",
11
+ "core-ingest",
12
+ "obsidian",
13
+ "obsidian-index",
11
14
  "sdk-path",
12
15
  "status",
16
+ "verify",
13
17
  "merge",
14
18
  "release",
15
19
  "run",
package/src/mcp-config.js CHANGED
@@ -1,7 +1,26 @@
1
1
  import fs from "node:fs/promises";
2
- import { getMcpServersPath } from "./paths.js";
2
+ import path from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ import { getBundledMcpPath, getMcpLocalCapabilitiesPath, getMcpServersPath } from "./paths.js";
3
5
 
4
6
  const gitMcpRepoPattern = /^https:\/\/gitmcp\.io\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
7
+ const localCapabilityNames = ["_state", "memory", "trace", "team_run", "code_intel", "playbooks"];
8
+ const localCapabilityNameSet = new Set(localCapabilityNames);
9
+
10
+ function validateObjectWithArray(parsed, arrayField, label) {
11
+ if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed[arrayField])) {
12
+ throw new Error(`${label} must be an object with a ${arrayField} array`);
13
+ }
14
+
15
+ return parsed;
16
+ }
17
+
18
+ function normalizeGitMcpDescriptor(server) {
19
+ return {
20
+ ...server,
21
+ kind: server.kind ?? "gitmcp-evidence",
22
+ };
23
+ }
5
24
 
6
25
  export function isValidGitMcpEndpoint(url) {
7
26
  return gitMcpRepoPattern.test(url);
@@ -9,18 +28,25 @@ export function isValidGitMcpEndpoint(url) {
9
28
 
10
29
  export async function loadMcpServers() {
11
30
  const raw = await fs.readFile(getMcpServersPath(), "utf8");
12
- const parsed = JSON.parse(raw);
13
-
14
- if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.servers)) {
15
- throw new Error("mcp/servers.json must be an object with a servers array");
16
- }
17
-
18
- return parsed;
31
+ const parsed = validateObjectWithArray(JSON.parse(raw), "servers", "mcp/servers.json");
32
+ return {
33
+ ...parsed,
34
+ servers: parsed.servers.map(normalizeGitMcpDescriptor),
35
+ };
19
36
  }
20
37
 
21
38
  export async function validateMcpServers() {
22
39
  const parsed = await loadMcpServers();
23
40
  for (const server of parsed.servers) {
41
+ if (server.kind !== "gitmcp-evidence") {
42
+ throw new Error(`Invalid GitMCP descriptor kind: ${server.kind ?? "(missing)"}`);
43
+ }
44
+ if (typeof server.category !== "string" || server.category.trim() === "") {
45
+ throw new Error("GitMCP descriptors require a category");
46
+ }
47
+ if (typeof server.repo !== "string" || server.repo.trim() === "") {
48
+ throw new Error("GitMCP descriptors require a repo");
49
+ }
24
50
  if (!isValidGitMcpEndpoint(server.endpoint)) {
25
51
  throw new Error(`Invalid GitMCP endpoint: ${server.endpoint}`);
26
52
  }
@@ -28,3 +54,99 @@ export async function validateMcpServers() {
28
54
 
29
55
  return parsed;
30
56
  }
57
+
58
+ export async function loadLocalCapabilities() {
59
+ const raw = await fs.readFile(getMcpLocalCapabilitiesPath(), "utf8");
60
+ return validateObjectWithArray(JSON.parse(raw), "capabilities", "mcp/local-capabilities.json");
61
+ }
62
+
63
+ export function getSupportedLocalCapabilities() {
64
+ return [...localCapabilityNames];
65
+ }
66
+
67
+ export async function validateLocalCapabilities() {
68
+ const parsed = await loadLocalCapabilities();
69
+ const seen = new Set();
70
+
71
+ for (const descriptor of parsed.capabilities) {
72
+ if (descriptor.kind !== "local-capability") {
73
+ throw new Error(`Invalid local capability kind: ${descriptor.kind ?? "(missing)"}`);
74
+ }
75
+ if (!localCapabilityNameSet.has(descriptor.capability)) {
76
+ throw new Error(`Unsupported local capability: ${descriptor.capability}`);
77
+ }
78
+ if (seen.has(descriptor.capability)) {
79
+ throw new Error(`Duplicate local capability: ${descriptor.capability}`);
80
+ }
81
+ if (descriptor.transport !== "inproc") {
82
+ throw new Error(
83
+ `Local capability ${descriptor.capability} must use inproc transport, received ${descriptor.transport}`,
84
+ );
85
+ }
86
+ if (typeof descriptor.module !== "string" || descriptor.module.trim() === "") {
87
+ throw new Error(`Local capability ${descriptor.capability} requires a module`);
88
+ }
89
+ if (typeof descriptor.readinessCheck !== "string" || descriptor.readinessCheck.trim() === "") {
90
+ throw new Error(`Local capability ${descriptor.capability} requires a readinessCheck`);
91
+ }
92
+ if (descriptor.seededByBootstrap !== true) {
93
+ throw new Error(
94
+ `Local capability ${descriptor.capability} must declare seededByBootstrap: true`,
95
+ );
96
+ }
97
+ seen.add(descriptor.capability);
98
+ }
99
+
100
+ for (const capability of localCapabilityNames) {
101
+ if (!seen.has(capability)) {
102
+ throw new Error(`Missing local capability descriptor: ${capability}`);
103
+ }
104
+ }
105
+
106
+ return parsed;
107
+ }
108
+
109
+ export function resolveLocalCapabilityModulePath(descriptor) {
110
+ if (path.isAbsolute(descriptor.module)) {
111
+ throw new Error(`Local capability ${descriptor.capability} cannot use an absolute module path`);
112
+ }
113
+
114
+ const bundledLocalRoot = path.resolve(getBundledMcpPath("local"));
115
+ const resolvedModulePath = path.resolve(getBundledMcpPath(descriptor.module));
116
+ if (!resolvedModulePath.startsWith(`${bundledLocalRoot}${path.sep}`)) {
117
+ throw new Error(
118
+ `Local capability ${descriptor.capability} must resolve inside bundled mcp/local`,
119
+ );
120
+ }
121
+
122
+ return resolvedModulePath;
123
+ }
124
+
125
+ export async function loadLocalCapabilityModule(descriptor) {
126
+ const modulePath = resolveLocalCapabilityModulePath(descriptor);
127
+ return import(pathToFileURL(modulePath).href);
128
+ }
129
+
130
+ export async function runLocalCapabilityReadinessChecks() {
131
+ const parsed = await validateLocalCapabilities();
132
+ const results = [];
133
+
134
+ for (const descriptor of parsed.capabilities) {
135
+ const moduleExports = await loadLocalCapabilityModule(descriptor);
136
+ const readinessCheck = moduleExports[descriptor.readinessCheck];
137
+ if (typeof readinessCheck !== "function") {
138
+ throw new Error(
139
+ `Local capability ${descriptor.capability} is missing readiness export ${descriptor.readinessCheck}`,
140
+ );
141
+ }
142
+
143
+ const outcome = await readinessCheck();
144
+ results.push({
145
+ capability: descriptor.capability,
146
+ ready: outcome?.ready !== false,
147
+ detail: outcome?.detail ?? "",
148
+ });
149
+ }
150
+
151
+ return results;
152
+ }
@@ -1,3 +1,54 @@
1
+ import { spawn } from "node:child_process";
2
+
3
+ const GITMCP_ENDPOINT_PATTERN = /^https:\/\/gitmcp\.io\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
4
+ const BRIDGE_COMMAND_ENV = "MA_MCP_REMOTE_BRIDGE_CMD";
5
+ const REQUEST_TIMEOUT_ENV = "MA_MCP_REQUEST_TIMEOUT_MS";
6
+ const DEFAULT_REQUEST_TIMEOUT_MS = 15000;
7
+
8
+ function readRequestTimeoutMs() {
9
+ const raw = process.env[REQUEST_TIMEOUT_ENV]?.trim();
10
+ if (!raw) return DEFAULT_REQUEST_TIMEOUT_MS;
11
+
12
+ const parsed = Number(raw);
13
+ if (!Number.isFinite(parsed) || parsed <= 0) {
14
+ return DEFAULT_REQUEST_TIMEOUT_MS;
15
+ }
16
+
17
+ return Math.max(50, Math.floor(parsed));
18
+ }
19
+
20
+ export class McpRemoteTransportRequiredError extends Error {
21
+ constructor(endpoint, status, bodyPreview = "") {
22
+ const bridgeHint =
23
+ `Set ${BRIDGE_COMMAND_ENV} to a trusted stdio bridge command with {url}, ` +
24
+ "for example a preinstalled mcp-remote wrapper, then rerun $sage.";
25
+ const bodyHint = bodyPreview ? ` Response preview: ${bodyPreview}` : "";
26
+ super(
27
+ `Remote MCP endpoint requires a bridge transport: ${endpoint} returned HTTP ${status}. ${bridgeHint}${bodyHint}`,
28
+ );
29
+ this.name = "McpRemoteTransportRequiredError";
30
+ this.endpoint = endpoint;
31
+ this.status = status;
32
+ this.bridgeEnv = BRIDGE_COMMAND_ENV;
33
+ }
34
+ }
35
+
36
+ export function isGitMcpEndpoint(endpoint) {
37
+ return GITMCP_ENDPOINT_PATTERN.test(endpoint);
38
+ }
39
+
40
+ export function hasConfiguredMcpRemoteBridge() {
41
+ return Boolean(process.env[BRIDGE_COMMAND_ENV]?.trim());
42
+ }
43
+
44
+ export function createMcpLiveClient(endpoint) {
45
+ if (hasConfiguredMcpRemoteBridge()) {
46
+ return new McpStdioBridgeClient(endpoint, process.env[BRIDGE_COMMAND_ENV]);
47
+ }
48
+
49
+ return new McpSseClient(endpoint);
50
+ }
51
+
1
52
  export class McpSseClient {
2
53
  constructor(baseUrl) {
3
54
  this.baseUrl = baseUrl;
@@ -17,6 +68,10 @@ export class McpSseClient {
17
68
  });
18
69
 
19
70
  if (!response.ok || !response.body) {
71
+ const bodyPreview = await readResponsePreview(response);
72
+ if (isGitMcpEndpoint(this.baseUrl) && response.status === 405) {
73
+ throw new McpRemoteTransportRequiredError(this.baseUrl, response.status, bodyPreview);
74
+ }
20
75
  throw new Error(`Failed to open MCP SSE stream: ${response.status}`);
21
76
  }
22
77
 
@@ -26,7 +81,7 @@ export class McpSseClient {
26
81
  5000,
27
82
  "Timed out waiting for MCP endpoint event",
28
83
  );
29
- if (!endpointEvent || endpointEvent.event !== "endpoint") {
84
+ if (endpointEvent?.event !== "endpoint") {
30
85
  throw new Error("MCP server did not provide an endpoint event");
31
86
  }
32
87
 
@@ -38,7 +93,7 @@ export class McpSseClient {
38
93
  capabilities: {},
39
94
  clientInfo: {
40
95
  name: "meta-architect",
41
- version: "0.1.11",
96
+ version: "0.1.13-dev",
42
97
  },
43
98
  });
44
99
 
@@ -52,7 +107,7 @@ export class McpSseClient {
52
107
  const timeout = setTimeout(() => {
53
108
  this.pending.delete(id);
54
109
  reject(new Error(`Timed out waiting for MCP response to ${method}`));
55
- }, 15000);
110
+ }, readRequestTimeoutMs());
56
111
 
57
112
  this.pending.set(id, {
58
113
  resolve: (payload) => {
@@ -171,6 +226,237 @@ export class McpSseClient {
171
226
  }
172
227
  }
173
228
 
229
+ export class McpStdioBridgeClient {
230
+ constructor(endpoint, commandTemplate, spawnImpl = spawn) {
231
+ this.endpoint = endpoint;
232
+ this.commandTemplate = commandTemplate;
233
+ this.spawnImpl = spawnImpl;
234
+ this.child = null;
235
+ this.buffer = "";
236
+ this.pending = new Map();
237
+ this.nextId = 1;
238
+ this.stderr = "";
239
+ this.closed = false;
240
+ }
241
+
242
+ async connect() {
243
+ const { command, args } = buildBridgeCommand(this.commandTemplate, this.endpoint);
244
+ this.child = this.spawnImpl(command, args, {
245
+ stdio: ["pipe", "pipe", "pipe"],
246
+ env: process.env,
247
+ });
248
+
249
+ this.child.stdout.setEncoding("utf8");
250
+ this.child.stderr.setEncoding("utf8");
251
+ this.child.stdout.on("data", (chunk) => this.#handleStdout(chunk));
252
+ this.child.stderr.on("data", (chunk) => {
253
+ this.stderr = `${this.stderr}${chunk}`.slice(-2000);
254
+ });
255
+ this.child.on("error", (error) => {
256
+ this.closed = true;
257
+ for (const [, handlers] of this.pending) {
258
+ handlers.reject(new Error(`MCP bridge failed to start: ${error.message}`));
259
+ }
260
+ this.pending.clear();
261
+ });
262
+ this.child.on("exit", (code, signal) => {
263
+ this.closed = true;
264
+ const reason = signal
265
+ ? `MCP bridge exited with signal ${signal}`
266
+ : `MCP bridge exited with code ${code}`;
267
+ for (const [, handlers] of this.pending) {
268
+ handlers.reject(new Error(`${reason}${this.stderr ? `: ${this.stderr.trim()}` : ""}`));
269
+ }
270
+ this.pending.clear();
271
+ });
272
+
273
+ const initResult = await this.request("initialize", {
274
+ protocolVersion: "2025-03-26",
275
+ capabilities: {},
276
+ clientInfo: {
277
+ name: "meta-architect",
278
+ version: "0.1.13-dev",
279
+ },
280
+ });
281
+
282
+ await this.notify("notifications/initialized");
283
+ return initResult;
284
+ }
285
+
286
+ async request(method, params = {}) {
287
+ const id = this.nextId++;
288
+ const responsePromise = new Promise((resolve, reject) => {
289
+ const timeout = setTimeout(() => {
290
+ this.pending.delete(id);
291
+ reject(new Error(`Timed out waiting for MCP bridge response to ${method}`));
292
+ }, readRequestTimeoutMs());
293
+
294
+ this.pending.set(id, {
295
+ resolve: (payload) => {
296
+ clearTimeout(timeout);
297
+ resolve(payload);
298
+ },
299
+ reject: (error) => {
300
+ clearTimeout(timeout);
301
+ reject(error);
302
+ },
303
+ });
304
+ });
305
+
306
+ this.#send({
307
+ jsonrpc: "2.0",
308
+ id,
309
+ method,
310
+ params,
311
+ });
312
+
313
+ return responsePromise;
314
+ }
315
+
316
+ async notify(method, params = {}) {
317
+ this.#send({
318
+ jsonrpc: "2.0",
319
+ method,
320
+ params,
321
+ });
322
+ }
323
+
324
+ async close() {
325
+ if (!this.child || this.closed) return;
326
+ this.child.stdin.end();
327
+ this.child.kill();
328
+ await new Promise((resolve) => {
329
+ const timeout = setTimeout(resolve, 1000);
330
+ this.child.once("exit", () => {
331
+ clearTimeout(timeout);
332
+ resolve();
333
+ });
334
+ });
335
+ }
336
+
337
+ #send(message) {
338
+ if (!this.child?.stdin.writable) {
339
+ throw new Error("MCP bridge is not writable");
340
+ }
341
+ this.child.stdin.write(`${JSON.stringify(message)}\n`);
342
+ }
343
+
344
+ #handleStdout(chunk) {
345
+ this.buffer += chunk;
346
+ while (true) {
347
+ const newlineIndex = this.buffer.indexOf("\n");
348
+ if (newlineIndex === -1) return;
349
+
350
+ const line = this.buffer.slice(0, newlineIndex).trim();
351
+ this.buffer = this.buffer.slice(newlineIndex + 1);
352
+ if (!line?.startsWith("{")) continue;
353
+
354
+ let payload;
355
+ try {
356
+ payload = JSON.parse(line);
357
+ } catch {
358
+ continue;
359
+ }
360
+
361
+ if (typeof payload.id !== "number" || !this.pending.has(payload.id)) {
362
+ continue;
363
+ }
364
+
365
+ const handlers = this.pending.get(payload.id);
366
+ this.pending.delete(payload.id);
367
+ if (payload.error) {
368
+ handlers.reject(new Error(payload.error.message ?? "Unknown MCP bridge error"));
369
+ } else {
370
+ handlers.resolve(payload.result);
371
+ }
372
+ }
373
+ }
374
+ }
375
+
376
+ function buildBridgeCommand(commandTemplate, endpoint) {
377
+ const template = commandTemplate?.trim();
378
+ if (!template) {
379
+ throw new Error(`${BRIDGE_COMMAND_ENV} is empty`);
380
+ }
381
+
382
+ const parts = splitCommand(template);
383
+ if (parts.length === 0) {
384
+ throw new Error(`${BRIDGE_COMMAND_ENV} is empty`);
385
+ }
386
+
387
+ const substituted = parts.map((part) => part.replaceAll("{url}", endpoint));
388
+ if (!template.includes("{url}")) {
389
+ substituted.push(endpoint);
390
+ }
391
+
392
+ const [command, ...args] = substituted;
393
+ return { command, args };
394
+ }
395
+
396
+ async function readResponsePreview(response) {
397
+ try {
398
+ return (await response.text()).replace(/\s+/g, " ").trim().slice(0, 240);
399
+ } catch {
400
+ return "";
401
+ }
402
+ }
403
+
404
+ function splitCommand(command) {
405
+ const parts = [];
406
+ let current = "";
407
+ let quote = null;
408
+ let escaping = false;
409
+
410
+ for (const char of command) {
411
+ if (escaping) {
412
+ current += char;
413
+ escaping = false;
414
+ continue;
415
+ }
416
+
417
+ if (char === "\\") {
418
+ escaping = true;
419
+ continue;
420
+ }
421
+
422
+ if (quote) {
423
+ if (char === quote) {
424
+ quote = null;
425
+ } else {
426
+ current += char;
427
+ }
428
+ continue;
429
+ }
430
+
431
+ if (char === "'" || char === '"') {
432
+ quote = char;
433
+ continue;
434
+ }
435
+
436
+ if (/\s/.test(char)) {
437
+ if (current) {
438
+ parts.push(current);
439
+ current = "";
440
+ }
441
+ continue;
442
+ }
443
+
444
+ current += char;
445
+ }
446
+
447
+ if (escaping) {
448
+ current += "\\";
449
+ }
450
+ if (quote) {
451
+ throw new Error(`${BRIDGE_COMMAND_ENV} has an unterminated quote`);
452
+ }
453
+ if (current) {
454
+ parts.push(current);
455
+ }
456
+
457
+ return parts;
458
+ }
459
+
174
460
  export function parseSseEvent(rawEvent) {
175
461
  const event = { event: "message", data: "" };
176
462
  for (const line of rawEvent.split("\n")) {
package/src/paths.js CHANGED
@@ -13,6 +13,14 @@ export function getRuntimeRoot() {
13
13
  return path.join(getRepoRoot(), ".ma");
14
14
  }
15
15
 
16
+ export function getRuntimeSubsystemPath(subsystem, ...parts) {
17
+ return path.join(getRuntimeRoot(), subsystem, ...parts);
18
+ }
19
+
20
+ export function getRuntimeStatePath(...parts) {
21
+ return getRuntimeSubsystemPath("state", ...parts);
22
+ }
23
+
16
24
  export function getRuntimeWritePath(...parts) {
17
25
  return path.join(getRuntimeRoot(), ...parts);
18
26
  }
@@ -21,6 +29,34 @@ export function getRuntimeReadPath(...parts) {
21
29
  return path.join(getRuntimeRoot(), ...parts);
22
30
  }
23
31
 
32
+ export function getMcpRootPath() {
33
+ return path.join(getRepoRoot(), "mcp");
34
+ }
35
+
36
+ export function getBundledMcpPath(...parts) {
37
+ return path.join(packageRoot, "mcp", ...parts);
38
+ }
39
+
24
40
  export function getMcpServersPath() {
25
- return path.join(getRepoRoot(), "mcp", "servers.json");
41
+ return path.join(getMcpRootPath(), "servers.json");
42
+ }
43
+
44
+ export function getMcpLocalCapabilitiesPath() {
45
+ return path.join(getMcpRootPath(), "local-capabilities.json");
46
+ }
47
+
48
+ export function getRuntimeMcpPolicyPath() {
49
+ return getRuntimeWritePath(".mcp.json");
50
+ }
51
+
52
+ export function getBundledNativePlaybooksPath() {
53
+ return getBundledMcpPath("native-playbooks.json");
54
+ }
55
+
56
+ export function getBundledDocsPath(...parts) {
57
+ return path.join(packageRoot, "docs", ...parts);
58
+ }
59
+
60
+ export function getBundledSkillsPath(...parts) {
61
+ return path.join(packageRoot, "skills", ...parts);
26
62
  }
package/src/policy.js CHANGED
@@ -19,5 +19,5 @@ export function validateMergeTarget(sourceBranch, targetBranch) {
19
19
  }
20
20
 
21
21
  export function canMarkBuildDone(releaseState) {
22
- return ["READY", "RUNNING"].includes(releaseState.build_status);
22
+ return releaseState.build_status === "DONE";
23
23
  }