@jstn-sdk/ma 0.1.12 → 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.
- package/.agents/plugins/marketplace.json +0 -0
- package/.codex/agents/Architect.toml +0 -2
- package/.codex/agents/Auditor.toml +0 -2
- package/.codex/agents/Builder.toml +0 -2
- package/.codex/agents/Flow.toml +0 -2
- package/.codex/agents/Sage.toml +0 -2
- package/.codex/agents/Vibe.toml +0 -2
- package/.codex/hooks.json +14 -13
- package/.codex/prompts/enforcement.md +4 -0
- package/.codex/prompts/skill-contract.md +4 -0
- package/COVERAGE.md +211 -0
- package/DEMO.md +265 -0
- package/README.md +159 -16
- package/bin/ma.js +219 -14
- 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 +4 -1
- 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 -0
- package/docs/getting-started.md +38 -22
- package/docs/installed-sdk.md +0 -0
- package/docs/mcp-setup.md +35 -0
- package/docs/onboarding.md +18 -2
- package/docs/prompt-guidance-contract.md +17 -0
- package/docs/prompt-guidance-fragments/active-autonomy-core.md +7 -0
- package/docs/qa/release-issue-gates-0.1.13.json +644 -0
- package/docs/qa/{release-readiness-0.1.12.md → release-readiness-0.1.13.md} +25 -9
- package/docs/qa/release-readiness-0.1.5.md +1 -1
- package/docs/reference/native-engineering-patterns.md +0 -0
- package/docs/reference/native-security-playbooks.md +6 -0
- package/docs/reference/native-source-selection.md +6 -0
- package/docs/reference/native-style-and-deslop.md +0 -0
- package/docs/release-spec.md +25 -9
- package/docs/skills.md +14 -1
- package/index.js +222 -2
- package/mcp/collections.json +23 -6
- package/mcp/local/code-intel.js +0 -0
- package/mcp/local/memory.js +0 -0
- package/mcp/local/playbooks.js +0 -0
- package/mcp/local/state.js +0 -0
- package/mcp/local/team-run.js +48 -1
- package/mcp/local/trace.js +0 -0
- package/mcp/local-capabilities.json +0 -0
- package/mcp/native-playbooks.json +0 -0
- package/mcp/servers.json +24 -0
- package/package.json +5 -2
- 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 +401 -0
- package/plugins/meta-architect/obsidian/manifest.json +9 -0
- package/plugins/meta-architect/obsidian/styles.css +7 -0
- package/plugins/meta-architect/skills/align/SKILL.md +0 -0
- package/plugins/meta-architect/skills/align/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/align/references/shared-language.md +0 -0
- package/plugins/meta-architect/skills/arch/SKILL.md +2 -0
- package/plugins/meta-architect/skills/arch/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/build/SKILL.md +3 -0
- package/plugins/meta-architect/skills/build/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/cleanup/SKILL.md +0 -0
- package/plugins/meta-architect/skills/cleanup/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/cleanup/references/style-and-deslop.md +0 -0
- package/plugins/meta-architect/skills/diagnose/SKILL.md +0 -0
- package/plugins/meta-architect/skills/diagnose/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/flow/SKILL.md +2 -0
- package/plugins/meta-architect/skills/flow/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/maestro/SKILL.md +0 -0
- package/plugins/meta-architect/skills/maestro/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/maestro/references/core-release-rules.md +0 -0
- package/plugins/meta-architect/skills/maestro/references/native-ingest-map.md +0 -0
- package/plugins/meta-architect/skills/sage/SKILL.md +2 -0
- package/plugins/meta-architect/skills/sage/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/sage/references/source-selection.md +8 -0
- package/plugins/meta-architect/skills/tdd/SKILL.md +0 -0
- package/plugins/meta-architect/skills/tdd/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/vet/SKILL.md +2 -0
- package/plugins/meta-architect/skills/vet/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/vet/references/security-playbooks.md +7 -0
- package/plugins/meta-architect/skills/vibe/SKILL.md +2 -0
- package/plugins/meta-architect/skills/vibe/agents/openai.yaml +0 -0
- package/scripts/active-autonomy-hook.mjs +96 -0
- package/scripts/biome-staged.js +0 -0
- package/scripts/build-linux-packages.mjs +0 -0
- package/scripts/doctor.js +17 -4
- package/scripts/install.sh +28 -0
- package/scripts/linux-package-lib.mjs +0 -0
- package/scripts/linux-package-smoke.mjs +0 -0
- package/scripts/plugin-sync.js +0 -0
- package/scripts/postinstall.js +0 -0
- package/scripts/ralph/prompt.md +35 -0
- package/scripts/release-metadata.js +0 -0
- package/scripts/release-sync.js +0 -0
- package/scripts/release-verify.js +166 -1
- package/scripts/setup-npmrc.js +0 -0
- package/skills/align/SKILL.md +0 -0
- package/skills/align/agents/openai.yaml +0 -0
- package/skills/align/references/shared-language.md +0 -0
- package/skills/arch/SKILL.md +2 -0
- package/skills/arch/agents/openai.yaml +0 -0
- package/skills/build/SKILL.md +3 -0
- package/skills/build/agents/openai.yaml +0 -0
- package/skills/cleanup/SKILL.md +0 -0
- package/skills/cleanup/agents/openai.yaml +0 -0
- package/skills/cleanup/references/style-and-deslop.md +0 -0
- package/skills/diagnose/SKILL.md +0 -0
- package/skills/diagnose/agents/openai.yaml +0 -0
- package/skills/flow/SKILL.md +2 -0
- package/skills/flow/agents/openai.yaml +0 -0
- package/skills/maestro/SKILL.md +0 -0
- package/skills/maestro/agents/openai.yaml +0 -0
- package/skills/maestro/references/core-release-rules.md +0 -0
- package/skills/maestro/references/native-ingest-map.md +0 -0
- package/skills/sage/SKILL.md +2 -0
- package/skills/sage/agents/openai.yaml +0 -0
- package/skills/sage/references/source-selection.md +8 -0
- package/skills/tdd/SKILL.md +0 -0
- package/skills/tdd/agents/openai.yaml +0 -0
- package/skills/vet/SKILL.md +2 -0
- package/skills/vet/agents/openai.yaml +0 -0
- package/skills/vet/references/security-playbooks.md +7 -0
- package/skills/vibe/SKILL.md +2 -0
- package/skills/vibe/agents/openai.yaml +0 -0
- package/src/bootstrap.js +17 -0
- package/src/build-gate.js +2 -2
- package/src/launcher.js +4 -0
- package/src/mcp-live-client.js +289 -3
- package/src/paths.js +4 -0
- package/src/policy.js +1 -1
- package/src/release-issue-gates.js +190 -0
- package/src/runtime/active-autonomy-core.js +208 -0
- package/src/runtime/alignment-sentinel.js +165 -0
- package/src/runtime/architect-review.js +88 -0
- package/src/runtime/build-readiness.js +28 -4
- package/src/runtime/code-graph-rehearse.js +113 -0
- package/src/runtime/context-economy-core.js +276 -0
- package/src/runtime/continuity-notes.js +0 -0
- package/src/runtime/core-source-ingest.js +379 -0
- package/src/runtime/detached-provider.js +74 -0
- package/src/runtime/environment-awareness-core.js +460 -0
- package/src/runtime/exposure-catalog.js +276 -0
- package/src/runtime/guidance-stack.js +0 -0
- package/src/runtime/helper-orchestration-core.js +307 -0
- package/src/runtime/learning-loop-core.js +238 -0
- package/src/runtime/maestro-events.js +18 -0
- package/src/runtime/maestro-manager.js +19 -0
- package/src/runtime/maestro-state.js +125 -0
- package/src/runtime/mcp-policy.js +192 -0
- package/src/runtime/obsidian-integration-core.js +851 -0
- package/src/runtime/obsidian-plugin-bridge.js +739 -0
- package/src/runtime/orchestrator.js +0 -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 +174 -0
- package/src/runtime/runtime-state.js +843 -2
- package/src/runtime/semantic-recording-core.js +147 -0
- package/src/runtime/signal-hooks.js +15 -0
- package/src/runtime/skills-registry-export.js +670 -0
- package/src/runtime/startup-path.js +0 -0
- package/src/runtime/universal-plugin-broker-core.js +575 -0
- package/src/runtime/workspace-intelligence-runtime.js +674 -0
- package/src/runtime/workspace-virtualizer.js +102 -0
- package/src/runtime/workspaces.js +0 -0
- package/src/runtime-artifacts.js +225 -62
- package/src/skill-installer.js +0 -0
- package/src/skills.js +876 -84
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { ensureDir, readJson, writeFileIfMissing } from "../fs-utils.js";
|
|
2
|
+
import { getRuntimeSubsystemPath } from "../paths.js";
|
|
3
|
+
|
|
4
|
+
export const contextEconomySchemaVersion = "0.1.0";
|
|
5
|
+
|
|
6
|
+
const safetySignals = [
|
|
7
|
+
"security warning",
|
|
8
|
+
"irreversible",
|
|
9
|
+
"destructive",
|
|
10
|
+
"credential",
|
|
11
|
+
"verification failed",
|
|
12
|
+
"validation failed",
|
|
13
|
+
"user confused",
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const removableWords = new Set([
|
|
17
|
+
"a",
|
|
18
|
+
"an",
|
|
19
|
+
"the",
|
|
20
|
+
"just",
|
|
21
|
+
"really",
|
|
22
|
+
"basically",
|
|
23
|
+
"sure",
|
|
24
|
+
"happy",
|
|
25
|
+
"glad",
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
export function getContextEconomyCorePath() {
|
|
29
|
+
return getRuntimeSubsystemPath("context", "context-economy-core.json");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function createDefaultContextEconomyCore() {
|
|
33
|
+
return {
|
|
34
|
+
schemaVersion: contextEconomySchemaVersion,
|
|
35
|
+
product: "Meta-Architect",
|
|
36
|
+
purpose:
|
|
37
|
+
"MA-owned default context compression policy for terse progress, summaries, MCP descriptors, and exported host payloads.",
|
|
38
|
+
style: "compact_technical",
|
|
39
|
+
evidence_source: {
|
|
40
|
+
repo: "JuliusBrussee/caveman",
|
|
41
|
+
endpoint: "https://gitmcp.io/JuliusBrussee/caveman",
|
|
42
|
+
applied_as: "MA Context Economy Core",
|
|
43
|
+
adoption_boundary:
|
|
44
|
+
"Use the compression and auto-clarity contract as core MA behavior without adopting third-party persona branding.",
|
|
45
|
+
},
|
|
46
|
+
applies_to: [
|
|
47
|
+
"$maestro",
|
|
48
|
+
"gated_lanes",
|
|
49
|
+
"helper_skills",
|
|
50
|
+
"executor_roles",
|
|
51
|
+
"reviewer_roles",
|
|
52
|
+
"verifier_roles",
|
|
53
|
+
"team_workers",
|
|
54
|
+
"mcp_descriptors",
|
|
55
|
+
"exported_host_payloads",
|
|
56
|
+
],
|
|
57
|
+
preserve_exact: [
|
|
58
|
+
"code_blocks",
|
|
59
|
+
"commands",
|
|
60
|
+
"schemas",
|
|
61
|
+
"security_warnings",
|
|
62
|
+
"authority_fields",
|
|
63
|
+
"verification_failures",
|
|
64
|
+
"known_gaps",
|
|
65
|
+
"commit_messages",
|
|
66
|
+
],
|
|
67
|
+
safety_valves: safetySignals,
|
|
68
|
+
compression_rules: [
|
|
69
|
+
"Remove filler and pleasantries from progress, summaries, and descriptors.",
|
|
70
|
+
"Fragments are allowed when meaning stays exact.",
|
|
71
|
+
"Keep technical nouns, commands, paths, code, schemas, evidence status, and blockers exact.",
|
|
72
|
+
"Use auto-clarity bypass for safety warnings, irreversible actions, failed checks, credentials, and confused users.",
|
|
73
|
+
],
|
|
74
|
+
hard_rules: [
|
|
75
|
+
"Context Economy is default MA behavior, not a third-party persona.",
|
|
76
|
+
"Technical terms, code, schemas, commands, warnings, and evidence boundaries stay exact.",
|
|
77
|
+
"Disable compression when safety, authority, or user clarity would degrade.",
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function compactPlainText(text) {
|
|
83
|
+
return text
|
|
84
|
+
.split(/\s+/)
|
|
85
|
+
.filter((word) => !removableWords.has(word.toLowerCase().replace(/[^a-z]/g, "")))
|
|
86
|
+
.join(" ")
|
|
87
|
+
.replace(/\s+([,.;:!?])/g, "$1")
|
|
88
|
+
.trim();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function shouldBypassContextEconomy({ text = "", tags = [] } = {}) {
|
|
92
|
+
const haystack = `${text} ${tags.join(" ")}`.toLowerCase();
|
|
93
|
+
return safetySignals.some((signal) => haystack.includes(signal));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function createContextEconomyView({ text, tags = [], level = "core" }) {
|
|
97
|
+
if (typeof text !== "string") {
|
|
98
|
+
throw new Error("Context Economy view requires text");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const bypassed = shouldBypassContextEconomy({ text, tags });
|
|
102
|
+
const parts = text.split(/(```[\s\S]*?```)/g);
|
|
103
|
+
const output = bypassed
|
|
104
|
+
? text
|
|
105
|
+
: parts
|
|
106
|
+
.map((part) => (part.startsWith("```") ? part : compactPlainText(part)))
|
|
107
|
+
.join("")
|
|
108
|
+
.trim();
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
record_type: "context_economy_view",
|
|
112
|
+
level,
|
|
113
|
+
bypassed,
|
|
114
|
+
records_as: "context_budget",
|
|
115
|
+
original_chars: text.length,
|
|
116
|
+
output_chars: output.length,
|
|
117
|
+
output,
|
|
118
|
+
preserved_exact: ["code_blocks", "commands", "warnings", "schemas", "authority_fields"],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function createContextEconomyPayload({
|
|
123
|
+
surface = "$maestro",
|
|
124
|
+
payload = {},
|
|
125
|
+
tags = [],
|
|
126
|
+
level = "core",
|
|
127
|
+
} = {}) {
|
|
128
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
129
|
+
throw new Error("Context Economy payload requires an object payload");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const transformed = {};
|
|
133
|
+
const fields = [];
|
|
134
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
135
|
+
const result = transformContextEconomyValue({ key, value, tags, level });
|
|
136
|
+
transformed[key] = result.value;
|
|
137
|
+
fields.push({
|
|
138
|
+
key,
|
|
139
|
+
compressed: result.compressed,
|
|
140
|
+
bypassed: result.bypassed,
|
|
141
|
+
original_chars: result.originalChars,
|
|
142
|
+
output_chars: result.outputChars,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
record_type: "context_economy_payload",
|
|
148
|
+
surface,
|
|
149
|
+
level,
|
|
150
|
+
records_as: "context_budget",
|
|
151
|
+
build_evidence: false,
|
|
152
|
+
payload: transformed,
|
|
153
|
+
fields,
|
|
154
|
+
preserved_exact: [
|
|
155
|
+
"code",
|
|
156
|
+
"commands",
|
|
157
|
+
"paths",
|
|
158
|
+
"schemas",
|
|
159
|
+
"authority_fields",
|
|
160
|
+
"security_warnings",
|
|
161
|
+
"verification_evidence",
|
|
162
|
+
],
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function createMcpDescriptorEconomy({ name, description, inputSchema = {}, tags = [] }) {
|
|
167
|
+
if (!name || typeof name !== "string") {
|
|
168
|
+
throw new Error("MCP descriptor economy requires a tool name");
|
|
169
|
+
}
|
|
170
|
+
const compacted = createContextEconomyView({
|
|
171
|
+
text: description ?? "",
|
|
172
|
+
tags,
|
|
173
|
+
level: "mcp_descriptor",
|
|
174
|
+
});
|
|
175
|
+
return {
|
|
176
|
+
record_type: "mcp_descriptor_economy",
|
|
177
|
+
records_as: "context_budget",
|
|
178
|
+
build_evidence: false,
|
|
179
|
+
name,
|
|
180
|
+
description: compacted.output,
|
|
181
|
+
inputSchema,
|
|
182
|
+
bypassed: compacted.bypassed,
|
|
183
|
+
preserved_exact: ["name", "inputSchema"],
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function validateContextEconomyCore(value) {
|
|
188
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
189
|
+
throw new Error("Context Economy core must be an object");
|
|
190
|
+
}
|
|
191
|
+
if (value.schemaVersion !== contextEconomySchemaVersion) {
|
|
192
|
+
throw new Error(`Unsupported Context Economy schemaVersion: ${value.schemaVersion}`);
|
|
193
|
+
}
|
|
194
|
+
if (value.evidence_source?.repo !== "JuliusBrussee/caveman") {
|
|
195
|
+
throw new Error("Context Economy core requires Caveman evidence source mapping");
|
|
196
|
+
}
|
|
197
|
+
for (const required of ["code_blocks", "security_warnings", "authority_fields"]) {
|
|
198
|
+
if (!value.preserve_exact?.includes(required)) {
|
|
199
|
+
throw new Error(`Context Economy core must preserve ${required}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (!value.compression_rules?.some((rule) => rule.includes("auto-clarity bypass"))) {
|
|
203
|
+
throw new Error("Context Economy core requires auto-clarity compression rule");
|
|
204
|
+
}
|
|
205
|
+
for (const signal of ["security warning", "irreversible", "verification failed"]) {
|
|
206
|
+
if (!value.safety_valves?.includes(signal)) {
|
|
207
|
+
throw new Error(`Context Economy core requires ${signal} safety valve`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (JSON.stringify(value).includes(".omx")) {
|
|
211
|
+
throw new Error("Context Economy core must not expose OMX runtime paths");
|
|
212
|
+
}
|
|
213
|
+
return value;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function transformContextEconomyValue({ key, value, tags, level }) {
|
|
217
|
+
if (typeof value !== "string") {
|
|
218
|
+
return {
|
|
219
|
+
value,
|
|
220
|
+
compressed: false,
|
|
221
|
+
bypassed: false,
|
|
222
|
+
originalChars: 0,
|
|
223
|
+
outputChars: 0,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
if (shouldPreserveField(key)) {
|
|
227
|
+
return {
|
|
228
|
+
value,
|
|
229
|
+
compressed: false,
|
|
230
|
+
bypassed: true,
|
|
231
|
+
originalChars: value.length,
|
|
232
|
+
outputChars: value.length,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
const view = createContextEconomyView({ text: value, tags, level });
|
|
236
|
+
return {
|
|
237
|
+
value: view.output,
|
|
238
|
+
compressed: !view.bypassed && view.output !== value,
|
|
239
|
+
bypassed: view.bypassed,
|
|
240
|
+
originalChars: view.original_chars,
|
|
241
|
+
outputChars: view.output_chars,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function shouldPreserveField(key) {
|
|
246
|
+
const normalized = key.toLowerCase();
|
|
247
|
+
return [
|
|
248
|
+
"code",
|
|
249
|
+
"command",
|
|
250
|
+
"commands",
|
|
251
|
+
"schema",
|
|
252
|
+
"inputschema",
|
|
253
|
+
"path",
|
|
254
|
+
"paths",
|
|
255
|
+
"authority",
|
|
256
|
+
"authority_boundary",
|
|
257
|
+
"security_warning",
|
|
258
|
+
"warning",
|
|
259
|
+
"verification_evidence",
|
|
260
|
+
"evidence",
|
|
261
|
+
"known_gap",
|
|
262
|
+
"known_gaps",
|
|
263
|
+
].includes(normalized);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export async function seedContextEconomyCoreArtifacts() {
|
|
267
|
+
await ensureDir(getRuntimeSubsystemPath("context"));
|
|
268
|
+
await writeFileIfMissing(
|
|
269
|
+
getContextEconomyCorePath(),
|
|
270
|
+
`${JSON.stringify(createDefaultContextEconomyCore(), null, 2)}\n`,
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export async function loadContextEconomyCore() {
|
|
275
|
+
return validateContextEconomyCore(await readJson(getContextEconomyCorePath()));
|
|
276
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { ensureDir, readJson, writeFileIfMissing, writeJson } from "../fs-utils.js";
|
|
6
|
+
import { getRepoRoot, getRuntimeSubsystemPath, getRuntimeWritePath } from "../paths.js";
|
|
7
|
+
|
|
8
|
+
export const coreSourceIngestSchemaVersion = "0.1.0";
|
|
9
|
+
|
|
10
|
+
export const coreSourceDefinitions = [
|
|
11
|
+
{
|
|
12
|
+
id: "obsidian-api",
|
|
13
|
+
capability: "obsidian_integration_core",
|
|
14
|
+
source_type: "external_clone",
|
|
15
|
+
repo: "obsidianmd/obsidian-api",
|
|
16
|
+
source_url: "https://github.com/obsidianmd/obsidian-api.git",
|
|
17
|
+
local_dir: "obsidian-api",
|
|
18
|
+
semantic_role: "obsidian_api_contract",
|
|
19
|
+
records_as: "core_source_snapshot",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "obsidian-sample-plugin",
|
|
23
|
+
capability: "obsidian_integration_core",
|
|
24
|
+
source_type: "external_clone",
|
|
25
|
+
repo: "obsidianmd/obsidian-sample-plugin",
|
|
26
|
+
source_url: "https://github.com/obsidianmd/obsidian-sample-plugin.git",
|
|
27
|
+
local_dir: "obsidian-sample-plugin",
|
|
28
|
+
semantic_role: "obsidian_plugin_compatibility_reference",
|
|
29
|
+
records_as: "core_source_snapshot",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: "caveman",
|
|
33
|
+
capability: "context_economy_core",
|
|
34
|
+
source_type: "external_clone",
|
|
35
|
+
repo: "JuliusBrussee/caveman",
|
|
36
|
+
source_url: "https://github.com/JuliusBrussee/caveman.git",
|
|
37
|
+
local_dir: "caveman",
|
|
38
|
+
semantic_role: "context_compression_contract",
|
|
39
|
+
records_as: "core_source_snapshot",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: "prompt-engineering",
|
|
43
|
+
capability: "prompt_strategy_core",
|
|
44
|
+
source_type: "external_clone",
|
|
45
|
+
repo: "NirDiamant/Prompt_Engineering",
|
|
46
|
+
source_url: "https://github.com/NirDiamant/Prompt_Engineering.git",
|
|
47
|
+
local_dir: "prompt-engineering",
|
|
48
|
+
semantic_role: "prompt_technique_catalog",
|
|
49
|
+
records_as: "core_source_snapshot",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "ralph-execution-core",
|
|
53
|
+
capability: "ralph_execution_core",
|
|
54
|
+
source_type: "ma_owned_core",
|
|
55
|
+
repo: "meta-architect/ralph-execution-core",
|
|
56
|
+
source_url: null,
|
|
57
|
+
local_dir: null,
|
|
58
|
+
semantic_role: "story_execution_contract",
|
|
59
|
+
records_as: "ma_core_contract",
|
|
60
|
+
local_paths: ["src/runtime/ralph-execution-core.js", "scripts/ralph/prompt.md"],
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
export function getCoreSourcesRootPath() {
|
|
65
|
+
return getRuntimeWritePath("core-sources");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function getCoreSourceIngestPath() {
|
|
69
|
+
return getRuntimeSubsystemPath("context", "core-source-ingest.json");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function createDefaultCoreSourceIngest() {
|
|
73
|
+
return {
|
|
74
|
+
schemaVersion: coreSourceIngestSchemaVersion,
|
|
75
|
+
record_type: "core_source_ingest",
|
|
76
|
+
product: "Meta-Architect",
|
|
77
|
+
purpose:
|
|
78
|
+
"Canonical MA core source snapshots. External projects are cloned/cached once, then MA reads local snapshots instead of fetching on every run.",
|
|
79
|
+
runtime_fetch_required: false,
|
|
80
|
+
refresh_policy: "manual_refresh_via_ma_core_ingest",
|
|
81
|
+
records_as: "core_source_snapshot",
|
|
82
|
+
sources: coreSourceDefinitions.map((definition) => ({
|
|
83
|
+
id: definition.id,
|
|
84
|
+
capability: definition.capability,
|
|
85
|
+
source_type: definition.source_type,
|
|
86
|
+
repo: definition.repo,
|
|
87
|
+
source_url: definition.source_url,
|
|
88
|
+
semantic_role: definition.semantic_role,
|
|
89
|
+
records_as: definition.records_as,
|
|
90
|
+
status: "NOT_INGESTED",
|
|
91
|
+
local_path: definition.local_dir
|
|
92
|
+
? path.join(".ma", "core-sources", definition.local_dir)
|
|
93
|
+
: null,
|
|
94
|
+
file_count: 0,
|
|
95
|
+
content_sha256: null,
|
|
96
|
+
git_commit: null,
|
|
97
|
+
sampled_files: [],
|
|
98
|
+
sample_text: null,
|
|
99
|
+
})),
|
|
100
|
+
hard_rules: [
|
|
101
|
+
"Obsidian, Caveman, Prompt Strategy, and Ralph are MA semantic cores, not MCP servers.",
|
|
102
|
+
"GitMCP or git remotes may refresh provenance, but core behavior reads local source snapshots.",
|
|
103
|
+
"Obsidian-derived claims remain vault_context unless promoted by an owning MA lane.",
|
|
104
|
+
"Ralph Execution Core is MA-owned and cannot mutate release or decision state directly.",
|
|
105
|
+
],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function validateCoreSourceIngest(value) {
|
|
110
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
111
|
+
throw new Error("core source ingest must be an object");
|
|
112
|
+
}
|
|
113
|
+
if (value.schemaVersion !== coreSourceIngestSchemaVersion) {
|
|
114
|
+
throw new Error(`Unsupported core source ingest schemaVersion: ${value.schemaVersion}`);
|
|
115
|
+
}
|
|
116
|
+
if (value.record_type !== "core_source_ingest") {
|
|
117
|
+
throw new Error("core source ingest requires record_type=core_source_ingest");
|
|
118
|
+
}
|
|
119
|
+
if (value.runtime_fetch_required !== false) {
|
|
120
|
+
throw new Error("core source ingest must not require runtime fetches");
|
|
121
|
+
}
|
|
122
|
+
if (!Array.isArray(value.sources) || value.sources.length < coreSourceDefinitions.length) {
|
|
123
|
+
throw new Error("core source ingest requires all core source entries");
|
|
124
|
+
}
|
|
125
|
+
const byId = new Map(value.sources.map((source) => [source.id, source]));
|
|
126
|
+
for (const definition of coreSourceDefinitions) {
|
|
127
|
+
const source = byId.get(definition.id);
|
|
128
|
+
if (!source) {
|
|
129
|
+
throw new Error(`core source ingest missing ${definition.id}`);
|
|
130
|
+
}
|
|
131
|
+
if (source.capability !== definition.capability) {
|
|
132
|
+
throw new Error(`core source ${definition.id} capability mismatch`);
|
|
133
|
+
}
|
|
134
|
+
if (source.records_as !== definition.records_as) {
|
|
135
|
+
throw new Error(`core source ${definition.id} records_as mismatch`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export async function seedCoreSourceIngestArtifacts() {
|
|
142
|
+
await ensureDir(getRuntimeSubsystemPath("context"));
|
|
143
|
+
await writeFileIfMissing(
|
|
144
|
+
getCoreSourceIngestPath(),
|
|
145
|
+
`${JSON.stringify(createDefaultCoreSourceIngest(), null, 2)}\n`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export async function loadCoreSourceIngest() {
|
|
150
|
+
return validateCoreSourceIngest(await readJson(getCoreSourceIngestPath()));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export async function ingestCoreSources({
|
|
154
|
+
definitions = coreSourceDefinitions,
|
|
155
|
+
refresh = false,
|
|
156
|
+
spawnImpl = spawn,
|
|
157
|
+
} = {}) {
|
|
158
|
+
await ensureDir(getCoreSourcesRootPath());
|
|
159
|
+
const sources = [];
|
|
160
|
+
const errors = [];
|
|
161
|
+
|
|
162
|
+
for (const definition of definitions) {
|
|
163
|
+
try {
|
|
164
|
+
if (definition.source_type === "ma_owned_core") {
|
|
165
|
+
sources.push(await inspectMaOwnedCore(definition));
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const localPath = path.join(getCoreSourcesRootPath(), definition.local_dir);
|
|
170
|
+
if (refresh) {
|
|
171
|
+
await fs.rm(localPath, { recursive: true, force: true });
|
|
172
|
+
}
|
|
173
|
+
if (!(await pathExists(localPath))) {
|
|
174
|
+
await cloneSource(definition.source_url, localPath, spawnImpl);
|
|
175
|
+
}
|
|
176
|
+
sources.push(await inspectExternalCore(definition, localPath));
|
|
177
|
+
} catch (error) {
|
|
178
|
+
errors.push({ id: definition.id, repo: definition.repo, error: error.message });
|
|
179
|
+
sources.push(createFailedSource(definition, error));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const manifest = validateCoreSourceIngest({
|
|
184
|
+
...createDefaultCoreSourceIngest(),
|
|
185
|
+
ingested_at: new Date().toISOString(),
|
|
186
|
+
status: errors.length === 0 ? "READY" : "PARTIAL",
|
|
187
|
+
sources,
|
|
188
|
+
errors,
|
|
189
|
+
});
|
|
190
|
+
await writeJson(getCoreSourceIngestPath(), manifest);
|
|
191
|
+
return manifest;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function findIngestedCoreSourceForRepo(manifest, repo) {
|
|
195
|
+
if (!manifest?.sources || !repo) return null;
|
|
196
|
+
return manifest.sources.find(
|
|
197
|
+
(source) => source.repo === repo && ["INGESTED", "LOCAL_CORE"].includes(source.status),
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function inspectExternalCore(definition, localPath) {
|
|
202
|
+
const files = await listSourceFiles(localPath);
|
|
203
|
+
const sampledFiles = chooseSampleFiles(files);
|
|
204
|
+
const sampleText = await readSampleText(localPath, sampledFiles);
|
|
205
|
+
const gitCommit = await readGitCommit(localPath);
|
|
206
|
+
return {
|
|
207
|
+
id: definition.id,
|
|
208
|
+
capability: definition.capability,
|
|
209
|
+
source_type: definition.source_type,
|
|
210
|
+
repo: definition.repo,
|
|
211
|
+
source_url: definition.source_url,
|
|
212
|
+
semantic_role: definition.semantic_role,
|
|
213
|
+
records_as: definition.records_as,
|
|
214
|
+
status: "INGESTED",
|
|
215
|
+
local_path: path.relative(getRepoRoot(), localPath),
|
|
216
|
+
file_count: files.length,
|
|
217
|
+
content_sha256: hashFileList(files),
|
|
218
|
+
git_commit: gitCommit,
|
|
219
|
+
sampled_files: sampledFiles,
|
|
220
|
+
sample_text: sampleText,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function inspectMaOwnedCore(definition) {
|
|
225
|
+
const repoRoot = getRepoRoot();
|
|
226
|
+
const files = [];
|
|
227
|
+
for (const relativePath of definition.local_paths ?? []) {
|
|
228
|
+
const absolutePath = path.join(repoRoot, relativePath);
|
|
229
|
+
if (await pathExists(absolutePath)) {
|
|
230
|
+
const stat = await fs.stat(absolutePath);
|
|
231
|
+
files.push({
|
|
232
|
+
relative_path: relativePath,
|
|
233
|
+
size: stat.size,
|
|
234
|
+
mtimeMs: Math.floor(stat.mtimeMs),
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const samplePath = files[0]?.relative_path;
|
|
239
|
+
const sampleText = samplePath
|
|
240
|
+
? (await fs.readFile(path.join(repoRoot, samplePath), "utf8")).slice(0, 600)
|
|
241
|
+
: null;
|
|
242
|
+
return {
|
|
243
|
+
id: definition.id,
|
|
244
|
+
capability: definition.capability,
|
|
245
|
+
source_type: definition.source_type,
|
|
246
|
+
repo: definition.repo,
|
|
247
|
+
source_url: null,
|
|
248
|
+
semantic_role: definition.semantic_role,
|
|
249
|
+
records_as: definition.records_as,
|
|
250
|
+
status: "LOCAL_CORE",
|
|
251
|
+
local_path: null,
|
|
252
|
+
file_count: files.length,
|
|
253
|
+
content_sha256: hashFileList(files),
|
|
254
|
+
git_commit: null,
|
|
255
|
+
sampled_files: files.map((file) => file.relative_path),
|
|
256
|
+
sample_text: sampleText,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function createFailedSource(definition, error) {
|
|
261
|
+
return {
|
|
262
|
+
id: definition.id,
|
|
263
|
+
capability: definition.capability,
|
|
264
|
+
source_type: definition.source_type,
|
|
265
|
+
repo: definition.repo,
|
|
266
|
+
source_url: definition.source_url,
|
|
267
|
+
semantic_role: definition.semantic_role,
|
|
268
|
+
records_as: definition.records_as,
|
|
269
|
+
status: "FAILED",
|
|
270
|
+
local_path: definition.local_dir
|
|
271
|
+
? path.join(".ma", "core-sources", definition.local_dir)
|
|
272
|
+
: null,
|
|
273
|
+
file_count: 0,
|
|
274
|
+
content_sha256: null,
|
|
275
|
+
git_commit: null,
|
|
276
|
+
sampled_files: [],
|
|
277
|
+
sample_text: null,
|
|
278
|
+
error: error.message,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function cloneSource(sourceUrl, localPath, spawnImpl) {
|
|
283
|
+
await ensureDir(path.dirname(localPath));
|
|
284
|
+
await new Promise((resolve, reject) => {
|
|
285
|
+
const child = spawnImpl("git", ["clone", "--depth", "1", sourceUrl, localPath], {
|
|
286
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
287
|
+
});
|
|
288
|
+
let stderr = "";
|
|
289
|
+
child.stderr.on("data", (chunk) => {
|
|
290
|
+
stderr = `${stderr}${chunk}`.slice(-2000);
|
|
291
|
+
});
|
|
292
|
+
child.on("error", reject);
|
|
293
|
+
child.on("exit", (code) => {
|
|
294
|
+
if (code === 0) {
|
|
295
|
+
resolve();
|
|
296
|
+
} else {
|
|
297
|
+
reject(new Error(`git clone failed for ${sourceUrl}: ${stderr.trim() || `exit ${code}`}`));
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function listSourceFiles(rootPath) {
|
|
304
|
+
const files = [];
|
|
305
|
+
async function walk(current) {
|
|
306
|
+
const entries = await fs.readdir(current, { withFileTypes: true });
|
|
307
|
+
for (const entry of entries) {
|
|
308
|
+
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
309
|
+
const absolutePath = path.join(current, entry.name);
|
|
310
|
+
if (entry.isDirectory()) {
|
|
311
|
+
await walk(absolutePath);
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (!entry.isFile()) continue;
|
|
315
|
+
const stat = await fs.stat(absolutePath);
|
|
316
|
+
files.push({
|
|
317
|
+
relative_path: path.relative(rootPath, absolutePath).split(path.sep).join("/"),
|
|
318
|
+
size: stat.size,
|
|
319
|
+
mtimeMs: Math.floor(stat.mtimeMs),
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
await walk(rootPath);
|
|
324
|
+
return files.sort((a, b) => a.relative_path.localeCompare(b.relative_path));
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function chooseSampleFiles(files) {
|
|
328
|
+
const preferred = [/^README\.md$/i, /^INSTALL\.md$/i, /^docs\//i, /SKILL\.md$/i, /prompt\.md$/i];
|
|
329
|
+
const selected = [];
|
|
330
|
+
for (const pattern of preferred) {
|
|
331
|
+
const match = files.find((file) => pattern.test(file.relative_path));
|
|
332
|
+
if (match && !selected.includes(match.relative_path)) {
|
|
333
|
+
selected.push(match.relative_path);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
for (const file of files) {
|
|
337
|
+
if (selected.length >= 4) break;
|
|
338
|
+
if (!selected.includes(file.relative_path) && /\.(md|ts|js|json)$/i.test(file.relative_path)) {
|
|
339
|
+
selected.push(file.relative_path);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return selected;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async function readSampleText(rootPath, sampledFiles) {
|
|
346
|
+
for (const relativePath of sampledFiles) {
|
|
347
|
+
try {
|
|
348
|
+
return (await fs.readFile(path.join(rootPath, relativePath), "utf8")).slice(0, 600);
|
|
349
|
+
} catch {}
|
|
350
|
+
}
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async function readGitCommit(localPath) {
|
|
355
|
+
try {
|
|
356
|
+
return (await fs.readFile(path.join(localPath, ".git", "shallow"), "utf8"))
|
|
357
|
+
.trim()
|
|
358
|
+
.split("\n")[0];
|
|
359
|
+
} catch {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function hashFileList(files) {
|
|
365
|
+
const hash = createHash("sha256");
|
|
366
|
+
for (const file of files) {
|
|
367
|
+
hash.update(`${file.relative_path}:${file.size}:${file.mtimeMs}\n`);
|
|
368
|
+
}
|
|
369
|
+
return hash.digest("hex");
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
async function pathExists(filePath) {
|
|
373
|
+
try {
|
|
374
|
+
await fs.access(filePath);
|
|
375
|
+
return true;
|
|
376
|
+
} catch {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
}
|