martin-loop 0.1.5 → 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 (274) hide show
  1. package/CODE_OF_CONDUCT.md +32 -0
  2. package/LICENSE +21 -21
  3. package/README.md +307 -398
  4. package/demo/seeded-workspace/README.md +35 -35
  5. package/demo/seeded-workspace/TASKS.md +29 -29
  6. package/demo/seeded-workspace/martin.config.yaml +11 -11
  7. package/demo/seeded-workspace/package.json +8 -8
  8. package/demo/seeded-workspace/src/invoice-summary.js +11 -11
  9. package/demo/seeded-workspace/test/invoice-summary.test.js +20 -20
  10. package/dist/bin/martin-loop.js +0 -0
  11. package/dist/vendor/adapters/counter.d.ts +1 -0
  12. package/dist/vendor/adapters/counter.js +4 -0
  13. package/dist/vendor/adapters/git-baseline.d.ts +50 -0
  14. package/dist/vendor/adapters/git-baseline.js +233 -0
  15. package/dist/vendor/adapters/openrouter-adapter.d.ts +15 -0
  16. package/dist/vendor/adapters/openrouter-adapter.js +302 -0
  17. package/dist/vendor/adapters/usage.d.ts +48 -0
  18. package/dist/vendor/adapters/usage.js +66 -0
  19. package/dist/vendor/cli/bin/exit.d.ts +12 -0
  20. package/dist/vendor/cli/bin/exit.js +28 -0
  21. package/dist/vendor/cli/commands/analyze.d.ts +5 -0
  22. package/dist/vendor/cli/commands/analyze.js +58 -0
  23. package/dist/vendor/cli/commands/audit-log-verify.d.ts +34 -0
  24. package/dist/vendor/cli/commands/audit-log-verify.js +99 -0
  25. package/dist/vendor/cli/commands/audit.d.ts +8 -0
  26. package/dist/vendor/cli/commands/audit.js +199 -0
  27. package/dist/vendor/cli/commands/corpus.d.ts +5 -0
  28. package/dist/vendor/cli/commands/corpus.js +60 -0
  29. package/dist/vendor/cli/commands/doctor.d.ts +8 -0
  30. package/dist/vendor/cli/commands/doctor.js +219 -0
  31. package/dist/vendor/cli/commands/explain.d.ts +17 -0
  32. package/dist/vendor/cli/commands/explain.js +176 -0
  33. package/dist/vendor/cli/commands/export.d.ts +5 -0
  34. package/dist/vendor/cli/commands/export.js +60 -0
  35. package/dist/vendor/cli/commands/governance.d.ts +8 -0
  36. package/dist/vendor/cli/commands/governance.js +95 -0
  37. package/dist/vendor/cli/commands/improve.d.ts +18 -0
  38. package/dist/vendor/cli/commands/improve.js +396 -0
  39. package/dist/vendor/cli/commands/init.d.ts +8 -0
  40. package/dist/vendor/cli/commands/init.js +281 -0
  41. package/dist/vendor/cli/commands/migration.d.ts +8 -0
  42. package/dist/vendor/cli/commands/migration.js +67 -0
  43. package/dist/vendor/cli/commands/prior.d.ts +23 -0
  44. package/dist/vendor/cli/commands/prior.js +145 -0
  45. package/dist/vendor/cli/commands/resume.d.ts +21 -0
  46. package/dist/vendor/cli/commands/resume.js +73 -0
  47. package/dist/vendor/cli/commands/verify.d.ts +6 -0
  48. package/dist/vendor/cli/commands/verify.js +43 -0
  49. package/dist/vendor/cli/research/public-corpus.d.ts +43 -0
  50. package/dist/vendor/cli/research/public-corpus.js +151 -0
  51. package/dist/vendor/cli/ui/error-card.d.ts +38 -0
  52. package/dist/vendor/cli/ui/error-card.js +103 -0
  53. package/dist/vendor/cli/ui/mission-brief.d.ts +41 -0
  54. package/dist/vendor/cli/ui/mission-brief.js +173 -0
  55. package/dist/vendor/cli/ui/summary-card.d.ts +34 -0
  56. package/dist/vendor/cli/ui/summary-card.js +102 -0
  57. package/dist/vendor/contracts/audit.d.ts +46 -0
  58. package/dist/vendor/contracts/audit.js +360 -0
  59. package/dist/vendor/contracts/post-phase15.d.ts +240 -0
  60. package/dist/vendor/contracts/post-phase15.js +166 -0
  61. package/dist/vendor/core/agent/mandates.d.ts +46 -0
  62. package/dist/vendor/core/agent/mandates.js +178 -0
  63. package/dist/vendor/core/agent/receipts.d.ts +38 -0
  64. package/dist/vendor/core/agent/receipts.js +131 -0
  65. package/dist/vendor/core/agent/signing.d.ts +17 -0
  66. package/dist/vendor/core/agent/signing.js +91 -0
  67. package/dist/vendor/core/attestation/sign.d.ts +25 -0
  68. package/dist/vendor/core/attestation/sign.js +216 -0
  69. package/dist/vendor/core/autonomy/autonomous-promotion.d.ts +120 -0
  70. package/dist/vendor/core/autonomy/autonomous-promotion.js +346 -0
  71. package/dist/vendor/core/autonomy/envelope-v2.d.ts +29 -0
  72. package/dist/vendor/core/autonomy/envelope-v2.js +60 -0
  73. package/dist/vendor/core/autonomy/envelope.d.ts +17 -0
  74. package/dist/vendor/core/autonomy/envelope.js +27 -0
  75. package/dist/vendor/core/autonomy/escalation-ledger.d.ts +20 -0
  76. package/dist/vendor/core/autonomy/escalation-ledger.js +18 -0
  77. package/dist/vendor/core/autonomy/resume.d.ts +15 -0
  78. package/dist/vendor/core/autonomy/resume.js +23 -0
  79. package/dist/vendor/core/circuit/circuit-breaker.d.ts +60 -0
  80. package/dist/vendor/core/circuit/circuit-breaker.js +143 -0
  81. package/dist/vendor/core/context-distillation.d.ts +3 -0
  82. package/dist/vendor/core/context-distillation.js +44 -0
  83. package/dist/vendor/core/context-flow/compile-context.d.ts +8 -0
  84. package/dist/vendor/core/context-flow/compile-context.js +111 -0
  85. package/dist/vendor/core/context-flow/entities.d.ts +2 -0
  86. package/dist/vendor/core/context-flow/entities.js +44 -0
  87. package/dist/vendor/core/context-flow/evaluate-policy.d.ts +2 -0
  88. package/dist/vendor/core/context-flow/evaluate-policy.js +42 -0
  89. package/dist/vendor/core/context-flow/index.d.ts +11 -0
  90. package/dist/vendor/core/context-flow/index.js +24 -0
  91. package/dist/vendor/core/context-flow/labels.d.ts +3 -0
  92. package/dist/vendor/core/context-flow/labels.js +17 -0
  93. package/dist/vendor/core/context-flow/normalizer.d.ts +9 -0
  94. package/dist/vendor/core/context-flow/normalizer.js +69 -0
  95. package/dist/vendor/core/context-flow/profiles.d.ts +33 -0
  96. package/dist/vendor/core/context-flow/profiles.js +36 -0
  97. package/dist/vendor/core/context-flow/redaction.d.ts +1 -0
  98. package/dist/vendor/core/context-flow/redaction.js +6 -0
  99. package/dist/vendor/core/context-flow/sensitivity.d.ts +2 -0
  100. package/dist/vendor/core/context-flow/sensitivity.js +27 -0
  101. package/dist/vendor/core/context-flow/sync-preview.d.ts +2 -0
  102. package/dist/vendor/core/context-flow/sync-preview.js +22 -0
  103. package/dist/vendor/core/context-flow/token-estimator.d.ts +3 -0
  104. package/dist/vendor/core/context-flow/token-estimator.js +13 -0
  105. package/dist/vendor/core/context-flow/types.d.ts +91 -0
  106. package/dist/vendor/core/context-flow/types.js +2 -0
  107. package/dist/vendor/core/context-utility.d.ts +47 -0
  108. package/dist/vendor/core/context-utility.js +405 -0
  109. package/dist/vendor/core/cost/pipeline.d.ts +92 -0
  110. package/dist/vendor/core/cost/pipeline.js +141 -0
  111. package/dist/vendor/core/cost/tagged-cost.d.ts +27 -0
  112. package/dist/vendor/core/cost/tagged-cost.js +55 -0
  113. package/dist/vendor/core/cost-governor.d.ts +2 -0
  114. package/dist/vendor/core/cost-governor.js +50 -0
  115. package/dist/vendor/core/cve/cve-check.d.ts +80 -0
  116. package/dist/vendor/core/cve/cve-check.js +172 -0
  117. package/dist/vendor/core/digital-twin/index.d.ts +27 -0
  118. package/dist/vendor/core/digital-twin/index.js +90 -0
  119. package/dist/vendor/core/drift/drift-graph.d.ts +47 -0
  120. package/dist/vendor/core/drift/drift-graph.js +100 -0
  121. package/dist/vendor/core/drift/objective-lock.d.ts +69 -0
  122. package/dist/vendor/core/drift/objective-lock.js +88 -0
  123. package/dist/vendor/core/drift/scope.d.ts +46 -0
  124. package/dist/vendor/core/drift/scope.js +102 -0
  125. package/dist/vendor/core/drift/signature-lock.d.ts +48 -0
  126. package/dist/vendor/core/drift/signature-lock.js +202 -0
  127. package/dist/vendor/core/drift/stale-proof-gate.d.ts +21 -0
  128. package/dist/vendor/core/drift/stale-proof-gate.js +19 -0
  129. package/dist/vendor/core/eval/known-bad-world-runner.d.ts +24 -0
  130. package/dist/vendor/core/eval/known-bad-world-runner.js +256 -0
  131. package/dist/vendor/core/evidence/claim-audit.d.ts +18 -0
  132. package/dist/vendor/core/evidence/claim-audit.js +89 -0
  133. package/dist/vendor/core/exit-intelligence.d.ts +2 -0
  134. package/dist/vendor/core/exit-intelligence.js +58 -0
  135. package/dist/vendor/core/explain/formatter.d.ts +42 -0
  136. package/dist/vendor/core/explain/formatter.js +171 -0
  137. package/dist/vendor/core/explain/timeline.d.ts +29 -0
  138. package/dist/vendor/core/explain/timeline.js +213 -0
  139. package/dist/vendor/core/failure-taxonomy.d.ts +2 -0
  140. package/dist/vendor/core/failure-taxonomy.js +76 -0
  141. package/dist/vendor/core/gateway/index.d.ts +10 -0
  142. package/dist/vendor/core/gateway/index.js +12 -0
  143. package/dist/vendor/core/gateway/registry.d.ts +40 -0
  144. package/dist/vendor/core/gateway/registry.js +97 -0
  145. package/dist/vendor/core/gateway/transport.d.ts +31 -0
  146. package/dist/vendor/core/gateway/transport.js +82 -0
  147. package/dist/vendor/core/gateway/vault.d.ts +19 -0
  148. package/dist/vendor/core/gateway/vault.js +29 -0
  149. package/dist/vendor/core/graph/adapters.d.ts +43 -0
  150. package/dist/vendor/core/graph/adapters.js +91 -0
  151. package/dist/vendor/core/graph/hotspots.d.ts +22 -0
  152. package/dist/vendor/core/graph/hotspots.js +30 -0
  153. package/dist/vendor/core/graph/index.d.ts +1 -0
  154. package/dist/vendor/core/graph/index.js +2 -0
  155. package/dist/vendor/core/honey/honey-tokens.d.ts +32 -0
  156. package/dist/vendor/core/honey/honey-tokens.js +44 -0
  157. package/dist/vendor/core/index.d.ts +2 -2
  158. package/dist/vendor/core/index.js +38 -12
  159. package/dist/vendor/core/learning/bayesian-update.d.ts +31 -0
  160. package/dist/vendor/core/learning/bayesian-update.js +60 -0
  161. package/dist/vendor/core/learning/prior-sets.d.ts +42 -0
  162. package/dist/vendor/core/learning/prior-sets.js +111 -0
  163. package/dist/vendor/core/learning/promotion-gate.d.ts +17 -0
  164. package/dist/vendor/core/learning/promotion-gate.js +23 -0
  165. package/dist/vendor/core/leash/blast-radius.d.ts +42 -0
  166. package/dist/vendor/core/leash/blast-radius.js +156 -0
  167. package/dist/vendor/core/leash/policy-leash.d.ts +31 -0
  168. package/dist/vendor/core/leash/policy-leash.js +117 -0
  169. package/dist/vendor/core/memo/memo.d.ts +63 -0
  170. package/dist/vendor/core/memo/memo.js +97 -0
  171. package/dist/vendor/core/memory/learning-pipeline.d.ts +154 -0
  172. package/dist/vendor/core/memory/learning-pipeline.js +391 -0
  173. package/dist/vendor/core/memory/palace.d.ts +84 -0
  174. package/dist/vendor/core/memory/palace.js +379 -0
  175. package/dist/vendor/core/merge/ast-merge.d.ts +22 -0
  176. package/dist/vendor/core/merge/ast-merge.js +350 -0
  177. package/dist/vendor/core/merge/text-merge.d.ts +12 -0
  178. package/dist/vendor/core/merge/text-merge.js +182 -0
  179. package/dist/vendor/core/otel/tracer.d.ts +45 -0
  180. package/dist/vendor/core/otel/tracer.js +116 -0
  181. package/dist/vendor/core/parallel/parallel-attempts.d.ts +28 -0
  182. package/dist/vendor/core/parallel/parallel-attempts.js +41 -0
  183. package/dist/vendor/core/parallel/scorer.d.ts +24 -0
  184. package/dist/vendor/core/parallel/scorer.js +65 -0
  185. package/dist/vendor/core/pattern-detection.d.ts +64 -0
  186. package/dist/vendor/core/pattern-detection.js +108 -0
  187. package/dist/vendor/core/persistence/checkpoint.d.ts +44 -0
  188. package/dist/vendor/core/persistence/checkpoint.js +156 -0
  189. package/dist/vendor/core/persistence/cleanup.d.ts +22 -0
  190. package/dist/vendor/core/persistence/cleanup.js +131 -0
  191. package/dist/vendor/core/persistence/index.d.ts +2 -0
  192. package/dist/vendor/core/persistence/index.js +1 -0
  193. package/dist/vendor/core/persistence/runs-reader.d.ts +52 -0
  194. package/dist/vendor/core/persistence/runs-reader.js +84 -0
  195. package/dist/vendor/core/persistence/store.d.ts +6 -1
  196. package/dist/vendor/core/persistence/store.js +5 -0
  197. package/dist/vendor/core/policy/file-touch-quota.d.ts +60 -0
  198. package/dist/vendor/core/policy/file-touch-quota.js +105 -0
  199. package/dist/vendor/core/policy/policy-loader.d.ts +30 -0
  200. package/dist/vendor/core/policy/policy-loader.js +170 -0
  201. package/dist/vendor/core/policy/policy-schema.d.ts +55 -0
  202. package/dist/vendor/core/policy/policy-schema.js +78 -0
  203. package/dist/vendor/core/probe/probe.d.ts +49 -0
  204. package/dist/vendor/core/probe/probe.js +115 -0
  205. package/dist/vendor/core/proof/patch-proof.d.ts +58 -0
  206. package/dist/vendor/core/proof/patch-proof.js +84 -0
  207. package/dist/vendor/core/proof/semantic-probe.d.ts +25 -0
  208. package/dist/vendor/core/proof/semantic-probe.js +82 -0
  209. package/dist/vendor/core/recovery/failure-mode-runner.d.ts +29 -0
  210. package/dist/vendor/core/recovery/failure-mode-runner.js +39 -0
  211. package/dist/vendor/core/red-blue/red-phase.d.ts +64 -0
  212. package/dist/vendor/core/red-blue/red-phase.js +141 -0
  213. package/dist/vendor/core/red-blue/risk-tiers.d.ts +22 -0
  214. package/dist/vendor/core/red-blue/risk-tiers.js +33 -0
  215. package/dist/vendor/core/replay/replay.d.ts +85 -0
  216. package/dist/vendor/core/replay/replay.js +109 -0
  217. package/dist/vendor/core/router/engine.d.ts +54 -0
  218. package/dist/vendor/core/router/engine.js +131 -0
  219. package/dist/vendor/core/router/index.d.ts +1 -0
  220. package/dist/vendor/core/router/index.js +2 -0
  221. package/dist/vendor/core/router/trust-calibration.d.ts +57 -0
  222. package/dist/vendor/core/router/trust-calibration.js +127 -0
  223. package/dist/vendor/core/run-martin.d.ts +2 -0
  224. package/dist/vendor/core/run-martin.js +287 -0
  225. package/dist/vendor/core/security/cve-scanner.d.ts +62 -0
  226. package/dist/vendor/core/security/cve-scanner.js +178 -0
  227. package/dist/vendor/core/sentinel/efficiency-sentinel.d.ts +29 -0
  228. package/dist/vendor/core/sentinel/efficiency-sentinel.js +30 -0
  229. package/dist/vendor/core/sentinel/progress-guard.d.ts +35 -0
  230. package/dist/vendor/core/sentinel/progress-guard.js +46 -0
  231. package/dist/vendor/core/siem/siem-emitter.d.ts +49 -0
  232. package/dist/vendor/core/siem/siem-emitter.js +157 -0
  233. package/dist/vendor/core/strategy/attempt-brief.d.ts +22 -0
  234. package/dist/vendor/core/strategy/attempt-brief.js +89 -0
  235. package/dist/vendor/core/summarize/diff-summary.d.ts +35 -0
  236. package/dist/vendor/core/summarize/diff-summary.js +204 -0
  237. package/dist/vendor/core/surface-signals.d.ts +21 -0
  238. package/dist/vendor/core/surface-signals.js +139 -0
  239. package/dist/vendor/core/truth/truth-wall.d.ts +51 -0
  240. package/dist/vendor/core/truth/truth-wall.js +69 -0
  241. package/dist/vendor/core/truth-spine.d.ts +26 -0
  242. package/dist/vendor/core/truth-spine.js +62 -0
  243. package/dist/vendor/core/types.d.ts +115 -0
  244. package/dist/vendor/core/types.js +2 -0
  245. package/dist/vendor/core/verification/tiered-verify.d.ts +17 -0
  246. package/dist/vendor/core/verification/tiered-verify.js +29 -0
  247. package/dist/vendor/core/verifier-pyramid.d.ts +32 -0
  248. package/dist/vendor/core/verifier-pyramid.js +111 -0
  249. package/dist/vendor/core/workflow-artifacts.d.ts +99 -0
  250. package/dist/vendor/core/workflow-artifacts.js +668 -0
  251. package/dist/vendor/core/wrap/supervised-run.d.ts +96 -0
  252. package/dist/vendor/core/wrap/supervised-run.js +178 -0
  253. package/docs/assets/cli-animated.svg +139 -0
  254. package/docs/assets/cli-static.svg +34 -0
  255. package/docs/assets/github-hero-v2.svg +23 -0
  256. package/docs/assets/martin-raplph.png.jpg +0 -0
  257. package/docs/assets/martinloop-logo.png +0 -0
  258. package/docs/assets/nvidia-inception-program-light.png +0 -0
  259. package/docs/assets/nvidia-inception-program.png +0 -0
  260. package/docs/assets/phase3c-sidesidebyside-demo.html +228 -0
  261. package/docs/assets/side-by-side.svg +134 -0
  262. package/docs/oss/CLAUDE-CODE-WALKTHROUGH.md +142 -142
  263. package/docs/oss/EXAMPLES.md +134 -134
  264. package/docs/oss/OSS-BOUNDARY-REPORT.json +1 -1
  265. package/docs/oss/OSS-BOUNDARY-REPORT.md +1 -1
  266. package/docs/oss/QUICKSTART.md +170 -165
  267. package/docs/oss/RALPH-LOOP-SAFETY.md +113 -113
  268. package/docs/oss/README.md +96 -96
  269. package/docs/oss/RELEASE-SURFACE-REPORT.json +2 -1
  270. package/docs/oss/RELEASE-SURFACE-REPORT.md +2 -1
  271. package/package.json +130 -58
  272. package/docs/distribution/DIRECTORY-SUBMISSIONS.md +0 -89
  273. package/docs/distribution/INTEGRATION-OUTREACH.md +0 -61
  274. package/docs/distribution/UNDER-3-CHALLENGE.md +0 -65
@@ -0,0 +1,281 @@
1
+ import { existsSync } from "node:fs";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import * as readline from "node:readline/promises";
5
+ const POLICY_EXAMPLE = `# martin.policy.yaml — runtime governance rules for Martin Loop
6
+ # This file is safe to commit. It contains no secrets.
7
+ # Secrets (API keys) always go in environment variables.
8
+
9
+ budgetUsd:
10
+ perRun: 5.00 # hard cap per individual run
11
+ perDay: 50.00 # daily cap across all runs (informational)
12
+
13
+ allowedVerifiers:
14
+ - "pnpm test"
15
+ - "bun run test"
16
+ - "npm test"
17
+ - "cargo test"
18
+ - "pytest"
19
+
20
+ # Commands appended to the built-in leash block list
21
+ blockedCommands:
22
+ # - "npm publish"
23
+ # - "git push --force"
24
+
25
+ # Restrict which files martin may touch (glob, optional)
26
+ # fileScopeGlob: "src/**"
27
+
28
+ # Hard cap on attempts per run
29
+ maxAttempts: 5
30
+
31
+ # Pause and require human approval before spending above this
32
+ requireApprovalAboveUsd: 2.00
33
+
34
+ # SIEM integration (optional — requires SLICE-16)
35
+ # siem:
36
+ # endpoint: "https://your-siem.example.com/events"
37
+ # format: "ocsf" # or "cef"
38
+ # apiKey: "$SIEM_API_KEY" # env var reference
39
+ `;
40
+ const MARTIN_CONFIG_TEMPLATE = (adapter, model) => `# martin.config.yaml
41
+ # Core runtime configuration for Martin Loop.
42
+ # Do not commit this file if it contains any secrets.
43
+
44
+ policyProfile: balanced
45
+
46
+ budget:
47
+ maxUsd: 5.00
48
+ softLimitUsd: 3.75
49
+ maxIterations: 5
50
+ maxTokens: 50000
51
+
52
+ governance:
53
+ destructiveActionPolicy: approval
54
+ telemetryDestination: local-only
55
+ verifierRules:
56
+ - "pnpm test"
57
+ `;
58
+ const GH_ACTIONS_TEMPLATE = `# .github/workflows/martin-loop.yml
59
+ # Martin Loop governed AI coding runtime — GitHub Actions integration
60
+ # Docs: https://github.com/your-org/martin-loop
61
+
62
+ name: Martin Loop
63
+
64
+ on:
65
+ workflow_dispatch:
66
+ inputs:
67
+ objective:
68
+ description: "What should Martin Loop fix or implement?"
69
+ required: true
70
+ budget_usd:
71
+ description: "Hard budget cap for this run (USD)"
72
+ required: false
73
+ default: "5.00"
74
+ issue_comment:
75
+ types: [created]
76
+
77
+ jobs:
78
+ martin:
79
+ # Trigger on workflow_dispatch OR on /martin comment in a PR
80
+ if: |
81
+ github.event_name == 'workflow_dispatch' ||
82
+ (github.event_name == 'issue_comment' &&
83
+ github.event.issue.pull_request != null &&
84
+ startsWith(github.event.comment.body, '/martin'))
85
+
86
+ runs-on: ubuntu-latest
87
+
88
+ permissions:
89
+ contents: write
90
+ pull-requests: write
91
+
92
+ steps:
93
+ - name: Check out repository
94
+ uses: actions/checkout@v4
95
+
96
+ - name: Set up pnpm
97
+ uses: pnpm/action-setup@v4
98
+ with:
99
+ version: 10.17.1
100
+
101
+ - name: Set up Node.js
102
+ uses: actions/setup-node@v4
103
+ with:
104
+ node-version: 22
105
+ cache: pnpm
106
+
107
+ - name: Install martin-loop
108
+ run: pnpm add -g @martin/cli
109
+
110
+ # Pre-flight check — fails the job if environment is misconfigured
111
+ - name: Pre-flight check
112
+ run: martin-loop doctor
113
+ env:
114
+ ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }}
115
+
116
+ - name: Run Martin Loop
117
+ id: martin
118
+ run: |
119
+ # Resolve objective from dispatch input or /martin comment
120
+ if [ "\${{ github.event_name }}" = "workflow_dispatch" ]; then
121
+ OBJECTIVE="\${{ github.event.inputs.objective }}"
122
+ BUDGET="\${{ github.event.inputs.budget_usd }}"
123
+ else
124
+ OBJECTIVE="\$(echo '\${{ github.event.comment.body }}' | sed 's|^/martin[[:space:]]*||')"
125
+ BUDGET="5.00"
126
+ fi
127
+
128
+ martin-loop run \\
129
+ --objective "\$OBJECTIVE" \\
130
+ --budget-usd "\$BUDGET" \\
131
+ --workspace "\${{ github.repository_owner }}" \\
132
+ --project "\${{ github.repository }}" \\
133
+ --json > martin-result.json
134
+ env:
135
+ ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }}
136
+ MARTIN_NO_SUMMARY: "1"
137
+
138
+ - name: Upload audit artifacts
139
+ if: always()
140
+ uses: actions/upload-artifact@v4
141
+ with:
142
+ name: martin-loop-audit-\${{ github.run_id }}
143
+ path: ~/.martin/runs/
144
+ retention-days: 30
145
+
146
+ - name: Commit any changes from Martin Loop
147
+ run: |
148
+ git config user.name "martin-loop[bot]"
149
+ git config user.email "martin-loop[bot]@users.noreply.github.com"
150
+ git add -A
151
+ git diff --staged --quiet || git commit -m "feat(martin-loop): apply governed patch from run \${{ github.run_id }}"
152
+ git push
153
+
154
+ - name: Post PR comment with run summary
155
+ if: github.event_name == 'issue_comment'
156
+ uses: actions/github-script@v7
157
+ with:
158
+ script: |
159
+ const fs = require('fs');
160
+ let body;
161
+ try {
162
+ const result = JSON.parse(fs.readFileSync('martin-result.json', 'utf8'));
163
+ const decision = result.decision ?? {};
164
+ const loop = result.loop ?? {};
165
+ const succeeded = decision.lifecycleState === 'succeeded';
166
+ const outcome = succeeded ? '✅ SUCCEEDED' : '❌ FAILED';
167
+ const cost = loop.costUsd ? \`$\${loop.costUsd.toFixed(2)}\` : 'n/a';
168
+ const loopId = loop.loopId ?? 'unknown';
169
+ body = [
170
+ \`## Martin Loop — \${outcome}\`,
171
+ \`\`,
172
+ \`| Field | Value |\`,
173
+ \`|---|---|\`,
174
+ \`| Outcome | \${decision.lifecycleState ?? 'unknown'} |\`,
175
+ \`| Cost | \${cost} |\`,
176
+ \`| Loop ID | \${loopId} |\`,
177
+ \`\`,
178
+ succeeded
179
+ ? \`Patch applied and committed. Audit artifacts uploaded to this run's Actions artifacts.\`
180
+ : \`Run failed. See Actions artifacts for the full audit trail.\\n\\nTip: martin-loop explain \${loopId}\`
181
+ ].join('\\n');
182
+ } catch (e) {
183
+ body = '## Martin Loop\\n\\nRun completed — see Actions artifacts for details.';
184
+ }
185
+ github.rest.issues.createComment({
186
+ issue_number: context.issue.number,
187
+ owner: context.repo.owner,
188
+ repo: context.repo.repo,
189
+ body
190
+ });
191
+ `;
192
+ export async function handleInitCommand(args, options = {}) {
193
+ const cwd = options.cwd ?? process.cwd();
194
+ const force = args.includes("--force");
195
+ const nonInteractive = !process.stdin.isTTY || args.includes("--yes") || args.includes("-y");
196
+ const configPath = join(cwd, "martin.config.yaml");
197
+ const policyExamplePath = join(cwd, "martin.policy.yaml.example");
198
+ const ghWorkflowDir = join(cwd, ".github", "workflows");
199
+ const ghWorkflowPath = join(ghWorkflowDir, "martin-loop.yml");
200
+ // Guard against overwriting existing config without --force
201
+ if (!force && existsSync(configPath)) {
202
+ return {
203
+ exitCode: 1,
204
+ stdout: "",
205
+ stderr: [
206
+ `martin.config.yaml already exists in ${cwd}.`,
207
+ `Use --force to overwrite, or delete it manually first.`
208
+ ].join("\n")
209
+ };
210
+ }
211
+ // Detect adapter from environment
212
+ const hasAnthropic = !!(process.env.ANTHROPIC_API_KEY && process.env.ANTHROPIC_API_KEY.length > 10);
213
+ const hasOpenAI = !!(process.env.OPENAI_API_KEY && process.env.OPENAI_API_KEY.length > 10);
214
+ let adapter = hasAnthropic ? "claude" : hasOpenAI ? "codex" : "claude";
215
+ let model = adapter === "claude" ? "claude-sonnet-4-6" : "gpt-4o";
216
+ // Ask for budget in interactive mode
217
+ let budget = "5.00";
218
+ let writeGhWorkflow = true;
219
+ if (!nonInteractive) {
220
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
221
+ try {
222
+ const budgetAnswer = await rl.question(` Budget cap per run (USD) [default: 5.00]: `);
223
+ if (budgetAnswer.trim()) {
224
+ const parsed = parseFloat(budgetAnswer.trim());
225
+ if (!isNaN(parsed) && parsed > 0)
226
+ budget = parsed.toFixed(2);
227
+ }
228
+ const ghAnswer = await rl.question(` Write GitHub Actions workflow? (Y/n) [default: Y]: `);
229
+ writeGhWorkflow = !ghAnswer.trim() || ghAnswer.trim().toLowerCase() !== "n";
230
+ }
231
+ finally {
232
+ rl.close();
233
+ }
234
+ }
235
+ const written = [];
236
+ // Write martin.config.yaml
237
+ await writeFile(configPath, MARTIN_CONFIG_TEMPLATE(adapter, model).replace("maxUsd: 5.00", `maxUsd: ${budget}`), "utf8");
238
+ written.push(" martin.config.yaml");
239
+ // Write martin.policy.yaml.example
240
+ await writeFile(policyExamplePath, POLICY_EXAMPLE, "utf8");
241
+ written.push(" martin.policy.yaml.example");
242
+ // Write GitHub Actions workflow
243
+ if (writeGhWorkflow) {
244
+ await mkdir(ghWorkflowDir, { recursive: true });
245
+ await writeFile(ghWorkflowPath, GH_ACTIONS_TEMPLATE, "utf8");
246
+ written.push(" .github/workflows/martin-loop.yml");
247
+ }
248
+ // Run doctor check after init
249
+ let doctorSummary = "";
250
+ try {
251
+ const { handleDoctorCommand } = await import("./doctor.js");
252
+ const doctorResult = await handleDoctorCommand([], { cwd });
253
+ doctorSummary = "\n" + doctorResult.stdout;
254
+ }
255
+ catch {
256
+ // Non-fatal
257
+ }
258
+ const credWarning = !hasAnthropic && !hasOpenAI
259
+ ? `\n ⚠ No API key found. Set ANTHROPIC_API_KEY before running martin-loop.\n`
260
+ : "";
261
+ const output = [
262
+ "Martin Loop initialized.",
263
+ "",
264
+ "Written:",
265
+ ...written,
266
+ "",
267
+ "Add to .gitignore (optional — no secrets but env-specific):",
268
+ " martin.config.yaml",
269
+ "",
270
+ credWarning,
271
+ "Next steps:",
272
+ " 1. Set ANTHROPIC_API_KEY in your environment or CI secrets",
273
+ " 2. Run: martin-loop doctor",
274
+ " 3. Run: martin-loop run --objective \"your first task\"",
275
+ "",
276
+ "Docs: https://github.com/your-org/martin-loop",
277
+ doctorSummary
278
+ ].filter((line) => line !== undefined).join("\n");
279
+ return { exitCode: 0, stdout: output, stderr: "" };
280
+ }
281
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1,8 @@
1
+ export interface MigrationCommandResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ exitCode: number;
5
+ }
6
+ export declare function handleMigrationCommand(args: string[], options?: {
7
+ cwd?: string;
8
+ }): Promise<MigrationCommandResult>;
@@ -0,0 +1,67 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname, resolve } from "node:path";
3
+ import { verifyMigrationPlan } from "@martin/sdk";
4
+ export async function handleMigrationCommand(args, options = {}) {
5
+ const subcommand = args[0];
6
+ try {
7
+ if (subcommand !== "verify") {
8
+ return {
9
+ stdout: "",
10
+ stderr: [
11
+ "Usage: martin migration verify --plan <path> [options]",
12
+ "",
13
+ "Options:",
14
+ " --now <timestamp>",
15
+ " --release-windows <start/end,start/end>",
16
+ " --output <path>"
17
+ ].join("\n"),
18
+ exitCode: 1
19
+ };
20
+ }
21
+ const planPath = parseFlag(args, "--plan");
22
+ if (!planPath) {
23
+ return { stdout: "", stderr: "Missing --plan", exitCode: 1 };
24
+ }
25
+ const cwd = options.cwd ?? process.cwd();
26
+ const resolvedPlanPath = resolve(cwd, planPath);
27
+ const rawPlan = await readFile(resolvedPlanPath, "utf8");
28
+ const plan = JSON.parse(rawPlan);
29
+ const releaseWindows = parseCsvFlag(args, "--release-windows");
30
+ const now = parseFlag(args, "--now");
31
+ const verificationOptions = {
32
+ ...(now ? { now } : {}),
33
+ ...(releaseWindows.length > 0 ? { releaseWindows } : {})
34
+ };
35
+ const readiness = verifyMigrationPlan(plan, verificationOptions);
36
+ const outputPath = resolve(cwd, parseFlag(args, "--output") ?? "cutover-readiness.json");
37
+ await mkdir(dirname(outputPath), { recursive: true });
38
+ await writeFile(outputPath, JSON.stringify(readiness, null, 2), "utf8");
39
+ return {
40
+ stdout: JSON.stringify({
41
+ command: "migration.verify",
42
+ outputPath,
43
+ result: readiness
44
+ }, null, 2),
45
+ stderr: "",
46
+ exitCode: 0
47
+ };
48
+ }
49
+ catch (error) {
50
+ const message = error instanceof Error ? error.message : String(error);
51
+ return { stdout: "", stderr: message, exitCode: 1 };
52
+ }
53
+ }
54
+ function parseFlag(args, flag) {
55
+ const index = args.indexOf(flag);
56
+ return index >= 0 ? args[index + 1] : undefined;
57
+ }
58
+ function parseCsvFlag(args, flag) {
59
+ const value = parseFlag(args, flag);
60
+ return value
61
+ ? value
62
+ .split(",")
63
+ .map((entry) => entry.trim())
64
+ .filter((entry) => entry.length > 0)
65
+ : [];
66
+ }
67
+ //# sourceMappingURL=migration.js.map
@@ -0,0 +1,23 @@
1
+ export interface PriorCommandResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ exitCode: number;
5
+ }
6
+ export declare function resolvePriorStoreDir(cwd?: string): string;
7
+ /**
8
+ * Handles `martin prior <subcommand> [args]`
9
+ *
10
+ * Subcommands:
11
+ * list — list all PriorSets
12
+ * status --prior-set-id <id> — show current gate for a PriorSet
13
+ * promote --prior-set-id <id> --gate <gate> — advance gate (sequential only)
14
+ * rollback --prior-set-id <id> — revert to previous gate
15
+ */
16
+ export declare function handlePriorCommand(args: string[], options?: {
17
+ cwd?: string;
18
+ }): Promise<PriorCommandResult>;
19
+ /**
20
+ * Seeds a PriorSet into the store if it doesn't already exist.
21
+ * Used by tests and the CLI init flow.
22
+ */
23
+ export declare function seedPriorSet(priorSetId: string, taskClass: string, priors: Record<string, number>, storeDir: string): Promise<void>;
@@ -0,0 +1,145 @@
1
+ import { join } from "node:path";
2
+ import { listPriorSets, getPriorSet, promotePriorSet, rollbackPriorSet, createPriorSet, savePriorStore, loadPriorStore } from "../../core/index.js";
3
+ const VALID_GATES = ["development", "backtest", "shadow", "live"];
4
+ // ─── Store path resolution ────────────────────────────────────────────────────
5
+ export function resolvePriorStoreDir(cwd) {
6
+ return join(cwd ?? process.cwd(), ".martin");
7
+ }
8
+ // ─── Command handler ──────────────────────────────────────────────────────────
9
+ /**
10
+ * Handles `martin prior <subcommand> [args]`
11
+ *
12
+ * Subcommands:
13
+ * list — list all PriorSets
14
+ * status --prior-set-id <id> — show current gate for a PriorSet
15
+ * promote --prior-set-id <id> --gate <gate> — advance gate (sequential only)
16
+ * rollback --prior-set-id <id> — revert to previous gate
17
+ */
18
+ export async function handlePriorCommand(args, options = {}) {
19
+ const storeDir = resolvePriorStoreDir(options.cwd);
20
+ const subcommand = args[0];
21
+ try {
22
+ switch (subcommand) {
23
+ case "list": {
24
+ const priorSets = await listPriorSets(storeDir);
25
+ if (priorSets.length === 0) {
26
+ return {
27
+ stdout: JSON.stringify({ priorSets: [], message: "No PriorSets found." }, null, 2),
28
+ stderr: "",
29
+ exitCode: 0
30
+ };
31
+ }
32
+ return {
33
+ stdout: JSON.stringify({ priorSets }, null, 2),
34
+ stderr: "",
35
+ exitCode: 0
36
+ };
37
+ }
38
+ case "status": {
39
+ const priorSetId = parseFlag(args, "--prior-set-id");
40
+ if (!priorSetId) {
41
+ return { stdout: "", stderr: "Missing --prior-set-id", exitCode: 1 };
42
+ }
43
+ const ps = await getPriorSet(priorSetId, storeDir);
44
+ if (!ps) {
45
+ return { stdout: "", stderr: `PriorSet not found: ${priorSetId}`, exitCode: 1 };
46
+ }
47
+ return {
48
+ stdout: JSON.stringify({
49
+ priorSetId: ps.priorSetId,
50
+ taskClass: ps.taskClass,
51
+ promotionGate: ps.promotionGate,
52
+ version: ps.version,
53
+ sampleCount: ps.sampleCount,
54
+ confidence: ps.confidence
55
+ }, null, 2),
56
+ stderr: "",
57
+ exitCode: 0
58
+ };
59
+ }
60
+ case "promote": {
61
+ const priorSetId = parseFlag(args, "--prior-set-id");
62
+ const gate = parseFlag(args, "--gate");
63
+ if (!priorSetId)
64
+ return { stdout: "", stderr: "Missing --prior-set-id", exitCode: 1 };
65
+ if (!gate)
66
+ return { stdout: "", stderr: "Missing --gate", exitCode: 1 };
67
+ if (!VALID_GATES.includes(gate)) {
68
+ return {
69
+ stdout: "",
70
+ stderr: `Invalid gate: ${gate}. Must be one of: ${VALID_GATES.join(", ")}`,
71
+ exitCode: 1
72
+ };
73
+ }
74
+ const updated = await promotePriorSet(priorSetId, gate, storeDir);
75
+ return {
76
+ stdout: JSON.stringify({
77
+ message: `PriorSet ${priorSetId} promoted to ${gate}`,
78
+ priorSetId: updated.priorSetId,
79
+ promotionGate: updated.promotionGate,
80
+ version: updated.version
81
+ }, null, 2),
82
+ stderr: "",
83
+ exitCode: 0
84
+ };
85
+ }
86
+ case "rollback": {
87
+ const priorSetId = parseFlag(args, "--prior-set-id");
88
+ if (!priorSetId)
89
+ return { stdout: "", stderr: "Missing --prior-set-id", exitCode: 1 };
90
+ const reverted = await rollbackPriorSet(priorSetId, storeDir);
91
+ return {
92
+ stdout: JSON.stringify({
93
+ message: `PriorSet ${priorSetId} rolled back to ${reverted.promotionGate}`,
94
+ priorSetId: reverted.priorSetId,
95
+ promotionGate: reverted.promotionGate,
96
+ version: reverted.version
97
+ }, null, 2),
98
+ stderr: "",
99
+ exitCode: 0
100
+ };
101
+ }
102
+ default:
103
+ return {
104
+ stdout: "",
105
+ stderr: [
106
+ "Usage: martin prior <subcommand> [options]",
107
+ "",
108
+ "Subcommands:",
109
+ " list List all PriorSets",
110
+ " status --prior-set-id <id> Show current gate",
111
+ " promote --prior-set-id <id> --gate <gate> Promote to next gate",
112
+ " rollback --prior-set-id <id> Revert to previous gate",
113
+ "",
114
+ "Gates (sequential): development → backtest → shadow → live"
115
+ ].join("\n"),
116
+ exitCode: 1
117
+ };
118
+ }
119
+ }
120
+ catch (err) {
121
+ const message = err instanceof Error ? err.message : String(err);
122
+ return { stdout: "", stderr: message, exitCode: 1 };
123
+ }
124
+ }
125
+ // ─── Seed helper (for testing / initial setup) ────────────────────────────────
126
+ /**
127
+ * Seeds a PriorSet into the store if it doesn't already exist.
128
+ * Used by tests and the CLI init flow.
129
+ */
130
+ export async function seedPriorSet(priorSetId, taskClass, priors, storeDir) {
131
+ const store = await loadPriorStore(storeDir);
132
+ if (store.priorSets.some(p => p.priorSetId === priorSetId))
133
+ return;
134
+ const ps = createPriorSet({ priorSetId, taskClass, priors });
135
+ store.priorSets.push(ps);
136
+ await savePriorStore(store, storeDir);
137
+ }
138
+ // ─── Internal helpers ─────────────────────────────────────────────────────────
139
+ function parseFlag(args, flag) {
140
+ const idx = args.indexOf(flag);
141
+ if (idx === -1 || idx >= args.length - 1)
142
+ return undefined;
143
+ return args[idx + 1];
144
+ }
145
+ //# sourceMappingURL=prior.js.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * resume.ts — SLICE-23: Resumable loops CLI handler
3
+ *
4
+ * Usage: martin resume <loop-id> [--force]
5
+ *
6
+ * 1. Reads checkpoint from ~/.martin/loops/<loop-id>/checkpoint.json
7
+ * 2. Validates workspace hashes
8
+ * 3. If modified and no --force: prints error and exits with code 1
9
+ * 4. If valid (or --force): prints resume state and exits with instructions
10
+ * (actual resume wiring into runMartin is future work — this slice ships
11
+ * the checkpoint infrastructure and the CLI entry point)
12
+ * 5. Prints: "Resuming loop <id> from attempt <N>. Cost so far: $<M>"
13
+ */
14
+ export interface ResumeCommandResult {
15
+ stdout: string;
16
+ stderr: string;
17
+ exitCode: number;
18
+ }
19
+ export declare function handleResumeCommand(args: string[], _options?: {
20
+ cwd?: string;
21
+ }): Promise<ResumeCommandResult>;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * resume.ts — SLICE-23: Resumable loops CLI handler
3
+ *
4
+ * Usage: martin resume <loop-id> [--force]
5
+ *
6
+ * 1. Reads checkpoint from ~/.martin/loops/<loop-id>/checkpoint.json
7
+ * 2. Validates workspace hashes
8
+ * 3. If modified and no --force: prints error and exits with code 1
9
+ * 4. If valid (or --force): prints resume state and exits with instructions
10
+ * (actual resume wiring into runMartin is future work — this slice ships
11
+ * the checkpoint infrastructure and the CLI entry point)
12
+ * 5. Prints: "Resuming loop <id> from attempt <N>. Cost so far: $<M>"
13
+ */
14
+ export async function handleResumeCommand(args, _options = {}) {
15
+ const loopId = readPositional(args);
16
+ const force = args.includes("--force");
17
+ if (!loopId) {
18
+ return {
19
+ exitCode: 1,
20
+ stdout: "",
21
+ stderr: "Error: resume requires a loop ID. Usage: martin resume <loopId> [--force]"
22
+ };
23
+ }
24
+ const { getCheckpointStorageDir, readCheckpoint, validateWorkspaceHashes, WorkspaceModifiedError } = await import("../../core/index.js");
25
+ const storageDir = getCheckpointStorageDir();
26
+ const checkpoint = readCheckpoint(loopId, storageDir);
27
+ if (!checkpoint) {
28
+ return {
29
+ exitCode: 1,
30
+ stdout: "",
31
+ stderr: `Error: no checkpoint found for loop ${loopId}. Run has not been interrupted or checkpoint was cleaned up.`
32
+ };
33
+ }
34
+ const { valid, modifiedFiles } = validateWorkspaceHashes(checkpoint, force);
35
+ if (!valid) {
36
+ const fileList = modifiedFiles.join(", ");
37
+ return {
38
+ exitCode: 1,
39
+ stdout: "",
40
+ stderr: [
41
+ `Error: workspace has been modified since checkpoint for loop ${loopId}.`,
42
+ `Modified files: ${fileList}`,
43
+ "Use --force to resume anyway."
44
+ ].join("\n")
45
+ };
46
+ }
47
+ const resumeFromAttempt = checkpoint.lastCompletedAttempt + 1;
48
+ const costFormatted = checkpoint.costSoFar.toFixed(4);
49
+ const summary = [
50
+ `Resuming loop ${loopId} from attempt ${resumeFromAttempt}. Cost so far: $${costFormatted}`,
51
+ `Phase at checkpoint: ${checkpoint.phase}`,
52
+ `Last attempt rolled back: ${String(checkpoint.lastAttemptRolledBack)}`,
53
+ ...(force && modifiedFiles.length > 0
54
+ ? [`Warning: resuming with --force despite ${modifiedFiles.length} modified file(s).`]
55
+ : []),
56
+ "",
57
+ "Note: this command prints resume state only. Pass the loop ID to martin run to re-execute."
58
+ ].join("\n");
59
+ return {
60
+ exitCode: 0,
61
+ stdout: summary,
62
+ stderr: ""
63
+ };
64
+ }
65
+ function readPositional(args) {
66
+ for (const arg of args) {
67
+ if (!arg.startsWith("--")) {
68
+ return arg;
69
+ }
70
+ }
71
+ return undefined;
72
+ }
73
+ //# sourceMappingURL=resume.js.map
@@ -0,0 +1,6 @@
1
+ export interface VerifyCommandResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ exitCode: number;
5
+ }
6
+ export declare function handleVerifyCommand(args: string[]): Promise<VerifyCommandResult>;
@@ -0,0 +1,43 @@
1
+ import { join } from "node:path";
2
+ import { resolveRunsRoot, verifyLoopRecordAttestation } from "../../core/index.js";
3
+ export async function handleVerifyCommand(args) {
4
+ const loopId = parseFlag(args, "--loop") ?? args[0];
5
+ if (!loopId) {
6
+ return {
7
+ stdout: "",
8
+ stderr: "Missing --loop. Usage: martin verify --loop <id>",
9
+ exitCode: 1
10
+ };
11
+ }
12
+ const runRoot = join(resolveRunsRoot(process.env), loopId);
13
+ const verification = await verifyLoopRecordAttestation(runRoot);
14
+ if (verification.ok) {
15
+ return {
16
+ stdout: JSON.stringify({
17
+ command: "verify",
18
+ status: "passed",
19
+ loopId,
20
+ file: verification.file,
21
+ keyId: verification.keyId
22
+ }, null, 2),
23
+ stderr: "",
24
+ exitCode: 0
25
+ };
26
+ }
27
+ return {
28
+ stdout: "",
29
+ stderr: JSON.stringify({
30
+ command: "verify",
31
+ status: "failed",
32
+ loopId,
33
+ file: verification.file,
34
+ reason: verification.reason
35
+ }, null, 2),
36
+ exitCode: 1
37
+ };
38
+ }
39
+ function parseFlag(args, flag) {
40
+ const index = args.indexOf(flag);
41
+ return index >= 0 ? args[index + 1] : undefined;
42
+ }
43
+ //# sourceMappingURL=verify.js.map