@jstn-sdk/ma 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +287 -37
- package/bin/ma.js +232 -71
- 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 +7 -1
- package/docs/getting-started.md +108 -37
- package/docs/installed-sdk.md +23 -0
- package/docs/mcp-setup.md +65 -1
- 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.13.md +116 -0
- package/docs/qa/release-readiness-0.1.5.md +1 -1
- package/docs/reference/native-engineering-patterns.md +35 -0
- package/docs/reference/native-security-playbooks.md +27 -0
- package/docs/reference/native-source-selection.md +27 -0
- package/docs/reference/native-style-and-deslop.md +20 -0
- package/docs/release-spec.md +37 -10
- package/docs/skills-publishing.md +25 -1
- package/docs/skills.md +37 -12
- package/index.js +222 -2
- package/mcp/collections.json +23 -6
- package/mcp/local/code-intel.js +113 -0
- package/mcp/local/memory.js +46 -0
- package/mcp/local/playbooks.js +168 -0
- package/mcp/local/state.js +71 -0
- package/mcp/local/team-run.js +113 -0
- package/mcp/local/trace.js +60 -0
- package/mcp/local-capabilities.json +56 -0
- package/mcp/native-playbooks.json +117 -0
- package/mcp/servers.json +34 -0
- package/package.json +8 -2
- package/plugins/meta-architect/.app.json +1 -1
- package/plugins/meta-architect/.codex-plugin/plugin.json +4 -4
- package/plugins/meta-architect/.mcp.json +1 -1
- package/plugins/meta-architect/README.md +34 -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 +24 -0
- package/plugins/meta-architect/skills/align/agents/openai.yaml +4 -0
- package/plugins/meta-architect/skills/align/references/shared-language.md +24 -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/cleanup/SKILL.md +23 -0
- package/plugins/meta-architect/skills/cleanup/agents/openai.yaml +4 -0
- package/plugins/meta-architect/skills/cleanup/references/style-and-deslop.md +18 -0
- package/plugins/meta-architect/skills/diagnose/SKILL.md +24 -0
- package/plugins/meta-architect/skills/diagnose/agents/openai.yaml +4 -0
- package/plugins/meta-architect/skills/flow/SKILL.md +2 -0
- package/plugins/meta-architect/skills/maestro/SKILL.md +36 -3
- package/plugins/meta-architect/skills/maestro/agents/openai.yaml +2 -2
- package/plugins/meta-architect/skills/maestro/references/native-ingest-map.md +44 -0
- package/plugins/meta-architect/skills/sage/SKILL.md +5 -1
- package/plugins/meta-architect/skills/sage/references/source-selection.md +36 -0
- package/plugins/meta-architect/skills/tdd/SKILL.md +24 -0
- package/plugins/meta-architect/skills/tdd/agents/openai.yaml +4 -0
- package/plugins/meta-architect/skills/vet/SKILL.md +5 -1
- package/plugins/meta-architect/skills/vet/references/security-playbooks.md +30 -0
- package/plugins/meta-architect/skills/vibe/SKILL.md +2 -0
- package/scripts/active-autonomy-hook.mjs +96 -0
- package/scripts/build-linux-packages.mjs +328 -0
- package/scripts/doctor.js +36 -4
- package/scripts/install.sh +28 -0
- package/scripts/linux-package-lib.mjs +40 -0
- package/scripts/linux-package-smoke.mjs +103 -0
- package/scripts/ralph/prompt.md +35 -0
- package/scripts/release-sync.js +13 -6
- package/scripts/release-verify.js +166 -1
- package/skills/align/SKILL.md +24 -0
- package/skills/align/agents/openai.yaml +4 -0
- package/skills/align/references/shared-language.md +24 -0
- package/skills/arch/SKILL.md +2 -0
- package/skills/build/SKILL.md +3 -0
- package/skills/cleanup/SKILL.md +23 -0
- package/skills/cleanup/agents/openai.yaml +4 -0
- package/skills/cleanup/references/style-and-deslop.md +18 -0
- package/skills/diagnose/SKILL.md +24 -0
- package/skills/diagnose/agents/openai.yaml +4 -0
- package/skills/flow/SKILL.md +2 -0
- package/skills/index.json +21 -6
- package/skills/maestro/SKILL.md +36 -3
- package/skills/maestro/agents/openai.yaml +2 -2
- package/skills/maestro/references/native-ingest-map.md +44 -0
- package/skills/sage/SKILL.md +5 -1
- package/skills/sage/references/source-selection.md +36 -0
- package/skills/tdd/SKILL.md +24 -0
- package/skills/tdd/agents/openai.yaml +4 -0
- package/skills/vet/SKILL.md +5 -1
- package/skills/vet/references/security-playbooks.md +30 -0
- package/skills/vibe/SKILL.md +2 -0
- package/src/bootstrap.js +141 -24
- package/src/build-gate.js +2 -2
- package/src/decision-log.js +12 -9
- package/src/launcher.js +4 -0
- package/src/mcp-config.js +130 -8
- package/src/mcp-live-client.js +289 -3
- package/src/paths.js +37 -1
- package/src/policy.js +1 -1
- package/src/release-issue-gates.js +190 -0
- package/src/release-state.js +30 -1
- 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 +62 -0
- package/src/runtime/code-graph-rehearse.js +113 -0
- package/src/runtime/context-economy-core.js +276 -0
- package/src/runtime/continuity-notes.js +79 -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 +42 -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 +605 -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 +158 -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 +1223 -0
- package/src/runtime/semantic-recording-core.js +147 -0
- package/src/runtime/signal-hooks.js +67 -0
- package/src/runtime/skills-registry-export.js +670 -0
- package/src/runtime/startup-path.js +14 -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 +25 -0
- package/src/runtime-artifacts.js +407 -84
- package/src/skill-installer.js +53 -5
- package/src/skills.js +1454 -78
- package/src/state-sync.js +51 -8
- package/docs/qa/release-readiness-0.1.11.md +0 -79
- package/plugins/meta-architect/skills/meta-architect/SKILL.md +0 -32
- package/plugins/meta-architect/skills/meta-architect/agents/openai.yaml +0 -4
- package/skills/meta-architect/SKILL.md +0 -32
- package/skills/meta-architect/agents/openai.yaml +0 -4
- /package/plugins/meta-architect/skills/{meta-architect → maestro}/references/core-release-rules.md +0 -0
- /package/skills/{meta-architect → maestro}/references/core-release-rules.md +0 -0
|
@@ -0,0 +1,670 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { ensureDir, readJson, writeFileIfMissing } from "../fs-utils.js";
|
|
6
|
+
import { getRepoRoot, getRuntimeSubsystemPath } from "../paths.js";
|
|
7
|
+
|
|
8
|
+
export const skillsRegistryExportSchemaVersion = "0.1.0";
|
|
9
|
+
export const canonicalSkillsDir = ".agents/skills";
|
|
10
|
+
|
|
11
|
+
const home = os.homedir();
|
|
12
|
+
const configHome = process.env.XDG_CONFIG_HOME || path.join(home, ".config");
|
|
13
|
+
const codexHome = process.env.CODEX_HOME?.trim() || path.join(home, ".codex");
|
|
14
|
+
const claudeHome = process.env.CLAUDE_CONFIG_DIR?.trim() || path.join(home, ".claude");
|
|
15
|
+
|
|
16
|
+
const universalTargets = [
|
|
17
|
+
"codex",
|
|
18
|
+
"opencode",
|
|
19
|
+
"cursor",
|
|
20
|
+
"amp",
|
|
21
|
+
"cline",
|
|
22
|
+
"gemini-cli",
|
|
23
|
+
"github-copilot",
|
|
24
|
+
"antigravity",
|
|
25
|
+
"deepagents",
|
|
26
|
+
"dexto",
|
|
27
|
+
"firebender",
|
|
28
|
+
"kimi-cli",
|
|
29
|
+
"warp",
|
|
30
|
+
"replit",
|
|
31
|
+
"universal",
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
const nonUniversalTargets = [
|
|
35
|
+
"claude-code",
|
|
36
|
+
"windsurf",
|
|
37
|
+
"goose",
|
|
38
|
+
"augment",
|
|
39
|
+
"aider-desk",
|
|
40
|
+
"continue",
|
|
41
|
+
"roo",
|
|
42
|
+
"kiro-cli",
|
|
43
|
+
"junie",
|
|
44
|
+
"devin",
|
|
45
|
+
"openhands",
|
|
46
|
+
"cortex",
|
|
47
|
+
"crush",
|
|
48
|
+
"forgecode",
|
|
49
|
+
"bob",
|
|
50
|
+
"codebuddy",
|
|
51
|
+
"codemaker",
|
|
52
|
+
"codestudio",
|
|
53
|
+
"command-code",
|
|
54
|
+
"codearts-agent",
|
|
55
|
+
"droid",
|
|
56
|
+
"hermes-agent",
|
|
57
|
+
"iflow-cli",
|
|
58
|
+
"kilo",
|
|
59
|
+
"kode",
|
|
60
|
+
"mcpjam",
|
|
61
|
+
"mistral-vibe",
|
|
62
|
+
"mux",
|
|
63
|
+
"neovate",
|
|
64
|
+
"openclaw",
|
|
65
|
+
"pi",
|
|
66
|
+
"pochi",
|
|
67
|
+
"adal",
|
|
68
|
+
"qoder",
|
|
69
|
+
"qwen-code",
|
|
70
|
+
"rovodev",
|
|
71
|
+
"tabnine-cli",
|
|
72
|
+
"trae",
|
|
73
|
+
"trae-cn",
|
|
74
|
+
"zencoder",
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
const nonUniversalDirs = {
|
|
78
|
+
"claude-code": ".claude/skills",
|
|
79
|
+
windsurf: ".windsurf/skills",
|
|
80
|
+
goose: ".goose/skills",
|
|
81
|
+
augment: ".augment/skills",
|
|
82
|
+
"aider-desk": ".aider-desk/skills",
|
|
83
|
+
continue: ".continue/skills",
|
|
84
|
+
roo: ".roo/skills",
|
|
85
|
+
"kiro-cli": ".kiro/skills",
|
|
86
|
+
junie: ".junie/skills",
|
|
87
|
+
devin: ".devin/skills",
|
|
88
|
+
openhands: ".openhands/skills",
|
|
89
|
+
cortex: ".cortex/skills",
|
|
90
|
+
crush: ".crush/skills",
|
|
91
|
+
forgecode: ".forge/skills",
|
|
92
|
+
bob: ".bob/skills",
|
|
93
|
+
codebuddy: ".codebuddy/skills",
|
|
94
|
+
codemaker: ".codemaker/skills",
|
|
95
|
+
codestudio: ".codestudio/skills",
|
|
96
|
+
"command-code": ".commandcode/skills",
|
|
97
|
+
"codearts-agent": ".codeartsdoer/skills",
|
|
98
|
+
droid: ".factory/skills",
|
|
99
|
+
"hermes-agent": ".hermes/skills",
|
|
100
|
+
"iflow-cli": ".iflow/skills",
|
|
101
|
+
kilo: ".kilocode/skills",
|
|
102
|
+
kode: ".kode/skills",
|
|
103
|
+
mcpjam: ".mcpjam/skills",
|
|
104
|
+
"mistral-vibe": ".vibe/skills",
|
|
105
|
+
mux: ".mux/skills",
|
|
106
|
+
neovate: ".neovate/skills",
|
|
107
|
+
openclaw: "skills",
|
|
108
|
+
pi: ".pi/agent/skills",
|
|
109
|
+
pochi: ".pochi/skills",
|
|
110
|
+
adal: ".adal/skills",
|
|
111
|
+
qoder: ".qoder/skills",
|
|
112
|
+
"qwen-code": ".qwen/skills",
|
|
113
|
+
rovodev: ".rovodev/skills",
|
|
114
|
+
"tabnine-cli": ".tabnine/agent/skills",
|
|
115
|
+
trae: ".trae/skills",
|
|
116
|
+
"trae-cn": ".trae/skills",
|
|
117
|
+
zencoder: ".zencoder/skills",
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const globalDirOverrides = {
|
|
121
|
+
codex: path.join(codexHome, "skills"),
|
|
122
|
+
opencode: path.join(configHome, "opencode", "skills"),
|
|
123
|
+
cursor: path.join(home, ".cursor", "skills"),
|
|
124
|
+
amp: path.join(configHome, "agents", "skills"),
|
|
125
|
+
"claude-code": path.join(claudeHome, "skills"),
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export const agentRegistry = Object.fromEntries([
|
|
129
|
+
...universalTargets.map((name) => [
|
|
130
|
+
name,
|
|
131
|
+
{
|
|
132
|
+
name,
|
|
133
|
+
displayName: name,
|
|
134
|
+
skillsDir: canonicalSkillsDir,
|
|
135
|
+
globalSkillsDir: globalDirOverrides[name] ?? path.join(home, ".agents", "skills"),
|
|
136
|
+
universal: true,
|
|
137
|
+
},
|
|
138
|
+
]),
|
|
139
|
+
...nonUniversalTargets.map((name) => [
|
|
140
|
+
name,
|
|
141
|
+
{
|
|
142
|
+
name,
|
|
143
|
+
displayName: name,
|
|
144
|
+
skillsDir: nonUniversalDirs[name],
|
|
145
|
+
globalSkillsDir: globalDirOverrides[name] ?? path.join(home, nonUniversalDirs[name]),
|
|
146
|
+
universal: false,
|
|
147
|
+
},
|
|
148
|
+
]),
|
|
149
|
+
]);
|
|
150
|
+
|
|
151
|
+
export function getSkillsRegistryExportPath() {
|
|
152
|
+
return getRuntimeSubsystemPath("context", "skills-registry-export.json");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function createDefaultSkillsRegistryExport() {
|
|
156
|
+
return {
|
|
157
|
+
schemaVersion: skillsRegistryExportSchemaVersion,
|
|
158
|
+
product: "Meta-Architect",
|
|
159
|
+
purpose:
|
|
160
|
+
"Defines MA-owned exported skill compatibility payloads across universal and non-universal host agents.",
|
|
161
|
+
canonical_dir: canonicalSkillsDir,
|
|
162
|
+
universal_targets: universalTargets,
|
|
163
|
+
non_universal_targets: nonUniversalTargets,
|
|
164
|
+
authority_boundary: {
|
|
165
|
+
exported_payloads_may_mutate_release_state: false,
|
|
166
|
+
exported_payloads_are_managed_workers: false,
|
|
167
|
+
canonical_ma_authority: "$maestro_or_owning_lane",
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function validateSkillsRegistryExport(value) {
|
|
173
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
174
|
+
throw new Error("skills registry export must be an object");
|
|
175
|
+
}
|
|
176
|
+
if (value.schemaVersion !== skillsRegistryExportSchemaVersion) {
|
|
177
|
+
throw new Error(`Unsupported skills registry export schemaVersion: ${value.schemaVersion}`);
|
|
178
|
+
}
|
|
179
|
+
if (value.canonical_dir !== canonicalSkillsDir) {
|
|
180
|
+
throw new Error("skills registry export must use .agents/skills as canonical_dir");
|
|
181
|
+
}
|
|
182
|
+
if (value.authority_boundary?.exported_payloads_may_mutate_release_state !== false) {
|
|
183
|
+
throw new Error("exported skill payloads must not mutate release state");
|
|
184
|
+
}
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export async function seedSkillsRegistryExportArtifacts() {
|
|
189
|
+
await ensureDir(getRuntimeSubsystemPath("context"));
|
|
190
|
+
await writeFileIfMissing(
|
|
191
|
+
getSkillsRegistryExportPath(),
|
|
192
|
+
`${JSON.stringify(createDefaultSkillsRegistryExport(), null, 2)}\n`,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export async function loadSkillsRegistryExport() {
|
|
197
|
+
return validateSkillsRegistryExport(await readJson(getSkillsRegistryExportPath()));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function isUniversalAgent(agentType) {
|
|
201
|
+
return agentRegistry[agentType]?.universal === true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function pathExists(targetPath) {
|
|
205
|
+
try {
|
|
206
|
+
await fs.access(targetPath);
|
|
207
|
+
return true;
|
|
208
|
+
} catch {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function copyCompatibilityFiles({ targetDir, skillMd, receipt, lockEntry }) {
|
|
214
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
215
|
+
await fs.writeFile(path.join(targetDir, "SKILL.md"), skillMd);
|
|
216
|
+
await fs.writeFile(
|
|
217
|
+
path.join(targetDir, "ma-install-receipt.json"),
|
|
218
|
+
`${JSON.stringify(receipt, null, 2)}\n`,
|
|
219
|
+
);
|
|
220
|
+
await fs.writeFile(
|
|
221
|
+
path.join(targetDir, "ma-skill-lock.json"),
|
|
222
|
+
`${JSON.stringify(lockEntry, null, 2)}\n`,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export async function detectProjectAgentRoot(agentType, cwd = getRepoRoot()) {
|
|
227
|
+
const agent = agentRegistry[agentType];
|
|
228
|
+
if (!agent || agent.universal) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const [rootDir] = agent.skillsDir.split("/");
|
|
233
|
+
return pathExists(path.join(cwd, rootDir));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function sanitizeSkillName(name) {
|
|
237
|
+
return `${name}`
|
|
238
|
+
.trim()
|
|
239
|
+
.toLowerCase()
|
|
240
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
241
|
+
.replace(/\.+/g, ".")
|
|
242
|
+
.replace(/^\.+/, "")
|
|
243
|
+
.replace(/^-+|-+$/g, "");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function resolveSkillInstallPlan({
|
|
247
|
+
skillName,
|
|
248
|
+
agentType,
|
|
249
|
+
cwd = getRepoRoot(),
|
|
250
|
+
global = false,
|
|
251
|
+
mode = "symlink",
|
|
252
|
+
agentRootExists = false,
|
|
253
|
+
}) {
|
|
254
|
+
const agent = agentRegistry[agentType];
|
|
255
|
+
if (!agent) {
|
|
256
|
+
throw new Error(`Unsupported agent type: ${agentType}`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const safeSkillName = sanitizeSkillName(skillName);
|
|
260
|
+
if (!safeSkillName) {
|
|
261
|
+
throw new Error("Skill name is required");
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const canonicalBase = global
|
|
265
|
+
? path.join(home, ".agents", "skills")
|
|
266
|
+
: path.join(cwd, canonicalSkillsDir);
|
|
267
|
+
const canonicalDir = path.join(canonicalBase, safeSkillName);
|
|
268
|
+
const agentBase = global
|
|
269
|
+
? agent.globalSkillsDir
|
|
270
|
+
: agent.universal
|
|
271
|
+
? canonicalBase
|
|
272
|
+
: path.join(cwd, agent.skillsDir);
|
|
273
|
+
const targetDir = path.join(agentBase, safeSkillName);
|
|
274
|
+
|
|
275
|
+
if (mode === "copy") {
|
|
276
|
+
return {
|
|
277
|
+
action: "copy",
|
|
278
|
+
canonicalDir,
|
|
279
|
+
targetDir,
|
|
280
|
+
skipped: false,
|
|
281
|
+
reason: "copy mode writes directly to target agent directory",
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (global && agent.universal) {
|
|
286
|
+
return {
|
|
287
|
+
action: "canonical-only",
|
|
288
|
+
canonicalDir,
|
|
289
|
+
targetDir: canonicalDir,
|
|
290
|
+
skipped: false,
|
|
291
|
+
reason: "global universal agent reads canonical directory directly",
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (!global && !agent.universal && !agentRootExists) {
|
|
296
|
+
return {
|
|
297
|
+
action: "skip-missing-agent-root",
|
|
298
|
+
canonicalDir,
|
|
299
|
+
targetDir,
|
|
300
|
+
skipped: true,
|
|
301
|
+
reason: "non-universal project agent root is absent",
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
action: agent.universal ? "canonical-only" : "symlink",
|
|
307
|
+
canonicalDir,
|
|
308
|
+
targetDir: agent.universal ? canonicalDir : targetDir,
|
|
309
|
+
skipped: false,
|
|
310
|
+
reason: agent.universal
|
|
311
|
+
? "universal agent reads canonical directory directly"
|
|
312
|
+
: "non-universal agent receives symlink from canonical payload",
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export function createSkillCompatibilityPayload({
|
|
317
|
+
name,
|
|
318
|
+
description,
|
|
319
|
+
trigger = null,
|
|
320
|
+
capabilities = [],
|
|
321
|
+
}) {
|
|
322
|
+
const safeName = sanitizeSkillName(name);
|
|
323
|
+
if (!safeName) {
|
|
324
|
+
throw new Error("Skill payload name is required");
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const normalizedCapabilities = capabilities
|
|
328
|
+
.filter((capability) => typeof capability === "string" && capability.trim())
|
|
329
|
+
.map((capability) => capability.trim());
|
|
330
|
+
|
|
331
|
+
return {
|
|
332
|
+
schemaVersion: skillsRegistryExportSchemaVersion,
|
|
333
|
+
record_type: "skill_compatibility_payload",
|
|
334
|
+
product: "Meta-Architect",
|
|
335
|
+
name: safeName,
|
|
336
|
+
description:
|
|
337
|
+
description ||
|
|
338
|
+
"Meta-Architect compatibility payload that routes host agents into MA-owned runtime contracts.",
|
|
339
|
+
trigger: trigger || `$${safeName}`,
|
|
340
|
+
capabilities: normalizedCapabilities,
|
|
341
|
+
canonical_dir: canonicalSkillsDir,
|
|
342
|
+
authority_boundary: {
|
|
343
|
+
may_mutate_release_state: false,
|
|
344
|
+
is_managed_worker: false,
|
|
345
|
+
canonical_authority: "$maestro_or_owning_lane",
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function renderSkillCompatibilitySkillMd(payload) {
|
|
351
|
+
if (payload?.record_type !== "skill_compatibility_payload") {
|
|
352
|
+
throw new Error("Expected skill compatibility payload");
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return [
|
|
356
|
+
"---",
|
|
357
|
+
`name: ${payload.name}`,
|
|
358
|
+
`description: ${payload.description}`,
|
|
359
|
+
"---",
|
|
360
|
+
"",
|
|
361
|
+
`# ${payload.name}`,
|
|
362
|
+
"",
|
|
363
|
+
"This exported host payload is a compatibility entrypoint for Meta-Architect.",
|
|
364
|
+
"",
|
|
365
|
+
"## Authority",
|
|
366
|
+
"",
|
|
367
|
+
"- Route execution through `$maestro` or the owning MA lane.",
|
|
368
|
+
"- Do not mutate `.ma/release.json` or `.ma/decisions.json` directly.",
|
|
369
|
+
"- Treat Obsidian/vault context as `vault_context`, not `build_evidence`.",
|
|
370
|
+
"- Preserve Context Economy safety valves for warnings, failed checks, and known gaps.",
|
|
371
|
+
"",
|
|
372
|
+
"## Capabilities",
|
|
373
|
+
"",
|
|
374
|
+
...(payload.capabilities.length === 0
|
|
375
|
+
? ["- shared_ma_runtime_contracts"]
|
|
376
|
+
: payload.capabilities.map((capability) => `- ${capability}`)),
|
|
377
|
+
"",
|
|
378
|
+
].join("\n");
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export function createHostInstallReceipt({ payload, installPlan }) {
|
|
382
|
+
if (payload?.record_type !== "skill_compatibility_payload") {
|
|
383
|
+
throw new Error("Host install receipt requires a compatibility payload");
|
|
384
|
+
}
|
|
385
|
+
if (!installPlan || typeof installPlan !== "object") {
|
|
386
|
+
throw new Error("Host install receipt requires an install plan");
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return {
|
|
390
|
+
schemaVersion: skillsRegistryExportSchemaVersion,
|
|
391
|
+
record_type: "host_install_receipt",
|
|
392
|
+
skill: payload.name,
|
|
393
|
+
action: installPlan.action,
|
|
394
|
+
canonicalDir: installPlan.canonicalDir,
|
|
395
|
+
targetDir: installPlan.targetDir,
|
|
396
|
+
skipped: installPlan.skipped === true,
|
|
397
|
+
authority_boundary: payload.authority_boundary,
|
|
398
|
+
records_as: "host_compatibility_payload",
|
|
399
|
+
production_evidence: false,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export function createSkillLockEntry({
|
|
404
|
+
payload,
|
|
405
|
+
source = "meta-architect",
|
|
406
|
+
sourceType = "local",
|
|
407
|
+
sourceUrl = "skills/",
|
|
408
|
+
ref = null,
|
|
409
|
+
skillPath = null,
|
|
410
|
+
skillFolderHash = null,
|
|
411
|
+
selectedAgentTargets = [],
|
|
412
|
+
installedAt = new Date().toISOString(),
|
|
413
|
+
updatedAt = installedAt,
|
|
414
|
+
} = {}) {
|
|
415
|
+
if (payload?.record_type !== "skill_compatibility_payload") {
|
|
416
|
+
throw new Error("Skill lock entry requires a compatibility payload");
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const renderedPayload = renderSkillCompatibilitySkillMd(payload);
|
|
420
|
+
const folderHash =
|
|
421
|
+
skillFolderHash ??
|
|
422
|
+
createHash("sha256")
|
|
423
|
+
.update(renderedPayload)
|
|
424
|
+
.update(JSON.stringify(payload.authority_boundary))
|
|
425
|
+
.digest("hex");
|
|
426
|
+
|
|
427
|
+
return {
|
|
428
|
+
schemaVersion: skillsRegistryExportSchemaVersion,
|
|
429
|
+
record_type: "skill_lock_entry",
|
|
430
|
+
skill: payload.name,
|
|
431
|
+
source,
|
|
432
|
+
sourceType,
|
|
433
|
+
sourceUrl,
|
|
434
|
+
ref,
|
|
435
|
+
skillPath,
|
|
436
|
+
skillFolderHash: folderHash,
|
|
437
|
+
installedAt,
|
|
438
|
+
updatedAt,
|
|
439
|
+
selectedAgentTargets: selectedAgentTargets.map((target) => `${target}`),
|
|
440
|
+
authority_boundary: payload.authority_boundary,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export function validateSkillLockEntry(entry) {
|
|
445
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
446
|
+
throw new Error("Skill lock entry must be an object");
|
|
447
|
+
}
|
|
448
|
+
if (entry.record_type !== "skill_lock_entry") {
|
|
449
|
+
throw new Error("Skill lock entry must use record_type skill_lock_entry");
|
|
450
|
+
}
|
|
451
|
+
for (const field of [
|
|
452
|
+
"skill",
|
|
453
|
+
"source",
|
|
454
|
+
"sourceType",
|
|
455
|
+
"sourceUrl",
|
|
456
|
+
"skillFolderHash",
|
|
457
|
+
"installedAt",
|
|
458
|
+
"updatedAt",
|
|
459
|
+
]) {
|
|
460
|
+
if (typeof entry[field] !== "string" || entry[field].trim() === "") {
|
|
461
|
+
throw new Error(`Skill lock entry requires ${field}`);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (!Array.isArray(entry.selectedAgentTargets)) {
|
|
465
|
+
throw new Error("Skill lock entry requires selectedAgentTargets");
|
|
466
|
+
}
|
|
467
|
+
if (entry.authority_boundary?.may_mutate_release_state !== false) {
|
|
468
|
+
throw new Error("Skill lock entries must preserve MA authority boundaries");
|
|
469
|
+
}
|
|
470
|
+
return entry;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
export async function writeSkillCompatibilityExport({
|
|
474
|
+
payload,
|
|
475
|
+
agentType,
|
|
476
|
+
cwd = getRepoRoot(),
|
|
477
|
+
global = false,
|
|
478
|
+
mode = "symlink",
|
|
479
|
+
agentRootExists = null,
|
|
480
|
+
createSymlink = fs.symlink,
|
|
481
|
+
lockMetadata = {},
|
|
482
|
+
}) {
|
|
483
|
+
const effectiveAgentRootExists =
|
|
484
|
+
agentRootExists ?? (await detectProjectAgentRoot(agentType, cwd));
|
|
485
|
+
const installPlan = resolveSkillInstallPlan({
|
|
486
|
+
skillName: payload.name,
|
|
487
|
+
agentType,
|
|
488
|
+
cwd,
|
|
489
|
+
global,
|
|
490
|
+
mode,
|
|
491
|
+
agentRootExists: effectiveAgentRootExists,
|
|
492
|
+
});
|
|
493
|
+
const skillMd = renderSkillCompatibilitySkillMd(payload);
|
|
494
|
+
const lockEntry = validateSkillLockEntry(
|
|
495
|
+
createSkillLockEntry({
|
|
496
|
+
payload,
|
|
497
|
+
selectedAgentTargets: [agentType],
|
|
498
|
+
...lockMetadata,
|
|
499
|
+
}),
|
|
500
|
+
);
|
|
501
|
+
|
|
502
|
+
await fs.mkdir(installPlan.canonicalDir, { recursive: true });
|
|
503
|
+
await fs.writeFile(path.join(installPlan.canonicalDir, "SKILL.md"), skillMd);
|
|
504
|
+
const receipt = createHostInstallReceipt({ payload, installPlan });
|
|
505
|
+
await fs.writeFile(
|
|
506
|
+
path.join(installPlan.canonicalDir, "ma-install-receipt.json"),
|
|
507
|
+
`${JSON.stringify(receipt, null, 2)}\n`,
|
|
508
|
+
);
|
|
509
|
+
await fs.writeFile(
|
|
510
|
+
path.join(installPlan.canonicalDir, "ma-skill-lock.json"),
|
|
511
|
+
`${JSON.stringify(lockEntry, null, 2)}\n`,
|
|
512
|
+
);
|
|
513
|
+
let fanoutMode = installPlan.action;
|
|
514
|
+
|
|
515
|
+
if (installPlan.action === "copy") {
|
|
516
|
+
await copyCompatibilityFiles({
|
|
517
|
+
targetDir: installPlan.targetDir,
|
|
518
|
+
skillMd,
|
|
519
|
+
receipt,
|
|
520
|
+
lockEntry,
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (installPlan.action === "symlink") {
|
|
525
|
+
await fs.mkdir(path.dirname(installPlan.targetDir), { recursive: true });
|
|
526
|
+
await fs.rm(installPlan.targetDir, { recursive: true, force: true });
|
|
527
|
+
try {
|
|
528
|
+
await createSymlink(installPlan.canonicalDir, installPlan.targetDir, "dir");
|
|
529
|
+
} catch {
|
|
530
|
+
fanoutMode = "copy-fallback";
|
|
531
|
+
await copyCompatibilityFiles({
|
|
532
|
+
targetDir: installPlan.targetDir,
|
|
533
|
+
skillMd,
|
|
534
|
+
receipt,
|
|
535
|
+
lockEntry,
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
return {
|
|
541
|
+
installPlan,
|
|
542
|
+
receipt,
|
|
543
|
+
lockEntry,
|
|
544
|
+
fanoutMode,
|
|
545
|
+
written: [
|
|
546
|
+
path.join(installPlan.canonicalDir, "SKILL.md"),
|
|
547
|
+
path.join(installPlan.canonicalDir, "ma-install-receipt.json"),
|
|
548
|
+
path.join(installPlan.canonicalDir, "ma-skill-lock.json"),
|
|
549
|
+
],
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
export async function inspectSkillCompatibilityInstall({ result, payload }) {
|
|
554
|
+
if (!result || typeof result !== "object") {
|
|
555
|
+
throw new Error("Skill install inspection requires an install result");
|
|
556
|
+
}
|
|
557
|
+
if (payload?.record_type !== "skill_compatibility_payload") {
|
|
558
|
+
throw new Error("Skill install inspection requires a compatibility payload");
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const { installPlan, fanoutMode } = result;
|
|
562
|
+
const errors = [];
|
|
563
|
+
const canonicalFiles = [
|
|
564
|
+
path.join(installPlan.canonicalDir, "SKILL.md"),
|
|
565
|
+
path.join(installPlan.canonicalDir, "ma-install-receipt.json"),
|
|
566
|
+
path.join(installPlan.canonicalDir, "ma-skill-lock.json"),
|
|
567
|
+
];
|
|
568
|
+
|
|
569
|
+
for (const file of canonicalFiles) {
|
|
570
|
+
if (!(await pathExists(file))) {
|
|
571
|
+
errors.push(`missing canonical file: ${file}`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
let targetKind = "canonical";
|
|
576
|
+
if (installPlan.skipped) {
|
|
577
|
+
targetKind = "skipped";
|
|
578
|
+
} else if (installPlan.targetDir !== installPlan.canonicalDir) {
|
|
579
|
+
try {
|
|
580
|
+
const targetStat = await fs.lstat(installPlan.targetDir);
|
|
581
|
+
if (targetStat.isSymbolicLink()) {
|
|
582
|
+
targetKind = "symlink";
|
|
583
|
+
} else if (targetStat.isDirectory()) {
|
|
584
|
+
targetKind = "directory-copy";
|
|
585
|
+
} else {
|
|
586
|
+
errors.push(`target is not a directory or symlink: ${installPlan.targetDir}`);
|
|
587
|
+
}
|
|
588
|
+
} catch {
|
|
589
|
+
errors.push(`missing target path: ${installPlan.targetDir}`);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
for (const file of ["SKILL.md", "ma-install-receipt.json", "ma-skill-lock.json"]) {
|
|
593
|
+
const targetFile = path.join(installPlan.targetDir, file);
|
|
594
|
+
if (!(await pathExists(targetFile))) {
|
|
595
|
+
errors.push(`missing target file: ${targetFile}`);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if (fanoutMode === "copy-fallback" && targetKind !== "directory-copy") {
|
|
601
|
+
errors.push("copy-fallback fanout must leave a target directory copy");
|
|
602
|
+
}
|
|
603
|
+
if (installPlan.action === "symlink" && fanoutMode === "symlink" && targetKind !== "symlink") {
|
|
604
|
+
errors.push("symlink fanout must leave a target symlink");
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return {
|
|
608
|
+
schemaVersion: skillsRegistryExportSchemaVersion,
|
|
609
|
+
record_type: "skill_install_verification",
|
|
610
|
+
skill: payload.name,
|
|
611
|
+
action: installPlan.action,
|
|
612
|
+
fanoutMode,
|
|
613
|
+
targetKind,
|
|
614
|
+
canonicalDir: installPlan.canonicalDir,
|
|
615
|
+
targetDir: installPlan.targetDir,
|
|
616
|
+
ok: errors.length === 0,
|
|
617
|
+
errors,
|
|
618
|
+
authority_boundary: payload.authority_boundary,
|
|
619
|
+
production_evidence: false,
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
export async function verifyCrossAgentInstallMatrix({
|
|
624
|
+
payload,
|
|
625
|
+
cwd = getRepoRoot(),
|
|
626
|
+
targets = ["codex", "claude-code", "aider-desk"],
|
|
627
|
+
existingAgentRoots = [],
|
|
628
|
+
copyFallbackTargets = [],
|
|
629
|
+
} = {}) {
|
|
630
|
+
if (payload?.record_type !== "skill_compatibility_payload") {
|
|
631
|
+
throw new Error("Cross-agent install verification requires a compatibility payload");
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
for (const agentType of existingAgentRoots) {
|
|
635
|
+
const agent = agentRegistry[agentType];
|
|
636
|
+
if (agent && !agent.universal) {
|
|
637
|
+
const [rootDir] = agent.skillsDir.split("/");
|
|
638
|
+
await fs.mkdir(path.join(cwd, rootDir), { recursive: true });
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
const results = [];
|
|
643
|
+
for (const agentType of targets) {
|
|
644
|
+
const forceFallback = copyFallbackTargets.includes(agentType);
|
|
645
|
+
const result = await writeSkillCompatibilityExport({
|
|
646
|
+
payload,
|
|
647
|
+
agentType,
|
|
648
|
+
cwd,
|
|
649
|
+
agentRootExists: await detectProjectAgentRoot(agentType, cwd),
|
|
650
|
+
createSymlink: forceFallback
|
|
651
|
+
? async () => {
|
|
652
|
+
throw new Error("forced symlink failure for verification");
|
|
653
|
+
}
|
|
654
|
+
: fs.symlink,
|
|
655
|
+
});
|
|
656
|
+
const verification = await inspectSkillCompatibilityInstall({ result, payload });
|
|
657
|
+
results.push(verification);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
return {
|
|
661
|
+
schemaVersion: skillsRegistryExportSchemaVersion,
|
|
662
|
+
record_type: "cross_agent_install_verification",
|
|
663
|
+
skill: payload.name,
|
|
664
|
+
target_count: results.length,
|
|
665
|
+
ok: results.every((result) => result.ok),
|
|
666
|
+
results,
|
|
667
|
+
authority_boundary: payload.authority_boundary,
|
|
668
|
+
production_evidence: false,
|
|
669
|
+
};
|
|
670
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function createStartupCheckpoint(name, startedAt = Date.now()) {
|
|
2
|
+
return {
|
|
3
|
+
name,
|
|
4
|
+
startedAt,
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function completeStartupCheckpoint(checkpoint, endedAt = Date.now()) {
|
|
9
|
+
return {
|
|
10
|
+
...checkpoint,
|
|
11
|
+
endedAt,
|
|
12
|
+
durationMs: endedAt - checkpoint.startedAt,
|
|
13
|
+
};
|
|
14
|
+
}
|