@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.
- package/.codex/agents/Architect.toml +1 -2
- package/.codex/agents/Auditor.toml +1 -2
- package/.codex/agents/Builder.toml +1 -2
- package/.codex/agents/Flow.toml +1 -2
- package/.codex/agents/Sage.toml +1 -2
- package/.codex/agents/Vibe.toml +1 -2
- package/.codex/hooks.json +24 -13
- package/.codex/prompts/enforcement.md +4 -0
- package/.codex/prompts/onboarding.md +6 -1
- package/.codex/prompts/release-rules.md +0 -0
- package/.codex/prompts/skill-contract.md +4 -0
- package/COVERAGE.md +211 -0
- package/DEMO.md +274 -0
- package/LICENSE +0 -0
- package/README.md +132 -656
- package/bin/ma.js +748 -55
- package/data/clone-data.ledger.json +41 -0
- package/data/clone-data.proof.json +50 -0
- package/data/clone-data.rvf +37 -0
- package/docs/README.md +10 -1
- package/docs/autonomous-tasks.md +46 -0
- package/docs/codex-integration.md +16 -0
- package/docs/getting-started.md +62 -22
- package/docs/mcp-setup.md +64 -0
- package/docs/qa/release-issue-gates-0.14.0.json +644 -0
- package/docs/qa/release-readiness-0.1.5.md +1 -1
- package/docs/qa/{release-readiness-0.1.12.md → release-readiness-0.14.0.md} +25 -9
- package/docs/quality.md +24 -0
- package/docs/reference/native-security-playbooks.md +6 -0
- package/docs/reference/native-source-selection.md +6 -0
- package/docs/release-spec.md +39 -10
- package/docs/skills.md +14 -1
- package/index.js +295 -2
- package/mcp/collections.json +23 -6
- package/mcp/fallback.json +0 -0
- package/mcp/local/code-intel.js +179 -41
- package/mcp/local/context.js +180 -0
- package/mcp/local/memory.js +17 -6
- package/mcp/local/quality.js +39 -0
- package/mcp/local/state.js +7 -3
- package/mcp/local/team-run.js +56 -4
- package/mcp/local-capabilities.json +9 -0
- package/mcp/servers.json +24 -0
- package/package.json +29 -4
- package/plugins/meta-architect/.app.json +1 -1
- package/plugins/meta-architect/.codex-plugin/plugin.json +1 -1
- package/plugins/meta-architect/.mcp.json +1 -1
- package/plugins/meta-architect/README.md +8 -1
- package/plugins/meta-architect/obsidian/main.js +534 -0
- package/plugins/meta-architect/obsidian/manifest.json +9 -0
- package/plugins/meta-architect/obsidian/styles.css +7 -0
- package/plugins/meta-architect/skills/arch/SKILL.md +2 -0
- package/plugins/meta-architect/skills/build/SKILL.md +3 -0
- package/plugins/meta-architect/skills/flow/SKILL.md +2 -0
- package/plugins/meta-architect/skills/sage/SKILL.md +2 -0
- package/plugins/meta-architect/skills/sage/references/source-selection.md +8 -0
- package/plugins/meta-architect/skills/vet/SKILL.md +2 -0
- package/plugins/meta-architect/skills/vet/references/security-playbooks.md +7 -0
- package/plugins/meta-architect/skills/vibe/SKILL.md +2 -0
- package/schemas/autonomous-task-queue.schema.json +56 -0
- package/schemas/handoff-packet.schema.json +36 -0
- package/schemas/setup-receipt.schema.json +28 -0
- package/schemas/skill-frontmatter.schema.json +20 -0
- package/schemas/support-bundle.schema.json +37 -0
- package/schemas/task-contract.schema.json +31 -0
- package/scripts/active-autonomy-hook.mjs +226 -0
- package/scripts/build-linux-packages.mjs +7 -3
- package/scripts/cleanup-test-fixtures.sh +55 -0
- package/scripts/context-hydration-hook.mjs +89 -0
- package/scripts/demo-smoke.js +285 -0
- package/scripts/doctor.js +27 -12
- package/scripts/install.sh +57 -0
- package/scripts/install.sh.sha256 +1 -0
- package/scripts/linux-package-smoke.mjs +2 -2
- package/scripts/package-size-check.mjs +26 -0
- package/scripts/plugin-sync.js +83 -17
- package/scripts/postinstall.js +17 -10
- package/scripts/prepack.js +9 -0
- package/scripts/ralph/prompt.md +35 -0
- package/scripts/release-sync.js +72 -7
- package/scripts/release-verify.js +260 -2
- package/scripts/setup-npmrc.js +48 -23
- package/scripts/skills-install.js +9 -2
- package/scripts/skills-manifest.js +2 -21
- package/scripts/skills-pack.js +2 -2
- package/scripts/skills-validate.js +5 -55
- package/skills/arch/SKILL.md +2 -0
- package/skills/build/SKILL.md +3 -0
- package/skills/flow/SKILL.md +2 -0
- package/skills/index.json +0 -0
- package/skills/sage/SKILL.md +2 -0
- package/skills/sage/references/source-selection.md +8 -0
- package/skills/vet/SKILL.md +2 -0
- package/skills/vet/references/security-playbooks.md +7 -0
- package/skills/vibe/SKILL.md +2 -0
- package/sprint/00-idea.md +0 -0
- package/sprint/01-architecture.md +0 -0
- package/sprint/02-oss-evidence.md +0 -0
- package/sprint/03-logic.md +0 -0
- package/sprint/04-security.md +0 -0
- package/sprint/05-dx-ux.md +0 -0
- package/sprint/06-build-plan.md +0 -0
- package/sprint/07-release.md +0 -0
- package/src/agents.js +279 -0
- package/src/bootstrap.js +268 -62
- package/src/build-gate.js +2 -2
- package/src/codex-app-server.js +291 -0
- package/src/decision-log.js +0 -0
- package/src/fs-utils.js +175 -4
- package/src/launcher.js +28 -21
- package/src/mcp-config.js +9 -1
- package/src/mcp-live-client.js +422 -3
- package/src/paths.js +10 -1
- package/src/policy.js +3 -3
- package/src/prelaunch.js +189 -0
- package/src/quality/ai-quality-orchestrator.js +328 -0
- package/src/release-issue-gates.js +252 -0
- package/src/release-operations.js +62 -0
- package/src/release-state.js +0 -0
- package/src/runtime/active-autonomy-core.js +208 -0
- package/src/runtime/agent-compat.js +31 -0
- package/src/runtime/alignment-sentinel.js +165 -0
- package/src/runtime/architect-review.js +326 -0
- package/src/runtime/autonomous-tasks.js +408 -0
- package/src/runtime/build-readiness.js +28 -4
- package/src/runtime/code-graph-rehearse.js +113 -0
- package/src/runtime/codeburn-core.js +112 -0
- package/src/runtime/context-authority.js +40 -0
- package/src/runtime/context-economy-core.js +316 -0
- package/src/runtime/continuity-graph.js +201 -0
- package/src/runtime/continuity-notes.js +71 -26
- package/src/runtime/core-source-ingest.js +379 -0
- package/src/runtime/detached-provider.js +142 -0
- package/src/runtime/doctor-report.js +18 -0
- package/src/runtime/environment-awareness-core.js +460 -0
- package/src/runtime/exposure-catalog.js +276 -0
- package/src/runtime/graphify-core.js +167 -0
- package/src/runtime/guidance-stack.js +9 -2
- package/src/runtime/handoff-packets.js +92 -0
- package/src/runtime/headroom-core.js +104 -0
- package/src/runtime/helper-orchestration-core.js +307 -0
- package/src/runtime/hook-profiles.js +20 -0
- package/src/runtime/learning-loop-core.js +344 -0
- package/src/runtime/live-agent-verification.js +122 -0
- package/src/runtime/maestro-events.js +18 -0
- package/src/runtime/maestro-manager.js +19 -0
- package/src/runtime/maestro-output.js +58 -0
- package/src/runtime/maestro-state.js +125 -0
- package/src/runtime/managed-markdown.js +29 -0
- package/src/runtime/mcp-authority.js +147 -0
- package/src/runtime/mcp-policy.js +192 -0
- package/src/runtime/obsidian-integration-core.js +1089 -0
- package/src/runtime/obsidian-plugin-bridge.js +1045 -0
- package/src/runtime/pi-maestro-core.js +89 -0
- package/src/runtime/preferences.js +79 -0
- package/src/runtime/project-context.js +636 -0
- package/src/runtime/prompt-strategy-core.js +230 -0
- package/src/runtime/quorum-review.js +90 -0
- package/src/runtime/ralph-execution-core.js +217 -0
- package/src/runtime/redaction-gateway.js +373 -0
- package/src/runtime/runtime-state.js +935 -2
- package/src/runtime/schema-migrations.js +162 -0
- package/src/runtime/semantic-recording-core.js +147 -0
- package/src/runtime/signal-hooks.js +28 -0
- package/src/runtime/skills-registry-export.js +754 -0
- package/src/runtime/task-contracts.js +83 -0
- package/src/runtime/universal-plugin-broker-core.js +898 -0
- package/src/runtime/workspace-intelligence-runtime.js +674 -0
- package/src/runtime/workspace-virtualizer.js +102 -0
- package/src/runtime-artifacts.js +225 -62
- package/src/setup-lifecycle.js +359 -0
- package/src/skill-frontmatter.js +78 -0
- package/src/skill-installer.js +226 -71
- package/src/skills.js +1166 -96
- package/src/state-sync.js +0 -0
- package/src/test-fixtures.js +543 -0
- package/src/tui/grid.js +67 -0
- package/src/tui/status-grid.js +23 -0
- package/support-bundle.json +114 -0
- package/templates/AGENTS.md +6 -2
- package/templates/catalog-manifest.json +0 -0
- package/templates/model-instructions/core.md +0 -0
- package/templates/model-instructions/release.md +0 -0
- package/templates/model-instructions/security.md +0 -0
- package/templates/quality/ai-quality-rules.yml +37 -0
- package/docs/assets/image/Screenshot(1).png +0 -0
- package/docs/assets/image/Screenshot(2).png +0 -0
- package/docs/assets/image/Screenshot(3).png +0 -0
- package/docs/assets/image/Screenshot(4).png +0 -0
- package/docs/assets/image/Screenshot(5).png +0 -0
- package/docs/assets/image/Screenshot(6).png +0 -0
- package/docs/assets/image/Screenshot(7).png +0 -0
- package/docs/assets/image/Screenshot(8).png +0 -0
- package/docs/assets/image/Screenshot(9).png +0 -0
- package/docs/assets/meta-architect-logo.png +0 -0
- package/docs/assets/meta-architect-logo.svg +0 -8
- package/docs/onboarding.md +0 -65
- package/docs/skills-publishing.md +0 -255
package/mcp/local/code-intel.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
152
|
+
if (!ignoredDirs.has(entry.name)) {
|
|
153
|
+
await walkFallback(rootDir, absolutePath, files, limit, patterns);
|
|
26
154
|
}
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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 >=
|
|
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
|
-
|
|
62
|
-
|
|
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
|
|
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
|
|
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,
|
|
80
|
-
const end = Math.min(lines.length,
|
|
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}
|
|
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
|
+
}
|
package/mcp/local/memory.js
CHANGED
|
@@ -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
|
|
35
|
-
...options,
|
|
36
|
-
|
|
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
|
+
}
|
package/mcp/local/state.js
CHANGED
|
@@ -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
|
|
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
|
|
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}`);
|
package/mcp/local/team-run.js
CHANGED
|
@@ -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 = [
|
|
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
|
-
|
|
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
|
|
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
|
}
|