@jstn-sdk/ma 0.1.12 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/.codex/agents/Architect.toml +1 -2
  2. package/.codex/agents/Auditor.toml +1 -2
  3. package/.codex/agents/Builder.toml +1 -2
  4. package/.codex/agents/Flow.toml +1 -2
  5. package/.codex/agents/Sage.toml +1 -2
  6. package/.codex/agents/Vibe.toml +1 -2
  7. package/.codex/hooks.json +24 -13
  8. package/.codex/prompts/enforcement.md +4 -0
  9. package/.codex/prompts/onboarding.md +6 -1
  10. package/.codex/prompts/release-rules.md +0 -0
  11. package/.codex/prompts/skill-contract.md +4 -0
  12. package/COVERAGE.md +211 -0
  13. package/DEMO.md +274 -0
  14. package/LICENSE +0 -0
  15. package/README.md +132 -656
  16. package/bin/ma.js +748 -55
  17. package/data/clone-data.ledger.json +41 -0
  18. package/data/clone-data.proof.json +50 -0
  19. package/data/clone-data.rvf +37 -0
  20. package/docs/README.md +10 -1
  21. package/docs/autonomous-tasks.md +46 -0
  22. package/docs/codex-integration.md +16 -0
  23. package/docs/getting-started.md +62 -22
  24. package/docs/mcp-setup.md +64 -0
  25. package/docs/qa/release-issue-gates-0.14.0.json +644 -0
  26. package/docs/qa/release-readiness-0.1.5.md +1 -1
  27. package/docs/qa/{release-readiness-0.1.12.md → release-readiness-0.14.0.md} +25 -9
  28. package/docs/quality.md +24 -0
  29. package/docs/reference/native-security-playbooks.md +6 -0
  30. package/docs/reference/native-source-selection.md +6 -0
  31. package/docs/release-spec.md +39 -10
  32. package/docs/skills.md +14 -1
  33. package/index.js +295 -2
  34. package/mcp/collections.json +23 -6
  35. package/mcp/fallback.json +0 -0
  36. package/mcp/local/code-intel.js +179 -41
  37. package/mcp/local/context.js +180 -0
  38. package/mcp/local/memory.js +17 -6
  39. package/mcp/local/quality.js +39 -0
  40. package/mcp/local/state.js +7 -3
  41. package/mcp/local/team-run.js +56 -4
  42. package/mcp/local-capabilities.json +9 -0
  43. package/mcp/servers.json +24 -0
  44. package/package.json +29 -4
  45. package/plugins/meta-architect/.app.json +1 -1
  46. package/plugins/meta-architect/.codex-plugin/plugin.json +1 -1
  47. package/plugins/meta-architect/.mcp.json +1 -1
  48. package/plugins/meta-architect/README.md +8 -1
  49. package/plugins/meta-architect/obsidian/main.js +534 -0
  50. package/plugins/meta-architect/obsidian/manifest.json +9 -0
  51. package/plugins/meta-architect/obsidian/styles.css +7 -0
  52. package/plugins/meta-architect/skills/arch/SKILL.md +2 -0
  53. package/plugins/meta-architect/skills/build/SKILL.md +3 -0
  54. package/plugins/meta-architect/skills/flow/SKILL.md +2 -0
  55. package/plugins/meta-architect/skills/sage/SKILL.md +2 -0
  56. package/plugins/meta-architect/skills/sage/references/source-selection.md +8 -0
  57. package/plugins/meta-architect/skills/vet/SKILL.md +2 -0
  58. package/plugins/meta-architect/skills/vet/references/security-playbooks.md +7 -0
  59. package/plugins/meta-architect/skills/vibe/SKILL.md +2 -0
  60. package/schemas/autonomous-task-queue.schema.json +56 -0
  61. package/schemas/handoff-packet.schema.json +36 -0
  62. package/schemas/setup-receipt.schema.json +28 -0
  63. package/schemas/skill-frontmatter.schema.json +20 -0
  64. package/schemas/support-bundle.schema.json +37 -0
  65. package/schemas/task-contract.schema.json +31 -0
  66. package/scripts/active-autonomy-hook.mjs +226 -0
  67. package/scripts/build-linux-packages.mjs +7 -3
  68. package/scripts/cleanup-test-fixtures.sh +55 -0
  69. package/scripts/context-hydration-hook.mjs +89 -0
  70. package/scripts/demo-smoke.js +285 -0
  71. package/scripts/doctor.js +27 -12
  72. package/scripts/install.sh +57 -0
  73. package/scripts/install.sh.sha256 +1 -0
  74. package/scripts/linux-package-smoke.mjs +2 -2
  75. package/scripts/package-size-check.mjs +26 -0
  76. package/scripts/plugin-sync.js +83 -17
  77. package/scripts/postinstall.js +17 -10
  78. package/scripts/prepack.js +9 -0
  79. package/scripts/ralph/prompt.md +35 -0
  80. package/scripts/release-sync.js +72 -7
  81. package/scripts/release-verify.js +260 -2
  82. package/scripts/setup-npmrc.js +48 -23
  83. package/scripts/skills-install.js +9 -2
  84. package/scripts/skills-manifest.js +2 -21
  85. package/scripts/skills-pack.js +2 -2
  86. package/scripts/skills-validate.js +5 -55
  87. package/skills/arch/SKILL.md +2 -0
  88. package/skills/build/SKILL.md +3 -0
  89. package/skills/flow/SKILL.md +2 -0
  90. package/skills/index.json +0 -0
  91. package/skills/sage/SKILL.md +2 -0
  92. package/skills/sage/references/source-selection.md +8 -0
  93. package/skills/vet/SKILL.md +2 -0
  94. package/skills/vet/references/security-playbooks.md +7 -0
  95. package/skills/vibe/SKILL.md +2 -0
  96. package/sprint/00-idea.md +0 -0
  97. package/sprint/01-architecture.md +0 -0
  98. package/sprint/02-oss-evidence.md +0 -0
  99. package/sprint/03-logic.md +0 -0
  100. package/sprint/04-security.md +0 -0
  101. package/sprint/05-dx-ux.md +0 -0
  102. package/sprint/06-build-plan.md +0 -0
  103. package/sprint/07-release.md +0 -0
  104. package/src/agents.js +279 -0
  105. package/src/bootstrap.js +268 -62
  106. package/src/build-gate.js +2 -2
  107. package/src/codex-app-server.js +291 -0
  108. package/src/decision-log.js +0 -0
  109. package/src/fs-utils.js +175 -4
  110. package/src/launcher.js +28 -21
  111. package/src/mcp-config.js +9 -1
  112. package/src/mcp-live-client.js +422 -3
  113. package/src/paths.js +10 -1
  114. package/src/policy.js +3 -3
  115. package/src/prelaunch.js +189 -0
  116. package/src/quality/ai-quality-orchestrator.js +328 -0
  117. package/src/release-issue-gates.js +252 -0
  118. package/src/release-operations.js +62 -0
  119. package/src/release-state.js +0 -0
  120. package/src/runtime/active-autonomy-core.js +208 -0
  121. package/src/runtime/agent-compat.js +31 -0
  122. package/src/runtime/alignment-sentinel.js +165 -0
  123. package/src/runtime/architect-review.js +326 -0
  124. package/src/runtime/autonomous-tasks.js +408 -0
  125. package/src/runtime/build-readiness.js +28 -4
  126. package/src/runtime/code-graph-rehearse.js +113 -0
  127. package/src/runtime/codeburn-core.js +112 -0
  128. package/src/runtime/context-authority.js +40 -0
  129. package/src/runtime/context-economy-core.js +316 -0
  130. package/src/runtime/continuity-graph.js +201 -0
  131. package/src/runtime/continuity-notes.js +71 -26
  132. package/src/runtime/core-source-ingest.js +379 -0
  133. package/src/runtime/detached-provider.js +142 -0
  134. package/src/runtime/doctor-report.js +18 -0
  135. package/src/runtime/environment-awareness-core.js +460 -0
  136. package/src/runtime/exposure-catalog.js +276 -0
  137. package/src/runtime/graphify-core.js +167 -0
  138. package/src/runtime/guidance-stack.js +9 -2
  139. package/src/runtime/handoff-packets.js +92 -0
  140. package/src/runtime/headroom-core.js +104 -0
  141. package/src/runtime/helper-orchestration-core.js +307 -0
  142. package/src/runtime/hook-profiles.js +20 -0
  143. package/src/runtime/learning-loop-core.js +344 -0
  144. package/src/runtime/live-agent-verification.js +122 -0
  145. package/src/runtime/maestro-events.js +18 -0
  146. package/src/runtime/maestro-manager.js +19 -0
  147. package/src/runtime/maestro-output.js +58 -0
  148. package/src/runtime/maestro-state.js +125 -0
  149. package/src/runtime/managed-markdown.js +29 -0
  150. package/src/runtime/mcp-authority.js +147 -0
  151. package/src/runtime/mcp-policy.js +192 -0
  152. package/src/runtime/obsidian-integration-core.js +1089 -0
  153. package/src/runtime/obsidian-plugin-bridge.js +1045 -0
  154. package/src/runtime/pi-maestro-core.js +89 -0
  155. package/src/runtime/preferences.js +79 -0
  156. package/src/runtime/project-context.js +636 -0
  157. package/src/runtime/prompt-strategy-core.js +230 -0
  158. package/src/runtime/quorum-review.js +90 -0
  159. package/src/runtime/ralph-execution-core.js +217 -0
  160. package/src/runtime/redaction-gateway.js +373 -0
  161. package/src/runtime/runtime-state.js +935 -2
  162. package/src/runtime/schema-migrations.js +162 -0
  163. package/src/runtime/semantic-recording-core.js +147 -0
  164. package/src/runtime/signal-hooks.js +28 -0
  165. package/src/runtime/skills-registry-export.js +754 -0
  166. package/src/runtime/task-contracts.js +83 -0
  167. package/src/runtime/universal-plugin-broker-core.js +898 -0
  168. package/src/runtime/workspace-intelligence-runtime.js +674 -0
  169. package/src/runtime/workspace-virtualizer.js +102 -0
  170. package/src/runtime-artifacts.js +225 -62
  171. package/src/setup-lifecycle.js +359 -0
  172. package/src/skill-frontmatter.js +78 -0
  173. package/src/skill-installer.js +226 -71
  174. package/src/skills.js +1166 -96
  175. package/src/state-sync.js +0 -0
  176. package/src/test-fixtures.js +543 -0
  177. package/src/tui/grid.js +67 -0
  178. package/src/tui/status-grid.js +23 -0
  179. package/support-bundle.json +114 -0
  180. package/templates/AGENTS.md +6 -2
  181. package/templates/catalog-manifest.json +0 -0
  182. package/templates/model-instructions/core.md +0 -0
  183. package/templates/model-instructions/release.md +0 -0
  184. package/templates/model-instructions/security.md +0 -0
  185. package/templates/quality/ai-quality-rules.yml +37 -0
  186. package/docs/assets/image/Screenshot(1).png +0 -0
  187. package/docs/assets/image/Screenshot(2).png +0 -0
  188. package/docs/assets/image/Screenshot(3).png +0 -0
  189. package/docs/assets/image/Screenshot(4).png +0 -0
  190. package/docs/assets/image/Screenshot(5).png +0 -0
  191. package/docs/assets/image/Screenshot(6).png +0 -0
  192. package/docs/assets/image/Screenshot(7).png +0 -0
  193. package/docs/assets/image/Screenshot(8).png +0 -0
  194. package/docs/assets/image/Screenshot(9).png +0 -0
  195. package/docs/assets/meta-architect-logo.png +0 -0
  196. package/docs/assets/meta-architect-logo.svg +0 -8
  197. package/docs/onboarding.md +0 -65
  198. package/docs/skills-publishing.md +0 -255
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { createBudgetedContext } from "../src/runtime/context-economy-core.js";
6
+ import { maskSensitiveText } from "../src/runtime/redaction-gateway.js";
7
+
8
+ const root = path.resolve(process.env.MA_ROOT || process.cwd());
9
+ const contextRoot = path.join(root, ".ma", "context");
10
+
11
+ async function read(relative, fallback = null) {
12
+ try {
13
+ return await fs.readFile(path.join(contextRoot, relative), "utf8");
14
+ } catch {
15
+ return fallback;
16
+ }
17
+ }
18
+
19
+ const [project, brief, commands, learning] = await Promise.all([
20
+ read("project-index.json"),
21
+ read("agent-brief.md", ""),
22
+ read("commands.json"),
23
+ fs.readFile(path.join(root, ".ma", "learning", "records.jsonl"), "utf8").catch(() => ""),
24
+ ]);
25
+ const preferences = await fs
26
+ .readFile(path.join(root, ".ma", "memory", "preferences.json"), "utf8")
27
+ .catch(() => "{}");
28
+ const records = learning
29
+ .split("\n")
30
+ .filter(Boolean)
31
+ .slice(-3)
32
+ .map((line) => {
33
+ try {
34
+ const value = JSON.parse(line);
35
+ return { claim: value.claim, status: value.status, authority: "learning_memory" };
36
+ } catch {
37
+ return null;
38
+ }
39
+ })
40
+ .filter(Boolean);
41
+
42
+ const result = {
43
+ record_type: "context_hydration",
44
+ root,
45
+ loaded: {
46
+ projectIndex: Boolean(project),
47
+ agentBrief: Boolean(brief),
48
+ commands: Boolean(commands),
49
+ learningRecords: records.length,
50
+ preferences: preferences !== "{}",
51
+ },
52
+ skipped: ["repository-wide source scan", "full Obsidian vault"],
53
+ stale: project
54
+ ? (() => {
55
+ try {
56
+ return JSON.parse(project).freshness?.status === "stale";
57
+ } catch {
58
+ return true;
59
+ }
60
+ })()
61
+ : true,
62
+ authority: "generated_context",
63
+ context: {
64
+ project: project ? maskSensitiveText(project).sanitizedText : null,
65
+ brief: maskSensitiveText(brief).sanitizedText,
66
+ commands: commands ? maskSensitiveText(commands).sanitizedText : null,
67
+ learning: records,
68
+ preferences: (() => {
69
+ try {
70
+ return JSON.parse(preferences).preferences ?? {};
71
+ } catch {
72
+ return {};
73
+ }
74
+ })(),
75
+ },
76
+ };
77
+ const budgeted = createBudgetedContext({
78
+ budgetChars: Number(process.env.MA_CONTEXT_BUDGET_CHARS || 12000),
79
+ topic: process.env.MA_CONTEXT_TOPIC,
80
+ items: [
81
+ { id: "agent-brief", tier: 1, topic: "brief", text: brief },
82
+ { id: "commands", tier: 3, topic: "commands", text: commands ?? "" },
83
+ { id: "learning", tier: 4, topic: "learning", text: JSON.stringify(records) },
84
+ { id: "preferences", tier: 4, topic: "preferences", text: preferences },
85
+ { id: "project-index", tier: 5, topic: "project", text: project ?? "" },
86
+ ],
87
+ });
88
+ result.budget = budgeted;
89
+ process.stdout.write(`${JSON.stringify(result)}\n`);
@@ -0,0 +1,285 @@
1
+ #!/usr/bin/env node
2
+
3
+ import assert from "node:assert/strict";
4
+ import fs from "node:fs/promises";
5
+ import path from "node:path";
6
+ import {
7
+ createContextEconomyView,
8
+ createDefaultLearningLoopCore,
9
+ createDefaultPromptStrategyCore,
10
+ createDiscoveredEnvironmentAwarenessCore,
11
+ createObsidianNote,
12
+ createRalphPrdContract,
13
+ evaluateLearningLoopReadiness,
14
+ indexObsidianVault,
15
+ readObsidianNote,
16
+ runIdea,
17
+ runInit,
18
+ updateObsidianNote,
19
+ validatePromptStrategyCore,
20
+ validateRalphPrdContract,
21
+ writeObsidianVaultIndex,
22
+ } from "../index.js";
23
+ import { loadRuntimeSnapshot } from "../src/runtime/runtime-state.js";
24
+ import { runMaestro } from "../src/skills.js";
25
+ import { createTestNamespace } from "../src/test-fixtures.js";
26
+
27
+ const demoIdea =
28
+ "Northstar Logistics wants to harden an existing dispatch analytics workspace for a production release. MA must inspect current runtime context, use Obsidian vault notes as brain context, route through relevant roles, preserve security gates, and produce a release-safe next action instead of asking for permission.";
29
+
30
+ async function writeFile(file, content) {
31
+ await fs.mkdir(path.dirname(file), { recursive: true });
32
+ await fs.writeFile(file, content, "utf8");
33
+ }
34
+
35
+ async function seedWorkspace(root) {
36
+ await writeFile(
37
+ path.join(root, "package.json"),
38
+ `${JSON.stringify(
39
+ {
40
+ name: "northstar-dispatch-analytics",
41
+ version: "2.4.0",
42
+ type: "module",
43
+ scripts: {
44
+ test: "node --test",
45
+ check: "node --check src/index.js",
46
+ release: "node scripts/release-check.js",
47
+ },
48
+ dependencies: {
49
+ "@modelcontextprotocol/sdk": "^1.0.0",
50
+ },
51
+ },
52
+ null,
53
+ 2,
54
+ )}\n`,
55
+ );
56
+ await writeFile(
57
+ path.join(root, "src", "index.js"),
58
+ "export const serviceName = 'northstar-dispatch-analytics';\n",
59
+ );
60
+ await writeFile(
61
+ path.join(root, "mcp", "servers.json"),
62
+ `${JSON.stringify(
63
+ {
64
+ mcpServers: {
65
+ "obsidian-api Docs": {
66
+ url: "https://gitmcp.io/obsidianmd/obsidian-api",
67
+ },
68
+ },
69
+ },
70
+ null,
71
+ 2,
72
+ )}\n`,
73
+ );
74
+ }
75
+
76
+ async function seedVault(vaultPath) {
77
+ await writeFile(
78
+ path.join(vaultPath, "Northstar", "Dispatch Release Objective.md"),
79
+ `---
80
+ ma_records_as: vault_context
81
+ ma_project: northstar-dispatch-analytics
82
+ ma_capabilities:
83
+ - maestro
84
+ - obsidian
85
+ - learning-loop
86
+ ma_source_workspace: northstar-dispatch-analytics
87
+ ---
88
+ # Dispatch Release Objective
89
+
90
+ Northstar Logistics needs a production-safe release plan for dispatch analytics.
91
+
92
+ ## Pain Points
93
+
94
+ - Missed gate ownership causes late release rollback risk. #release
95
+ - Unstructured knowledge lives in operations notes and is not visible to build planning. #knowledge
96
+ - Security and DX review happen after implementation instead of before build execution. #security #dx
97
+
98
+ ## Links
99
+
100
+ - [[Northstar/Risk Register]]
101
+ - [[Meta-Architect/Core Brain Context]]
102
+ `,
103
+ );
104
+ await writeFile(
105
+ path.join(vaultPath, "Northstar", "Risk Register.md"),
106
+ `---
107
+ ma_records_as: vault_context
108
+ ma_project: northstar-dispatch-analytics
109
+ ---
110
+ # Risk Register
111
+
112
+ Known risks:
113
+
114
+ - Third-party carrier API failures can block dispatch reconciliation.
115
+ - Customer PII must not be copied into prompt-bound release artifacts.
116
+ - [[Northstar/Dispatch Release Objective]] owns the business target.
117
+ `,
118
+ );
119
+ await writeFile(
120
+ path.join(vaultPath, "Meta-Architect", "Core Brain Context.md"),
121
+ `---
122
+ ma_records_as: vault_context
123
+ ma_project: northstar-dispatch-analytics
124
+ ---
125
+ # Core Brain Context
126
+
127
+ MA should treat this vault as semantic brain context only.
128
+ Authoritative build evidence must return through $maestro or the owning lane.
129
+ `,
130
+ );
131
+ }
132
+
133
+ function assertNoDemoBranding(value) {
134
+ const serialized = JSON.stringify(value);
135
+ assert.doesNotMatch(serialized, /Demo Account|Test Company|Sample Co|Acme/i);
136
+ }
137
+
138
+ async function main() {
139
+ const originalCwd = process.cwd();
140
+ const workspace = createTestNamespace("ma-real-demo");
141
+ const vaultPath = path.join(workspace, "Northstar Vault");
142
+ await seedWorkspace(workspace);
143
+ await seedVault(vaultPath);
144
+
145
+ process.chdir(workspace);
146
+ try {
147
+ await runInit();
148
+ await runIdea(demoIdea);
149
+ await runMaestro();
150
+
151
+ const createdNote = await createObsidianNote({
152
+ vaultPath,
153
+ notePath: "Meta-Architect/Release Gate Plan.md",
154
+ overwrite: true,
155
+ content: `---
156
+ ma_records_as: vault_context
157
+ ma_project: northstar-dispatch-analytics
158
+ ma_source_workspace: northstar-dispatch-analytics
159
+ ---
160
+ # Release Gate Plan
161
+
162
+ This note records the MA demo path for Northstar Logistics.
163
+
164
+ - Start at [[Meta-Architect/Map of Content]]
165
+ - Use [[Northstar/Dispatch Release Objective]] for buyer context
166
+ - Keep build evidence in MA lane artifacts
167
+ `,
168
+ });
169
+ await updateObsidianNote({
170
+ vaultPath,
171
+ notePath: "Meta-Architect/Release Gate Plan.md",
172
+ content: `${createdNote.relative_path ? "" : ""}---
173
+ ma_records_as: vault_context
174
+ ma_project: northstar-dispatch-analytics
175
+ ma_source_workspace: northstar-dispatch-analytics
176
+ ---
177
+ # Release Gate Plan
178
+
179
+ This note records the MA demo path for Northstar Logistics.
180
+
181
+ - Start at [[Meta-Architect/Map of Content]]
182
+ - Use [[Northstar/Dispatch Release Objective]] for buyer context
183
+ - Use [[Northstar/Risk Register]] for security and rollback concerns
184
+ - Keep build evidence in MA lane artifacts
185
+ `,
186
+ });
187
+ const readNote = await readObsidianNote({
188
+ vaultPath,
189
+ notePath: "Meta-Architect/Release Gate Plan.md",
190
+ });
191
+ const vaultIndex = await writeObsidianVaultIndex({ vaultPath });
192
+ const directVaultIndex = await indexObsidianVault({ vaultPath });
193
+
194
+ const learningReadiness = evaluateLearningLoopReadiness(createDefaultLearningLoopCore());
195
+ const promptStrategy = validatePromptStrategyCore(createDefaultPromptStrategyCore());
196
+ const contextEconomy = createContextEconomyView({
197
+ text: "The architecture lane should preserve exact commands, risk language, and verification gaps while compressing low-value filler for the live demo operator.",
198
+ level: "full",
199
+ });
200
+ const environmentAwareness = await createDiscoveredEnvironmentAwarenessCore({ cwd: workspace });
201
+ const ralphContract = validateRalphPrdContract(
202
+ createRalphPrdContract({
203
+ project: "northstar-dispatch-analytics",
204
+ branchName: "ralph/northstar-release-gates",
205
+ description: "Prepare release-safe dispatch analytics execution handoff.",
206
+ buildSlice: [
207
+ "Verify MA release gates before Ralph-style execution begins.",
208
+ "Preserve Obsidian context as vault_context, not build_evidence.",
209
+ ],
210
+ verificationPlan: [
211
+ "$arch, $sage, $flow, $vet, and $vibe are represented before $build.",
212
+ "Obsidian context is tagged as vault_context, not build_evidence.",
213
+ ],
214
+ }),
215
+ );
216
+ const runtimeSnapshot = await loadRuntimeSnapshot();
217
+
218
+ const proof = {
219
+ record_type: "ma_real_demo_smoke_proof",
220
+ workspace,
221
+ vaultPath,
222
+ idea: demoIdea,
223
+ maestro: {
224
+ global_status: runtimeSnapshot.maestroState.global_status,
225
+ track_count: Object.keys(runtimeSnapshot.maestroState.runtime_tracks).length,
226
+ next_plan_exists: true,
227
+ },
228
+ obsidian: {
229
+ note_count: vaultIndex.note_count,
230
+ records_as: vaultIndex.records_as,
231
+ build_evidence: vaultIndex.build_evidence,
232
+ graph_map_present: vaultIndex.notes.some(
233
+ (note) => note.relative_path === "Meta-Architect/Map of Content.md",
234
+ ),
235
+ linked_release_plan: readNote.content.includes("[[Meta-Architect/Map of Content]]"),
236
+ direct_index_note_count: directVaultIndex.note_count,
237
+ },
238
+ learning: {
239
+ domain_count: learningReadiness.domains,
240
+ verified_domain_count: learningReadiness.verified_domains,
241
+ ready: learningReadiness.ready,
242
+ },
243
+ prompt_strategy: {
244
+ lane_count: Object.keys(promptStrategy.lane_policy).length,
245
+ },
246
+ context_economy: {
247
+ mode: contextEconomy.mode,
248
+ preserved_exact_count: contextEconomy.preserved_exact.length,
249
+ },
250
+ environment_awareness: {
251
+ capability_count: environmentAwareness.capabilities.length,
252
+ capability_types: [
253
+ ...new Set(environmentAwareness.capabilities.map((item) => item.capability_type)),
254
+ ].sort(),
255
+ },
256
+ ralph: {
257
+ story_count: ralphContract.userStories.length,
258
+ authority: ralphContract.authority,
259
+ },
260
+ };
261
+
262
+ assert.equal(proof.obsidian.records_as, "vault_context");
263
+ assert.equal(proof.obsidian.build_evidence, false);
264
+ assert.equal(proof.obsidian.graph_map_present, true);
265
+ assert.equal(proof.obsidian.linked_release_plan, true);
266
+ assert.equal(proof.learning.ready, true);
267
+ assert.equal(proof.ralph.story_count, 1);
268
+ assertNoDemoBranding(proof);
269
+
270
+ await writeFile(
271
+ path.join(workspace, ".ma", "evidence", "real-demo-smoke-proof.json"),
272
+ `${JSON.stringify(proof, null, 2)}\n`,
273
+ );
274
+ console.log(JSON.stringify(proof, null, 2));
275
+ } finally {
276
+ process.chdir(originalCwd);
277
+ }
278
+ }
279
+
280
+ try {
281
+ await main();
282
+ } catch (error) {
283
+ console.error(error instanceof Error ? error.message : String(error));
284
+ process.exitCode = 1;
285
+ }
package/scripts/doctor.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import fs from "node:fs";
4
+ import { printDoctorStatuses } from "../src/runtime/doctor-report.js";
4
5
  import {
5
6
  getDebArtifactName,
6
7
  getPacmanArtifactName,
@@ -28,7 +29,24 @@ const checks = [
28
29
  [".codex/hooks.json", fs.existsSync(".codex/hooks.json")],
29
30
  [".ma/decisions.json", fs.existsSync(".ma/decisions.json")],
30
31
  [".ma/release.json", fs.existsSync(".ma/release.json")],
32
+ [
33
+ ".ma/context/helper-orchestration-core.json",
34
+ fs.existsSync(".ma/context/helper-orchestration-core.json"),
35
+ ],
36
+ [
37
+ ".ma/context/environment-awareness-core.json",
38
+ fs.existsSync(".ma/context/environment-awareness-core.json"),
39
+ ],
40
+ [
41
+ ".ma/context/universal-plugin-broker-core.json",
42
+ fs.existsSync(".ma/context/universal-plugin-broker-core.json"),
43
+ ],
31
44
  ["docs/release-spec.md", fs.existsSync("docs/release-spec.md")],
45
+ ["DEMO.md", fs.existsSync("DEMO.md")],
46
+ ["COVERAGE.md", fs.existsSync("COVERAGE.md")],
47
+ ["data/clone-data.proof.json", fs.existsSync("data/clone-data.proof.json")],
48
+ ["data/clone-data.ledger.json", fs.existsSync("data/clone-data.ledger.json")],
49
+ ["data/clone-data.rvf", fs.existsSync("data/clone-data.rvf")],
32
50
  [".agents/plugins/marketplace.json", fs.existsSync(".agents/plugins/marketplace.json")],
33
51
  ["plugins/meta-architect/.app.json", fs.existsSync("plugins/meta-architect/.app.json")],
34
52
  [
@@ -36,21 +54,18 @@ const checks = [
36
54
  fs.existsSync("plugins/meta-architect/.codex-plugin/plugin.json"),
37
55
  ],
38
56
  ["plugins/meta-architect/.mcp.json", fs.existsSync("plugins/meta-architect/.mcp.json")],
39
- [
40
- "missions/collaborative-whiteboard/mission.md",
41
- fs.existsSync("missions/collaborative-whiteboard/mission.md"),
42
- ],
43
57
  ["prompts/architect.md", fs.existsSync("prompts/architect.md")],
44
58
  ];
45
59
 
46
- let failures = 0;
60
+ const statuses = [];
47
61
  for (const [label, ok] of checks) {
48
- if (ok) {
49
- console.log(`[OK] ${label}`);
50
- } else {
51
- console.log(`[MISSING] ${label}`);
52
- failures += 1;
53
- }
62
+ statuses.push({
63
+ kind: ok ? "OK" : "BLOCKED",
64
+ label,
65
+ detail: ok ? "present" : "missing release artifact",
66
+ });
54
67
  }
55
68
 
56
- process.exitCode = failures === 0 ? 0 : 1;
69
+ console.log("Scope: maintainer release/package artifacts (not installed environment health)");
70
+ const result = printDoctorStatuses("Meta-Architect Package Doctor", statuses);
71
+ process.exitCode = result === "READY" ? 0 : 1;
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env sh
2
+ set -eu
3
+
4
+ MA_PACKAGE="${MA_PACKAGE:-@jstn-sdk/ma@0.14.0}"
5
+ CODEX_PACKAGE="${CODEX_PACKAGE:-@openai/codex@latest}"
6
+ NO_SETUP=0
7
+ NO_SKILLS=0
8
+ DRY_RUN=0
9
+
10
+ for arg in "$@"; do
11
+ case "$arg" in
12
+ --no-setup) NO_SETUP=1 ;;
13
+ --no-skills) NO_SKILLS=1 ;;
14
+ --dry-run) DRY_RUN=1 ;;
15
+ *) echo "meta-architect install: unknown option: $arg" >&2; exit 2 ;;
16
+ esac
17
+ done
18
+
19
+ need_cmd() {
20
+ command -v "$1" >/dev/null 2>&1 || {
21
+ echo "meta-architect install: missing required command: $1" >&2
22
+ exit 1
23
+ }
24
+ }
25
+
26
+ need_cmd npm
27
+ need_cmd node
28
+
29
+ echo "meta-architect install: plan"
30
+ echo " install: $CODEX_PACKAGE $MA_PACKAGE"
31
+ echo " setup: $([ "$NO_SETUP" -eq 1 ] && echo disabled || echo enabled)"
32
+ echo " skills: $([ "$NO_SKILLS" -eq 1 ] && echo disabled || echo enabled)"
33
+ echo " undo: npm uninstall -g $MA_PACKAGE $CODEX_PACKAGE"
34
+
35
+ if [ "$DRY_RUN" -eq 1 ]; then
36
+ echo "meta-architect install: dry-run complete"
37
+ exit 0
38
+ fi
39
+
40
+ if [ "$NO_SKILLS" -eq 1 ]; then export MA_SKIP_SKILLS=1; fi
41
+ npm i -g "$CODEX_PACKAGE" "$MA_PACKAGE"
42
+ echo "meta-architect install: versions"
43
+ node --version
44
+ npm --version
45
+ command -v codex >/dev/null 2>&1 && codex --version || echo "codex: unavailable"
46
+
47
+ if command -v ma >/dev/null 2>&1 && [ "$NO_SETUP" -eq 0 ]; then
48
+ echo "meta-architect install: seeding local MA runtime"
49
+ ma setup
50
+ echo "meta-architect install: ready"
51
+ echo "start: ma --madmax --high"
52
+ elif [ "$NO_SETUP" -eq 1 ]; then
53
+ echo "meta-architect install: setup disabled by --no-setup"
54
+ else
55
+ echo "meta-architect install: ma command not found after npm install" >&2
56
+ exit 1
57
+ fi
@@ -0,0 +1 @@
1
+ 0a18d94ded8872eae750cd2cc4a5b6ab05a6c48796def3634bc2414b3d48f7f7 scripts/install.sh
@@ -3,9 +3,9 @@
3
3
  import assert from "node:assert/strict";
4
4
  import { spawnSync } from "node:child_process";
5
5
  import fs from "node:fs/promises";
6
- import os from "node:os";
7
6
  import path from "node:path";
8
7
  import process from "node:process";
8
+ import { createTestNamespace } from "../src/test-fixtures.js";
9
9
  import {
10
10
  getDebArtifactPath,
11
11
  getPacmanArtifactPath,
@@ -61,7 +61,7 @@ async function main() {
61
61
  throw new Error("Native Linux package smoke validation is supported only on Linux hosts");
62
62
  }
63
63
 
64
- const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "meta-architect-linux-smoke-"));
64
+ const tempRoot = createTestNamespace("meta-architect-linux-smoke");
65
65
  const debExtractRoot = path.join(tempRoot, "deb");
66
66
  const pacmanExtractRoot = path.join(tempRoot, "pacman");
67
67
  const rpmExtractRoot = path.join(tempRoot, "rpm");
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from "node:child_process";
4
+
5
+ const budget = Number(process.env.MA_PACKAGE_SIZE_BUDGET ?? 5 * 1024 * 1024);
6
+ const result = spawnSync("npm", ["pack", "--dry-run", "--json", "--ignore-scripts"], {
7
+ encoding: "utf8",
8
+ });
9
+ if (result.status !== 0) throw new Error(result.stderr || "npm pack inspection failed");
10
+ const pack = JSON.parse(result.stdout)[0];
11
+ const excludedMedia = pack.files.filter((file) => /docs\/assets\//.test(file.path));
12
+ if (excludedMedia.length > 0)
13
+ throw new Error(
14
+ `Heavy demo media included: ${excludedMedia.map((file) => file.path).join(", ")}`,
15
+ );
16
+ const unexpectedExecutables = pack.files.filter(
17
+ (file) =>
18
+ file.mode === 493 && !file.path.startsWith("bin/") && !/^scripts\/[^/]+\.sh$/.test(file.path),
19
+ );
20
+ if (unexpectedExecutables.length > 0) {
21
+ throw new Error(
22
+ `Unexpected executable package files: ${unexpectedExecutables.map((file) => file.path).join(", ")}`,
23
+ );
24
+ }
25
+ console.log(`package size: ${pack.size} bytes (budget ${budget})`);
26
+ if (pack.size > budget) throw new Error(`Package exceeds ${budget} byte budget`);
@@ -3,6 +3,7 @@
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import process from "node:process";
6
+ import { fileURLToPath } from "node:url";
6
7
 
7
8
  const repoRoot = process.cwd();
8
9
  const skillsRoot = path.join(repoRoot, "skills");
@@ -30,10 +31,23 @@ async function copyDir(src, dest) {
30
31
  }
31
32
  }
32
33
 
34
+ async function linkOrCopyDir(src, dest) {
35
+ if (process.env.MA_PLUGIN_SYNC_MODE !== "copy") {
36
+ try {
37
+ await fs.symlink(path.relative(path.dirname(dest), src), dest, "dir");
38
+ return "symlink";
39
+ } catch {
40
+ // Restricted worktrees and Windows may reject directory links.
41
+ }
42
+ }
43
+ await copyDir(src, dest);
44
+ return "copy";
45
+ }
46
+
33
47
  async function listSkillDirs(root) {
34
- const entries = await fs.readdir(root, { withFileTypes: true }).catch(() => []);
48
+ const entries = await fs.readdir(root, { withFileTypes: true });
35
49
  return entries
36
- .filter((entry) => entry.isDirectory())
50
+ .filter((entry) => entry.isDirectory() || entry.isSymbolicLink())
37
51
  .map((entry) => entry.name)
38
52
  .sort();
39
53
  }
@@ -53,24 +67,74 @@ async function syncPluginSkills() {
53
67
  const src = path.join(skillsRoot, name);
54
68
  const dest = path.join(pluginSkillsRoot, name);
55
69
  await fs.rm(dest, { recursive: true, force: true });
56
- await copyDir(src, dest);
70
+ await linkOrCopyDir(src, dest);
57
71
  }
58
72
 
59
73
  return skillDirs;
60
74
  }
61
75
 
62
- async function verifyPluginSkills() {
63
- const skillDirs = await listSkillDirs(skillsRoot);
64
- const pluginDirs = await listSkillDirs(pluginSkillsRoot);
65
- if (skillDirs.length !== pluginDirs.length) {
66
- throw new Error("Plugin skill mirror drift: directory count mismatch");
76
+ async function listFiles(root, current = root, files = []) {
77
+ const entries = await fs.readdir(current, { withFileTypes: true });
78
+ for (const entry of entries) {
79
+ const absolutePath = path.join(current, entry.name);
80
+ if (entry.isDirectory() || entry.isSymbolicLink()) {
81
+ await listFiles(root, absolutePath, files);
82
+ } else if (entry.isFile()) {
83
+ files.push(path.relative(root, absolutePath));
84
+ }
85
+ }
86
+ return files.sort();
87
+ }
88
+
89
+ export async function verifyPluginSkills(sourceRoot = skillsRoot, mirrorRoot = pluginSkillsRoot) {
90
+ await fs.access(sourceRoot);
91
+ await fs.access(mirrorRoot);
92
+ const sourceSkillDirs = await listSkillDirs(sourceRoot);
93
+ const mirrorSkillDirs = await listSkillDirs(mirrorRoot);
94
+ const filesForSkillDirs = async (root, skillDirs) => {
95
+ const files = [];
96
+ for (const skillDir of skillDirs) {
97
+ for (const relativePath of await listFiles(path.join(root, skillDir))) {
98
+ files.push(path.join(skillDir, relativePath));
99
+ }
100
+ }
101
+ return files.sort();
102
+ };
103
+ const sourceFiles = await filesForSkillDirs(sourceRoot, sourceSkillDirs);
104
+ const mirrorFiles = await filesForSkillDirs(mirrorRoot, mirrorSkillDirs);
105
+ if (JSON.stringify(sourceFiles) !== JSON.stringify(mirrorFiles)) {
106
+ throw new Error("Plugin skill mirror drift: file set mismatch");
67
107
  }
68
108
 
69
- for (let index = 0; index < skillDirs.length; index += 1) {
70
- if (skillDirs[index] !== pluginDirs[index]) {
71
- throw new Error(
72
- `Plugin skill mirror drift: expected ${skillDirs[index]}, found ${pluginDirs[index]}`,
73
- );
109
+ for (const relativePath of sourceFiles) {
110
+ const sourcePath = path.join(sourceRoot, relativePath);
111
+ const mirrorPath = path.join(mirrorRoot, relativePath);
112
+ const [source, mirror, sourceStat, mirrorStat] = await Promise.all([
113
+ fs.readFile(sourcePath),
114
+ fs.readFile(mirrorPath),
115
+ fs.stat(sourcePath),
116
+ fs.stat(mirrorPath),
117
+ ]);
118
+ if (!source.equals(mirror)) {
119
+ throw new Error(`Plugin skill mirror drift: content mismatch at ${relativePath}`);
120
+ }
121
+ if ((sourceStat.mode & 0o777) !== (mirrorStat.mode & 0o777)) {
122
+ throw new Error(`Plugin skill mirror drift: mode mismatch at ${relativePath}`);
123
+ }
124
+ }
125
+ for (const skillDir of sourceSkillDirs) {
126
+ const mirrorPath = path.join(mirrorRoot, skillDir);
127
+ const mirrorStat = await fs.lstat(mirrorPath);
128
+ if (mirrorStat.isSymbolicLink()) {
129
+ const [sourceRealPath, mirrorRealPath] = await Promise.all([
130
+ fs.realpath(path.join(sourceRoot, skillDir)),
131
+ fs.realpath(mirrorPath),
132
+ ]);
133
+ if (sourceRealPath !== mirrorRealPath) {
134
+ throw new Error(`Plugin skill mirror drift: invalid symlink target at ${skillDir}`);
135
+ }
136
+ } else if (!mirrorStat.isDirectory()) {
137
+ throw new Error(`Plugin skill mirror drift: invalid mirror entry at ${skillDir}`);
74
138
  }
75
139
  }
76
140
  }
@@ -87,7 +151,9 @@ async function main() {
87
151
  console.log(pluginSkillsRoot);
88
152
  }
89
153
 
90
- main().catch((error) => {
91
- console.error(error.message);
92
- process.exitCode = 1;
93
- });
154
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
155
+ main().catch((error) => {
156
+ console.error(error.message);
157
+ process.exitCode = 1;
158
+ });
159
+ }