martin-loop 0.1.4 → 1.3.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 (286) hide show
  1. package/CODE_OF_CONDUCT.md +32 -0
  2. package/README.md +172 -227
  3. package/demo/seeded-workspace/README.md +35 -0
  4. package/demo/seeded-workspace/TASKS.md +29 -0
  5. package/demo/seeded-workspace/martin.config.yaml +11 -0
  6. package/demo/seeded-workspace/package.json +8 -0
  7. package/demo/seeded-workspace/src/invoice-summary.js +11 -0
  8. package/demo/seeded-workspace/test/invoice-summary.test.js +20 -0
  9. package/dist/bin/martin-loop.js +0 -0
  10. package/dist/vendor/adapters/claude-cli.d.ts +19 -4
  11. package/dist/vendor/adapters/claude-cli.js +55 -24
  12. package/dist/vendor/adapters/cli-bridge.d.ts +1 -0
  13. package/dist/vendor/adapters/cli-bridge.js +154 -28
  14. package/dist/vendor/adapters/counter.d.ts +1 -0
  15. package/dist/vendor/adapters/counter.js +4 -0
  16. package/dist/vendor/adapters/git-baseline.d.ts +50 -0
  17. package/dist/vendor/adapters/git-baseline.js +233 -0
  18. package/dist/vendor/adapters/index.d.ts +1 -0
  19. package/dist/vendor/adapters/index.js +1 -0
  20. package/dist/vendor/adapters/openrouter-adapter.d.ts +15 -0
  21. package/dist/vendor/adapters/openrouter-adapter.js +302 -0
  22. package/dist/vendor/adapters/usage.d.ts +48 -0
  23. package/dist/vendor/adapters/usage.js +66 -0
  24. package/dist/vendor/adapters/verifier-only.d.ts +7 -0
  25. package/dist/vendor/adapters/verifier-only.js +57 -0
  26. package/dist/vendor/cli/bin/exit.d.ts +12 -0
  27. package/dist/vendor/cli/bin/exit.js +28 -0
  28. package/dist/vendor/cli/commands/analyze.d.ts +5 -0
  29. package/dist/vendor/cli/commands/analyze.js +58 -0
  30. package/dist/vendor/cli/commands/audit-log-verify.d.ts +34 -0
  31. package/dist/vendor/cli/commands/audit-log-verify.js +99 -0
  32. package/dist/vendor/cli/commands/audit.d.ts +8 -0
  33. package/dist/vendor/cli/commands/audit.js +199 -0
  34. package/dist/vendor/cli/commands/corpus.d.ts +5 -0
  35. package/dist/vendor/cli/commands/corpus.js +60 -0
  36. package/dist/vendor/cli/commands/doctor.d.ts +8 -0
  37. package/dist/vendor/cli/commands/doctor.js +219 -0
  38. package/dist/vendor/cli/commands/explain.d.ts +17 -0
  39. package/dist/vendor/cli/commands/explain.js +176 -0
  40. package/dist/vendor/cli/commands/export.d.ts +5 -0
  41. package/dist/vendor/cli/commands/export.js +60 -0
  42. package/dist/vendor/cli/commands/governance.d.ts +8 -0
  43. package/dist/vendor/cli/commands/governance.js +95 -0
  44. package/dist/vendor/cli/commands/improve.d.ts +18 -0
  45. package/dist/vendor/cli/commands/improve.js +396 -0
  46. package/dist/vendor/cli/commands/init.d.ts +8 -0
  47. package/dist/vendor/cli/commands/init.js +281 -0
  48. package/dist/vendor/cli/commands/migration.d.ts +8 -0
  49. package/dist/vendor/cli/commands/migration.js +67 -0
  50. package/dist/vendor/cli/commands/prior.d.ts +23 -0
  51. package/dist/vendor/cli/commands/prior.js +145 -0
  52. package/dist/vendor/cli/commands/resume.d.ts +21 -0
  53. package/dist/vendor/cli/commands/resume.js +73 -0
  54. package/dist/vendor/cli/commands/verify.d.ts +6 -0
  55. package/dist/vendor/cli/commands/verify.js +43 -0
  56. package/dist/vendor/cli/index.d.ts +6 -1
  57. package/dist/vendor/cli/index.js +124 -7
  58. package/dist/vendor/cli/research/public-corpus.d.ts +43 -0
  59. package/dist/vendor/cli/research/public-corpus.js +151 -0
  60. package/dist/vendor/cli/ui/error-card.d.ts +38 -0
  61. package/dist/vendor/cli/ui/error-card.js +103 -0
  62. package/dist/vendor/cli/ui/mission-brief.d.ts +41 -0
  63. package/dist/vendor/cli/ui/mission-brief.js +173 -0
  64. package/dist/vendor/cli/ui/summary-card.d.ts +34 -0
  65. package/dist/vendor/cli/ui/summary-card.js +102 -0
  66. package/dist/vendor/contracts/audit.d.ts +46 -0
  67. package/dist/vendor/contracts/audit.js +360 -0
  68. package/dist/vendor/contracts/index.d.ts +3 -1
  69. package/dist/vendor/contracts/post-phase15.d.ts +240 -0
  70. package/dist/vendor/contracts/post-phase15.js +166 -0
  71. package/dist/vendor/core/agent/mandates.d.ts +46 -0
  72. package/dist/vendor/core/agent/mandates.js +178 -0
  73. package/dist/vendor/core/agent/receipts.d.ts +38 -0
  74. package/dist/vendor/core/agent/receipts.js +131 -0
  75. package/dist/vendor/core/agent/signing.d.ts +17 -0
  76. package/dist/vendor/core/agent/signing.js +91 -0
  77. package/dist/vendor/core/attestation/sign.d.ts +25 -0
  78. package/dist/vendor/core/attestation/sign.js +216 -0
  79. package/dist/vendor/core/autonomy/autonomous-promotion.d.ts +120 -0
  80. package/dist/vendor/core/autonomy/autonomous-promotion.js +346 -0
  81. package/dist/vendor/core/autonomy/envelope-v2.d.ts +29 -0
  82. package/dist/vendor/core/autonomy/envelope-v2.js +60 -0
  83. package/dist/vendor/core/autonomy/envelope.d.ts +17 -0
  84. package/dist/vendor/core/autonomy/envelope.js +27 -0
  85. package/dist/vendor/core/autonomy/escalation-ledger.d.ts +20 -0
  86. package/dist/vendor/core/autonomy/escalation-ledger.js +18 -0
  87. package/dist/vendor/core/autonomy/resume.d.ts +15 -0
  88. package/dist/vendor/core/autonomy/resume.js +23 -0
  89. package/dist/vendor/core/circuit/circuit-breaker.d.ts +60 -0
  90. package/dist/vendor/core/circuit/circuit-breaker.js +143 -0
  91. package/dist/vendor/core/compiler.d.ts +2 -0
  92. package/dist/vendor/core/compiler.js +10 -4
  93. package/dist/vendor/core/context-distillation.d.ts +3 -0
  94. package/dist/vendor/core/context-distillation.js +44 -0
  95. package/dist/vendor/core/context-flow/compile-context.d.ts +8 -0
  96. package/dist/vendor/core/context-flow/compile-context.js +111 -0
  97. package/dist/vendor/core/context-flow/entities.d.ts +2 -0
  98. package/dist/vendor/core/context-flow/entities.js +44 -0
  99. package/dist/vendor/core/context-flow/evaluate-policy.d.ts +2 -0
  100. package/dist/vendor/core/context-flow/evaluate-policy.js +42 -0
  101. package/dist/vendor/core/context-flow/index.d.ts +11 -0
  102. package/dist/vendor/core/context-flow/index.js +24 -0
  103. package/dist/vendor/core/context-flow/labels.d.ts +3 -0
  104. package/dist/vendor/core/context-flow/labels.js +17 -0
  105. package/dist/vendor/core/context-flow/normalizer.d.ts +9 -0
  106. package/dist/vendor/core/context-flow/normalizer.js +69 -0
  107. package/dist/vendor/core/context-flow/profiles.d.ts +33 -0
  108. package/dist/vendor/core/context-flow/profiles.js +36 -0
  109. package/dist/vendor/core/context-flow/redaction.d.ts +1 -0
  110. package/dist/vendor/core/context-flow/redaction.js +6 -0
  111. package/dist/vendor/core/context-flow/sensitivity.d.ts +2 -0
  112. package/dist/vendor/core/context-flow/sensitivity.js +27 -0
  113. package/dist/vendor/core/context-flow/sync-preview.d.ts +2 -0
  114. package/dist/vendor/core/context-flow/sync-preview.js +22 -0
  115. package/dist/vendor/core/context-flow/token-estimator.d.ts +3 -0
  116. package/dist/vendor/core/context-flow/token-estimator.js +13 -0
  117. package/dist/vendor/core/context-flow/types.d.ts +91 -0
  118. package/dist/vendor/core/context-flow/types.js +2 -0
  119. package/dist/vendor/core/context-integrity.d.ts +26 -0
  120. package/dist/vendor/core/context-integrity.js +56 -0
  121. package/dist/vendor/core/context-utility.d.ts +47 -0
  122. package/dist/vendor/core/context-utility.js +405 -0
  123. package/dist/vendor/core/cost/pipeline.d.ts +92 -0
  124. package/dist/vendor/core/cost/pipeline.js +141 -0
  125. package/dist/vendor/core/cost/tagged-cost.d.ts +27 -0
  126. package/dist/vendor/core/cost/tagged-cost.js +55 -0
  127. package/dist/vendor/core/cost-governor.d.ts +2 -0
  128. package/dist/vendor/core/cost-governor.js +50 -0
  129. package/dist/vendor/core/cve/cve-check.d.ts +80 -0
  130. package/dist/vendor/core/cve/cve-check.js +172 -0
  131. package/dist/vendor/core/digital-twin/index.d.ts +27 -0
  132. package/dist/vendor/core/digital-twin/index.js +90 -0
  133. package/dist/vendor/core/drift/drift-graph.d.ts +47 -0
  134. package/dist/vendor/core/drift/drift-graph.js +100 -0
  135. package/dist/vendor/core/drift/objective-lock.d.ts +69 -0
  136. package/dist/vendor/core/drift/objective-lock.js +88 -0
  137. package/dist/vendor/core/drift/scope.d.ts +46 -0
  138. package/dist/vendor/core/drift/scope.js +102 -0
  139. package/dist/vendor/core/drift/signature-lock.d.ts +48 -0
  140. package/dist/vendor/core/drift/signature-lock.js +202 -0
  141. package/dist/vendor/core/drift/stale-proof-gate.d.ts +21 -0
  142. package/dist/vendor/core/drift/stale-proof-gate.js +19 -0
  143. package/dist/vendor/core/eval/known-bad-world-runner.d.ts +24 -0
  144. package/dist/vendor/core/eval/known-bad-world-runner.js +256 -0
  145. package/dist/vendor/core/evidence/claim-audit.d.ts +18 -0
  146. package/dist/vendor/core/evidence/claim-audit.js +89 -0
  147. package/dist/vendor/core/exit-intelligence.d.ts +2 -0
  148. package/dist/vendor/core/exit-intelligence.js +58 -0
  149. package/dist/vendor/core/explain/formatter.d.ts +42 -0
  150. package/dist/vendor/core/explain/formatter.js +171 -0
  151. package/dist/vendor/core/explain/timeline.d.ts +29 -0
  152. package/dist/vendor/core/explain/timeline.js +213 -0
  153. package/dist/vendor/core/failure-taxonomy.d.ts +2 -0
  154. package/dist/vendor/core/failure-taxonomy.js +76 -0
  155. package/dist/vendor/core/gateway/index.d.ts +10 -0
  156. package/dist/vendor/core/gateway/index.js +12 -0
  157. package/dist/vendor/core/gateway/registry.d.ts +40 -0
  158. package/dist/vendor/core/gateway/registry.js +97 -0
  159. package/dist/vendor/core/gateway/transport.d.ts +31 -0
  160. package/dist/vendor/core/gateway/transport.js +82 -0
  161. package/dist/vendor/core/gateway/vault.d.ts +19 -0
  162. package/dist/vendor/core/gateway/vault.js +29 -0
  163. package/dist/vendor/core/graph/adapters.d.ts +43 -0
  164. package/dist/vendor/core/graph/adapters.js +91 -0
  165. package/dist/vendor/core/graph/hotspots.d.ts +22 -0
  166. package/dist/vendor/core/graph/hotspots.js +30 -0
  167. package/dist/vendor/core/graph/index.d.ts +1 -0
  168. package/dist/vendor/core/graph/index.js +2 -0
  169. package/dist/vendor/core/honey/honey-tokens.d.ts +32 -0
  170. package/dist/vendor/core/honey/honey-tokens.js +44 -0
  171. package/dist/vendor/core/index.d.ts +7 -4
  172. package/dist/vendor/core/index.js +222 -64
  173. package/dist/vendor/core/learning/bayesian-update.d.ts +31 -0
  174. package/dist/vendor/core/learning/bayesian-update.js +60 -0
  175. package/dist/vendor/core/learning/prior-sets.d.ts +42 -0
  176. package/dist/vendor/core/learning/prior-sets.js +111 -0
  177. package/dist/vendor/core/learning/promotion-gate.d.ts +17 -0
  178. package/dist/vendor/core/learning/promotion-gate.js +23 -0
  179. package/dist/vendor/core/leash/blast-radius.d.ts +42 -0
  180. package/dist/vendor/core/leash/blast-radius.js +156 -0
  181. package/dist/vendor/core/leash/policy-leash.d.ts +31 -0
  182. package/dist/vendor/core/leash/policy-leash.js +117 -0
  183. package/dist/vendor/core/memo/memo.d.ts +63 -0
  184. package/dist/vendor/core/memo/memo.js +97 -0
  185. package/dist/vendor/core/memory/learning-pipeline.d.ts +154 -0
  186. package/dist/vendor/core/memory/learning-pipeline.js +391 -0
  187. package/dist/vendor/core/memory/palace.d.ts +84 -0
  188. package/dist/vendor/core/memory/palace.js +379 -0
  189. package/dist/vendor/core/merge/ast-merge.d.ts +22 -0
  190. package/dist/vendor/core/merge/ast-merge.js +350 -0
  191. package/dist/vendor/core/merge/text-merge.d.ts +12 -0
  192. package/dist/vendor/core/merge/text-merge.js +182 -0
  193. package/dist/vendor/core/otel/tracer.d.ts +45 -0
  194. package/dist/vendor/core/otel/tracer.js +116 -0
  195. package/dist/vendor/core/parallel/parallel-attempts.d.ts +28 -0
  196. package/dist/vendor/core/parallel/parallel-attempts.js +41 -0
  197. package/dist/vendor/core/parallel/scorer.d.ts +24 -0
  198. package/dist/vendor/core/parallel/scorer.js +65 -0
  199. package/dist/vendor/core/pattern-detection.d.ts +64 -0
  200. package/dist/vendor/core/pattern-detection.js +108 -0
  201. package/dist/vendor/core/persistence/checkpoint.d.ts +44 -0
  202. package/dist/vendor/core/persistence/checkpoint.js +156 -0
  203. package/dist/vendor/core/persistence/cleanup.d.ts +22 -0
  204. package/dist/vendor/core/persistence/cleanup.js +131 -0
  205. package/dist/vendor/core/persistence/index.d.ts +2 -0
  206. package/dist/vendor/core/persistence/index.js +1 -0
  207. package/dist/vendor/core/persistence/runs-reader.d.ts +52 -0
  208. package/dist/vendor/core/persistence/runs-reader.js +84 -0
  209. package/dist/vendor/core/persistence/store.d.ts +6 -1
  210. package/dist/vendor/core/persistence/store.js +5 -0
  211. package/dist/vendor/core/policy/file-touch-quota.d.ts +60 -0
  212. package/dist/vendor/core/policy/file-touch-quota.js +105 -0
  213. package/dist/vendor/core/policy/policy-loader.d.ts +30 -0
  214. package/dist/vendor/core/policy/policy-loader.js +170 -0
  215. package/dist/vendor/core/policy/policy-schema.d.ts +55 -0
  216. package/dist/vendor/core/policy/policy-schema.js +78 -0
  217. package/dist/vendor/core/policy.d.ts +6 -0
  218. package/dist/vendor/core/probe/probe.d.ts +49 -0
  219. package/dist/vendor/core/probe/probe.js +115 -0
  220. package/dist/vendor/core/proof/patch-proof.d.ts +58 -0
  221. package/dist/vendor/core/proof/patch-proof.js +84 -0
  222. package/dist/vendor/core/proof/semantic-probe.d.ts +25 -0
  223. package/dist/vendor/core/proof/semantic-probe.js +82 -0
  224. package/dist/vendor/core/recovery/failure-mode-runner.d.ts +29 -0
  225. package/dist/vendor/core/recovery/failure-mode-runner.js +39 -0
  226. package/dist/vendor/core/red-blue/red-phase.d.ts +64 -0
  227. package/dist/vendor/core/red-blue/red-phase.js +141 -0
  228. package/dist/vendor/core/red-blue/risk-tiers.d.ts +22 -0
  229. package/dist/vendor/core/red-blue/risk-tiers.js +33 -0
  230. package/dist/vendor/core/replay/replay.d.ts +85 -0
  231. package/dist/vendor/core/replay/replay.js +109 -0
  232. package/dist/vendor/core/router/engine.d.ts +54 -0
  233. package/dist/vendor/core/router/engine.js +131 -0
  234. package/dist/vendor/core/router/index.d.ts +1 -0
  235. package/dist/vendor/core/router/index.js +2 -0
  236. package/dist/vendor/core/router/trust-calibration.d.ts +57 -0
  237. package/dist/vendor/core/router/trust-calibration.js +127 -0
  238. package/dist/vendor/core/run-martin.d.ts +2 -0
  239. package/dist/vendor/core/run-martin.js +287 -0
  240. package/dist/vendor/core/security/cve-scanner.d.ts +62 -0
  241. package/dist/vendor/core/security/cve-scanner.js +178 -0
  242. package/dist/vendor/core/sentinel/efficiency-sentinel.d.ts +29 -0
  243. package/dist/vendor/core/sentinel/efficiency-sentinel.js +30 -0
  244. package/dist/vendor/core/sentinel/progress-guard.d.ts +35 -0
  245. package/dist/vendor/core/sentinel/progress-guard.js +46 -0
  246. package/dist/vendor/core/siem/siem-emitter.d.ts +49 -0
  247. package/dist/vendor/core/siem/siem-emitter.js +157 -0
  248. package/dist/vendor/core/strategy/attempt-brief.d.ts +22 -0
  249. package/dist/vendor/core/strategy/attempt-brief.js +89 -0
  250. package/dist/vendor/core/summarize/diff-summary.d.ts +35 -0
  251. package/dist/vendor/core/summarize/diff-summary.js +204 -0
  252. package/dist/vendor/core/surface-signals.d.ts +21 -0
  253. package/dist/vendor/core/surface-signals.js +139 -0
  254. package/dist/vendor/core/truth/truth-wall.d.ts +51 -0
  255. package/dist/vendor/core/truth/truth-wall.js +69 -0
  256. package/dist/vendor/core/truth-spine.d.ts +26 -0
  257. package/dist/vendor/core/truth-spine.js +62 -0
  258. package/dist/vendor/core/types.d.ts +115 -0
  259. package/dist/vendor/core/types.js +2 -0
  260. package/dist/vendor/core/verification/tiered-verify.d.ts +17 -0
  261. package/dist/vendor/core/verification/tiered-verify.js +29 -0
  262. package/dist/vendor/core/verifier-pyramid.d.ts +32 -0
  263. package/dist/vendor/core/verifier-pyramid.js +111 -0
  264. package/dist/vendor/core/workflow-artifacts.d.ts +99 -0
  265. package/dist/vendor/core/workflow-artifacts.js +668 -0
  266. package/dist/vendor/core/wrap/supervised-run.d.ts +96 -0
  267. package/dist/vendor/core/wrap/supervised-run.js +178 -0
  268. package/docs/assets/cli-animated.svg +139 -0
  269. package/docs/assets/cli-static.svg +34 -0
  270. package/docs/assets/github-hero-v2.svg +23 -0
  271. package/docs/assets/martin-raplph.png.jpg +0 -0
  272. package/docs/assets/martinloop-logo.png +0 -0
  273. package/docs/assets/nvidia-inception-program-light.png +0 -0
  274. package/docs/assets/nvidia-inception-program.png +0 -0
  275. package/docs/assets/phase3c-sidesidebyside-demo.html +228 -0
  276. package/docs/assets/side-by-side.svg +134 -0
  277. package/docs/oss/CLAUDE-CODE-WALKTHROUGH.md +142 -0
  278. package/docs/oss/EXAMPLES.md +9 -1
  279. package/docs/oss/OSS-BOUNDARY-REPORT.json +109 -113
  280. package/docs/oss/OSS-BOUNDARY-REPORT.md +48 -48
  281. package/docs/oss/QUICKSTART.md +39 -4
  282. package/docs/oss/RALPH-LOOP-SAFETY.md +113 -0
  283. package/docs/oss/README.md +7 -4
  284. package/docs/oss/RELEASE-SURFACE-REPORT.json +46 -45
  285. package/docs/oss/RELEASE-SURFACE-REPORT.md +36 -35
  286. package/package.json +129 -49
@@ -0,0 +1,219 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { basename, join } from "node:path";
4
+ const RUNS_ROOT = join(homedir(), ".martin", "runs");
5
+ const CONFIG_SEARCH_PATHS = [
6
+ join(process.cwd(), "martin.config.yaml"),
7
+ join(homedir(), ".martin", "martin.config.yaml")
8
+ ];
9
+ export async function handleDoctorCommand(args, options = {}) {
10
+ const checkFilter = parseFlag(args, "--check");
11
+ const allMode = args.includes("--all");
12
+ const checks = await runChecks(checkFilter, options.cwd ?? process.cwd());
13
+ const failed = checks.filter((c) => c.status === "fail");
14
+ const passed = checks.filter((c) => c.status === "pass");
15
+ if (args.includes("--json")) {
16
+ return {
17
+ exitCode: failed.length > 0 ? 1 : 0,
18
+ stdout: JSON.stringify({ checks, passed: passed.length, failed: failed.length }, null, 2),
19
+ stderr: ""
20
+ };
21
+ }
22
+ const lines = [
23
+ "martin doctor",
24
+ "─".repeat(54)
25
+ ];
26
+ for (const check of checks) {
27
+ const icon = check.status === "pass" ? "✓" : check.status === "skip" ? "─" : "✗";
28
+ const label = check.name.padEnd(34);
29
+ const detail = check.detail;
30
+ lines.push(`${icon} ${label} ${detail}`);
31
+ if (check.status === "fail" && check.hint) {
32
+ lines.push(` ${" ".repeat(34)} Hint: ${check.hint}`);
33
+ }
34
+ }
35
+ lines.push("─".repeat(54));
36
+ if (failed.length === 0) {
37
+ lines.push(`All ${passed.length} checks passed. Martin Loop is ready.`);
38
+ }
39
+ else {
40
+ const plural = failed.length === 1 ? "check" : "checks";
41
+ lines.push(`${failed.length} ${plural} failed. Fix the above and re-run martin doctor.`);
42
+ }
43
+ return {
44
+ exitCode: failed.length > 0 ? 1 : 0,
45
+ stdout: lines.join("\n"),
46
+ stderr: ""
47
+ };
48
+ }
49
+ async function runChecks(filter, cwd) {
50
+ const allChecks = [
51
+ { id: "credentials", fn: checkAdapterCredentials },
52
+ { id: "budget", fn: () => checkBudgetConfig(cwd) },
53
+ { id: "policy", fn: () => checkPolicyFile(cwd) },
54
+ { id: "leash", fn: checkLeashConfig },
55
+ { id: "store", fn: checkRunStore },
56
+ ];
57
+ const selected = filter
58
+ ? allChecks.filter((c) => c.id === filter)
59
+ : allChecks;
60
+ const results = [];
61
+ for (const check of selected) {
62
+ try {
63
+ results.push(await check.fn());
64
+ }
65
+ catch (err) {
66
+ results.push({
67
+ name: check.id,
68
+ status: "fail",
69
+ detail: "FAIL — unexpected error",
70
+ hint: err instanceof Error ? err.message : String(err)
71
+ });
72
+ }
73
+ }
74
+ return results;
75
+ }
76
+ async function checkAdapterCredentials() {
77
+ const anthropicKey = process.env.ANTHROPIC_API_KEY;
78
+ const openaiKey = process.env.OPENAI_API_KEY;
79
+ if (anthropicKey && anthropicKey.length > 10) {
80
+ return {
81
+ name: "Adapter credentials",
82
+ status: "pass",
83
+ detail: "OK (ANTHROPIC_API_KEY set)"
84
+ };
85
+ }
86
+ if (openaiKey && openaiKey.length > 10) {
87
+ return {
88
+ name: "Adapter credentials",
89
+ status: "pass",
90
+ detail: "OK (OPENAI_API_KEY set)"
91
+ };
92
+ }
93
+ return {
94
+ name: "Adapter credentials",
95
+ status: "fail",
96
+ detail: "FAIL — no API key found",
97
+ hint: "Set ANTHROPIC_API_KEY or OPENAI_API_KEY in your environment"
98
+ };
99
+ }
100
+ async function checkBudgetConfig(cwd) {
101
+ // Check for config file with a budget set
102
+ for (const configPath of [join(cwd, "martin.config.yaml"), ...CONFIG_SEARCH_PATHS]) {
103
+ try {
104
+ const contents = await readFile(configPath, "utf8");
105
+ const maxUsdMatch = contents.match(/maxUsd:\s*([0-9.]+)/);
106
+ const maxUsdValue = maxUsdMatch?.[1];
107
+ if (maxUsdValue) {
108
+ const val = parseFloat(maxUsdValue);
109
+ if (val > 0) {
110
+ return {
111
+ name: "Budget configured",
112
+ status: "pass",
113
+ detail: `OK ($${val.toFixed(2)} max per run in ${basename(configPath)})`
114
+ };
115
+ }
116
+ }
117
+ }
118
+ catch { /* not found — continue */ }
119
+ }
120
+ return {
121
+ name: "Budget configured",
122
+ status: "pass",
123
+ detail: "OK (using default $10.00 — set maxUsd in martin.config.yaml to override)"
124
+ };
125
+ }
126
+ async function checkPolicyFile(cwd) {
127
+ const policyPaths = [
128
+ join(cwd, "martin.policy.yaml"),
129
+ join(homedir(), ".martin", "martin.policy.yaml")
130
+ ];
131
+ for (const policyPath of policyPaths) {
132
+ try {
133
+ const contents = await readFile(policyPath, "utf8");
134
+ // Basic YAML validation: check for obvious typos in known fields
135
+ const knownFields = ["budgetUsd", "allowedVerifiers", "blockedCommands", "fileScopeGlob", "maxAttempts", "requireApprovalAboveUsd", "siem", "cveCheck", "circuitBreaker", "output"];
136
+ const lines = contents.split("\n");
137
+ for (const line of lines) {
138
+ const topLevelMatch = line.match(/^([A-Za-z][\w-]*):/);
139
+ const key = topLevelMatch?.[1];
140
+ if (key) {
141
+ if (!knownFields.includes(key)) {
142
+ return {
143
+ name: "Policy file",
144
+ status: "fail",
145
+ detail: `FAIL — ${basename(policyPath)}: unknown field '${key}'`,
146
+ hint: `Known fields: ${knownFields.join(", ")}`
147
+ };
148
+ }
149
+ }
150
+ }
151
+ return {
152
+ name: "Policy file",
153
+ status: "pass",
154
+ detail: `OK (${basename(policyPath)} valid)`
155
+ };
156
+ }
157
+ catch { /* not found — continue */ }
158
+ }
159
+ return {
160
+ name: "Policy file",
161
+ status: "pass",
162
+ detail: "OK (no policy file — using built-in defaults)"
163
+ };
164
+ }
165
+ async function checkLeashConfig() {
166
+ // Leash is part of core — if the relevant exports are importable, the guard is active.
167
+ try {
168
+ const core = await import("../../core/index.js");
169
+ if (typeof core.evaluateVerificationLeash === "function") {
170
+ return {
171
+ name: "Leash config",
172
+ status: "pass",
173
+ detail: "OK (verification leash loaded)"
174
+ };
175
+ }
176
+ return {
177
+ name: "Leash config",
178
+ status: "fail",
179
+ detail: "FAIL — leash export missing from @martin/core",
180
+ hint: "Run: pnpm --filter @martin/core build"
181
+ };
182
+ }
183
+ catch (err) {
184
+ return {
185
+ name: "Leash config",
186
+ status: "fail",
187
+ detail: "FAIL — @martin/core failed to import",
188
+ hint: "Run: pnpm build in the repo root"
189
+ };
190
+ }
191
+ }
192
+ async function checkRunStore() {
193
+ try {
194
+ await mkdir(RUNS_ROOT, { recursive: true });
195
+ // Test write access by creating and removing a temp file
196
+ const testPath = join(RUNS_ROOT, `.doctor-check-${Date.now()}`);
197
+ await writeFile(testPath, "ok", "utf8");
198
+ const { unlink } = await import("node:fs/promises");
199
+ await unlink(testPath);
200
+ return {
201
+ name: "Run store writable",
202
+ status: "pass",
203
+ detail: `OK (${RUNS_ROOT.replace(homedir(), "~")})`
204
+ };
205
+ }
206
+ catch (err) {
207
+ return {
208
+ name: "Run store writable",
209
+ status: "fail",
210
+ detail: `FAIL — cannot write to ${RUNS_ROOT}`,
211
+ hint: `Check permissions on ${RUNS_ROOT} or set MARTIN_RUNS_ROOT env var`
212
+ };
213
+ }
214
+ }
215
+ function parseFlag(args, flag) {
216
+ const index = args.indexOf(flag);
217
+ return index >= 0 ? args[index + 1] : undefined;
218
+ }
219
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * explain.ts — SLICE-25
3
+ *
4
+ * CLI command: martin explain <loop-id>
5
+ *
6
+ * Reads stored run artifacts from ~/.martin/loops/<loop-id>/ and produces
7
+ * plain-English Markdown (or JSON) explaining every gate decision.
8
+ * No model calls — pure artifact reading.
9
+ */
10
+ export interface ExplainCommandResult {
11
+ stdout: string;
12
+ stderr: string;
13
+ exitCode: number;
14
+ }
15
+ export declare function handleExplainCommand(args: string[], options?: {
16
+ cwd?: string;
17
+ }): Promise<ExplainCommandResult>;
@@ -0,0 +1,176 @@
1
+ /**
2
+ * explain.ts — SLICE-25
3
+ *
4
+ * CLI command: martin explain <loop-id>
5
+ *
6
+ * Reads stored run artifacts from ~/.martin/loops/<loop-id>/ and produces
7
+ * plain-English Markdown (or JSON) explaining every gate decision.
8
+ * No model calls — pure artifact reading.
9
+ */
10
+ import { readdir, readFile } from "node:fs/promises";
11
+ import { homedir } from "node:os";
12
+ import { join } from "node:path";
13
+ import { formatRunExplanation, formatRunTimeline, resolveRunsRoot } from "../../core/index.js";
14
+ export async function handleExplainCommand(args, options = {}) {
15
+ // Extract loop-id — first positional argument
16
+ const loopId = args.find(a => !a.startsWith("--")) ?? "";
17
+ if (!loopId) {
18
+ return {
19
+ exitCode: 1,
20
+ stdout: "",
21
+ stderr: "Error: explain requires a loop ID. Usage: martin explain <loop-id> [--attempt N] [--decision <gate>] [--timeline] [--html] [--json] [--debug]"
22
+ };
23
+ }
24
+ const attemptFlag = parseFlag(args, "--attempt");
25
+ const decisionFlag = parseFlag(args, "--decision");
26
+ const timelineMode = args.includes("--timeline");
27
+ const htmlMode = args.includes("--html");
28
+ const jsonMode = args.includes("--json");
29
+ const debugMode = args.includes("--debug");
30
+ const loopDir = await resolveExplainDirectory(loopId);
31
+ // Attempt to load artifacts
32
+ let artifacts;
33
+ try {
34
+ artifacts = await loadArtifacts(loopDir, debugMode);
35
+ }
36
+ catch (err) {
37
+ const message = err instanceof Error ? err.message : String(err);
38
+ const isNotFound = message.includes("ENOENT") || message.includes("no such file");
39
+ if (isNotFound) {
40
+ return {
41
+ exitCode: 1,
42
+ stdout: "",
43
+ stderr: `No artifacts found for loop ${loopId}. Run \`martin doctor\` to check your storage path.`
44
+ };
45
+ }
46
+ return {
47
+ exitCode: 1,
48
+ stdout: "",
49
+ stderr: `Error reading artifacts: ${message}`
50
+ };
51
+ }
52
+ if (timelineMode) {
53
+ try {
54
+ const timelineData = await loadTimelineData(loopDir);
55
+ const result = formatRunTimeline({
56
+ loopId,
57
+ objective: timelineData.objective,
58
+ lifecycleState: timelineData.lifecycleState,
59
+ events: timelineData.events,
60
+ ledgerEvents: timelineData.ledgerEvents
61
+ });
62
+ if (jsonMode) {
63
+ return {
64
+ exitCode: 0,
65
+ stdout: JSON.stringify(result.json, null, 2),
66
+ stderr: ""
67
+ };
68
+ }
69
+ return {
70
+ exitCode: 0,
71
+ stdout: htmlMode ? result.html : result.markdown,
72
+ stderr: ""
73
+ };
74
+ }
75
+ catch (err) {
76
+ const message = err instanceof Error ? err.message : String(err);
77
+ return {
78
+ exitCode: 1,
79
+ stdout: "",
80
+ stderr: `Error building timeline: ${message}`
81
+ };
82
+ }
83
+ }
84
+ if (artifacts.length === 0) {
85
+ return {
86
+ exitCode: 1,
87
+ stdout: "",
88
+ stderr: `No artifacts found for loop ${loopId}. Run \`martin doctor\` to check your storage path.`
89
+ };
90
+ }
91
+ const result = formatRunExplanation(artifacts, {
92
+ loopId,
93
+ attemptFilter: attemptFlag != null ? parseInt(attemptFlag, 10) : undefined,
94
+ decisionFilter: decisionFlag ?? undefined,
95
+ json: jsonMode
96
+ });
97
+ if (jsonMode) {
98
+ return {
99
+ exitCode: 0,
100
+ stdout: JSON.stringify(result.json, null, 2),
101
+ stderr: ""
102
+ };
103
+ }
104
+ return {
105
+ exitCode: 0,
106
+ stdout: result.markdown,
107
+ stderr: ""
108
+ };
109
+ }
110
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
111
+ async function loadArtifacts(loopDir, debug) {
112
+ const entries = await readdir(loopDir);
113
+ const artifactFiles = entries.filter(f => f.endsWith(".json") && (f.startsWith("attempt-") || f.includes("artifact")));
114
+ const artifacts = [];
115
+ for (const file of artifactFiles.sort()) {
116
+ try {
117
+ const raw = await readFile(join(loopDir, file), "utf8");
118
+ const parsed = JSON.parse(raw);
119
+ artifacts.push(parsed);
120
+ }
121
+ catch (err) {
122
+ if (debug) {
123
+ const msg = err instanceof Error ? err.message : String(err);
124
+ process.stderr.write(`[debug] skipped ${file}: ${msg}\n`);
125
+ }
126
+ // Non-fatal — skip malformed artifact files
127
+ }
128
+ }
129
+ // Also accept a single loop-record.json that may contain an attempts array
130
+ if (artifacts.length === 0 && entries.includes("loop-record.json")) {
131
+ const raw = await readFile(join(loopDir, "loop-record.json"), "utf8");
132
+ const record = JSON.parse(raw);
133
+ if (Array.isArray(record.attempts)) {
134
+ return record.attempts;
135
+ }
136
+ }
137
+ return artifacts;
138
+ }
139
+ async function loadTimelineData(loopDir) {
140
+ const loopRecordRaw = await readFile(join(loopDir, "loop-record.json"), "utf8");
141
+ const loopRecord = JSON.parse(loopRecordRaw);
142
+ const ledgerPath = join(loopDir, "ledger.jsonl");
143
+ const ledgerRaw = await readFile(ledgerPath, "utf8");
144
+ const ledgerEvents = ledgerRaw
145
+ .split(/\r?\n/u)
146
+ .filter((line) => line.trim().length > 0)
147
+ .map((line) => JSON.parse(line));
148
+ return {
149
+ objective: loopRecord.task?.objective ?? "unknown",
150
+ ...(loopRecord.lifecycleState ? { lifecycleState: loopRecord.lifecycleState } : {}),
151
+ events: loopRecord.events ?? [],
152
+ ledgerEvents
153
+ };
154
+ }
155
+ async function resolveExplainDirectory(loopId) {
156
+ const roots = [
157
+ resolveRunsRoot(process.env),
158
+ join(homedir(), ".martin", "loops")
159
+ ];
160
+ for (const root of roots) {
161
+ const candidate = join(root, loopId);
162
+ try {
163
+ await readdir(candidate);
164
+ return candidate;
165
+ }
166
+ catch {
167
+ // Try the next legacy root.
168
+ }
169
+ }
170
+ return join(roots[0] ?? join(homedir(), ".martin", "runs"), loopId);
171
+ }
172
+ function parseFlag(args, flag) {
173
+ const idx = args.indexOf(flag);
174
+ return idx >= 0 ? args[idx + 1] : undefined;
175
+ }
176
+ //# sourceMappingURL=explain.js.map
@@ -0,0 +1,5 @@
1
+ export declare function handleExportCommand(args: string[]): Promise<{
2
+ stdout: string;
3
+ stderr: string;
4
+ exitCode: number;
5
+ }>;
@@ -0,0 +1,60 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ import { pushOtlp, readRuns, runsToOtlp } from "@martin/trace-intelligence";
4
+ export async function handleExportCommand(args) {
5
+ const maxRuns = parseIntFlag(args, "--runs") ?? 20;
6
+ const since = parseFlag(args, "--since");
7
+ const runsRoot = parseFlag(args, "--runs-root");
8
+ const outputPath = parseFlag(args, "--output") ?? "spans.json";
9
+ const endpoint = parseFlag(args, "--endpoint");
10
+ try {
11
+ const runs = await readRuns({ maxRuns, since, runsRoot });
12
+ if (runs.length === 0) {
13
+ return {
14
+ exitCode: 0,
15
+ stdout: "",
16
+ stderr: "No runs found. Run martin-loop at least once to generate trace data."
17
+ };
18
+ }
19
+ const otlpRequest = runsToOtlp(runs);
20
+ const totalSpans = otlpRequest.resourceSpans.reduce((sum, rs) => sum + rs.scopeSpans.reduce((s, ss) => s + ss.spans.length, 0), 0);
21
+ if (endpoint) {
22
+ const pushResult = await pushOtlp(otlpRequest, endpoint);
23
+ if (!pushResult.ok) {
24
+ return {
25
+ exitCode: 1,
26
+ stdout: "",
27
+ stderr: `OTLP push failed (HTTP ${pushResult.status}): ${pushResult.body}`
28
+ };
29
+ }
30
+ return {
31
+ exitCode: 0,
32
+ stdout: `Exported ${totalSpans} spans from ${runs.length} runs to ${endpoint}`,
33
+ stderr: ""
34
+ };
35
+ }
36
+ const resolved = resolve(outputPath);
37
+ await writeFile(resolved, JSON.stringify(otlpRequest, null, 2), "utf8");
38
+ return {
39
+ exitCode: 0,
40
+ stdout: `Exported ${totalSpans} spans from ${runs.length} runs to ${resolved}`,
41
+ stderr: ""
42
+ };
43
+ }
44
+ catch (err) {
45
+ const message = err instanceof Error ? err.message : String(err);
46
+ return { exitCode: 1, stdout: "", stderr: `Error: ${message}` };
47
+ }
48
+ }
49
+ function parseFlag(args, flag) {
50
+ const idx = args.indexOf(flag);
51
+ return idx >= 0 ? args[idx + 1] : undefined;
52
+ }
53
+ function parseIntFlag(args, flag) {
54
+ const val = parseFlag(args, flag);
55
+ if (val === undefined)
56
+ return undefined;
57
+ const n = parseInt(val, 10);
58
+ return isNaN(n) ? undefined : n;
59
+ }
60
+ //# sourceMappingURL=export.js.map
@@ -0,0 +1,8 @@
1
+ export interface GovernanceCommandResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ exitCode: number;
5
+ }
6
+ export declare function handleGovernanceCommand(args: string[], options?: {
7
+ cwd?: string;
8
+ }): Promise<GovernanceCommandResult>;
@@ -0,0 +1,95 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { dirname, resolve } from "node:path";
3
+ import { createBlastRadiusReport } from "@martin/sdk";
4
+ export async function handleGovernanceCommand(args, options = {}) {
5
+ const subcommand = args[0];
6
+ try {
7
+ if (subcommand !== "check") {
8
+ return {
9
+ stdout: "",
10
+ stderr: [
11
+ "Usage: martin governance check --change-description <text> --repos <repo1,repo2> [options]",
12
+ "",
13
+ "Options:",
14
+ " --services <svc1,svc2>",
15
+ " --truth-facts <fact1,fact2>",
16
+ " --active-treaties <treaty1,treaty2>",
17
+ " --release-window-conflicts <conflict1,conflict2>",
18
+ " --lock-conflicts <resource@holder@expiresAt,...>",
19
+ " --window-ends-at <timestamp>",
20
+ " --output <path>"
21
+ ].join("\n"),
22
+ exitCode: 1
23
+ };
24
+ }
25
+ const changeDescription = parseFlag(args, "--change-description");
26
+ const reposAffected = parseCsvFlag(args, "--repos");
27
+ if (!changeDescription) {
28
+ return { stdout: "", stderr: "Missing --change-description", exitCode: 1 };
29
+ }
30
+ if (reposAffected.length === 0) {
31
+ return { stdout: "", stderr: "Missing --repos", exitCode: 1 };
32
+ }
33
+ const report = createBlastRadiusReport({
34
+ proposedChange: changeDescription,
35
+ reposAffected,
36
+ servicesAffected: parseCsvFlag(args, "--services"),
37
+ truthFactsAffected: parseCsvFlag(args, "--truth-facts"),
38
+ activeTreatiesAffected: parseCsvFlag(args, "--active-treaties"),
39
+ releaseWindowConflicts: parseCsvFlag(args, "--release-window-conflicts"),
40
+ lockConflicts: parseLockConflicts(parseFlag(args, "--lock-conflicts")),
41
+ ...(parseFlag(args, "--window-ends-at")
42
+ ? { evaluationWindowEndsAt: parseFlag(args, "--window-ends-at") }
43
+ : {})
44
+ });
45
+ const outputPath = parseFlag(args, "--output");
46
+ if (outputPath) {
47
+ const resolvedOutputPath = resolve(options.cwd ?? process.cwd(), outputPath);
48
+ await mkdir(dirname(resolvedOutputPath), { recursive: true });
49
+ await writeFile(resolvedOutputPath, JSON.stringify(report, null, 2), "utf8");
50
+ }
51
+ return {
52
+ stdout: JSON.stringify(report, null, 2),
53
+ stderr: "",
54
+ exitCode: 0
55
+ };
56
+ }
57
+ catch (error) {
58
+ const message = error instanceof Error ? error.message : String(error);
59
+ return { stdout: "", stderr: message, exitCode: 1 };
60
+ }
61
+ }
62
+ function parseFlag(args, flag) {
63
+ const index = args.indexOf(flag);
64
+ return index >= 0 ? args[index + 1] : undefined;
65
+ }
66
+ function parseCsvFlag(args, flag) {
67
+ const value = parseFlag(args, flag);
68
+ return value
69
+ ? value
70
+ .split(",")
71
+ .map((entry) => entry.trim())
72
+ .filter((entry) => entry.length > 0)
73
+ : [];
74
+ }
75
+ function parseLockConflicts(value) {
76
+ if (!value) {
77
+ return [];
78
+ }
79
+ return value
80
+ .split(",")
81
+ .map((entry) => entry.trim())
82
+ .filter((entry) => entry.length > 0)
83
+ .map((entry) => {
84
+ const [resource, lockHolder, expiresAt] = entry.split("@");
85
+ if (!resource || !lockHolder) {
86
+ throw new Error(`Invalid lock conflict entry: ${entry}`);
87
+ }
88
+ return {
89
+ resource,
90
+ lockHolder,
91
+ ...(expiresAt ? { expiresAt } : {})
92
+ };
93
+ });
94
+ }
95
+ //# sourceMappingURL=governance.js.map
@@ -0,0 +1,18 @@
1
+ export declare function handleImproveCommand(args: string[]): Promise<{
2
+ stdout: string;
3
+ stderr: string;
4
+ exitCode: number;
5
+ }>;
6
+ export type ImproveLiveEngine = "claude" | "codex";
7
+ export interface ImproveAdapterSelection {
8
+ engine: ImproveLiveEngine;
9
+ model?: string;
10
+ }
11
+ export declare function resolveImproveAdapterSelection(args: string[], env?: NodeJS.ProcessEnv): ImproveAdapterSelection;
12
+ export declare function resolveImproveLiveAdapterTimeoutMs(env?: NodeJS.ProcessEnv): number;
13
+ export declare function resolveImproveMaxIterations(args: string[], env?: NodeJS.ProcessEnv, options?: {
14
+ certifyTraceAutonomy?: boolean;
15
+ }): number;
16
+ export declare function resolveImproveRetryLimit(options?: {
17
+ certifyTraceAutonomy?: boolean;
18
+ }): number;