@jstn-sdk/ma 0.1.12 → 0.14.0

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 (198) hide show
  1. package/.codex/agents/Architect.toml +1 -2
  2. package/.codex/agents/Auditor.toml +1 -2
  3. package/.codex/agents/Builder.toml +1 -2
  4. package/.codex/agents/Flow.toml +1 -2
  5. package/.codex/agents/Sage.toml +1 -2
  6. package/.codex/agents/Vibe.toml +1 -2
  7. package/.codex/hooks.json +24 -13
  8. package/.codex/prompts/enforcement.md +4 -0
  9. package/.codex/prompts/onboarding.md +6 -1
  10. package/.codex/prompts/release-rules.md +0 -0
  11. package/.codex/prompts/skill-contract.md +4 -0
  12. package/COVERAGE.md +211 -0
  13. package/DEMO.md +274 -0
  14. package/LICENSE +0 -0
  15. package/README.md +132 -656
  16. package/bin/ma.js +748 -55
  17. package/data/clone-data.ledger.json +41 -0
  18. package/data/clone-data.proof.json +50 -0
  19. package/data/clone-data.rvf +37 -0
  20. package/docs/README.md +10 -1
  21. package/docs/autonomous-tasks.md +46 -0
  22. package/docs/codex-integration.md +16 -0
  23. package/docs/getting-started.md +62 -22
  24. package/docs/mcp-setup.md +64 -0
  25. package/docs/qa/release-issue-gates-0.14.0.json +644 -0
  26. package/docs/qa/release-readiness-0.1.5.md +1 -1
  27. package/docs/qa/{release-readiness-0.1.12.md → release-readiness-0.14.0.md} +25 -9
  28. package/docs/quality.md +24 -0
  29. package/docs/reference/native-security-playbooks.md +6 -0
  30. package/docs/reference/native-source-selection.md +6 -0
  31. package/docs/release-spec.md +39 -10
  32. package/docs/skills.md +14 -1
  33. package/index.js +295 -2
  34. package/mcp/collections.json +23 -6
  35. package/mcp/fallback.json +0 -0
  36. package/mcp/local/code-intel.js +179 -41
  37. package/mcp/local/context.js +180 -0
  38. package/mcp/local/memory.js +17 -6
  39. package/mcp/local/quality.js +39 -0
  40. package/mcp/local/state.js +7 -3
  41. package/mcp/local/team-run.js +56 -4
  42. package/mcp/local-capabilities.json +9 -0
  43. package/mcp/servers.json +24 -0
  44. package/package.json +29 -4
  45. package/plugins/meta-architect/.app.json +1 -1
  46. package/plugins/meta-architect/.codex-plugin/plugin.json +1 -1
  47. package/plugins/meta-architect/.mcp.json +1 -1
  48. package/plugins/meta-architect/README.md +8 -1
  49. package/plugins/meta-architect/obsidian/main.js +534 -0
  50. package/plugins/meta-architect/obsidian/manifest.json +9 -0
  51. package/plugins/meta-architect/obsidian/styles.css +7 -0
  52. package/plugins/meta-architect/skills/arch/SKILL.md +2 -0
  53. package/plugins/meta-architect/skills/build/SKILL.md +3 -0
  54. package/plugins/meta-architect/skills/flow/SKILL.md +2 -0
  55. package/plugins/meta-architect/skills/sage/SKILL.md +2 -0
  56. package/plugins/meta-architect/skills/sage/references/source-selection.md +8 -0
  57. package/plugins/meta-architect/skills/vet/SKILL.md +2 -0
  58. package/plugins/meta-architect/skills/vet/references/security-playbooks.md +7 -0
  59. package/plugins/meta-architect/skills/vibe/SKILL.md +2 -0
  60. package/schemas/autonomous-task-queue.schema.json +56 -0
  61. package/schemas/handoff-packet.schema.json +36 -0
  62. package/schemas/setup-receipt.schema.json +28 -0
  63. package/schemas/skill-frontmatter.schema.json +20 -0
  64. package/schemas/support-bundle.schema.json +37 -0
  65. package/schemas/task-contract.schema.json +31 -0
  66. package/scripts/active-autonomy-hook.mjs +226 -0
  67. package/scripts/build-linux-packages.mjs +7 -3
  68. package/scripts/cleanup-test-fixtures.sh +55 -0
  69. package/scripts/context-hydration-hook.mjs +89 -0
  70. package/scripts/demo-smoke.js +285 -0
  71. package/scripts/doctor.js +27 -12
  72. package/scripts/install.sh +57 -0
  73. package/scripts/install.sh.sha256 +1 -0
  74. package/scripts/linux-package-smoke.mjs +2 -2
  75. package/scripts/package-size-check.mjs +26 -0
  76. package/scripts/plugin-sync.js +83 -17
  77. package/scripts/postinstall.js +17 -10
  78. package/scripts/prepack.js +9 -0
  79. package/scripts/ralph/prompt.md +35 -0
  80. package/scripts/release-sync.js +72 -7
  81. package/scripts/release-verify.js +260 -2
  82. package/scripts/setup-npmrc.js +48 -23
  83. package/scripts/skills-install.js +9 -2
  84. package/scripts/skills-manifest.js +2 -21
  85. package/scripts/skills-pack.js +2 -2
  86. package/scripts/skills-validate.js +5 -55
  87. package/skills/arch/SKILL.md +2 -0
  88. package/skills/build/SKILL.md +3 -0
  89. package/skills/flow/SKILL.md +2 -0
  90. package/skills/index.json +0 -0
  91. package/skills/sage/SKILL.md +2 -0
  92. package/skills/sage/references/source-selection.md +8 -0
  93. package/skills/vet/SKILL.md +2 -0
  94. package/skills/vet/references/security-playbooks.md +7 -0
  95. package/skills/vibe/SKILL.md +2 -0
  96. package/sprint/00-idea.md +0 -0
  97. package/sprint/01-architecture.md +0 -0
  98. package/sprint/02-oss-evidence.md +0 -0
  99. package/sprint/03-logic.md +0 -0
  100. package/sprint/04-security.md +0 -0
  101. package/sprint/05-dx-ux.md +0 -0
  102. package/sprint/06-build-plan.md +0 -0
  103. package/sprint/07-release.md +0 -0
  104. package/src/agents.js +279 -0
  105. package/src/bootstrap.js +268 -62
  106. package/src/build-gate.js +2 -2
  107. package/src/codex-app-server.js +291 -0
  108. package/src/decision-log.js +0 -0
  109. package/src/fs-utils.js +175 -4
  110. package/src/launcher.js +28 -21
  111. package/src/mcp-config.js +9 -1
  112. package/src/mcp-live-client.js +422 -3
  113. package/src/paths.js +10 -1
  114. package/src/policy.js +3 -3
  115. package/src/prelaunch.js +189 -0
  116. package/src/quality/ai-quality-orchestrator.js +328 -0
  117. package/src/release-issue-gates.js +252 -0
  118. package/src/release-operations.js +62 -0
  119. package/src/release-state.js +0 -0
  120. package/src/runtime/active-autonomy-core.js +208 -0
  121. package/src/runtime/agent-compat.js +31 -0
  122. package/src/runtime/alignment-sentinel.js +165 -0
  123. package/src/runtime/architect-review.js +326 -0
  124. package/src/runtime/autonomous-tasks.js +408 -0
  125. package/src/runtime/build-readiness.js +28 -4
  126. package/src/runtime/code-graph-rehearse.js +113 -0
  127. package/src/runtime/codeburn-core.js +112 -0
  128. package/src/runtime/context-authority.js +40 -0
  129. package/src/runtime/context-economy-core.js +316 -0
  130. package/src/runtime/continuity-graph.js +201 -0
  131. package/src/runtime/continuity-notes.js +71 -26
  132. package/src/runtime/core-source-ingest.js +379 -0
  133. package/src/runtime/detached-provider.js +142 -0
  134. package/src/runtime/doctor-report.js +18 -0
  135. package/src/runtime/environment-awareness-core.js +460 -0
  136. package/src/runtime/exposure-catalog.js +276 -0
  137. package/src/runtime/graphify-core.js +167 -0
  138. package/src/runtime/guidance-stack.js +9 -2
  139. package/src/runtime/handoff-packets.js +92 -0
  140. package/src/runtime/headroom-core.js +104 -0
  141. package/src/runtime/helper-orchestration-core.js +307 -0
  142. package/src/runtime/hook-profiles.js +20 -0
  143. package/src/runtime/learning-loop-core.js +344 -0
  144. package/src/runtime/live-agent-verification.js +122 -0
  145. package/src/runtime/maestro-events.js +18 -0
  146. package/src/runtime/maestro-manager.js +19 -0
  147. package/src/runtime/maestro-output.js +58 -0
  148. package/src/runtime/maestro-state.js +125 -0
  149. package/src/runtime/managed-markdown.js +29 -0
  150. package/src/runtime/mcp-authority.js +147 -0
  151. package/src/runtime/mcp-policy.js +192 -0
  152. package/src/runtime/obsidian-integration-core.js +1089 -0
  153. package/src/runtime/obsidian-plugin-bridge.js +1045 -0
  154. package/src/runtime/pi-maestro-core.js +89 -0
  155. package/src/runtime/preferences.js +79 -0
  156. package/src/runtime/project-context.js +636 -0
  157. package/src/runtime/prompt-strategy-core.js +230 -0
  158. package/src/runtime/quorum-review.js +90 -0
  159. package/src/runtime/ralph-execution-core.js +217 -0
  160. package/src/runtime/redaction-gateway.js +373 -0
  161. package/src/runtime/runtime-state.js +935 -2
  162. package/src/runtime/schema-migrations.js +162 -0
  163. package/src/runtime/semantic-recording-core.js +147 -0
  164. package/src/runtime/signal-hooks.js +28 -0
  165. package/src/runtime/skills-registry-export.js +754 -0
  166. package/src/runtime/task-contracts.js +83 -0
  167. package/src/runtime/universal-plugin-broker-core.js +898 -0
  168. package/src/runtime/workspace-intelligence-runtime.js +674 -0
  169. package/src/runtime/workspace-virtualizer.js +102 -0
  170. package/src/runtime-artifacts.js +225 -62
  171. package/src/setup-lifecycle.js +359 -0
  172. package/src/skill-frontmatter.js +78 -0
  173. package/src/skill-installer.js +226 -71
  174. package/src/skills.js +1166 -96
  175. package/src/state-sync.js +0 -0
  176. package/src/test-fixtures.js +543 -0
  177. package/src/tui/grid.js +67 -0
  178. package/src/tui/status-grid.js +23 -0
  179. package/support-bundle.json +114 -0
  180. package/templates/AGENTS.md +6 -2
  181. package/templates/catalog-manifest.json +0 -0
  182. package/templates/model-instructions/core.md +0 -0
  183. package/templates/model-instructions/release.md +0 -0
  184. package/templates/model-instructions/security.md +0 -0
  185. package/templates/quality/ai-quality-rules.yml +37 -0
  186. package/docs/assets/image/Screenshot(1).png +0 -0
  187. package/docs/assets/image/Screenshot(2).png +0 -0
  188. package/docs/assets/image/Screenshot(3).png +0 -0
  189. package/docs/assets/image/Screenshot(4).png +0 -0
  190. package/docs/assets/image/Screenshot(5).png +0 -0
  191. package/docs/assets/image/Screenshot(6).png +0 -0
  192. package/docs/assets/image/Screenshot(7).png +0 -0
  193. package/docs/assets/image/Screenshot(8).png +0 -0
  194. package/docs/assets/image/Screenshot(9).png +0 -0
  195. package/docs/assets/meta-architect-logo.png +0 -0
  196. package/docs/assets/meta-architect-logo.svg +0 -8
  197. package/docs/onboarding.md +0 -65
  198. package/docs/skills-publishing.md +0 -255
@@ -1,39 +1,181 @@
1
+ import { execFile } from "node:child_process";
1
2
  import fs from "node:fs/promises";
2
3
  import path from "node:path";
4
+ import { promisify } from "node:util";
3
5
  import { getRepoRoot } from "../../src/paths.js";
4
6
 
5
- const ignoredDirs = new Set([".git", ".ma", ".omx", "node_modules"]);
7
+ const execFileAsync = promisify(execFile);
8
+ const MAX_SCAN_BYTES = 1024 * 1024;
9
+ const ignoredDirs = new Set([
10
+ ".git",
11
+ ".ma",
12
+ ".omx",
13
+ ".next",
14
+ ".turbo",
15
+ ".vercel",
16
+ "build",
17
+ "coverage",
18
+ "dist",
19
+ "node_modules",
20
+ ]);
21
+ const artifactExtensions = new Set([
22
+ ".7z",
23
+ ".bin",
24
+ ".bmp",
25
+ ".class",
26
+ ".dll",
27
+ ".dylib",
28
+ ".gif",
29
+ ".gz",
30
+ ".ico",
31
+ ".jar",
32
+ ".jpeg",
33
+ ".jpg",
34
+ ".lockb",
35
+ ".mp3",
36
+ ".mp4",
37
+ ".o",
38
+ ".pdf",
39
+ ".png",
40
+ ".so",
41
+ ".svg",
42
+ ".tar",
43
+ ".tgz",
44
+ ".wasm",
45
+ ".webp",
46
+ ".woff",
47
+ ".woff2",
48
+ ".zip",
49
+ ".zst",
50
+ ]);
51
+ const ignoredFiles = new Set([".active"]);
6
52
  const codeIntelTools = [
7
53
  "code_intel.list_files",
8
54
  "code_intel.search_text",
9
55
  "code_intel.read_excerpt",
10
56
  ];
11
57
 
12
- async function walk(rootDir, currentDir = rootDir, files = [], limit = Number.POSITIVE_INFINITY) {
13
- if (files.length >= limit) {
14
- return files;
58
+ function normalizeRelative(relativePath) {
59
+ const normalized = path.posix.normalize(relativePath.replaceAll(path.sep, "/"));
60
+ if (
61
+ normalized === "." ||
62
+ normalized === ".." ||
63
+ normalized.startsWith("../") ||
64
+ normalized.includes("\0")
65
+ ) {
66
+ throw new Error("code_intel path must stay inside the repository");
15
67
  }
68
+ return normalized;
69
+ }
16
70
 
17
- const entries = await fs.readdir(currentDir, { withFileTypes: true });
18
- for (const entry of entries) {
19
- if (files.length >= limit) {
20
- break;
21
- }
71
+ function isArtifactPath(relativePath) {
72
+ const segments = relativePath.split("/");
73
+ return (
74
+ ignoredFiles.has(segments.at(-1)) ||
75
+ segments.some((segment) => ignoredDirs.has(segment)) ||
76
+ artifactExtensions.has(path.extname(relativePath).toLowerCase())
77
+ );
78
+ }
79
+
80
+ async function isReadableTextFile(absolutePath) {
81
+ const stat = await fs.lstat(absolutePath);
82
+ if (stat.isSymbolicLink()) return false;
83
+ if (!stat.isFile() || stat.size > MAX_SCAN_BYTES) return false;
84
+ const sample = await fs.readFile(absolutePath, { encoding: null, flag: "r" });
85
+ return !sample.subarray(0, Math.min(sample.length, 8192)).includes(0);
86
+ }
87
+
88
+ async function assertSafeRepoPath(rootDir, relativePath) {
89
+ let current = rootDir;
90
+ for (const component of relativePath.split("/")) {
91
+ current = path.join(current, component);
92
+ const stat = await fs.lstat(current).catch((error) => {
93
+ if (error?.code === "ENOENT") return null;
94
+ throw error;
95
+ });
96
+ if (stat?.isSymbolicLink()) throw new Error("code_intel cannot follow symlinks");
97
+ }
98
+ return path.join(rootDir, relativePath);
99
+ }
100
+
101
+ async function listGitFiles(rootDir) {
102
+ try {
103
+ const { stdout } = await execFileAsync(
104
+ "git",
105
+ ["ls-files", "--cached", "--others", "--exclude-standard", "-z"],
106
+ { cwd: rootDir, encoding: "buffer", maxBuffer: 4 * 1024 * 1024 },
107
+ );
108
+ return stdout
109
+ .toString("utf8")
110
+ .split("\0")
111
+ .filter(Boolean)
112
+ .map((file) => file.replaceAll("\\", "/"));
113
+ } catch {
114
+ return null;
115
+ }
116
+ }
117
+
118
+ async function loadFallbackIgnorePatterns(rootDir) {
119
+ const content = await fs.readFile(path.join(rootDir, ".gitignore"), "utf8").catch(() => "");
120
+ return content
121
+ .split("\n")
122
+ .map((line) => line.trim())
123
+ .filter((line) => line && !line.startsWith("#") && !line.startsWith("!"));
124
+ }
125
+
126
+ function isFallbackIgnored(relativePath, patterns) {
127
+ const normalized = relativePath.split(path.sep).join("/");
128
+ return patterns.some((pattern) => {
129
+ const directoryPattern = pattern.endsWith("/") ? pattern.slice(0, -1) : pattern;
130
+ const escaped = directoryPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
131
+ const expression = escaped
132
+ .replaceAll("**", ".*")
133
+ .replaceAll("*", "[^/]*")
134
+ .replaceAll("?", "[^/]");
135
+ const matcher = new RegExp(`(?:^|/)${expression}(?:/|$)`);
136
+ return (
137
+ matcher.test(normalized) ||
138
+ (!pattern.includes("/") && new RegExp(`(^|/)${expression}$`).test(normalized))
139
+ );
140
+ });
141
+ }
22
142
 
143
+ async function walkFallback(rootDir, currentDir = rootDir, files = [], limit = 200, patterns = []) {
144
+ if (files.length >= limit) return files;
145
+ const entries = await fs.readdir(currentDir, { withFileTypes: true }).catch(() => []);
146
+ for (const entry of entries) {
147
+ if (files.length >= limit) break;
148
+ const absolutePath = path.join(currentDir, entry.name);
149
+ const relativePath = path.relative(rootDir, absolutePath);
150
+ if (isFallbackIgnored(relativePath, patterns)) continue;
23
151
  if (entry.isDirectory()) {
24
- if (ignoredDirs.has(entry.name)) {
25
- continue;
152
+ if (!ignoredDirs.has(entry.name)) {
153
+ await walkFallback(rootDir, absolutePath, files, limit, patterns);
26
154
  }
27
- await walk(rootDir, path.join(currentDir, entry.name), files, limit);
28
- continue;
155
+ } else if (entry.isFile() && !isArtifactPath(relativePath)) {
156
+ files.push(relativePath.split(path.sep).join("/"));
29
157
  }
158
+ }
159
+ return files;
160
+ }
30
161
 
31
- if (!entry.isFile()) {
32
- continue;
162
+ async function listSafeFiles(rootDir, limit = 200) {
163
+ const gitFiles = await listGitFiles(rootDir);
164
+ const candidates =
165
+ gitFiles ??
166
+ (await walkFallback(rootDir, rootDir, [], limit, await loadFallbackIgnorePatterns(rootDir)));
167
+ const files = [];
168
+ for (const relativePath of candidates) {
169
+ if (files.length >= limit) break;
170
+ const normalized = normalizeRelative(relativePath);
171
+ if (isArtifactPath(normalized)) continue;
172
+ try {
173
+ const absolute = await assertSafeRepoPath(rootDir, normalized);
174
+ if (await isReadableTextFile(absolute)) files.push(normalized);
175
+ } catch {
176
+ // Ignore files that disappear or cannot be inspected.
33
177
  }
34
- files.push(path.relative(rootDir, path.join(currentDir, entry.name)));
35
178
  }
36
-
37
179
  return files;
38
180
  }
39
181
 
@@ -42,52 +184,49 @@ export function listCodeIntelTools() {
42
184
  }
43
185
 
44
186
  export async function listRepoFiles(limit = 200) {
45
- const repoRoot = getRepoRoot();
46
- return walk(repoRoot, repoRoot, [], limit);
187
+ return listSafeFiles(getRepoRoot(), Math.max(1, Math.min(Number(limit) || 200, 2000)));
47
188
  }
48
189
 
49
190
  export async function searchRepoText(query, limit = 20) {
50
191
  const repoRoot = getRepoRoot();
51
192
  const files = await listRepoFiles(500);
52
193
  const matches = [];
53
-
194
+ const boundedLimit = Math.max(1, Math.min(Number(limit) || 20, 100));
54
195
  for (const relativePath of files) {
55
- if (matches.length >= limit) {
56
- break;
57
- }
58
-
196
+ if (matches.length >= boundedLimit) break;
59
197
  try {
60
198
  const content = await fs.readFile(path.join(repoRoot, relativePath), "utf8");
61
- const lines = content.split("\n");
62
- lines.forEach((line, index) => {
63
- if (matches.length < limit && line.includes(query)) {
199
+ content.split("\n").forEach((line, index) => {
200
+ if (matches.length < boundedLimit && line.includes(query)) {
64
201
  matches.push({ file: relativePath, line: index + 1, text: line.trim() });
65
202
  }
66
203
  });
67
204
  } catch {
68
- // Skip non-text files.
205
+ // Skip files that disappear or fail the text boundary.
69
206
  }
70
207
  }
71
-
72
208
  return matches;
73
209
  }
74
210
 
75
211
  export async function readRepoExcerpt(relativePath, line = 1, context = 2) {
76
212
  const repoRoot = getRepoRoot();
77
- const content = await fs.readFile(path.join(repoRoot, relativePath), "utf8");
213
+ const normalized = normalizeRelative(relativePath);
214
+ if (isArtifactPath(normalized))
215
+ throw new Error("code_intel cannot read generated or binary artifacts");
216
+ const absolutePath = await assertSafeRepoPath(repoRoot, normalized);
217
+ if (!(await isReadableTextFile(absolutePath)))
218
+ throw new Error("code_intel can only read bounded text files");
219
+ const content = await fs.readFile(absolutePath, "utf8");
220
+ const safeLine = Math.max(1, Number(line) || 1);
221
+ const safeContext = Math.max(0, Math.min(Number(context) || 2, 20));
78
222
  const lines = content.split("\n");
79
- const start = Math.max(0, line - context - 1);
80
- const end = Math.min(lines.length, line + context);
81
- return lines.slice(start, end).map((text, index) => ({
82
- line: start + index + 1,
83
- text,
84
- }));
223
+ const start = Math.max(0, safeLine - safeContext - 1);
224
+ const end = Math.min(lines.length, safeLine + safeContext);
225
+ return lines.slice(start, end).map((text, index) => ({ line: start + index + 1, text }));
85
226
  }
86
227
 
87
228
  export async function callCodeIntelTool(name, args = {}) {
88
- if (name === "code_intel.list_files") {
89
- return listRepoFiles(args.limit);
90
- }
229
+ if (name === "code_intel.list_files") return listRepoFiles(args.limit);
91
230
  if (name === "code_intel.search_text") {
92
231
  if (typeof args.query !== "string" || args.query.trim() === "") {
93
232
  throw new Error("code_intel.search_text requires a non-empty query");
@@ -100,7 +239,6 @@ export async function callCodeIntelTool(name, args = {}) {
100
239
  }
101
240
  return readRepoExcerpt(args.file, args.line, args.context);
102
241
  }
103
-
104
242
  throw new Error(`Unknown code_intel tool: ${name}`);
105
243
  }
106
244
 
@@ -108,6 +246,6 @@ export async function checkCodeIntelCapability() {
108
246
  const files = await listRepoFiles(10);
109
247
  return {
110
248
  ready: files.length > 0,
111
- detail: `code intel indexed ${files.length} repo file(s) in the readiness sample`,
249
+ detail: `code intel indexed ${files.length} bounded text file(s) with Git-ignore and artifact filtering`,
112
250
  };
113
251
  }
@@ -0,0 +1,180 @@
1
+ import path from "node:path";
2
+ import { loadLearningLoopCore, loadLearningRecords } from "../../src/runtime/learning-loop-core.js";
3
+ import {
4
+ loadObsidianVaultIndex,
5
+ loadObsidianVaultOperations,
6
+ } from "../../src/runtime/obsidian-integration-core.js";
7
+ import { resolvePreferences } from "../../src/runtime/preferences.js";
8
+ import { loadProjectIndex } from "../../src/runtime/project-context.js";
9
+ import {
10
+ loadRuntimeHookReceipts,
11
+ loadRuntimeHooksAuditLog,
12
+ loadRuntimeHooksConfig,
13
+ } from "../../src/runtime/signal-hooks.js";
14
+
15
+ const contextResources = [
16
+ "context://project-index",
17
+ "context://learning",
18
+ "context://obsidian",
19
+ "context://hooks",
20
+ "context://freshness",
21
+ "context://commands",
22
+ "context://agent-brief",
23
+ "context://architecture",
24
+ "context://preferences",
25
+ ];
26
+
27
+ export function listContextResources() {
28
+ return [...contextResources];
29
+ }
30
+
31
+ async function readOptional(loader, fallback) {
32
+ try {
33
+ return { available: true, data: await loader() };
34
+ } catch (error) {
35
+ if (error?.code === "ENOENT") return { available: false, data: fallback };
36
+ throw error;
37
+ }
38
+ }
39
+
40
+ function envelope(resource, result, extra = {}) {
41
+ const freshness = extra.freshness ?? result.data?.freshness ?? null;
42
+ return {
43
+ record_type: `mcp_context:${resource}`,
44
+ authority: "source_truth",
45
+ source: "meta-architect-runtime",
46
+ resource,
47
+ ...extra,
48
+ ...result,
49
+ context: {
50
+ authority: extra.authority ?? "generated_context",
51
+ source: "meta-architect-runtime",
52
+ freshness,
53
+ provenance: result.data?.sourceFiles?.map((file) => file.path) ?? [],
54
+ },
55
+ quality: result.data?.quality ?? extra.quality ?? null,
56
+ };
57
+ }
58
+
59
+ export async function readContextResource(uri) {
60
+ if (uri === "context://project-index") {
61
+ const result = await readOptional(loadProjectIndex, null);
62
+ return envelope("project-index", result, {
63
+ authority: result.available ? "source_truth" : "missing_source_truth",
64
+ fact_ids: result.data?.facts?.map((fact) => fact.id) ?? [],
65
+ provenance:
66
+ result.data?.facts?.map((fact) => ({ id: fact.id, provenance: fact.provenance })) ?? [],
67
+ });
68
+ }
69
+
70
+ if (uri === "context://learning") {
71
+ const [core, records] = await Promise.all([
72
+ readOptional(loadLearningLoopCore, null),
73
+ readOptional(loadLearningRecords, []),
74
+ ]);
75
+ return envelope(
76
+ "learning",
77
+ {
78
+ available: core.available || records.available,
79
+ data: {
80
+ core: core.data,
81
+ records: records.data,
82
+ failure_records: records.data.filter((record) => Boolean(record.failure_state)),
83
+ },
84
+ },
85
+ {
86
+ authority: "generated_context",
87
+ fact_ids: records.data.flatMap((record) => record.fact_ids ?? []),
88
+ },
89
+ );
90
+ }
91
+
92
+ if (uri === "context://obsidian") {
93
+ const [index, operations] = await Promise.all([
94
+ readOptional(loadObsidianVaultIndex, null),
95
+ readOptional(loadObsidianVaultOperations, null),
96
+ ]);
97
+ return envelope(
98
+ "obsidian",
99
+ {
100
+ available: index.available || operations.available,
101
+ data: { index: index.data, operations: operations.data },
102
+ },
103
+ { authority: "vault_note" },
104
+ );
105
+ }
106
+
107
+ if (uri === "context://hooks") {
108
+ const [config, audit, receipts] = await Promise.all([
109
+ readOptional(loadRuntimeHooksConfig, null),
110
+ readOptional(loadRuntimeHooksAuditLog, ""),
111
+ readOptional(loadRuntimeHookReceipts, []),
112
+ ]);
113
+ return envelope(
114
+ "hooks",
115
+ {
116
+ available: config.available || audit.available,
117
+ data: { config: config.data, audit: audit.data, receipts: receipts.data },
118
+ },
119
+ { authority: "hook_evidence" },
120
+ );
121
+ }
122
+
123
+ if (
124
+ uri === "context://commands" ||
125
+ uri === "context://agent-brief" ||
126
+ uri === "context://architecture"
127
+ ) {
128
+ const relative = uri.slice("context://".length);
129
+ const loader = async () => {
130
+ if (relative === "commands") return readOptional(() => readJsonFile("commands.json"), null);
131
+ return readOptional(() => readTextFile(`${relative}.md`), "");
132
+ };
133
+ const result = await loader();
134
+ const project = await readOptional(loadProjectIndex, null);
135
+ return envelope(relative, result, {
136
+ authority: "generated_context",
137
+ freshness: { status: "derived", stale: false, source: "project-index" },
138
+ quality: project.data?.quality ?? null,
139
+ });
140
+ }
141
+
142
+ if (uri === "context://freshness") {
143
+ const result = await readOptional(loadProjectIndex, null);
144
+ return envelope(
145
+ "freshness",
146
+ { available: result.available, data: result.data?.freshness ?? null },
147
+ { authority: "source_truth" },
148
+ );
149
+ }
150
+
151
+ if (uri === "context://preferences") {
152
+ const result = await readOptional(resolvePreferences, {});
153
+ return envelope("preferences", result, { authority: "learning_memory" });
154
+ }
155
+
156
+ throw new Error(`Unknown context resource: ${uri}`);
157
+ }
158
+
159
+ async function readJsonFile(name) {
160
+ const { readJson } = await import("../../src/fs-utils.js");
161
+ return readJson(path.join(process.env.MA_ROOT || process.cwd(), ".ma", "context", name));
162
+ }
163
+
164
+ async function readTextFile(name) {
165
+ const fs = await import("node:fs/promises");
166
+ return fs.readFile(
167
+ path.join(process.env.MA_ROOT || process.cwd(), ".ma", "context", name),
168
+ "utf8",
169
+ );
170
+ }
171
+
172
+ export async function checkContextCapability() {
173
+ const project = await readContextResource("context://project-index");
174
+ return {
175
+ ready: true,
176
+ detail: project.available
177
+ ? `project context loaded with ${project.data.sourceFiles?.length ?? 0} source file(s)`
178
+ : "project context has not been generated; run ma setup",
179
+ };
180
+ }
@@ -1,11 +1,13 @@
1
+ import { loadContinuityGraph, queryContinuityGraph } from "../../src/runtime/continuity-graph.js";
1
2
  import {
2
3
  loadContinuityIndex,
3
4
  loadContinuityNotes,
4
5
  storeContinuityNote,
5
6
  } from "../../src/runtime/continuity-notes.js";
7
+ import { withMcpWriteGate } from "../../src/runtime/mcp-authority.js";
6
8
 
7
- const memoryResources = ["memory://notes", "memory://index"];
8
- const memoryTools = ["memory.store_note"];
9
+ const memoryResources = ["memory://notes", "memory://index", "memory://graph"];
10
+ const memoryTools = ["memory.store_note", "memory.query_graph"];
9
11
 
10
12
  export function listMemoryResources() {
11
13
  return [...memoryResources];
@@ -22,19 +24,28 @@ export async function readMemoryResource(uri) {
22
24
  if (uri === "memory://index") {
23
25
  return loadContinuityIndex();
24
26
  }
27
+ if (uri === "memory://graph") {
28
+ return loadContinuityGraph();
29
+ }
25
30
 
26
31
  throw new Error(`Unknown memory resource: ${uri}`);
27
32
  }
28
33
 
29
34
  export async function callMemoryTool(name, args = {}, options = {}) {
35
+ if (name === "memory.query_graph") {
36
+ return queryMemoryGraph(args);
37
+ }
30
38
  if (name !== "memory.store_note") {
31
39
  throw new Error(`Unknown memory tool: ${name}`);
32
40
  }
33
41
 
34
- return storeContinuityNote(args.content ?? "", {
35
- ...options,
36
- actor: "local-capability:memory",
37
- });
42
+ return withMcpWriteGate({ tool: name, options, payload: args }, (metadata) =>
43
+ storeContinuityNote(args.content ?? "", { ...options, ...metadata }),
44
+ );
45
+ }
46
+
47
+ export async function queryMemoryGraph(args = {}) {
48
+ return queryContinuityGraph(await loadContinuityGraph(), args);
38
49
  }
39
50
 
40
51
  export async function checkMemoryCapability() {
@@ -0,0 +1,39 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { getRepoRoot } from "../../src/paths.js";
4
+
5
+ const qualityRoot = () => path.join(getRepoRoot(), ".ma", "quality");
6
+
7
+ export const qualityResources = ["quality://violations", "quality://kpis"];
8
+
9
+ export async function readQualityResource(uri) {
10
+ const file =
11
+ uri === "quality://violations"
12
+ ? "violations.json"
13
+ : uri === "quality://kpis"
14
+ ? "kpis.json"
15
+ : null;
16
+ if (!file) throw new Error(`Unknown quality resource: ${uri}`);
17
+ try {
18
+ return JSON.parse(await fs.readFile(path.join(qualityRoot(), file), "utf8"));
19
+ } catch (error) {
20
+ if (error.code === "ENOENT") return [];
21
+ throw error;
22
+ }
23
+ }
24
+
25
+ export async function queryQualityStatus() {
26
+ const [violations, kpis] = await Promise.all([
27
+ readQualityResource("quality://violations"),
28
+ readQualityResource("quality://kpis"),
29
+ ]);
30
+ return {
31
+ violations,
32
+ kpis,
33
+ passed:
34
+ violations.length > 0 &&
35
+ kpis.length > 0 &&
36
+ violations.every((record) => record.violations?.length === 0) &&
37
+ kpis.every((record) => record.kpis?.qualityScore >= 60),
38
+ };
39
+ }
@@ -1,5 +1,6 @@
1
1
  import { appendDecision } from "../../src/decision-log.js";
2
2
  import { loadManagerRunRegistryOrDefault } from "../../src/runtime/maestro-manager.js";
3
+ import { withMcpWriteGate } from "../../src/runtime/mcp-authority.js";
3
4
  import { createRuntimeSummary, loadRuntimeSnapshot } from "../../src/runtime/runtime-state.js";
4
5
  import { loadCombinedState, syncStatusUpdates } from "../../src/state-sync.js";
5
6
 
@@ -45,15 +46,18 @@ export async function readStateResource(uri) {
45
46
  }
46
47
 
47
48
  export async function callStateTool(name, args = {}, options = {}) {
48
- const localOptions = { ...options, actor: "local-capability:_state" };
49
49
  if (name === "state.sync_release_status") {
50
50
  const statusUpdates = args.statusUpdates ?? args;
51
- return syncStatusUpdates(statusUpdates, localOptions);
51
+ return withMcpWriteGate({ tool: name, options, payload: statusUpdates }, (metadata) =>
52
+ syncStatusUpdates(statusUpdates, metadata),
53
+ );
52
54
  }
53
55
 
54
56
  if (name === "state.append_decision") {
55
57
  const entry = args.entry ?? args;
56
- return appendDecision(entry, localOptions);
58
+ return withMcpWriteGate({ tool: name, options, payload: entry }, (metadata) =>
59
+ appendDecision(entry, metadata),
60
+ );
57
61
  }
58
62
 
59
63
  throw new Error(`Unknown _state tool: ${name}`);
@@ -1,4 +1,5 @@
1
1
  import fs from "node:fs/promises";
2
+ import { withMcpWriteGate } from "../../src/runtime/mcp-authority.js";
2
3
  import {
3
4
  controlTask,
4
5
  getTask,
@@ -15,7 +16,14 @@ const teamRunResources = [
15
16
  "team-run://registry",
16
17
  "team-run://mailbox",
17
18
  ];
18
- const teamRunTools = ["team_run.submit_task", "team_run.get_status", "team_run.control_task"];
19
+ const teamRunTools = [
20
+ "team_run.submit_task",
21
+ "team_run.get_status",
22
+ "team_run.list_tasks",
23
+ "team_run.wait_task",
24
+ "team_run.control_task",
25
+ ];
26
+ const terminalTaskStatuses = new Set(["done", "blocked", "cancelled"]);
19
27
 
20
28
  export function listTeamRunResources() {
21
29
  return [...teamRunResources];
@@ -43,20 +51,64 @@ export async function readTeamRunResource(uri) {
43
51
  }
44
52
 
45
53
  export async function callTeamRunTool(name, args = {}, options = {}) {
46
- const localOptions = { ...options, actor: "local-capability:team_run" };
47
54
  if (name === "team_run.submit_task") {
48
- return submitTask(args.task ?? args, localOptions);
55
+ const task = args.task ?? args;
56
+ return withMcpWriteGate({ tool: name, options, payload: task }, (metadata) =>
57
+ submitTask(task, metadata),
58
+ );
49
59
  }
50
60
  if (name === "team_run.get_status") {
51
61
  return getTask(args.taskId);
52
62
  }
63
+ if (name === "team_run.list_tasks") {
64
+ return args.managerOwner === "$maestro" ? listMaestroTasks() : listTasks();
65
+ }
66
+ if (name === "team_run.wait_task") {
67
+ return waitForTask(args.taskId, args);
68
+ }
53
69
  if (name === "team_run.control_task") {
54
- return controlTask(args.taskId, args.action, localOptions);
70
+ return withMcpWriteGate({ tool: name, options, payload: args }, (metadata) =>
71
+ controlTask(args.taskId, args.action, metadata),
72
+ );
55
73
  }
56
74
 
57
75
  throw new Error(`Unknown team_run tool: ${name}`);
58
76
  }
59
77
 
78
+ export async function waitForTask(taskId, { timeoutMs = 1_000, pollMs = 50 } = {}) {
79
+ if (typeof taskId !== "string" || taskId.trim() === "") {
80
+ throw new Error("team_run.wait_task requires a taskId");
81
+ }
82
+
83
+ const boundedTimeoutMs = Math.max(0, Math.min(Number(timeoutMs) || 0, 30_000));
84
+ const boundedPollMs = Math.max(10, Math.min(Number(pollMs) || 50, 1_000));
85
+ const startedAt = Date.now();
86
+
87
+ while (Date.now() - startedAt <= boundedTimeoutMs) {
88
+ const task = await getTask(taskId);
89
+ if (terminalTaskStatuses.has(task.status)) {
90
+ return {
91
+ record_type: "team_run_wait_result",
92
+ terminal: true,
93
+ timed_out: false,
94
+ task,
95
+ };
96
+ }
97
+
98
+ if (boundedTimeoutMs === 0) {
99
+ break;
100
+ }
101
+ await new Promise((resolve) => setTimeout(resolve, boundedPollMs));
102
+ }
103
+
104
+ return {
105
+ record_type: "team_run_wait_result",
106
+ terminal: false,
107
+ timed_out: true,
108
+ task: await getTask(taskId),
109
+ };
110
+ }
111
+
60
112
  export async function checkTeamRunCapability() {
61
113
  const registry = await readTeamRunResource("team-run://registry");
62
114
  return {
@@ -51,6 +51,15 @@
51
51
  "module": "./local/playbooks.js",
52
52
  "readinessCheck": "checkPlaybooksCapability",
53
53
  "seededByBootstrap": true
54
+ },
55
+ {
56
+ "kind": "local-capability",
57
+ "capability": "context",
58
+ "transport": "inproc",
59
+ "module": "./local/context.js",
60
+ "readinessCheck": "checkContextCapability",
61
+ "seededByBootstrap": true,
62
+ "authorityMode": "read-only-evidence"
54
63
  }
55
64
  ]
56
65
  }