martin-loop
Advanced tools
@@ -360,2 +360,6 @@ import { spawnSync } from "node:child_process"; | ||
| dirs.push(join(localAppData, "OpenAI", "Codex", "bin")); | ||
| // Claude Code native installer places binary at %USERPROFILE%\.local\bin | ||
| const userProfile = env.USERPROFILE ?? env.HOMEPATH; | ||
| if (userProfile) | ||
| dirs.push(join(userProfile, ".local", "bin")); | ||
| if (home) | ||
@@ -387,4 +391,9 @@ dirs.push(join(home, "scoop", "shims")); | ||
| function suggestInstall(command) { | ||
| if (command === "claude") { | ||
| const installCmd = process.platform === "win32" | ||
| ? "irm https://claude.ai/install.ps1 | iex" | ||
| : "curl -fsSL https://claude.ai/install.sh | bash"; | ||
| return `Install with: ${installCmd}`; | ||
| } | ||
| const installs = { | ||
| claude: "Install with: npm install -g @anthropic-ai/claude-code", | ||
| codex: "Install with: npm install -g @openai/codex", | ||
@@ -391,0 +400,0 @@ gemini: "Install with: npm install -g @google/gemini-cli" |
| { | ||
| "name": "@martin/cli", | ||
| "version": "0.4.2", | ||
| "version": "0.4.3", | ||
| "type": "module", | ||
@@ -5,0 +5,0 @@ "description": "Martin Loop CLI โ budget-aware coding loops with failure classification and verified exits.", |
@@ -71,2 +71,3 @@ import { createHash } from "node:crypto"; | ||
| ...(input.receiptScope ? { scopeKey: hashReceiptScope(input.receiptScope) } : {}), | ||
| ...(input.receiptScope ? { canonicalScopeKey: hashCanonicalReceiptScope(input.receiptScope) } : {}), | ||
| pathScopeKey: hashPathScope(input.allowedPaths ?? [], input.deniedPaths ?? []), | ||
@@ -92,7 +93,16 @@ ...(input.budget ? { budgetKey: hashBudget(input.budget) } : {}) | ||
| }); | ||
| const canonicalScopeKey = hashCanonicalReceiptScope(input.receiptScope ?? { | ||
| invocationRoot: input.workingDirectory, | ||
| workingDirectory: input.workingDirectory, | ||
| repoRoot: input.workingDirectory, | ||
| runsRoot: input.runsRoot | ||
| }); | ||
| const pathScopeKey = hashPathScope(input.allowedPaths ?? [], input.deniedPaths ?? []); | ||
| const budgetKey = input.budget ? hashBudget(input.budget) : undefined; | ||
| const missingSteps = []; | ||
| // Doctor/session-start are repo-scoped readiness checks. They should remain | ||
| // valid when INIT_CWD changes inside the same repo, but must still fail | ||
| // closed if the canonical repo/runsRoot identity changes. | ||
| const doctorReady = isFresh(cliState["doctor"], DOCTOR_TTL_MS, (receipt) => receipt.workingDirectory === workingDirectory && | ||
| receipt.scopeKey === scopeKey); | ||
| matchesCanonicalScope(receipt, canonicalScopeKey, scopeKey)); | ||
| if (!doctorReady) { | ||
@@ -113,16 +123,20 @@ missingSteps.push("doctor"); | ||
| isFresh(cliState["session-start"], SESSION_TTL_MS, (receipt) => receipt.workingDirectory === workingDirectory && | ||
| receipt.scopeKey === scopeKey) || | ||
| matchesCanonicalScope(receipt, canonicalScopeKey, scopeKey)) || | ||
| isFresh(cliState["start"], SESSION_TTL_MS, (receipt) => receipt.workingDirectory === workingDirectory && | ||
| receipt.scopeKey === scopeKey) || | ||
| matchesCanonicalScope(receipt, canonicalScopeKey, scopeKey)) || | ||
| isFresh(cliState["tour"], SESSION_TTL_MS, (receipt) => receipt.workingDirectory === workingDirectory && | ||
| receipt.scopeKey === scopeKey); | ||
| matchesCanonicalScope(receipt, canonicalScopeKey, scopeKey)); | ||
| if (!sessionReady) { | ||
| missingSteps.push("session-start"); | ||
| } | ||
| // Preflight check: match on workingDirectory + engine only (not objective/verifier hash). | ||
| // Preflight check: match on workingDirectory + engine + execution bounds. | ||
| // The full hash match was too strict โ minor objective wording differences would break | ||
| // the receipt chain. The key governance signal is that preflight ran for this directory | ||
| // and engine recently; the exact objective text can drift between preflight and run. | ||
| // Path policy and budget are execution bounds, so changing them requires a fresh preflight. | ||
| const preflightReady = isFresh(cliState["preflight"], PREFLIGHT_TTL_MS, (receipt) => receipt.workingDirectory === workingDirectory && | ||
| receipt.engine === engine); | ||
| receipt.engine === engine && | ||
| receipt.verificationPlanKey === verificationPlanKey && | ||
| receipt.pathScopeKey === pathScopeKey && | ||
| (!budgetKey || receipt.budgetKey === budgetKey)); | ||
| if (!preflightReady) { | ||
@@ -162,3 +176,6 @@ missingSteps.push("preflight"); | ||
| const labels = missingSteps.map((step) => step === "session-start" ? "session start" : step); | ||
| return `Governed run blocked until MartinLoop receipts exist for ${labels.join(", ")}. Next command: ${nextCommand}`; | ||
| const preflightReason = missingSteps.includes("preflight") | ||
| ? " Preflight must be rerun when engine, verifier, path scope or budget changed." | ||
| : ""; | ||
| return `Governed run blocked until MartinLoop receipts exist for ${labels.join(", ")}.${preflightReason} Next command: ${nextCommand}`; | ||
| } | ||
@@ -214,2 +231,16 @@ async function readWorkflowState(runsRoot) { | ||
| } | ||
| function hashCanonicalReceiptScope(receiptScope) { | ||
| const normalized = { | ||
| workingDirectory: normalizeWorkingDirectory(receiptScope.workingDirectory ?? receiptScope.repoRoot ?? ""), | ||
| repoRoot: normalizeWorkingDirectory(receiptScope.repoRoot ?? receiptScope.workingDirectory ?? ""), | ||
| runsRoot: normalizeWorkingDirectory(receiptScope.runsRoot ?? "") | ||
| }; | ||
| return createHash("sha256").update(JSON.stringify(normalized)).digest("hex").slice(0, 12); | ||
| } | ||
| function matchesCanonicalScope(receipt, canonicalScopeKey, exactScopeKey) { | ||
| if (receipt.canonicalScopeKey) { | ||
| return receipt.canonicalScopeKey === canonicalScopeKey; | ||
| } | ||
| return receipt.scopeKey === exactScopeKey; | ||
| } | ||
| function hashPathScope(allowedPaths, deniedPaths) { | ||
@@ -216,0 +247,0 @@ const normalized = { |
+1
-1
| { | ||
| "name": "martin-loop", | ||
| "private": false, | ||
| "version": "0.4.2", | ||
| "version": "0.4.3", | ||
| "type": "module", | ||
@@ -6,0 +6,0 @@ "description": "Open-source command center for governed AI coding agents with built-in onboarding, hard gates, MCP, and shareable run receipts.", |
+11
-11
@@ -77,3 +77,3 @@ # MartinLoop | ||
| `start` prints the first-run guided path. `run` auto-checks `doctor`, `session-start`, and `preflight`, then executes when the environment is ready. Use `--proof` only when you intentionally want an explicit no-spend lane. | ||
| `start` prints the first-run guided path. `run` auto-checks `doctor`, `session-start`, and `preflight`, then executes when the environment is ready. | ||
@@ -90,3 +90,3 @@ Inspect-first flow: | ||
| Release notes for the current root package: [MartinLoop 0.4.2](./docs/release/OSS-0.4.2-RELEASE-NOTES.md). | ||
| Release notes for the current root package: [MartinLoop 0.4.3](./docs/release/OSS-0.4.3-RELEASE-NOTES.md). | ||
@@ -120,3 +120,3 @@ ## Visual Proof | ||
| ```sh | ||
| npx -y martin-loop@latest run "Summarize the demo workspace and prove tests still pass" --proof --verify "npm test" | ||
| npx -y martin-loop@latest run "Summarize the demo workspace and prove tests still pass" --verify "npm test" | ||
| npx -y martin-loop@latest runs verify --latest | ||
@@ -133,13 +133,13 @@ npx -y martin-loop@latest share --latest | ||
| ```sh | ||
| npx -y martin-loop@0.4.2 --version | ||
| npx -y martin-loop@0.4.2 start | ||
| npx -y martin-loop@0.4.2 demo | ||
| npx -y martin-loop@0.4.3 --version | ||
| npx -y martin-loop@0.4.3 start | ||
| npx -y martin-loop@0.4.3 demo | ||
| cd martin-loop-demo | ||
| npm install | ||
| npx -y martin-loop@0.4.1 run "Summarize the demo workspace and prove tests still pass" --verify "npm test" --budget-usd 2 --max-iterations 1 --json | ||
| npx -y martin-loop@0.4.1 dossier --latest --json | ||
| npx -y martin-loop@0.4.1 share --latest --json | ||
| npx -y martin-loop@0.4.3 run "Summarize the demo workspace and prove tests still pass" --verify "npm test" --budget-usd 2 --max-iterations 1 --json | ||
| npx -y martin-loop@0.4.3 dossier --latest --json | ||
| npx -y martin-loop@0.4.3 share --latest --json | ||
| ``` | ||
| For deterministic installs, pin the package line (`martin-loop@0.4.1`) or use `martin-loop@latest`. Plain `npx martin-loop` can resolve a stale local cache on some machines. | ||
| For deterministic installs, pin the package line (`martin-loop@0.4.3`) or use `martin-loop@latest`. Plain `npx martin-loop` can resolve a stale local cache on some machines. | ||
@@ -312,3 +312,3 @@ Default share bundle outputs: | ||
| The root `martin-loop` package and the standalone `@martinloop/mcp` package move on separate version lines. The current root package line here is `0.4.2`; the current standalone MCP source line is `0.3.7`, and the live npm baseline remains `0.3.6` until that standalone release is cut. | ||
| The root `martin-loop` package and the standalone `@martinloop/mcp` package move on separate version lines. The current root package line here is `0.4.3`; the current standalone MCP source line is `0.3.7`, and the live npm baseline is `0.3.7`. | ||
@@ -315,0 +315,0 @@ The public MCP release train labels are: |
| # Benchmarks | ||
| This workspace contains deterministic benchmark suites used to evaluate governed agent execution and reproducibility. | ||
| Use `npx martin-loop bench --suite under-3-challenge` for the primary public benchmark lane. |
| # Demo Workspace | ||
| The `seeded-workspace` folder provides a disposable project for first-run guided demos and receipt walkthroughs. |
Sorry, the diff of this file is too big to display
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
915748
0.3%20957
0.28%128
-1.54%