@martinloop/mcp 0.2.5 → 0.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 (70) hide show
  1. package/README.md +40 -132
  2. package/dist/discovery-metadata.d.ts +10 -5
  3. package/dist/discovery-metadata.js +95 -5
  4. package/dist/package-version.d.ts +1 -1
  5. package/dist/package-version.js +1 -1
  6. package/dist/prompts.d.ts +1 -1
  7. package/dist/prompts.js +93 -1
  8. package/dist/resources.d.ts +9 -1
  9. package/dist/resources.js +247 -16
  10. package/dist/server-validation.d.ts +2 -1
  11. package/dist/server-validation.js +124 -0
  12. package/dist/server.js +379 -5
  13. package/dist/tools/doctor.d.ts +14 -1
  14. package/dist/tools/doctor.js +43 -8
  15. package/dist/tools/eval.d.ts +24 -0
  16. package/dist/tools/eval.js +66 -0
  17. package/dist/tools/get-run.d.ts +2 -0
  18. package/dist/tools/get-run.js +2 -1
  19. package/dist/tools/get-status.d.ts +8 -0
  20. package/dist/tools/get-status.js +18 -0
  21. package/dist/tools/get-verification-results.d.ts +2 -0
  22. package/dist/tools/get-verification-results.js +2 -1
  23. package/dist/tools/logs.d.ts +25 -0
  24. package/dist/tools/logs.js +49 -0
  25. package/dist/tools/plan.d.ts +20 -0
  26. package/dist/tools/plan.js +10 -0
  27. package/dist/tools/pr-tools.d.ts +31 -0
  28. package/dist/tools/pr-tools.js +112 -0
  29. package/dist/tools/preflight.d.ts +24 -1
  30. package/dist/tools/preflight.js +47 -7
  31. package/dist/tools/run-controls.d.ts +36 -0
  32. package/dist/tools/run-controls.js +88 -0
  33. package/dist/tools/run-dossier.d.ts +16 -0
  34. package/dist/tools/run-dossier.js +64 -2
  35. package/dist/tools/run-loop.d.ts +3 -2
  36. package/dist/tools/run-loop.js +52 -13
  37. package/dist/tools/tool-errors.d.ts +1 -1
  38. package/dist/tools/tool-errors.js +1 -1
  39. package/dist/tools/tool-support.d.ts +6 -3
  40. package/dist/tools/tool-support.js +37 -3
  41. package/dist/tools/workflow-governance.d.ts +133 -0
  42. package/dist/tools/workflow-governance.js +581 -0
  43. package/dist/vendor/adapters/claude-cli.d.ts +25 -0
  44. package/dist/vendor/adapters/claude-cli.js +279 -19
  45. package/dist/vendor/adapters/cli-bridge.d.ts +6 -0
  46. package/dist/vendor/adapters/cli-bridge.js +58 -9
  47. package/dist/vendor/adapters/codex-launcher.d.ts +44 -0
  48. package/dist/vendor/adapters/codex-launcher.js +247 -0
  49. package/dist/vendor/adapters/index.d.ts +4 -2
  50. package/dist/vendor/adapters/index.js +4 -1
  51. package/dist/vendor/adapters/openai-compatible.d.ts +62 -0
  52. package/dist/vendor/adapters/openai-compatible.js +267 -0
  53. package/dist/vendor/adapters/runtime-support.d.ts +3 -0
  54. package/dist/vendor/adapters/runtime-support.js +8 -1
  55. package/dist/vendor/adapters/verifier-only.js +4 -3
  56. package/dist/vendor/contracts/index.d.ts +39 -0
  57. package/dist/vendor/contracts/index.js +2 -0
  58. package/dist/vendor/core/index.d.ts +23 -3
  59. package/dist/vendor/core/index.js +88 -15
  60. package/dist/vendor/core/persistence/index.d.ts +2 -0
  61. package/dist/vendor/core/persistence/index.js +1 -0
  62. package/dist/vendor/core/persistence/integrity.d.ts +38 -0
  63. package/dist/vendor/core/persistence/integrity.js +239 -0
  64. package/dist/vendor/core/persistence/store.d.ts +7 -0
  65. package/dist/vendor/core/persistence/store.js +25 -1
  66. package/dist/vendor/core/policy.d.ts +9 -0
  67. package/dist/workflow-state.d.ts +25 -0
  68. package/dist/workflow-state.js +102 -0
  69. package/package.json +3 -3
  70. package/server.json +2 -2
package/dist/resources.js CHANGED
@@ -3,6 +3,8 @@ import { MARTIN_MCP_PACKAGE_VERSION } from "./package-version.js";
3
3
  import { inspectLoopTool } from "./tools/inspect-loop.js";
4
4
  import { martinDoctorTool } from "./tools/doctor.js";
5
5
  import { martinTriageRunsTool } from "./tools/triage-runs.js";
6
+ import { martinRunDossierTool } from "./tools/run-dossier.js";
7
+ import { inspectRepoSignals, buildPolicyPackDefinition, buildRepoRiskMap } from "./tools/workflow-governance.js";
6
8
  import { buildAttemptSnapshot, buildPersistedLoopPreview, buildVerificationHistorySnapshot, parseAttemptIndex, resolveMartinDiscoveryContext, toPrettyJson } from "./discovery-support.js";
7
9
  import { loadDetailedLoopRecord, readLedgerEvents } from "./tools/run-store.js";
8
10
  import { invalidArgumentsError, MartinToolError } from "./tools/tool-errors.js";
@@ -10,13 +12,21 @@ export const MARTIN_STATIC_RESOURCE_URIS = {
10
12
  serverHealth: "martin://server/health",
11
13
  recentRuns: "martin://runs/recent",
12
14
  triage: "martin://runs/triage",
15
+ latestRun: "martin://runs/latest",
13
16
  latestSummary: "martin://runs/latest/summary",
14
17
  latestProofCard: "martin://runs/latest/proof-card",
15
18
  latestBudgetStatus: "martin://runs/latest/budget-status",
16
19
  latestVerifierEvidence: "martin://runs/latest/verifier-evidence",
17
20
  latestRollbackEvidence: "martin://runs/latest/rollback-evidence",
21
+ currentPolicies: "martin://policies/current",
22
+ repoRiskMap: "martin://repo/risk-map",
23
+ verifierResults: "martin://verifiers/results",
18
24
  agentNextStep: "martin://agent/next-step",
19
25
  mcpUsageGuide: "martin://guides/mcp-usage",
26
+ agentStartGuide: "martin://guides/agent-start",
27
+ commandMapGuide: "martin://guides/command-map",
28
+ ideOnboardingGuide: "martin://guides/ide-onboarding",
29
+ operatingRulesGuide: "martin://guides/operating-rules",
20
30
  publishReadinessGuide: "martin://guides/publish-readiness"
21
31
  };
22
32
  export const MARTIN_RESOURCE_TEMPLATES = [
@@ -26,6 +36,12 @@ export const MARTIN_RESOURCE_TEMPLATES = [
26
36
  uriTemplate: "martin://runs/{loopId}",
27
37
  description: "Read a canonical Martin loop record using the same selector path as martin_status."
28
38
  },
39
+ {
40
+ name: "martin_run_dossier_resource",
41
+ title: "Martin Run Dossier",
42
+ uriTemplate: "martin://runs/{loopId}/dossier",
43
+ description: "Read the dossier-shaped summary for a persisted Martin loop."
44
+ },
29
45
  {
30
46
  name: "martin_run_attempt",
31
47
  title: "Martin Run Attempt",
@@ -61,6 +77,13 @@ export const MARTIN_STATIC_RESOURCES = [
61
77
  description: "Priority-ranked Martin runs that likely need attention.",
62
78
  mimeType: "application/json"
63
79
  },
80
+ {
81
+ uri: MARTIN_STATIC_RESOURCE_URIS.latestRun,
82
+ name: "martin_latest_run",
83
+ title: "Martin Latest Run",
84
+ description: "Full latest-run dossier surface for agents that need more than the compact summary.",
85
+ mimeType: "application/json"
86
+ },
64
87
  {
65
88
  uri: MARTIN_STATIC_RESOURCE_URIS.latestSummary,
66
89
  name: "martin_latest_summary",
@@ -96,6 +119,27 @@ export const MARTIN_STATIC_RESOURCES = [
96
119
  description: "Compact rollback and artifact evidence for the latest run.",
97
120
  mimeType: "application/json"
98
121
  },
122
+ {
123
+ uri: MARTIN_STATIC_RESOURCE_URIS.currentPolicies,
124
+ name: "martin_current_policies",
125
+ title: "Martin Current Policies",
126
+ description: "Local policy-pack presets and the recommended default pack for the current repo.",
127
+ mimeType: "application/json"
128
+ },
129
+ {
130
+ uri: MARTIN_STATIC_RESOURCE_URIS.repoRiskMap,
131
+ name: "martin_repo_risk_map",
132
+ title: "Martin Repo Risk Map",
133
+ description: "Sensitive repo surfaces and the recommended policy pack for this workspace.",
134
+ mimeType: "application/json"
135
+ },
136
+ {
137
+ uri: MARTIN_STATIC_RESOURCE_URIS.verifierResults,
138
+ name: "martin_latest_verifier_results",
139
+ title: "Martin Latest Verifier Results",
140
+ description: "Latest verifier results alias for compact run evidence.",
141
+ mimeType: "application/json"
142
+ },
99
143
  {
100
144
  uri: MARTIN_STATIC_RESOURCE_URIS.agentNextStep,
101
145
  name: "martin_agent_next_step",
@@ -110,6 +154,34 @@ export const MARTIN_STATIC_RESOURCES = [
110
154
  description: "Recommended workflow for using Martin Loop over MCP.",
111
155
  mimeType: "text/markdown"
112
156
  },
157
+ {
158
+ uri: MARTIN_STATIC_RESOURCE_URIS.agentStartGuide,
159
+ name: "martin_agent_start_guide",
160
+ title: "Martin Agent Start Guide",
161
+ description: "Short agent-facing guide for using Martin with minimal context and low tool bloat.",
162
+ mimeType: "text/markdown"
163
+ },
164
+ {
165
+ uri: MARTIN_STATIC_RESOURCE_URIS.commandMapGuide,
166
+ name: "martin_command_map_guide",
167
+ title: "Martin Command Map Guide",
168
+ description: "Command-by-command guide for choosing the right Martin tool or surface.",
169
+ mimeType: "text/markdown"
170
+ },
171
+ {
172
+ uri: MARTIN_STATIC_RESOURCE_URIS.ideOnboardingGuide,
173
+ name: "martin_ide_onboarding_guide",
174
+ title: "Martin IDE Onboarding Guide",
175
+ description: "IDE-facing setup guide for making MartinLoop part of the default MCP workflow.",
176
+ mimeType: "text/markdown"
177
+ },
178
+ {
179
+ uri: MARTIN_STATIC_RESOURCE_URIS.operatingRulesGuide,
180
+ name: "martin_operating_rules_guide",
181
+ title: "Martin Operating Rules",
182
+ description: "Built-in operating rules that tell agents when Martin commands must be used before work proceeds.",
183
+ mimeType: "text/markdown"
184
+ },
113
185
  {
114
186
  uri: MARTIN_STATIC_RESOURCE_URIS.publishReadinessGuide,
115
187
  name: "martin_publish_readiness_guide",
@@ -147,6 +219,8 @@ export async function readMartinResource(input) {
147
219
  const triage = await martinTriageRunsTool({ runsDir: context.runsRoot });
148
220
  return jsonResource(input.uri, withDiscoveryMetadata(triage, context.runsRoot));
149
221
  }
222
+ case MARTIN_STATIC_RESOURCE_URIS.latestRun:
223
+ return jsonResource(input.uri, withDiscoveryMetadata(await martinRunDossierTool({ latest: true, runsDir: context.runsRoot }), context.runsRoot));
150
224
  case MARTIN_STATIC_RESOURCE_URIS.latestSummary:
151
225
  return jsonResource(input.uri, withDiscoveryMetadata(await buildLatestSummaryResource(context.runsRoot), context.runsRoot));
152
226
  case MARTIN_STATIC_RESOURCE_URIS.latestProofCard:
@@ -157,10 +231,24 @@ export async function readMartinResource(input) {
157
231
  return jsonResource(input.uri, withDiscoveryMetadata(await buildLatestVerifierEvidenceResource(context.runsRoot), context.runsRoot));
158
232
  case MARTIN_STATIC_RESOURCE_URIS.latestRollbackEvidence:
159
233
  return jsonResource(input.uri, withDiscoveryMetadata(await buildLatestRollbackEvidenceResource(context.runsRoot), context.runsRoot));
234
+ case MARTIN_STATIC_RESOURCE_URIS.currentPolicies:
235
+ return jsonResource(input.uri, withDiscoveryMetadata(buildCurrentPoliciesResource(context.workingDirectory), context.runsRoot));
236
+ case MARTIN_STATIC_RESOURCE_URIS.repoRiskMap:
237
+ return jsonResource(input.uri, withDiscoveryMetadata(buildRepoRiskMap(inspectRepoSignals(context.workingDirectory)), context.runsRoot));
238
+ case MARTIN_STATIC_RESOURCE_URIS.verifierResults:
239
+ return jsonResource(input.uri, withDiscoveryMetadata(await buildLatestVerifierEvidenceResource(context.runsRoot), context.runsRoot));
160
240
  case MARTIN_STATIC_RESOURCE_URIS.agentNextStep:
161
241
  return jsonResource(input.uri, withDiscoveryMetadata(await buildAgentNextStepResource(context.runsRoot), context.runsRoot));
162
242
  case MARTIN_STATIC_RESOURCE_URIS.mcpUsageGuide:
163
243
  return textResource(input.uri, "text/markdown", buildMcpUsageGuide(context.runsRoot));
244
+ case MARTIN_STATIC_RESOURCE_URIS.agentStartGuide:
245
+ return textResource(input.uri, "text/markdown", buildAgentStartGuide(context.runsRoot));
246
+ case MARTIN_STATIC_RESOURCE_URIS.commandMapGuide:
247
+ return textResource(input.uri, "text/markdown", buildCommandMapGuide(context.runsRoot));
248
+ case MARTIN_STATIC_RESOURCE_URIS.ideOnboardingGuide:
249
+ return textResource(input.uri, "text/markdown", buildIdeOnboardingGuide(context.runsRoot));
250
+ case MARTIN_STATIC_RESOURCE_URIS.operatingRulesGuide:
251
+ return textResource(input.uri, "text/markdown", buildOperatingRulesGuide(context.runsRoot));
164
252
  case MARTIN_STATIC_RESOURCE_URIS.publishReadinessGuide:
165
253
  return textResource(input.uri, "text/markdown", buildPublishReadinessGuide(context.runsRoot));
166
254
  default:
@@ -203,6 +291,12 @@ async function readDynamicMartinResource(input) {
203
291
  warnings: [...detail.warnings, ...verification.warnings]
204
292
  }, input.runsDir));
205
293
  }
294
+ const dossierMatch = /^martin:\/\/runs\/([^/]+)\/dossier$/u.exec(input.uri);
295
+ if (dossierMatch?.[1]) {
296
+ const loopId = decodeURIComponent(dossierMatch[1]);
297
+ const dossier = await martinRunDossierTool({ loopId, runsDir: input.runsDir });
298
+ return jsonResource(input.uri, withDiscoveryMetadata(dossier, input.runsDir));
299
+ }
206
300
  const attemptMatch = /^martin:\/\/runs\/([^/]+)\/attempts\/([^/]+)$/u.exec(input.uri);
207
301
  if (attemptMatch?.[1] && attemptMatch[2]) {
208
302
  const loopId = decodeURIComponent(attemptMatch[1]);
@@ -244,7 +338,7 @@ async function loadLatestRunForCompactResource(runsRoot) {
244
338
  empty: true,
245
339
  warnings: [
246
340
  "No Martin run records were found yet.",
247
- "Run `npx martin-loop demo`, then run a governed task or set MARTIN_LIVE=false for a no-spend local proof run."
341
+ "Run `npx martin-loop demo`, then run `npx martin-loop run ... --proof --verify <command>` for a no-spend local proof pass."
248
342
  ]
249
343
  };
250
344
  }
@@ -254,7 +348,7 @@ async function loadLatestRunForCompactResource(runsRoot) {
254
348
  async function buildLatestSummaryResource(runsRoot) {
255
349
  const latest = await loadLatestRunForCompactResource(runsRoot);
256
350
  if (latest.empty || !latest.detail) {
257
- return compactEmptyState("latest-summary", latest.warnings);
351
+ return compactEmptyState("latest-summary", runsRoot, latest.warnings);
258
352
  }
259
353
  const ledgerEvents = await readLedgerEvents(latest.detail);
260
354
  const loop = latest.detail.loop;
@@ -294,7 +388,7 @@ async function buildLatestSummaryResource(runsRoot) {
294
388
  async function buildLatestBudgetStatusResource(runsRoot) {
295
389
  const latest = await loadLatestRunForCompactResource(runsRoot);
296
390
  if (latest.empty || !latest.detail) {
297
- return compactEmptyState("budget-status", latest.warnings);
391
+ return compactEmptyState("budget-status", runsRoot, latest.warnings);
298
392
  }
299
393
  const loop = latest.detail.loop;
300
394
  const preview = buildPersistedLoopPreview(loop);
@@ -325,7 +419,7 @@ async function buildLatestBudgetStatusResource(runsRoot) {
325
419
  async function buildLatestVerifierEvidenceResource(runsRoot) {
326
420
  const latest = await loadLatestRunForCompactResource(runsRoot);
327
421
  if (latest.empty || !latest.detail) {
328
- return compactEmptyState("verifier-evidence", latest.warnings);
422
+ return compactEmptyState("verifier-evidence", runsRoot, latest.warnings);
329
423
  }
330
424
  const ledgerEvents = await readLedgerEvents(latest.detail);
331
425
  const loop = latest.detail.loop;
@@ -348,7 +442,7 @@ async function buildLatestVerifierEvidenceResource(runsRoot) {
348
442
  async function buildLatestRollbackEvidenceResource(runsRoot) {
349
443
  const latest = await loadLatestRunForCompactResource(runsRoot);
350
444
  if (latest.empty || !latest.detail) {
351
- return compactEmptyState("rollback-evidence", latest.warnings);
445
+ return compactEmptyState("rollback-evidence", runsRoot, latest.warnings);
352
446
  }
353
447
  const loop = latest.detail.loop;
354
448
  const artifacts = loop.artifacts ?? [];
@@ -374,7 +468,7 @@ async function buildAgentNextStepResource(runsRoot) {
374
468
  const latest = await loadLatestRunForCompactResource(runsRoot);
375
469
  if (latest.empty || !latest.detail) {
376
470
  return {
377
- ...compactEmptyState("agent-next-step", latest.warnings),
471
+ ...compactEmptyState("agent-next-step", runsRoot, latest.warnings),
378
472
  nextTool: "martin_doctor",
379
473
  reason: "No run store evidence exists yet; confirm environment and run-store visibility first."
380
474
  };
@@ -398,8 +492,17 @@ async function buildAgentNextStepResource(runsRoot) {
398
492
  ? "failed"
399
493
  : "unavailable"
400
494
  },
495
+ requiredWorkflow: [
496
+ "martin_doctor",
497
+ "martin_plan",
498
+ "martin_preflight",
499
+ "martin_run",
500
+ "martin_dossier",
501
+ "martin_eval"
502
+ ],
401
503
  preferredResource: MARTIN_STATIC_RESOURCE_URIS.latestSummary,
402
- proofCard: MARTIN_STATIC_RESOURCE_URIS.latestProofCard
504
+ proofCard: MARTIN_STATIC_RESOURCE_URIS.latestProofCard,
505
+ operatingRules: MARTIN_STATIC_RESOURCE_URIS.operatingRulesGuide
403
506
  };
404
507
  }
405
508
  async function buildLatestProofCardResource(runsRoot) {
@@ -452,15 +555,30 @@ async function buildLatestProofCardResource(runsRoot) {
452
555
  ""
453
556
  ].join("\n");
454
557
  }
455
- function compactEmptyState(kind, warnings) {
558
+ function compactEmptyState(kind, runsRoot, warnings) {
456
559
  return {
457
560
  kind,
458
561
  status: "empty",
562
+ runsRoot,
459
563
  summary: "No Martin run records are available yet.",
460
- nextStep: "Run `martin doctor`, create the demo workspace with `npx martin-loop demo`, then run a no-spend stub task with MARTIN_LIVE=false.",
564
+ nextStep: "Run `npx martin-loop doctor`, create the demo workspace with `npx martin-loop demo`, then run `npx martin-loop run ... --proof --verify <command>`.",
461
565
  warnings
462
566
  };
463
567
  }
568
+ function buildCurrentPoliciesResource(workingDirectory) {
569
+ const signals = inspectRepoSignals(workingDirectory);
570
+ const recommended = buildPolicyPackDefinition(undefined, signals);
571
+ return {
572
+ recommended: recommended.name,
573
+ packs: [
574
+ buildPolicyPackDefinition("solo-founder", signals),
575
+ buildPolicyPackDefinition("startup-team", signals),
576
+ buildPolicyPackDefinition("enterprise-strict", signals),
577
+ buildPolicyPackDefinition("oss-maintainer", signals),
578
+ buildPolicyPackDefinition("security-sensitive", signals)
579
+ ]
580
+ };
581
+ }
464
582
  function describePrevention(loop) {
465
583
  const prevented = [];
466
584
  const latestAttempt = loop.attempts.at(-1);
@@ -506,13 +624,13 @@ function inferAgentNextStep(loop, verification) {
506
624
  instruction: "Call `martin_preflight` with explicit verifier, budget, and path scope before the next run."
507
625
  };
508
626
  }
509
- function buildMcpUsageGuide(_runsRoot) {
627
+ function buildMcpUsageGuide(runsRoot) {
510
628
  const metadata = buildMartinDiscoveryMetadata(MARTIN_MCP_PACKAGE_VERSION);
511
629
  return `# Martin Loop MCP Usage
512
630
 
513
631
  Discovery revision: \`${metadata.discoveryRevision}\`
514
632
  Server version: \`${metadata.serverVersion}\`
515
- Runs root: inspect \`${MARTIN_STATIC_RESOURCE_URIS.serverHealth}\` when you need the active local path.
633
+ Runs root: \`${runsRoot}\`
516
634
 
517
635
  Martin Loop exposes governed coding workflows over MCP. Use the server health and run-store views to decide when to preflight, execute, inspect, or escalate.
518
636
 
@@ -529,7 +647,7 @@ Martin Loop exposes governed coding workflows over MCP. Use the server health an
529
647
  ## Current Martin MCP Surface
530
648
 
531
649
  - Tools: \`martin_run\`, \`martin_inspect\`, \`martin_status\`, \`martin_doctor\`, \`martin_preflight\`, \`martin_list_runs\`, \`martin_triage_runs\`, \`martin_get_run\`, \`martin_get_attempt\`, \`martin_get_verification_results\`, \`martin_run_dossier\`
532
- - Static resources: \`${MARTIN_STATIC_RESOURCE_URIS.serverHealth}\`, \`${MARTIN_STATIC_RESOURCE_URIS.recentRuns}\`, \`${MARTIN_STATIC_RESOURCE_URIS.triage}\`, \`${MARTIN_STATIC_RESOURCE_URIS.latestSummary}\`, \`${MARTIN_STATIC_RESOURCE_URIS.latestProofCard}\`, \`${MARTIN_STATIC_RESOURCE_URIS.latestBudgetStatus}\`, \`${MARTIN_STATIC_RESOURCE_URIS.latestVerifierEvidence}\`, \`${MARTIN_STATIC_RESOURCE_URIS.latestRollbackEvidence}\`, \`${MARTIN_STATIC_RESOURCE_URIS.agentNextStep}\`, \`${MARTIN_STATIC_RESOURCE_URIS.mcpUsageGuide}\`, \`${MARTIN_STATIC_RESOURCE_URIS.publishReadinessGuide}\`
650
+ - Static resources: \`${MARTIN_STATIC_RESOURCE_URIS.serverHealth}\`, \`${MARTIN_STATIC_RESOURCE_URIS.recentRuns}\`, \`${MARTIN_STATIC_RESOURCE_URIS.triage}\`, \`${MARTIN_STATIC_RESOURCE_URIS.latestSummary}\`, \`${MARTIN_STATIC_RESOURCE_URIS.latestProofCard}\`, \`${MARTIN_STATIC_RESOURCE_URIS.latestBudgetStatus}\`, \`${MARTIN_STATIC_RESOURCE_URIS.latestVerifierEvidence}\`, \`${MARTIN_STATIC_RESOURCE_URIS.latestRollbackEvidence}\`, \`${MARTIN_STATIC_RESOURCE_URIS.agentNextStep}\`, \`${MARTIN_STATIC_RESOURCE_URIS.mcpUsageGuide}\`, \`${MARTIN_STATIC_RESOURCE_URIS.agentStartGuide}\`, \`${MARTIN_STATIC_RESOURCE_URIS.publishReadinessGuide}\`
533
651
  - Resource templates: \`martin://runs/{loopId}\`, \`martin://runs/{loopId}/attempts/{attemptIndex}\`, \`martin://runs/{loopId}/verification\`
534
652
  - Prompts: \`martin_start\`, \`martin_preflight\`, \`martin_triage\`, \`martin_resume\`, \`martin_prove\`, \`martin_release_check\`, \`martin_governed_coding_kickoff\`, \`martin_debug_failed_run\`, \`martin_publish_readiness_review\`, \`martin_triage_run_store\`
535
653
 
@@ -540,13 +658,125 @@ Martin Loop exposes governed coding workflows over MCP. Use the server health an
540
658
  - Attempt inspection stays aligned with the same loop selectors used by \`martin_status\` and \`martin_inspect\`.
541
659
  `;
542
660
  }
543
- function buildPublishReadinessGuide(_runsRoot) {
661
+ function buildAgentStartGuide(runsRoot) {
662
+ const metadata = buildMartinDiscoveryMetadata(MARTIN_MCP_PACKAGE_VERSION);
663
+ return `# Agent Start Here
664
+
665
+ Discovery revision: \`${metadata.discoveryRevision}\`
666
+ Server version: \`${metadata.serverVersion}\`
667
+ Runs root: \`${runsRoot}\`
668
+
669
+ Use Martin Loop as the local governor before you spend agent tokens or retry a failed coding loop.
670
+
671
+ ## Cheap Default Flow
672
+
673
+ 1. Read \`${MARTIN_STATIC_RESOURCE_URIS.agentNextStep}\` first. It is the smallest "what should I do now?" payload.
674
+ 2. If no runs exist, call \`martin_doctor\`, then \`martin_preflight\`.
675
+ 3. If a run exists, read \`${MARTIN_STATIC_RESOURCE_URIS.latestSummary}\` before reading full run JSON.
676
+ 4. If the verifier failed, use prompt \`martin_debug_failed_run\` or \`martin_triage\`.
677
+ 5. If you need a shareable receipt, read \`${MARTIN_STATIC_RESOURCE_URIS.latestProofCard}\`.
678
+
679
+ ## Install Profiles
680
+
681
+ - \`minimal\` (default): doctor, preflight, list, triage, dossier. Good for low tool bloat.
682
+ - \`diagnostic\`: read-only inspection tools for debugging without execution.
683
+ - \`full-local\`: all local tools, including \`martin_run\`.
684
+ - \`github-review\`: review-oriented local profile for PR drafting and reviewer evidence.
685
+ - \`starter\` and \`full\`: compatibility aliases.
686
+
687
+ ## Copy-Paste Agent Rule
688
+
689
+ Before running or retrying an autonomous coding task, call Martin. Prefer compact resources first, then full dossiers only when the compact receipt says more evidence is needed. Never claim success unless Martin shows verifier-backed completion or a clear blocked reason.
690
+ `;
691
+ }
692
+ function buildCommandMapGuide(runsRoot) {
693
+ const metadata = buildMartinDiscoveryMetadata(MARTIN_MCP_PACKAGE_VERSION);
694
+ return `# Martin Command Map
695
+
696
+ Discovery revision: \`${metadata.discoveryRevision}\`
697
+ Server version: \`${metadata.serverVersion}\`
698
+ Runs root: \`${runsRoot}\`
699
+
700
+ Use this guide when an IDE or agent needs to know which Martin surface to call next.
701
+
702
+ ## Default Governed Sequence
703
+
704
+ 1. \`martin_doctor\` — confirm environment and visibility.
705
+ 2. \`martin_plan\` — propose the bounded approach without spending a run.
706
+ 3. \`martin_preflight\` — turn the plan into an explicit contract.
707
+ 4. \`martin_run\` — execute only after the contract is safe.
708
+ 5. \`martin_dossier\` — inspect what happened and what Martin prevented.
709
+ 6. \`martin_eval\` — convert the result into review posture when needed.
710
+
711
+ ## When To Use Which Surface
712
+
713
+ - Need the smallest next action: \`${MARTIN_STATIC_RESOURCE_URIS.agentNextStep}\`
714
+ - Need a quick receipt: \`${MARTIN_STATIC_RESOURCE_URIS.latestSummary}\`
715
+ - Need a shareable proof object: \`${MARTIN_STATIC_RESOURCE_URIS.latestProofCard}\`
716
+ - Need a full run review: \`martin_dossier\`
717
+ - Need to decide whether to retry: \`martin_triage_runs\`
718
+ - Need IDE setup guidance: \`${MARTIN_STATIC_RESOURCE_URIS.ideOnboardingGuide}\`
719
+ `;
720
+ }
721
+ function buildIdeOnboardingGuide(runsRoot) {
722
+ const metadata = buildMartinDiscoveryMetadata(MARTIN_MCP_PACKAGE_VERSION);
723
+ return `# Martin IDE Onboarding
724
+
725
+ Discovery revision: \`${metadata.discoveryRevision}\`
726
+ Server version: \`${metadata.serverVersion}\`
727
+ Runs root: \`${runsRoot}\`
728
+
729
+ Use this guide after the MCP server is installed so the host naturally uses MartinLoop during real work.
730
+
731
+ ## IDE Default Flow
732
+
733
+ 1. Read \`${MARTIN_STATIC_RESOURCE_URIS.agentStartGuide}\`
734
+ 2. Read \`${MARTIN_STATIC_RESOURCE_URIS.operatingRulesGuide}\`
735
+ 3. Use prompt \`martin_start\` or \`martin_governed_coding_kickoff\`
736
+ 4. Call \`martin_doctor\`
737
+ 5. Call \`martin_plan\`
738
+ 6. Call \`martin_preflight\`
739
+ 7. Only then call \`martin_run\`
740
+
741
+ ## Goal
742
+
743
+ MartinLoop should become the normal path for coding work in the host, not a manual extra step the human has to remember.
744
+ `;
745
+ }
746
+ function buildOperatingRulesGuide(runsRoot) {
747
+ const metadata = buildMartinDiscoveryMetadata(MARTIN_MCP_PACKAGE_VERSION);
748
+ return `# Martin Operating Rules
749
+
750
+ Discovery revision: \`${metadata.discoveryRevision}\`
751
+ Server version: \`${metadata.serverVersion}\`
752
+ Runs root: \`${runsRoot}\`
753
+
754
+ Use these rules inside the IDE or MCP host as the default MartinLoop operating posture.
755
+
756
+ ## Required Sequence Before Real Work
757
+
758
+ 1. \`martin_doctor\`
759
+ 2. \`martin_plan\`
760
+ 3. \`martin_preflight\`
761
+ 4. \`martin_run\`
762
+ 5. \`martin_dossier\`
763
+
764
+ ## Enforcement Rule
765
+
766
+ Do not execute real coding work through MartinLoop until doctor and preflight have both been used for the current task.
767
+
768
+ ## Completion Rule
769
+
770
+ Do not claim success until MartinLoop shows verifier-backed completion or an explicit blocked reason backed by evidence.
771
+ `;
772
+ }
773
+ function buildPublishReadinessGuide(runsRoot) {
544
774
  const metadata = buildMartinDiscoveryMetadata(MARTIN_MCP_PACKAGE_VERSION);
545
775
  return `# Martin MCP Publish Readiness
546
776
 
547
777
  Discovery revision: \`${metadata.discoveryRevision}\`
548
778
  Server version: \`${metadata.serverVersion}\`
549
- Runs root: inspect \`${MARTIN_STATIC_RESOURCE_URIS.serverHealth}\` when you need the active local path.
779
+ Runs root: \`${runsRoot}\`
550
780
 
551
781
  Use this guide when reviewing whether the public MCP package is ready to publish, promote, or hand off for registry submission.
552
782
 
@@ -565,10 +795,11 @@ Use this guide when reviewing whether the public MCP package is ready to publish
565
795
  - If a discovery helper exists but is not yet wired into the server, report that as an integration gap instead of treating the capability as fully shipped.
566
796
  `;
567
797
  }
568
- function withDiscoveryMetadata(value, _runsRoot) {
798
+ function withDiscoveryMetadata(value, runsRoot) {
569
799
  return {
570
800
  metadata: {
571
- ...buildMartinDiscoveryMetadata(MARTIN_MCP_PACKAGE_VERSION)
801
+ ...buildMartinDiscoveryMetadata(MARTIN_MCP_PACKAGE_VERSION),
802
+ ...(runsRoot ? { runsRoot } : {})
572
803
  },
573
804
  value
574
805
  };
@@ -1,7 +1,8 @@
1
- type ToolName = "martin_run" | "martin_inspect" | "martin_status" | "martin_doctor" | "martin_preflight" | "martin_list_runs" | "martin_triage_runs" | "martin_get_run" | "martin_get_attempt" | "martin_get_verification_results" | "martin_run_dossier";
1
+ type ToolName = "martin_run" | "martin_inspect" | "martin_status" | "martin_doctor" | "martin_plan" | "martin_preflight" | "martin_logs" | "martin_cancel" | "martin_pause" | "martin_continue" | "martin_list_runs" | "martin_triage_runs" | "martin_get_run" | "martin_get_attempt" | "martin_get_verification_results" | "martin_run_dossier" | "martin_dossier" | "martin_eval" | "martin_pr_summary" | "martin_create_pr" | "martin_review_pr";
2
2
  export { sanitizeToolErrorMessage } from "./tools/tool-errors.js";
3
3
  export declare function validateToolInput(name: ToolName, args: unknown): unknown;
4
4
  export declare function resolveSafeRepoRoot(repoRoot?: string, workspaceRoot?: string): string;
5
+ export declare function resolveTrustedLoopRepoRoot(repoRoot?: string, workspaceRoot?: string): string;
5
6
  export declare function resolveSafeRunsJsonPath(file: string, runsRoot?: string): string;
6
7
  export declare function resolveSafeRunsPath(file: string, runsRoot?: string): string;
7
8
  export declare function resolveSafeRunsRootPath(runsRoot?: string, fallbackRunsRoot?: string): string;
@@ -13,8 +13,16 @@ export function validateToolInput(name, args) {
13
13
  return validateStatusInput(args);
14
14
  case "martin_doctor":
15
15
  return validateDoctorInput(args);
16
+ case "martin_plan":
17
+ return validatePlanInput(args);
16
18
  case "martin_preflight":
17
19
  return validatePreflightInput(args);
20
+ case "martin_logs":
21
+ return validateLogsInput(args);
22
+ case "martin_cancel":
23
+ case "martin_pause":
24
+ case "martin_continue":
25
+ return validateRunControlInput(args);
18
26
  case "martin_list_runs":
19
27
  return validateListRunsInput(args);
20
28
  case "martin_triage_runs":
@@ -26,7 +34,16 @@ export function validateToolInput(name, args) {
26
34
  case "martin_get_verification_results":
27
35
  return validateGetVerificationResultsInput(args);
28
36
  case "martin_run_dossier":
37
+ case "martin_dossier":
29
38
  return validateRunDossierInput(args);
39
+ case "martin_eval":
40
+ return validateEvalInput(args);
41
+ case "martin_pr_summary":
42
+ return validateRunDossierInput(args);
43
+ case "martin_create_pr":
44
+ return validateCreatePrInput(args);
45
+ case "martin_review_pr":
46
+ return validateReviewPrInput(args);
30
47
  default:
31
48
  throw invalidArgumentsError(`Unknown tool: ${name}`, "Refresh the Martin tool manifest and retry.");
32
49
  }
@@ -40,6 +57,14 @@ export function resolveSafeRepoRoot(repoRoot, workspaceRoot = process.env.MARTIN
40
57
  });
41
58
  return candidate;
42
59
  }
60
+ export function resolveTrustedLoopRepoRoot(repoRoot, workspaceRoot = process.env.MARTIN_MCP_WORKSPACE_ROOT ?? process.cwd()) {
61
+ try {
62
+ return resolveSafeRepoRoot(repoRoot, workspaceRoot);
63
+ }
64
+ catch {
65
+ throw invalidPathError("Run record points outside the trusted workspace.", "Inspect or promote only loop records that were created under the current workspace root.");
66
+ }
67
+ }
43
68
  export function resolveSafeRunsJsonPath(file, runsRoot = resolveRunsRoot(process.env)) {
44
69
  const baseRoot = resolve(runsRoot);
45
70
  const candidate = resolve(baseRoot, file);
@@ -69,12 +94,27 @@ export function resolveSafeRunsPath(file, runsRoot = resolveRunsRoot(process.env
69
94
  export function resolveSafeRunsRootPath(runsRoot, fallbackRunsRoot = resolveRunsRoot(process.env)) {
70
95
  const baseRoot = resolve(fallbackRunsRoot);
71
96
  const candidate = runsRoot ? resolve(baseRoot, runsRoot) : baseRoot;
97
+ if (runsRoot && !existsSync(candidate)) {
98
+ if (!isAbsolute(runsRoot)) {
99
+ assertRawPathWithinRoot(candidate, baseRoot, "runsDir");
100
+ }
101
+ return candidate;
102
+ }
72
103
  assertPathWithinRoot(candidate, baseRoot, "runsDir", {
73
104
  requireExistingCandidate: false,
74
105
  requireExistingRoot: false
75
106
  });
76
107
  return candidate;
77
108
  }
109
+ function assertRawPathWithinRoot(candidatePath, rootPath, name) {
110
+ const relativePath = relative(resolve(rootPath), resolve(candidatePath));
111
+ if (relativePath === "" || relativePath === ".") {
112
+ return;
113
+ }
114
+ if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
115
+ throw invalidPathError(`Invalid ${name}.`);
116
+ }
117
+ }
78
118
  export function resolveSafeLoopRecordPath(loopId, runsRoot = resolveRunsRoot(process.env)) {
79
119
  const normalizedLoopId = requireLoopId(loopId, "loopId");
80
120
  return resolveSafeRunsJsonPath(`${normalizedLoopId}/loop-record.json`, runsRoot);
@@ -102,9 +142,14 @@ function validateRunInput(args) {
102
142
  "workingDirectory",
103
143
  "engine",
104
144
  "model",
145
+ "context",
146
+ "policyPack",
105
147
  "maxUsd",
106
148
  "maxIterations",
107
149
  "maxTokens",
150
+ "maxMinutes",
151
+ "maxFilesChanged",
152
+ "maxCommands",
108
153
  "verificationPlan",
109
154
  "allowedPaths",
110
155
  "deniedPaths",
@@ -119,9 +164,20 @@ function validateRunInput(args) {
119
164
  : {}),
120
165
  ...(engine ? { engine } : {}),
121
166
  ...optionalString(record.model, "model"),
167
+ ...optionalString(record.context, "context"),
168
+ ...optionalEnumAsObject(record.policyPack, "policyPack", [
169
+ "solo-founder",
170
+ "startup-team",
171
+ "enterprise-strict",
172
+ "oss-maintainer",
173
+ "security-sensitive"
174
+ ]),
122
175
  ...optionalPositiveNumber(record.maxUsd, "maxUsd"),
123
176
  ...optionalPositiveInteger(record.maxIterations, "maxIterations"),
124
177
  ...optionalPositiveInteger(record.maxTokens, "maxTokens"),
178
+ ...optionalPositiveInteger(record.maxMinutes, "maxMinutes"),
179
+ ...optionalPositiveInteger(record.maxFilesChanged, "maxFilesChanged"),
180
+ ...optionalPositiveInteger(record.maxCommands, "maxCommands"),
125
181
  ...optionalStringArrayAsObject(record.verificationPlan, "verificationPlan"),
126
182
  ...optionalPathPatternArrayAsObject(record.allowedPaths, "allowedPaths"),
127
183
  ...optionalPathPatternArrayAsObject(record.deniedPaths, "deniedPaths"),
@@ -196,6 +252,35 @@ function validateDoctorInput(args) {
196
252
  function validatePreflightInput(args) {
197
253
  return validateRunInput(args);
198
254
  }
255
+ function validatePlanInput(args) {
256
+ return validateRunInput(args);
257
+ }
258
+ function validateLogsInput(args) {
259
+ const record = requireObject(args);
260
+ assertAllowedKeys(record, ["file", "loopId", "runsDir", "latest", "limit"]);
261
+ const resolvedRunsDir = record.runsDir !== undefined
262
+ ? resolveSafeRunsRootPath(requireString(record.runsDir, "runsDir"))
263
+ : undefined;
264
+ const selectors = [
265
+ record.file !== undefined ? "file" : null,
266
+ record.loopId !== undefined ? "loopId" : null,
267
+ record.latest !== undefined ? "latest" : null
268
+ ].filter((value) => value !== null);
269
+ if (selectors.length !== 1) {
270
+ throw invalidSelectorError("Provide exactly one of file, loopId, or latest.", "Choose exactly one run selector per call.");
271
+ }
272
+ return {
273
+ ...(record.file !== undefined
274
+ ? {
275
+ file: resolveSafeRunsPath(requireString(record.file, "file"), resolvedRunsDir ?? resolveRunsRoot(process.env))
276
+ }
277
+ : {}),
278
+ ...(record.loopId !== undefined ? { loopId: requireLoopId(record.loopId, "loopId") } : {}),
279
+ ...(resolvedRunsDir ? { runsDir: resolvedRunsDir } : {}),
280
+ ...(record.latest === true ? { latest: true } : {}),
281
+ ...optionalPositiveInteger(record.limit, "limit")
282
+ };
283
+ }
199
284
  function validateListRunsInput(args) {
200
285
  const record = requireObject(args);
201
286
  assertAllowedKeys(record, [
@@ -326,8 +411,47 @@ function validateGetVerificationResultsInput(args) {
326
411
  };
327
412
  }
328
413
  function validateRunDossierInput(args) {
414
+ const record = requireObject(args);
415
+ assertAllowedKeys(record, ["file", "loopId", "runsDir", "latest", "format"]);
416
+ const base = validateGetRunInput(args);
417
+ return {
418
+ ...base,
419
+ ...optionalEnumAsObject(record.format, "format", ["json", "md", "github-pr"])
420
+ };
421
+ }
422
+ function validateEvalInput(args) {
329
423
  return validateGetRunInput(args);
330
424
  }
425
+ function validateRunControlInput(args) {
426
+ const record = requireObject(args);
427
+ assertAllowedKeys(record, ["file", "loopId", "runsDir", "latest", "reason", "requestedBy"]);
428
+ const base = validateGetRunInput(args);
429
+ return {
430
+ ...base,
431
+ ...optionalString(record.reason, "reason"),
432
+ ...optionalString(record.requestedBy, "requestedBy")
433
+ };
434
+ }
435
+ function validateCreatePrInput(args) {
436
+ const record = requireObject(args);
437
+ assertAllowedKeys(record, ["file", "loopId", "runsDir", "latest", "format", "title", "base", "execute"]);
438
+ const base = validateRunDossierInput(args);
439
+ return {
440
+ ...base,
441
+ ...optionalString(record.title, "title"),
442
+ ...optionalString(record.base, "base"),
443
+ ...optionalBoolean(record.execute, "execute")
444
+ };
445
+ }
446
+ function validateReviewPrInput(args) {
447
+ const record = requireObject(args);
448
+ assertAllowedKeys(record, ["file", "loopId", "runsDir", "latest", "format", "prBody"]);
449
+ const base = validateRunDossierInput(args);
450
+ return {
451
+ ...base,
452
+ ...optionalString(record.prBody, "prBody")
453
+ };
454
+ }
331
455
  function requireObject(value) {
332
456
  if (!value || typeof value !== "object" || Array.isArray(value)) {
333
457
  throw invalidArgumentsError("Tool arguments must be an object.");