@jstn-sdk/ma 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/.codex/agents/Architect.toml +0 -2
  2. package/.codex/agents/Auditor.toml +0 -2
  3. package/.codex/agents/Builder.toml +0 -2
  4. package/.codex/agents/Flow.toml +0 -2
  5. package/.codex/agents/Sage.toml +0 -2
  6. package/.codex/agents/Vibe.toml +0 -2
  7. package/.codex/hooks.json +14 -13
  8. package/.codex/prompts/enforcement.md +4 -0
  9. package/.codex/prompts/skill-contract.md +4 -0
  10. package/COVERAGE.md +211 -0
  11. package/DEMO.md +265 -0
  12. package/README.md +287 -37
  13. package/bin/ma.js +232 -71
  14. package/data/clone-data.ledger.json +41 -0
  15. package/data/clone-data.proof.json +50 -0
  16. package/data/clone-data.rvf +37 -0
  17. package/docs/README.md +7 -1
  18. package/docs/getting-started.md +108 -37
  19. package/docs/installed-sdk.md +23 -0
  20. package/docs/mcp-setup.md +65 -1
  21. package/docs/onboarding.md +18 -2
  22. package/docs/prompt-guidance-contract.md +17 -0
  23. package/docs/prompt-guidance-fragments/active-autonomy-core.md +7 -0
  24. package/docs/qa/release-issue-gates-0.1.13.json +644 -0
  25. package/docs/qa/release-readiness-0.1.13.md +116 -0
  26. package/docs/qa/release-readiness-0.1.5.md +1 -1
  27. package/docs/reference/native-engineering-patterns.md +35 -0
  28. package/docs/reference/native-security-playbooks.md +27 -0
  29. package/docs/reference/native-source-selection.md +27 -0
  30. package/docs/reference/native-style-and-deslop.md +20 -0
  31. package/docs/release-spec.md +37 -10
  32. package/docs/skills-publishing.md +25 -1
  33. package/docs/skills.md +37 -12
  34. package/index.js +222 -2
  35. package/mcp/collections.json +23 -6
  36. package/mcp/local/code-intel.js +113 -0
  37. package/mcp/local/memory.js +46 -0
  38. package/mcp/local/playbooks.js +168 -0
  39. package/mcp/local/state.js +71 -0
  40. package/mcp/local/team-run.js +113 -0
  41. package/mcp/local/trace.js +60 -0
  42. package/mcp/local-capabilities.json +56 -0
  43. package/mcp/native-playbooks.json +117 -0
  44. package/mcp/servers.json +34 -0
  45. package/package.json +8 -2
  46. package/plugins/meta-architect/.app.json +1 -1
  47. package/plugins/meta-architect/.codex-plugin/plugin.json +4 -4
  48. package/plugins/meta-architect/.mcp.json +1 -1
  49. package/plugins/meta-architect/README.md +34 -1
  50. package/plugins/meta-architect/obsidian/main.js +401 -0
  51. package/plugins/meta-architect/obsidian/manifest.json +9 -0
  52. package/plugins/meta-architect/obsidian/styles.css +7 -0
  53. package/plugins/meta-architect/skills/align/SKILL.md +24 -0
  54. package/plugins/meta-architect/skills/align/agents/openai.yaml +4 -0
  55. package/plugins/meta-architect/skills/align/references/shared-language.md +24 -0
  56. package/plugins/meta-architect/skills/arch/SKILL.md +2 -0
  57. package/plugins/meta-architect/skills/build/SKILL.md +3 -0
  58. package/plugins/meta-architect/skills/cleanup/SKILL.md +23 -0
  59. package/plugins/meta-architect/skills/cleanup/agents/openai.yaml +4 -0
  60. package/plugins/meta-architect/skills/cleanup/references/style-and-deslop.md +18 -0
  61. package/plugins/meta-architect/skills/diagnose/SKILL.md +24 -0
  62. package/plugins/meta-architect/skills/diagnose/agents/openai.yaml +4 -0
  63. package/plugins/meta-architect/skills/flow/SKILL.md +2 -0
  64. package/plugins/meta-architect/skills/maestro/SKILL.md +36 -3
  65. package/plugins/meta-architect/skills/maestro/agents/openai.yaml +2 -2
  66. package/plugins/meta-architect/skills/maestro/references/native-ingest-map.md +44 -0
  67. package/plugins/meta-architect/skills/sage/SKILL.md +5 -1
  68. package/plugins/meta-architect/skills/sage/references/source-selection.md +36 -0
  69. package/plugins/meta-architect/skills/tdd/SKILL.md +24 -0
  70. package/plugins/meta-architect/skills/tdd/agents/openai.yaml +4 -0
  71. package/plugins/meta-architect/skills/vet/SKILL.md +5 -1
  72. package/plugins/meta-architect/skills/vet/references/security-playbooks.md +30 -0
  73. package/plugins/meta-architect/skills/vibe/SKILL.md +2 -0
  74. package/scripts/active-autonomy-hook.mjs +96 -0
  75. package/scripts/build-linux-packages.mjs +328 -0
  76. package/scripts/doctor.js +36 -4
  77. package/scripts/install.sh +28 -0
  78. package/scripts/linux-package-lib.mjs +40 -0
  79. package/scripts/linux-package-smoke.mjs +103 -0
  80. package/scripts/ralph/prompt.md +35 -0
  81. package/scripts/release-sync.js +13 -6
  82. package/scripts/release-verify.js +166 -1
  83. package/skills/align/SKILL.md +24 -0
  84. package/skills/align/agents/openai.yaml +4 -0
  85. package/skills/align/references/shared-language.md +24 -0
  86. package/skills/arch/SKILL.md +2 -0
  87. package/skills/build/SKILL.md +3 -0
  88. package/skills/cleanup/SKILL.md +23 -0
  89. package/skills/cleanup/agents/openai.yaml +4 -0
  90. package/skills/cleanup/references/style-and-deslop.md +18 -0
  91. package/skills/diagnose/SKILL.md +24 -0
  92. package/skills/diagnose/agents/openai.yaml +4 -0
  93. package/skills/flow/SKILL.md +2 -0
  94. package/skills/index.json +21 -6
  95. package/skills/maestro/SKILL.md +36 -3
  96. package/skills/maestro/agents/openai.yaml +2 -2
  97. package/skills/maestro/references/native-ingest-map.md +44 -0
  98. package/skills/sage/SKILL.md +5 -1
  99. package/skills/sage/references/source-selection.md +36 -0
  100. package/skills/tdd/SKILL.md +24 -0
  101. package/skills/tdd/agents/openai.yaml +4 -0
  102. package/skills/vet/SKILL.md +5 -1
  103. package/skills/vet/references/security-playbooks.md +30 -0
  104. package/skills/vibe/SKILL.md +2 -0
  105. package/src/bootstrap.js +141 -24
  106. package/src/build-gate.js +2 -2
  107. package/src/decision-log.js +12 -9
  108. package/src/launcher.js +4 -0
  109. package/src/mcp-config.js +130 -8
  110. package/src/mcp-live-client.js +289 -3
  111. package/src/paths.js +37 -1
  112. package/src/policy.js +1 -1
  113. package/src/release-issue-gates.js +190 -0
  114. package/src/release-state.js +30 -1
  115. package/src/runtime/active-autonomy-core.js +208 -0
  116. package/src/runtime/alignment-sentinel.js +165 -0
  117. package/src/runtime/architect-review.js +88 -0
  118. package/src/runtime/build-readiness.js +62 -0
  119. package/src/runtime/code-graph-rehearse.js +113 -0
  120. package/src/runtime/context-economy-core.js +276 -0
  121. package/src/runtime/continuity-notes.js +79 -0
  122. package/src/runtime/core-source-ingest.js +379 -0
  123. package/src/runtime/detached-provider.js +74 -0
  124. package/src/runtime/environment-awareness-core.js +460 -0
  125. package/src/runtime/exposure-catalog.js +276 -0
  126. package/src/runtime/guidance-stack.js +42 -0
  127. package/src/runtime/helper-orchestration-core.js +307 -0
  128. package/src/runtime/learning-loop-core.js +238 -0
  129. package/src/runtime/maestro-events.js +18 -0
  130. package/src/runtime/maestro-manager.js +605 -0
  131. package/src/runtime/maestro-state.js +125 -0
  132. package/src/runtime/mcp-policy.js +192 -0
  133. package/src/runtime/obsidian-integration-core.js +851 -0
  134. package/src/runtime/obsidian-plugin-bridge.js +739 -0
  135. package/src/runtime/orchestrator.js +158 -0
  136. package/src/runtime/prompt-strategy-core.js +230 -0
  137. package/src/runtime/quorum-review.js +90 -0
  138. package/src/runtime/ralph-execution-core.js +217 -0
  139. package/src/runtime/redaction-gateway.js +174 -0
  140. package/src/runtime/runtime-state.js +1223 -0
  141. package/src/runtime/semantic-recording-core.js +147 -0
  142. package/src/runtime/signal-hooks.js +67 -0
  143. package/src/runtime/skills-registry-export.js +670 -0
  144. package/src/runtime/startup-path.js +14 -0
  145. package/src/runtime/universal-plugin-broker-core.js +575 -0
  146. package/src/runtime/workspace-intelligence-runtime.js +674 -0
  147. package/src/runtime/workspace-virtualizer.js +102 -0
  148. package/src/runtime/workspaces.js +25 -0
  149. package/src/runtime-artifacts.js +407 -84
  150. package/src/skill-installer.js +53 -5
  151. package/src/skills.js +1454 -78
  152. package/src/state-sync.js +51 -8
  153. package/docs/qa/release-readiness-0.1.11.md +0 -79
  154. package/plugins/meta-architect/skills/meta-architect/SKILL.md +0 -32
  155. package/plugins/meta-architect/skills/meta-architect/agents/openai.yaml +0 -4
  156. package/skills/meta-architect/SKILL.md +0 -32
  157. package/skills/meta-architect/agents/openai.yaml +0 -4
  158. /package/plugins/meta-architect/skills/{meta-architect → maestro}/references/core-release-rules.md +0 -0
  159. /package/skills/{meta-architect → maestro}/references/core-release-rules.md +0 -0
@@ -0,0 +1,575 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { ensureDir, readJson, writeFileIfMissing, writeJson } from "../fs-utils.js";
5
+ import { getRuntimeSubsystemPath } from "../paths.js";
6
+ import { agentRegistry, sanitizeSkillName } from "./skills-registry-export.js";
7
+
8
+ export const universalPluginBrokerSchemaVersion = "0.1.0";
9
+ export const universalPluginManifestSchemaVersion = "0.1.0";
10
+
11
+ const mcpInjectorHosts = ["claude-code", "antigravity", "cursor", "codex"];
12
+
13
+ function asArray(value) {
14
+ return Array.isArray(value) ? value : [];
15
+ }
16
+
17
+ function quoteTomlString(value) {
18
+ return JSON.stringify(`${value}`);
19
+ }
20
+
21
+ function dedupe(values) {
22
+ return [...new Set(values)];
23
+ }
24
+
25
+ function mcpServerNameForPlugin(pluginName) {
26
+ const safeName = sanitizeSkillName(pluginName);
27
+ const baseName = safeName.startsWith("ma-") ? safeName.slice(3) : safeName;
28
+ return `ma-plugin-${baseName}`;
29
+ }
30
+
31
+ async function pathExists(targetPath) {
32
+ try {
33
+ await fs.access(targetPath);
34
+ return true;
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ async function directoryExists(targetPath) {
41
+ try {
42
+ return (await fs.stat(targetPath)).isDirectory();
43
+ } catch {
44
+ return false;
45
+ }
46
+ }
47
+
48
+ async function readJsonOrDefault(filePath, fallback) {
49
+ try {
50
+ return JSON.parse(await fs.readFile(filePath, "utf8"));
51
+ } catch {
52
+ return structuredClone(fallback);
53
+ }
54
+ }
55
+
56
+ async function writeJsonConfig(filePath, config) {
57
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
58
+ await fs.writeFile(filePath, `${JSON.stringify(config, null, 2)}\n`);
59
+ }
60
+
61
+ function cursorStoragePath({
62
+ home = os.homedir(),
63
+ platform = process.platform,
64
+ env = process.env,
65
+ } = {}) {
66
+ if (platform === "win32") {
67
+ return path.join(
68
+ env.APPDATA || path.join(home, "AppData", "Roaming"),
69
+ "Cursor",
70
+ "User",
71
+ "globalStorage",
72
+ "storage.json",
73
+ );
74
+ }
75
+ if (platform === "darwin") {
76
+ return path.join(
77
+ home,
78
+ "Library",
79
+ "Application Support",
80
+ "Cursor",
81
+ "User",
82
+ "globalStorage",
83
+ "storage.json",
84
+ );
85
+ }
86
+ return path.join(home, ".config", "Cursor", "User", "globalStorage", "storage.json");
87
+ }
88
+
89
+ function createMcpDefinition({ manifest, pluginDir, wrapperPath, useWrapper = true }) {
90
+ const manifestArgs = asArray(manifest.mcp?.args).map((arg) =>
91
+ `${arg}`.replaceAll("{{PLUGIN_DIR}}", pluginDir),
92
+ );
93
+ if (useWrapper) {
94
+ return {
95
+ command: wrapperPath,
96
+ args: [],
97
+ };
98
+ }
99
+ return {
100
+ command: manifest.mcp.command,
101
+ args: manifestArgs,
102
+ };
103
+ }
104
+
105
+ function renderWrapperScript({ manifest, pluginDir }) {
106
+ const args = asArray(manifest.mcp?.args).map((arg) =>
107
+ `${arg}`.replaceAll("{{PLUGIN_DIR}}", pluginDir),
108
+ );
109
+ if (manifest.mcp.command === "node" && args.length > 0) {
110
+ const [entrypoint, ...rest] = args;
111
+ const quotedRest = rest.map((arg) => `"${arg.replaceAll('"', '\\"')}"`).join(" ");
112
+ return `#!/usr/bin/env sh\nexec node "${entrypoint.replaceAll('"', '\\"')}" ${quotedRest} "$@"\n`;
113
+ }
114
+ const quotedArgs = args.map((arg) => `"${arg.replaceAll('"', '\\"')}"`).join(" ");
115
+ return `#!/usr/bin/env sh\nexec "${manifest.mcp.command.replaceAll('"', '\\"')}" ${quotedArgs} "$@"\n`;
116
+ }
117
+
118
+ export function getUniversalPluginBrokerCorePath() {
119
+ return getRuntimeSubsystemPath("context", "universal-plugin-broker-core.json");
120
+ }
121
+
122
+ export function createDefaultUniversalPluginBrokerCore() {
123
+ const supportedAgents = Object.keys(agentRegistry).sort();
124
+ return {
125
+ schemaVersion: universalPluginBrokerSchemaVersion,
126
+ product: "Meta-Architect",
127
+ purpose:
128
+ "Defines the MA-owned hybrid plugin contract: universal MCP tooling plus vendor-agnostic MA skill context payloads across supported host agents.",
129
+ architecture: {
130
+ tooling_layer: {
131
+ protocol: "MCP stdio JSON-RPC",
132
+ server_contract: "ma-manifest.json#mcp",
133
+ usable_by: mcpInjectorHosts,
134
+ },
135
+ context_layer: {
136
+ canonical_skill_dir: ".agents/skills",
137
+ prompt_contract: "ma-manifest.json#ma_roles",
138
+ usable_by: supportedAgents,
139
+ },
140
+ },
141
+ lifecycle: ["resolve", "isolate", "generate", "mutate"],
142
+ mutation_policy: {
143
+ may_write_vendor_configs: true,
144
+ writes_only_detected_or_requested_hosts: true,
145
+ may_mutate_release_state: false,
146
+ may_mutate_decisions: false,
147
+ records_as: "plugin_compatibility_configuration",
148
+ never_records_as: "build_evidence",
149
+ requires_session_reload_for: ["claude-code", "antigravity", "codex"],
150
+ },
151
+ supported_agents: supportedAgents,
152
+ mcp_injector_hosts: mcpInjectorHosts,
153
+ };
154
+ }
155
+
156
+ export function validateUniversalPluginBrokerCore(value) {
157
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
158
+ throw new Error("universal plugin broker core must be an object");
159
+ }
160
+ if (value.schemaVersion !== universalPluginBrokerSchemaVersion) {
161
+ throw new Error(`Unsupported universal plugin broker schemaVersion: ${value.schemaVersion}`);
162
+ }
163
+ if (value.mutation_policy?.may_mutate_release_state !== false) {
164
+ throw new Error("plugin broker must not mutate release state");
165
+ }
166
+ if (value.mutation_policy?.may_mutate_decisions !== false) {
167
+ throw new Error("plugin broker must not mutate decisions");
168
+ }
169
+ if (value.mutation_policy?.records_as !== "plugin_compatibility_configuration") {
170
+ throw new Error("plugin broker must record as plugin_compatibility_configuration");
171
+ }
172
+ if (value.mutation_policy?.never_records_as !== "build_evidence") {
173
+ throw new Error("plugin broker must never record as build_evidence");
174
+ }
175
+ for (const host of mcpInjectorHosts) {
176
+ if (!value.mcp_injector_hosts?.includes(host)) {
177
+ throw new Error(`plugin broker missing MCP injector host: ${host}`);
178
+ }
179
+ }
180
+ return value;
181
+ }
182
+
183
+ export async function seedUniversalPluginBrokerCoreArtifacts() {
184
+ await ensureDir(getRuntimeSubsystemPath("context"));
185
+ await writeFileIfMissing(
186
+ getUniversalPluginBrokerCorePath(),
187
+ `${JSON.stringify(createDefaultUniversalPluginBrokerCore(), null, 2)}\n`,
188
+ );
189
+ }
190
+
191
+ export async function loadUniversalPluginBrokerCore() {
192
+ return validateUniversalPluginBrokerCore(await readJson(getUniversalPluginBrokerCorePath()));
193
+ }
194
+
195
+ export function createUniversalPluginManifest({
196
+ name,
197
+ version = "0.1.0",
198
+ description = "",
199
+ entrypoint,
200
+ mcp = null,
201
+ ma_roles: maRoles = {},
202
+ }) {
203
+ const safeName = sanitizeSkillName(name);
204
+ if (!safeName) {
205
+ throw new Error("plugin manifest requires name");
206
+ }
207
+ const normalizedMcp = mcp ?? {
208
+ command: "node",
209
+ args: ["{{PLUGIN_DIR}}/dist/index.js"],
210
+ };
211
+ return validateUniversalPluginManifest({
212
+ schemaVersion: universalPluginManifestSchemaVersion,
213
+ name: safeName,
214
+ version,
215
+ description,
216
+ entrypoint,
217
+ mcp: {
218
+ command: normalizedMcp.command,
219
+ args: asArray(normalizedMcp.args),
220
+ },
221
+ ma_roles: {
222
+ bind: dedupe(asArray(maRoles.bind)),
223
+ prompt_injection_file: maRoles.prompt_injection_file ?? null,
224
+ },
225
+ });
226
+ }
227
+
228
+ export function validateUniversalPluginManifest(value) {
229
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
230
+ throw new Error("plugin manifest must be an object");
231
+ }
232
+ const name = sanitizeSkillName(value.name);
233
+ if (!name) {
234
+ throw new Error("plugin manifest requires name");
235
+ }
236
+ if (typeof value.version !== "string" || value.version.trim() === "") {
237
+ throw new Error("plugin manifest requires version");
238
+ }
239
+ if (typeof value.entrypoint !== "string" || value.entrypoint.trim() === "") {
240
+ throw new Error("plugin manifest requires entrypoint");
241
+ }
242
+ if (typeof value.mcp?.command !== "string" || value.mcp.command.trim() === "") {
243
+ throw new Error("plugin manifest requires mcp.command");
244
+ }
245
+ if (!Array.isArray(value.mcp?.args)) {
246
+ throw new Error("plugin manifest requires mcp.args array");
247
+ }
248
+ const roleBindings = asArray(value.ma_roles?.bind);
249
+ for (const role of roleBindings) {
250
+ if (typeof role !== "string" || !role.startsWith("$")) {
251
+ throw new Error("plugin manifest role bindings must use MA trigger names such as $vet");
252
+ }
253
+ }
254
+ return {
255
+ schemaVersion: value.schemaVersion ?? universalPluginManifestSchemaVersion,
256
+ name,
257
+ version: value.version,
258
+ description: value.description ?? "",
259
+ entrypoint: value.entrypoint,
260
+ mcp: {
261
+ command: value.mcp.command,
262
+ args: value.mcp.args.map((arg) => `${arg}`),
263
+ },
264
+ ma_roles: {
265
+ bind: dedupe(roleBindings),
266
+ prompt_injection_file: value.ma_roles?.prompt_injection_file ?? null,
267
+ },
268
+ };
269
+ }
270
+
271
+ export async function loadUniversalPluginManifest(sourceDir) {
272
+ return validateUniversalPluginManifest(await readJson(path.join(sourceDir, "ma-manifest.json")));
273
+ }
274
+
275
+ export function renderUniversalMcpServerTemplate({
276
+ serverName = "ma-plugin-universal-executor",
277
+ toolName = "execute_plugin_action",
278
+ } = {}) {
279
+ return `import { Server } from "@modelcontextprotocol/sdk/server/index.js";
280
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
281
+ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
282
+
283
+ const server = new Server({ name: ${JSON.stringify(serverName)}, version: "1.0.0" }, { capabilities: { tools: {} } });
284
+
285
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
286
+ tools: [{
287
+ name: ${JSON.stringify(toolName)},
288
+ description: "Executes plugin logic through MA-compatible MCP stdio.",
289
+ inputSchema: {
290
+ type: "object",
291
+ properties: {
292
+ targetFilePath: { type: "string" },
293
+ payload: { type: "string" }
294
+ },
295
+ required: ["targetFilePath", "payload"]
296
+ }
297
+ }]
298
+ }));
299
+
300
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
301
+ if (request.params.name !== ${JSON.stringify(toolName)}) throw new Error("Tool not found");
302
+ const { targetFilePath, payload } = request.params.arguments;
303
+ return { content: [{ type: "text", text: JSON.stringify({ status: "success", targetFilePath, payload }) }] };
304
+ });
305
+
306
+ await server.connect(new StdioServerTransport());
307
+ `;
308
+ }
309
+
310
+ export function renderPluginContextSkillMd(manifest) {
311
+ const plugin = validateUniversalPluginManifest(manifest);
312
+ const serverName = mcpServerNameForPlugin(plugin.name);
313
+ const roles = plugin.ma_roles.bind.length > 0 ? plugin.ma_roles.bind : ["$maestro"];
314
+ return [
315
+ "---",
316
+ `name: ${serverName}`,
317
+ `description: ${plugin.description || `MA universal plugin context payload for ${plugin.name}`}`,
318
+ "---",
319
+ "",
320
+ `# ${serverName}`,
321
+ "",
322
+ "This is a Meta-Architect universal plugin context payload.",
323
+ "",
324
+ "## Role Binding",
325
+ "",
326
+ ...roles.map((role) => `- ${role}`),
327
+ "",
328
+ "## Execution Contract",
329
+ "",
330
+ `- MCP server name: \`${serverName}\``,
331
+ "- Use the MCP tool only when the active MA lane selects this plugin as task-relevant.",
332
+ "- Route authoritative state changes through `$maestro` or the owning lane.",
333
+ "- Do not mutate `.ma/release.json`, `.ma/decisions.json`, `.ma/plans/`, or `.ma/specs/` directly.",
334
+ "- Record plugin output as `plugin_compatibility_configuration` or lane-scoped context, never as `build_evidence`.",
335
+ "- If the host MCP client was already running, report that `/reload-mcp` or a session restart may be required.",
336
+ "",
337
+ ].join("\n");
338
+ }
339
+
340
+ export async function writePluginContextSkill({ manifest, home = os.homedir() }) {
341
+ const plugin = validateUniversalPluginManifest(manifest);
342
+ const serverName = mcpServerNameForPlugin(plugin.name);
343
+ const skillDir = path.join(home, ".agents", "skills", serverName);
344
+ await fs.mkdir(skillDir, { recursive: true });
345
+ await fs.writeFile(path.join(skillDir, "SKILL.md"), renderPluginContextSkillMd(plugin));
346
+ await writeJson(path.join(skillDir, "ma-plugin-manifest.json"), plugin);
347
+ const receipt = {
348
+ schemaVersion: universalPluginBrokerSchemaVersion,
349
+ record_type: "plugin_context_skill_receipt",
350
+ plugin: plugin.name,
351
+ skill: serverName,
352
+ path: skillDir,
353
+ records_as: "plugin_compatibility_configuration",
354
+ build_evidence: false,
355
+ };
356
+ await writeJson(path.join(skillDir, "ma-plugin-broker-receipt.json"), receipt);
357
+ return receipt;
358
+ }
359
+
360
+ export async function detectInstalledPluginHosts({
361
+ home = os.homedir(),
362
+ platform = process.platform,
363
+ env = process.env,
364
+ } = {}) {
365
+ const hosts = [];
366
+ if (
367
+ (await pathExists(path.join(home, ".claude.json"))) ||
368
+ (await directoryExists(path.join(home, ".claude")))
369
+ ) {
370
+ hosts.push("claude-code");
371
+ }
372
+ if (await directoryExists(path.join(home, ".antigravity"))) {
373
+ hosts.push("antigravity");
374
+ }
375
+ if (
376
+ (await pathExists(cursorStoragePath({ home, platform, env }))) ||
377
+ (await directoryExists(path.dirname(cursorStoragePath({ home, platform, env }))))
378
+ ) {
379
+ hosts.push("cursor");
380
+ }
381
+ if (await directoryExists(path.join(home, ".codex"))) {
382
+ hosts.push("codex");
383
+ }
384
+ return hosts;
385
+ }
386
+
387
+ export async function injectClaudeCodeMcpServer({
388
+ home = os.homedir(),
389
+ serverName,
390
+ command,
391
+ args,
392
+ }) {
393
+ const claudeJsonPath = path.join(home, ".claude.json");
394
+ if (!(await pathExists(claudeJsonPath)) && !(await directoryExists(path.join(home, ".claude")))) {
395
+ return { host: "claude-code", status: "skipped", reason: "Claude Code config not detected" };
396
+ }
397
+ const config = await readJsonOrDefault(claudeJsonPath, {});
398
+ config.mcpServers = config.mcpServers ?? {};
399
+ config.mcpServers[serverName] = { command, args };
400
+ await writeJsonConfig(claudeJsonPath, config);
401
+ return { host: "claude-code", status: "configured", path: claudeJsonPath };
402
+ }
403
+
404
+ export async function injectAntigravityMcpServer({
405
+ home = os.homedir(),
406
+ serverName,
407
+ command,
408
+ args,
409
+ }) {
410
+ const antigravityRoot = path.join(home, ".antigravity");
411
+ if (!(await directoryExists(antigravityRoot))) {
412
+ return {
413
+ host: "antigravity",
414
+ status: "skipped",
415
+ reason: "Antigravity config root not detected",
416
+ };
417
+ }
418
+ const configPath = path.join(antigravityRoot, "config.toml");
419
+ const existing = (await fs.readFile(configPath, "utf8").catch(() => "")).trimEnd();
420
+ const marker = `name = ${quoteTomlString(serverName)}`;
421
+ if (existing.includes(marker)) {
422
+ return { host: "antigravity", status: "configured", path: configPath, idempotent: true };
423
+ }
424
+ const argList = `[${args.map((arg) => quoteTomlString(arg)).join(", ")}]`;
425
+ const block = [
426
+ "",
427
+ "[[mcp_servers]]",
428
+ `name = ${quoteTomlString(serverName)}`,
429
+ `command = ${quoteTomlString(command)}`,
430
+ `args = ${argList}`,
431
+ 'env = ["NODE_ENV=production"]',
432
+ "",
433
+ ].join("\n");
434
+ await fs.mkdir(path.dirname(configPath), { recursive: true });
435
+ await fs.writeFile(configPath, `${existing}${block}`);
436
+ return { host: "antigravity", status: "configured", path: configPath };
437
+ }
438
+
439
+ export async function injectCursorMcpServer({
440
+ home = os.homedir(),
441
+ platform = process.platform,
442
+ env = process.env,
443
+ serverName,
444
+ command,
445
+ args,
446
+ }) {
447
+ const settingsPath = cursorStoragePath({ home, platform, env });
448
+ if (!(await pathExists(settingsPath)) && !(await directoryExists(path.dirname(settingsPath)))) {
449
+ return { host: "cursor", status: "skipped", reason: "Cursor storage not detected" };
450
+ }
451
+ const storage = await readJsonOrDefault(settingsPath, {});
452
+ storage["mcp.mcpServers"] = storage["mcp.mcpServers"] ?? {};
453
+ storage["mcp.mcpServers"][serverName] = {
454
+ name: serverName,
455
+ type: "command",
456
+ command: [command, ...args.map((arg) => `"${arg.replaceAll('"', '\\"')}"`)].join(" ").trim(),
457
+ enabled: true,
458
+ };
459
+ await writeJsonConfig(settingsPath, storage);
460
+ return { host: "cursor", status: "configured", path: settingsPath };
461
+ }
462
+
463
+ export async function injectCodexMcpServer({ home = os.homedir(), serverName, command, args }) {
464
+ const codexRoot = path.join(home, ".codex");
465
+ if (!(await directoryExists(codexRoot))) {
466
+ return { host: "codex", status: "skipped", reason: "Codex config root not detected" };
467
+ }
468
+ const configPath = path.join(codexRoot, "config.toml");
469
+ const existing = (await fs.readFile(configPath, "utf8").catch(() => "")).trimEnd();
470
+ const header = `[mcp_servers.${quoteTomlString(serverName)}]`;
471
+ if (existing.includes(header)) {
472
+ return { host: "codex", status: "configured", path: configPath, idempotent: true };
473
+ }
474
+ const argList = `[${args.map((arg) => quoteTomlString(arg)).join(", ")}]`;
475
+ const block = ["", header, `command = ${quoteTomlString(command)}`, `args = ${argList}`, ""].join(
476
+ "\n",
477
+ );
478
+ await fs.mkdir(path.dirname(configPath), { recursive: true });
479
+ await fs.writeFile(configPath, `${existing}${block}`);
480
+ return { host: "codex", status: "configured", path: configPath };
481
+ }
482
+
483
+ export async function injectPluginToVendors({
484
+ manifest,
485
+ pluginDir,
486
+ wrapperPath,
487
+ home = os.homedir(),
488
+ targets = null,
489
+ platform = process.platform,
490
+ env = process.env,
491
+ useWrapper = true,
492
+ } = {}) {
493
+ const plugin = validateUniversalPluginManifest(manifest);
494
+ const selectedTargets = targets ?? (await detectInstalledPluginHosts({ home, platform, env }));
495
+ const serverName = mcpServerNameForPlugin(plugin.name);
496
+ const definition = createMcpDefinition({ manifest: plugin, pluginDir, wrapperPath, useWrapper });
497
+ const receipts = [];
498
+ for (const target of selectedTargets) {
499
+ if (target === "claude-code") {
500
+ receipts.push(await injectClaudeCodeMcpServer({ home, serverName, ...definition }));
501
+ } else if (target === "antigravity") {
502
+ receipts.push(await injectAntigravityMcpServer({ home, serverName, ...definition }));
503
+ } else if (target === "cursor") {
504
+ receipts.push(
505
+ await injectCursorMcpServer({ home, platform, env, serverName, ...definition }),
506
+ );
507
+ } else if (target === "codex") {
508
+ receipts.push(await injectCodexMcpServer({ home, serverName, ...definition }));
509
+ } else {
510
+ receipts.push({ host: target, status: "skipped", reason: "No MA MCP injector for host" });
511
+ }
512
+ }
513
+ return {
514
+ record_type: "vendor_manifest_injection",
515
+ plugin: plugin.name,
516
+ serverName,
517
+ mcp: definition,
518
+ receipts,
519
+ configured_hosts: receipts
520
+ .filter((receipt) => receipt.status === "configured")
521
+ .map((receipt) => receipt.host),
522
+ records_as: "plugin_compatibility_configuration",
523
+ build_evidence: false,
524
+ };
525
+ }
526
+
527
+ export async function installUniversalPlugin({
528
+ sourceDir,
529
+ home = os.homedir(),
530
+ targets = null,
531
+ platform = process.platform,
532
+ env = process.env,
533
+ useWrapper = true,
534
+ } = {}) {
535
+ if (!sourceDir) {
536
+ throw new Error("installUniversalPlugin requires sourceDir");
537
+ }
538
+ const manifest = await loadUniversalPluginManifest(sourceDir);
539
+ const sourceEntrypoint = path.join(sourceDir, manifest.entrypoint);
540
+ if (!(await pathExists(sourceEntrypoint))) {
541
+ throw new Error(`Plugin entrypoint missing: ${manifest.entrypoint}`);
542
+ }
543
+ const pluginDir = path.join(home, ".ma", "plugins", manifest.name);
544
+ const wrapperPath = path.join(home, ".ma", "bin", mcpServerNameForPlugin(manifest.name));
545
+ await fs.mkdir(path.dirname(pluginDir), { recursive: true });
546
+ await fs.rm(pluginDir, { recursive: true, force: true });
547
+ await fs.cp(sourceDir, pluginDir, { recursive: true });
548
+ await fs.mkdir(path.dirname(wrapperPath), { recursive: true });
549
+ await fs.writeFile(wrapperPath, renderWrapperScript({ manifest, pluginDir }));
550
+ await fs.chmod(wrapperPath, 0o755);
551
+ const contextSkillReceipt = await writePluginContextSkill({ manifest, home });
552
+ const vendorInjection = await injectPluginToVendors({
553
+ manifest,
554
+ pluginDir,
555
+ wrapperPath,
556
+ home,
557
+ targets,
558
+ platform,
559
+ env,
560
+ useWrapper,
561
+ });
562
+ return {
563
+ schemaVersion: universalPluginBrokerSchemaVersion,
564
+ record_type: "universal_plugin_broker_install_receipt",
565
+ plugin: manifest.name,
566
+ pluginDir,
567
+ wrapperPath,
568
+ contextSkillReceipt,
569
+ vendorInjection,
570
+ activation_notice:
571
+ "Plugin configured. Run /reload-mcp where supported or restart active agent sessions to activate newly injected MCP servers.",
572
+ records_as: "plugin_compatibility_configuration",
573
+ build_evidence: false,
574
+ };
575
+ }