@mutmutco/cli 4.3.15 → 4.3.16

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 (2) hide show
  1. package/dist/main.cjs +394 -173
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -3416,7 +3416,7 @@ function useColor() {
3416
3416
  var program = new Command();
3417
3417
 
3418
3418
  // src/command-composition.ts
3419
- var import_node_fs49 = require("node:fs");
3419
+ var import_node_fs50 = require("node:fs");
3420
3420
 
3421
3421
  // src/clean-exit.ts
3422
3422
  var UNDICI_GLOBAL_DISPATCHER_SYMBOL = Object.getOwnPropertySymbols(globalThis).find(
@@ -5226,8 +5226,8 @@ function unknownCommandDomainGuide(parentPath, token) {
5226
5226
  }
5227
5227
 
5228
5228
  // src/command-composition.ts
5229
- var import_node_os21 = require("node:os");
5230
- var import_node_path46 = require("node:path");
5229
+ var import_node_os22 = require("node:os");
5230
+ var import_node_path47 = require("node:path");
5231
5231
 
5232
5232
  // src/board-read.ts
5233
5233
  var import_node_fs13 = require("node:fs");
@@ -13706,7 +13706,7 @@ function createDocsIndexDeps(repoRoot2) {
13706
13706
  }
13707
13707
 
13708
13708
  // src/gate-budget.ts
13709
- var BLESSED_RUN_WITH_BUDGET_SHA = "ea2cf8710949dec53566c29836e29cd7515a4e23";
13709
+ var BLESSED_RUN_WITH_BUDGET_SHA = "5ce541add569e54e7ca96de67a768577d3d604e4";
13710
13710
  var BLESSED_RUNNER_GATE_SHA = "a6ee756d94fd95884451381e94408f0348eb11db";
13711
13711
  var REMOTE_USE = /^mutmutco\/MMI-Hub\/\.github\/actions\/run-with-budget@(\S+)$/;
13712
13712
  var LOCAL_USE = "./.github/actions/run-with-budget";
@@ -14044,6 +14044,8 @@ function gateSeedVars(cls, releaseTrack, runtime = "node", requiredCheckBranches
14044
14044
  const runtimeVars = {
14045
14045
  GATE_RUNTIME: runtime,
14046
14046
  GATE_CMD: rt.cmd,
14047
+ // #6254: affected-only PR command; empty means "same as GATE_CMD" (withDerivedRepoVars fills it).
14048
+ GATE_AFFECTED_CMD: "",
14047
14049
  GATE_INSTALL_CMD: rt.install,
14048
14050
  GATE_WORKDIR: ".",
14049
14051
  GATE_CACHE_DEP_PATH: "package-lock.json",
@@ -14091,6 +14093,7 @@ function withDerivedRepoVars(vars, parsed, cls, releaseTrack, requiredCheckBranc
14091
14093
  for (const [key, value] of Object.entries(gateSeedVars(cls, track, runtime, requiredCheckBranches))) {
14092
14094
  out[key] ??= value;
14093
14095
  }
14096
+ if (!out.GATE_AFFECTED_CMD.trim()) out.GATE_AFFECTED_CMD = out.GATE_CMD;
14094
14097
  return out;
14095
14098
  }
14096
14099
  function gateConfigToVars(gate) {
@@ -14098,6 +14101,7 @@ function gateConfigToVars(gate) {
14098
14101
  if (!gate || typeof gate !== "object") return out;
14099
14102
  if (gate.runtime === "node" || gate.runtime === "python") out.GATE_RUNTIME = gate.runtime;
14100
14103
  if (typeof gate.cmd === "string" && gate.cmd.trim()) out.GATE_CMD = gate.cmd;
14104
+ if (typeof gate.affectedCmd === "string" && gate.affectedCmd.trim()) out.GATE_AFFECTED_CMD = gate.affectedCmd;
14101
14105
  if (typeof gate.workdir === "string" && gate.workdir.trim()) out.GATE_WORKDIR = gate.workdir;
14102
14106
  if (typeof gate.cacheDepPath === "string" && gate.cacheDepPath.trim()) out.GATE_CACHE_DEP_PATH = gate.cacheDepPath;
14103
14107
  if (typeof gate.pyVersion === "string" && gate.pyVersion.trim()) out.GATE_PY_VERSION = gate.pyVersion;
@@ -15483,10 +15487,10 @@ var rollout_plan_default = {
15483
15487
  note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
15484
15488
  },
15485
15489
  baseline: {
15486
- version: "4.3.15",
15487
- tag: "v4.3.15",
15488
- commit: "006f11ed8921",
15489
- npm: "@mutmutco/cli@4.3.15"
15490
+ version: "4.3.16",
15491
+ tag: "v4.3.16",
15492
+ commit: "5a4b8e29fd1a",
15493
+ npm: "@mutmutco/cli@4.3.16"
15490
15494
  },
15491
15495
  exitCriterion: "fleet-n-of-n",
15492
15496
  hubOnlyShortcut: "forbidden",
@@ -15503,14 +15507,14 @@ var rollout_plan_default = {
15503
15507
  repo: "mutmutco/mmi-hub",
15504
15508
  role: "canary",
15505
15509
  schedule: "train",
15506
- v3Target: "v4.3.15"
15510
+ v3Target: "v4.3.16"
15507
15511
  }
15508
15512
  ],
15509
15513
  rollbackTrigger: "Any red inside the post-contract soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a pre-v4 client admitted instead of receiving actionable HTTP 426, or npm consumer install/doctor failure on the v4-only dist.",
15510
15514
  rollback: {
15511
15515
  independent: true,
15512
- mechanism: "npm dist-tag latest -> 4.3.15 and redeploy the Hub Lambda from tag v4.3.15 (006f11ed8921); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15513
- v3Target: "v4.3.15 (@mutmutco/cli@4.3.15, tag commit 006f11ed8921 \u2014 last known-good release carrying the repo-index v4-only contract)"
15516
+ mechanism: "npm dist-tag latest -> 4.3.16 and redeploy the Hub Lambda from tag v4.3.16 (5a4b8e29fd1a); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15517
+ v3Target: "v4.3.16 (@mutmutco/cli@4.3.16, tag commit 5a4b8e29fd1a \u2014 last known-good release carrying the repo-index v4-only contract)"
15514
15518
  }
15515
15519
  },
15516
15520
  {
@@ -28778,7 +28782,7 @@ function parseGateVar(raw) {
28778
28782
  throw new Error('org project set: gate must be JSON, e.g. {"runtime":"python","cmd":"pytest","workdir":"app"}');
28779
28783
  }
28780
28784
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
28781
- throw new Error("org project set: gate must be a {runtime,cmd,workdir,cacheDepPath,pyVersion} object");
28785
+ throw new Error("org project set: gate must be a {runtime,cmd,affectedCmd,workdir,cacheDepPath,pyVersion} object");
28782
28786
  }
28783
28787
  const map = parsed;
28784
28788
  const out = {};
@@ -28786,11 +28790,11 @@ function parseGateVar(raw) {
28786
28790
  if (key === "runtime") {
28787
28791
  if (value !== "node" && value !== "python") throw new Error('org project set: gate.runtime must be "node" or "python"');
28788
28792
  out.runtime = value;
28789
- } else if (key === "cmd" || key === "workdir" || key === "cacheDepPath" || key === "pyVersion") {
28793
+ } else if (key === "cmd" || key === "affectedCmd" || key === "workdir" || key === "cacheDepPath" || key === "pyVersion") {
28790
28794
  if (typeof value !== "string" || !value.trim()) throw new Error(`org project set: gate.${key} must be a non-empty string`);
28791
28795
  out[key] = value.trim();
28792
28796
  } else {
28793
- throw new Error(`org project set: gate key "${key}" \u2014 expected only runtime/cmd/workdir/cacheDepPath/pyVersion`);
28797
+ throw new Error(`org project set: gate key "${key}" \u2014 expected only runtime/cmd/affectedCmd/workdir/cacheDepPath/pyVersion`);
28794
28798
  }
28795
28799
  }
28796
28800
  return out;
@@ -28891,7 +28895,7 @@ var SETTABLE_VAR_HINTS = {
28891
28895
  requiredChecks: 'JSON array, e.g. ["gate"] or [] for no-ci',
28892
28896
  requiredCheckBranches: "non-empty JSON branch-name array; exact product-ruleset scope (#5210)",
28893
28897
  ciExemptReason: "non-empty string \u2014 declares this deployable repo has no CI surface (#4265)",
28894
- gate: "JSON {runtime,cmd,workdir,cacheDepPath,pyVersion}"
28898
+ gate: "JSON {runtime,cmd,affectedCmd,workdir,cacheDepPath,pyVersion}"
28895
28899
  };
28896
28900
  function settableVarHelp() {
28897
28901
  const keys = SETTABLE_VAR_KEYS.map((k) => {
@@ -35908,8 +35912,8 @@ function renderVerifySecrets(body) {
35908
35912
  }
35909
35913
 
35910
35914
  // src/command-register-collaboration.ts
35911
- var import_node_child_process15 = require("node:child_process");
35912
- var import_node_fs42 = require("node:fs");
35915
+ var import_node_child_process16 = require("node:child_process");
35916
+ var import_node_fs43 = require("node:fs");
35913
35917
  var import_promises7 = require("node:fs/promises");
35914
35918
 
35915
35919
  // src/session-runtime.ts
@@ -35929,7 +35933,7 @@ function spawnDetachedSelf(args, deps, opts = {}) {
35929
35933
  }
35930
35934
 
35931
35935
  // src/command-register-collaboration.ts
35932
- var import_node_path39 = require("node:path");
35936
+ var import_node_path40 = require("node:path");
35933
35937
 
35934
35938
  // src/attach-to-project.ts
35935
35939
  function boardAttachRateLimitedReceipt(resetEpochSeconds) {
@@ -37581,15 +37585,193 @@ function postMergeReconWarnings(input) {
37581
37585
  return lines2;
37582
37586
  }
37583
37587
 
37584
- // src/pr-create-docs-check.ts
37588
+ // src/review-verdict.ts
37585
37589
  var import_node_child_process14 = require("node:child_process");
37590
+ var import_node_fs39 = require("node:fs");
37591
+ var import_node_os19 = require("node:os");
37592
+ var import_node_path36 = require("node:path");
37593
+ var REVIEW_VERDICT_MARKER = "<!-- zeroci-review v1 -->";
37594
+ var REVIEW_WAIVER_MARKER = "<!-- zeroci-review-waived v1 -->";
37595
+ var REVIEW_VERDICTS = ["PROCEED", "CORRECT", "ESCALATE"];
37596
+ function isReviewVerdict(value) {
37597
+ return typeof value === "string" && REVIEW_VERDICTS.includes(value);
37598
+ }
37599
+ function renderReviewVerdictComment(input) {
37600
+ const payload = {
37601
+ v: 1,
37602
+ patch: input.patch,
37603
+ head: input.head,
37604
+ verdict: input.verdict,
37605
+ scope: input.scope,
37606
+ risk: input.risk,
37607
+ unverified: input.unverified,
37608
+ reviewer: input.reviewer
37609
+ };
37610
+ const findings = input.findings?.trim();
37611
+ return `${REVIEW_VERDICT_MARKER}
37612
+ \`\`\`json
37613
+ ${JSON.stringify(payload, null, 2)}
37614
+ \`\`\`
37615
+ ${findings ? `
37616
+ ${findings}
37617
+ ` : ""}`;
37618
+ }
37619
+ function isReviewVerdictComment(body) {
37620
+ return body.trimStart().startsWith(REVIEW_VERDICT_MARKER);
37621
+ }
37622
+ var FENCE_RE = /^(?:`{3,}|~{3,})[^\n]*\n([\s\S]*?)\n(?:`{3,}|~{3,})\s*$/m;
37623
+ function parseReviewVerdictComment(body) {
37624
+ if (!isReviewVerdictComment(body)) return void 0;
37625
+ const rest = body.trimStart().slice(REVIEW_VERDICT_MARKER.length);
37626
+ const fence = FENCE_RE.exec(rest);
37627
+ if (!fence) return void 0;
37628
+ let parsed;
37629
+ try {
37630
+ parsed = JSON.parse(fence[1]);
37631
+ } catch {
37632
+ return void 0;
37633
+ }
37634
+ if (!parsed || typeof parsed !== "object") return void 0;
37635
+ const p = parsed;
37636
+ if (p.v !== 1 || !isReviewVerdict(p.verdict)) return void 0;
37637
+ if (typeof p.patch !== "string" || !/^[0-9a-f]{40}$/.test(p.patch)) return void 0;
37638
+ if (typeof p.head !== "string" || typeof p.scope !== "string" || typeof p.risk !== "string" || typeof p.reviewer !== "string") return void 0;
37639
+ const unverified = Array.isArray(p.unverified) ? p.unverified.filter((u) => typeof u === "string") : [];
37640
+ return { v: 1, patch: p.patch, head: p.head, verdict: p.verdict, scope: p.scope, risk: p.risk, unverified, reviewer: p.reviewer };
37641
+ }
37642
+ function evaluateReviewVerdict(comments, currentPatchId) {
37643
+ let latest;
37644
+ for (const c of comments) {
37645
+ if (!isReviewVerdictComment(c.body)) continue;
37646
+ if (!latest || c.createdAt >= latest.createdAt) latest = c;
37647
+ }
37648
+ if (!latest) return { ok: false, reason: "none" };
37649
+ const payload = parseReviewVerdictComment(latest.body);
37650
+ if (!payload) return { ok: false, reason: "malformed" };
37651
+ if (payload.verdict !== "PROCEED") return { ok: false, reason: "not-proceed", verdict: payload.verdict, patch: payload.patch };
37652
+ if (payload.patch !== currentPatchId) return { ok: false, reason: "stale", verdict: payload.verdict, patch: payload.patch };
37653
+ return { ok: true, reason: "proceed", verdict: payload.verdict, patch: payload.patch };
37654
+ }
37655
+ function reviewVerdictRemedyCommand(number, repo) {
37656
+ return `mmi-cli devops pr review-verdict ${number} --repo ${repo} --verdict PROCEED --scope "<one line>" --risk "<one line>" --reviewer <seat-or-model-id>`;
37657
+ }
37658
+ function reviewVerdictRefusalMessage(context, number, repo, evaluation, currentPatchId) {
37659
+ const why = {
37660
+ none: "no review verdict comment on the PR",
37661
+ stale: `the latest verdict is bound to patch ${evaluation.patch} but the PR's current diff is ${currentPatchId} \u2014 the content changed since the review`,
37662
+ "not-proceed": `the latest verdict is ${evaluation.verdict}, not PROCEED`,
37663
+ malformed: "the latest verdict comment is malformed (marker present, JSON block unreadable or wrong shape)",
37664
+ proceed: "proceed"
37665
+ }[evaluation.reason];
37666
+ return `${context}: REVIEW VERDICT REQUIRED (#6255) \u2014 ${why}. Review PR #${number} and post the verdict, then rerun:
37667
+ ${reviewVerdictRemedyCommand(number, repo)}
37668
+ To land without a review, pass --without-review "<reason>" \u2014 it posts a waiver comment on the PR.`;
37669
+ }
37670
+ function renderReviewWaiverComment(reason, actor) {
37671
+ return `${REVIEW_WAIVER_MARKER}
37672
+ Review gate waived by @${actor}: ${reason.trim()}
37673
+ `;
37674
+ }
37675
+ function computePrPatchId(number, repo) {
37676
+ return new Promise((resolve7, reject) => {
37677
+ const gh = (0, import_node_child_process14.spawn)("gh", ["pr", "diff", number, "--repo", repo], { windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
37678
+ const git3 = (0, import_node_child_process14.spawn)("git", ["patch-id", "--stable"], { windowsHide: true, stdio: ["pipe", "pipe", "pipe"] });
37679
+ let out = "";
37680
+ let ghErr = "";
37681
+ let gitErr = "";
37682
+ const timer = setTimeout(() => {
37683
+ gh.kill();
37684
+ git3.kill();
37685
+ reject(new Error(`patch-id: timed out after ${GC_GH_TIMEOUT_MS4}ms`));
37686
+ }, GC_GH_TIMEOUT_MS4);
37687
+ gh.stdout.pipe(git3.stdin);
37688
+ gh.stderr.on("data", (d) => {
37689
+ ghErr += d.toString();
37690
+ });
37691
+ git3.stderr.on("data", (d) => {
37692
+ gitErr += d.toString();
37693
+ });
37694
+ git3.stdout.on("data", (d) => {
37695
+ out += d.toString();
37696
+ });
37697
+ gh.on("error", (e) => {
37698
+ clearTimeout(timer);
37699
+ reject(e);
37700
+ });
37701
+ git3.on("error", (e) => {
37702
+ clearTimeout(timer);
37703
+ reject(e);
37704
+ });
37705
+ let ghCode;
37706
+ gh.on("close", (code) => {
37707
+ ghCode = code;
37708
+ });
37709
+ git3.on("close", (code) => {
37710
+ clearTimeout(timer);
37711
+ if (ghCode) return reject(Object.assign(new Error(`gh pr diff ${number} --repo ${repo} exited ${ghCode}: ${ghErr.trim()}`), { stderr: ghErr }));
37712
+ if (code) return reject(new Error(`git patch-id --stable exited ${code}: ${gitErr.trim()}`));
37713
+ const id = out.trim().split(/\s+/)[0];
37714
+ if (!/^[0-9a-f]{40}$/.test(id ?? "")) return reject(new Error(`patch-id: PR #${number} diff is empty or unreadable (gh: ${ghErr.trim() || "no stderr"})`));
37715
+ resolve7(id);
37716
+ });
37717
+ });
37718
+ }
37719
+ async function readPrHeadSha(number, repo) {
37720
+ const { stdout } = await execFileP("gh", ["api", `repos/${repo}/pulls/${number}`, "--jq", ".head.sha"], { timeout: GC_GH_TIMEOUT_MS4 });
37721
+ const sha = stdout.trim();
37722
+ if (!/^[0-9a-f]{40}$/.test(sha)) throw new Error(`could not read PR #${number} head sha`);
37723
+ return sha;
37724
+ }
37725
+ async function readPrIssueComments(number, repo) {
37726
+ const { stdout } = await execFileP("gh", ["api", "--paginate", `repos/${repo}/issues/${number}/comments?per_page=100`, "--jq", ".[] | {body, createdAt: .created_at}"], { timeout: GC_GH_TIMEOUT_MS4 });
37727
+ return parseNdjsonLines(stdout);
37728
+ }
37729
+ async function postPrCommentFromFile(number, repo, body) {
37730
+ const dir = (0, import_node_fs39.mkdtempSync)((0, import_node_path36.join)((0, import_node_os19.tmpdir)(), "mmi-review-verdict-"));
37731
+ const path2 = (0, import_node_path36.join)(dir, "body.md");
37732
+ try {
37733
+ (0, import_node_fs39.writeFileSync)(path2, body, "utf8");
37734
+ const { stdout } = await execFileP("gh", ["pr", "comment", number, "--repo", repo, "--body-file", path2], { timeout: GH_MUTATION_TIMEOUT_MS });
37735
+ return stdout.trim();
37736
+ } finally {
37737
+ try {
37738
+ (0, import_node_fs39.rmSync)(dir, { recursive: true, force: true });
37739
+ } catch {
37740
+ }
37741
+ }
37742
+ }
37743
+ async function requireReviewVerdict(context, number, repo, opts) {
37744
+ if (opts.withoutReview !== void 0) {
37745
+ const reason = opts.withoutReview.trim();
37746
+ if (!reason) throw new Error(`${context}: --without-review needs a non-empty reason`);
37747
+ const actor = await execFileP("gh", ["api", "user", "--jq", ".login"], { timeout: GC_GH_TIMEOUT_MS4 }).then((r) => r.stdout.trim()).catch(() => "") || "unknown";
37748
+ await postPrCommentFromFile(number, repo, renderReviewWaiverComment(reason, actor));
37749
+ console.warn(`${context}: review gate WAIVED by @${actor} \u2014 ${reason} (waiver comment posted on PR #${number})`);
37750
+ return void 0;
37751
+ }
37752
+ let comments;
37753
+ let currentPatchId;
37754
+ try {
37755
+ [comments, currentPatchId] = await Promise.all([readPrIssueComments(number, repo), computePrPatchId(number, repo)]);
37756
+ } catch (e) {
37757
+ const detail = e.message || String(e);
37758
+ const limited = isGitHubRateLimitError(e) ? " (gh rate-limited \u2014 retry after the pool resets)" : "";
37759
+ throw new Error(`${context}: cannot read PR #${number}'s review verdict${limited} \u2014 ${detail}. Refusing to merge WITHOUT the review gate (#6255).`);
37760
+ }
37761
+ const evaluation = evaluateReviewVerdict(comments, currentPatchId);
37762
+ if (evaluation.ok) return void 0;
37763
+ return reviewVerdictRefusalMessage(context, number, repo, evaluation, currentPatchId);
37764
+ }
37765
+
37766
+ // src/pr-create-docs-check.ts
37767
+ var import_node_child_process15 = require("node:child_process");
37586
37768
  var GIT_TIMEOUT_MS2 = 15e3;
37587
37769
  function catFileBatch(root, ref, paths) {
37588
37770
  if (paths.length === 0) return Promise.resolve([]);
37589
37771
  return new Promise((resolve7) => {
37590
37772
  const chunks = [];
37591
37773
  let settled = false;
37592
- const child2 = (0, import_node_child_process14.spawn)("git", ["-C", root, "cat-file", "--batch", "--buffer"], { windowsHide: true });
37774
+ const child2 = (0, import_node_child_process15.spawn)("git", ["-C", root, "cat-file", "--batch", "--buffer"], { windowsHide: true });
37593
37775
  const finish = () => {
37594
37776
  if (settled) return;
37595
37777
  settled = true;
@@ -37759,26 +37941,26 @@ async function prCreateClaimRefusal(body, repoOption, deps = {}) {
37759
37941
  }
37760
37942
 
37761
37943
  // src/worktree-merge-cleanup.ts
37762
- var import_node_fs41 = require("node:fs");
37763
- var import_node_path38 = require("node:path");
37944
+ var import_node_fs42 = require("node:fs");
37945
+ var import_node_path39 = require("node:path");
37764
37946
 
37765
37947
  // src/jervcode-node-modules-cleanup.ts
37766
- var import_node_fs39 = require("node:fs");
37767
- var import_node_os19 = require("node:os");
37768
- var import_node_path36 = require("node:path");
37769
- var JERVCODE_PACKAGE_ENTRY = (0, import_node_path36.join)("node_modules", "@jervaise", "jervcode", "dist", "launcher-entry.js");
37948
+ var import_node_fs40 = require("node:fs");
37949
+ var import_node_os20 = require("node:os");
37950
+ var import_node_path37 = require("node:path");
37951
+ var JERVCODE_PACKAGE_ENTRY = (0, import_node_path37.join)("node_modules", "@jervaise", "jervcode", "dist", "launcher-entry.js");
37770
37952
  var WIN_NAMES2 = ["jervcode.cmd", "jervcode"];
37771
37953
  var POSIX_NAMES2 = ["jervcode"];
37772
37954
  var NODE_MODULES_CLEANUP_TIMEOUT_MS = 3e5;
37773
- function jervcodeCandidatePaths(env = process.env, home = (0, import_node_os19.homedir)(), platform2 = process.platform) {
37955
+ function jervcodeCandidatePaths(env = process.env, home = (0, import_node_os20.homedir)(), platform2 = process.platform) {
37774
37956
  const names = platform2 === "win32" ? WIN_NAMES2 : POSIX_NAMES2;
37775
37957
  const out = [];
37776
37958
  for (const dir of jervCliCandidateDirs(env, home, platform2)) {
37777
- for (const name of names) out.push((0, import_node_path36.join)(dir, name));
37959
+ for (const name of names) out.push((0, import_node_path37.join)(dir, name));
37778
37960
  }
37779
37961
  return out;
37780
37962
  }
37781
- function resolveJervcodePath(env = process.env, home = (0, import_node_os19.homedir)(), platform2 = process.platform, exists = import_node_fs39.existsSync) {
37963
+ function resolveJervcodePath(env = process.env, home = (0, import_node_os20.homedir)(), platform2 = process.platform, exists = import_node_fs40.existsSync) {
37782
37964
  for (const candidate of jervcodeCandidatePaths(env, home, platform2)) {
37783
37965
  if (exists(candidate)) return candidate;
37784
37966
  }
@@ -37786,10 +37968,10 @@ function resolveJervcodePath(env = process.env, home = (0, import_node_os19.home
37786
37968
  }
37787
37969
  function jervcodeExecFileArgs(args, opts = {}) {
37788
37970
  const platform2 = opts.platform ?? process.platform;
37789
- const exists = opts.exists ?? import_node_fs39.existsSync;
37790
- const resolved = resolveJervcodePath(opts.env ?? process.env, opts.home ?? (0, import_node_os19.homedir)(), platform2, exists);
37971
+ const exists = opts.exists ?? import_node_fs40.existsSync;
37972
+ const resolved = resolveJervcodePath(opts.env ?? process.env, opts.home ?? (0, import_node_os20.homedir)(), platform2, exists);
37791
37973
  if (resolved) {
37792
- const entry = (0, import_node_path36.join)((0, import_node_path36.join)(resolved, ".."), JERVCODE_PACKAGE_ENTRY);
37974
+ const entry = (0, import_node_path37.join)((0, import_node_path37.join)(resolved, ".."), JERVCODE_PACKAGE_ENTRY);
37793
37975
  if (exists(entry)) {
37794
37976
  return { file: opts.execPath ?? process.execPath, args: [entry, ...args], via: "node-entry" };
37795
37977
  }
@@ -37820,8 +38002,8 @@ async function removeWorktreeNodeModulesViaHelper(wtPath, opts = {}) {
37820
38002
  }
37821
38003
 
37822
38004
  // src/worktree-evidence-archive.ts
37823
- var import_node_fs40 = require("node:fs");
37824
- var import_node_path37 = require("node:path");
38005
+ var import_node_fs41 = require("node:fs");
38006
+ var import_node_path38 = require("node:path");
37825
38007
  var JERV_ARTIFACT_RUN_SCOPE_ENV_VARS = ["JERV_RUN_ID", ...SESSION_ID_ENV_VARS];
37826
38008
  function sanitizeArchiveSegment(value, max = 80) {
37827
38009
  const scrubbed = value.replace(/[^A-Za-z0-9._@+-]+/g, "-").replace(/^-+|-+$/g, "");
@@ -37836,24 +38018,24 @@ function resolveArtifactRunScope(env = process.env) {
37836
38018
  }
37837
38019
  function defaultIsDirectory(path2) {
37838
38020
  try {
37839
- return (0, import_node_fs40.lstatSync)(path2).isDirectory();
38021
+ return (0, import_node_fs41.lstatSync)(path2).isDirectory();
37840
38022
  } catch {
37841
38023
  return false;
37842
38024
  }
37843
38025
  }
37844
38026
  function archiveWorktreeJervArtifacts(args, deps = {}) {
37845
- const exists = deps.exists ?? import_node_fs40.existsSync;
38027
+ const exists = deps.exists ?? import_node_fs41.existsSync;
37846
38028
  const isDirectory = deps.isDirectory ?? defaultIsDirectory;
37847
- const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs40.cpSync)(from, to, { recursive: true, force: true }));
38029
+ const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs41.cpSync)(from, to, { recursive: true, force: true }));
37848
38030
  const mkdirp = deps.mkdirp ?? ((path2) => {
37849
- (0, import_node_fs40.mkdirSync)(path2, { recursive: true });
38031
+ (0, import_node_fs41.mkdirSync)(path2, { recursive: true });
37850
38032
  });
37851
38033
  const env = deps.env ?? process.env;
37852
38034
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
37853
38035
  const resolveRoot = deps.resolveArchiveRoot ?? repoRuntimeStatePath;
37854
38036
  if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
37855
38037
  if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
37856
- const source = (0, import_node_path37.join)(args.worktreePath, ".jerv");
38038
+ const source = (0, import_node_path38.join)(args.worktreePath, ".jerv");
37857
38039
  if (!exists(source)) return { status: "absent" };
37858
38040
  if (!isDirectory(source)) return { status: "skipped", reason: "jerv-not-a-directory" };
37859
38041
  const runScope = resolveArtifactRunScope(env);
@@ -37861,7 +38043,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
37861
38043
  const stamp = now().toISOString().replace(/[:.]/g, "-");
37862
38044
  const dest = resolveRoot(args.primaryRoot, "jerv-artifacts", runScope, branchSlug, stamp, ".jerv");
37863
38045
  try {
37864
- mkdirp((0, import_node_path37.dirname)(dest));
38046
+ mkdirp((0, import_node_path38.dirname)(dest));
37865
38047
  copyDir(source, dest);
37866
38048
  if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
37867
38049
  return { status: "archived", path: dest, runScope };
@@ -37870,14 +38052,14 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
37870
38052
  }
37871
38053
  }
37872
38054
  function defaultStat(path2) {
37873
- const st = (0, import_node_fs40.statSync)(path2);
38055
+ const st = (0, import_node_fs41.statSync)(path2);
37874
38056
  return { mtimeMs: st.mtimeMs, size: st.size, isDirectory: () => st.isDirectory() };
37875
38057
  }
37876
38058
  function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
37877
- const exists = deps.exists ?? import_node_fs40.existsSync;
38059
+ const exists = deps.exists ?? import_node_fs41.existsSync;
37878
38060
  const stat4 = deps.stat ?? defaultStat;
37879
- const readdir2 = deps.readdir ?? import_node_fs40.readdirSync;
37880
- const tmpRoot = (0, import_node_path37.join)(worktreePath, "tmp");
38061
+ const readdir2 = deps.readdir ?? import_node_fs41.readdirSync;
38062
+ const tmpRoot = (0, import_node_path38.join)(worktreePath, "tmp");
37881
38063
  if (!exists(tmpRoot)) return [];
37882
38064
  const entries = [];
37883
38065
  const walk2 = (dir) => {
@@ -37888,14 +38070,14 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
37888
38070
  return;
37889
38071
  }
37890
38072
  for (const name of names) {
37891
- const full = (0, import_node_path37.join)(dir, name);
38073
+ const full = (0, import_node_path38.join)(dir, name);
37892
38074
  let st;
37893
38075
  try {
37894
38076
  st = stat4(full);
37895
38077
  } catch {
37896
38078
  continue;
37897
38079
  }
37898
- const relPath = (0, import_node_path37.relative)(worktreePath, full).replace(/\\/g, "/");
38080
+ const relPath = (0, import_node_path38.relative)(worktreePath, full).replace(/\\/g, "/");
37899
38081
  if (st.isDirectory()) {
37900
38082
  if (st.mtimeMs > newerThanMs) entries.push({ relPath, bytes: 0, mtimeMs: st.mtimeMs });
37901
38083
  walk2(full);
@@ -37909,10 +38091,10 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
37909
38091
  return entries;
37910
38092
  }
37911
38093
  function archiveWorktreeTmpArtifacts(args, deps = {}) {
37912
- const exists = deps.exists ?? import_node_fs40.existsSync;
37913
- const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs40.cpSync)(from, to, { recursive: true, force: true }));
38094
+ const exists = deps.exists ?? import_node_fs41.existsSync;
38095
+ const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs41.cpSync)(from, to, { recursive: true, force: true }));
37914
38096
  const mkdirp = deps.mkdirp ?? ((path2) => {
37915
- (0, import_node_fs40.mkdirSync)(path2, { recursive: true });
38097
+ (0, import_node_fs41.mkdirSync)(path2, { recursive: true });
37916
38098
  });
37917
38099
  const env = deps.env ?? process.env;
37918
38100
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
@@ -37920,7 +38102,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
37920
38102
  if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
37921
38103
  if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
37922
38104
  const scanned = scanWorktreeTmpEvidence(args.worktreePath, args.newerThanMs, deps);
37923
- const source = (0, import_node_path37.join)(args.worktreePath, "tmp");
38105
+ const source = (0, import_node_path38.join)(args.worktreePath, "tmp");
37924
38106
  if (!scanned.length) return { status: "absent" };
37925
38107
  if (!exists(source)) return { status: "absent" };
37926
38108
  const runScope = resolveArtifactRunScope(env);
@@ -37928,7 +38110,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
37928
38110
  const stamp = now().toISOString().replace(/[:.]/g, "-");
37929
38111
  const dest = resolveRoot(args.primaryRoot, "worktree-artifacts", runScope, branchSlug, stamp, "tmp");
37930
38112
  try {
37931
- mkdirp((0, import_node_path37.dirname)(dest));
38113
+ mkdirp((0, import_node_path38.dirname)(dest));
37932
38114
  copyDir(source, dest);
37933
38115
  if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
37934
38116
  const bytes = scanned.reduce((sum, e) => sum + e.bytes, 0);
@@ -37997,9 +38179,9 @@ function normPath2(p) {
37997
38179
  return p.replace(/\\/g, "/").replace(/\/+$/, "");
37998
38180
  }
37999
38181
  function unlinkNodeModulesJunction(wtPath) {
38000
- const nm = (0, import_node_path38.join)(wtPath, "node_modules");
38182
+ const nm = (0, import_node_path39.join)(wtPath, "node_modules");
38001
38183
  try {
38002
- if ((0, import_node_fs41.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs41.rmdirSync)(nm);
38184
+ if ((0, import_node_fs42.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs42.rmdirSync)(nm);
38003
38185
  return { ok: true };
38004
38186
  } catch (e) {
38005
38187
  if (e.code === "ENOENT") return { ok: true };
@@ -38010,8 +38192,8 @@ function defaultSleep2(ms) {
38010
38192
  return new Promise((resolve7) => setTimeout(resolve7, ms));
38011
38193
  }
38012
38194
  async function removeResidueDirectory(wtPath, options = {}) {
38013
- const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs41.rmdirSync, exists = import_node_fs41.existsSync } = options;
38014
- const removeRecursive = options.removeRecursive ?? ((path2) => (0, import_node_fs41.rmSync)(path2, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }));
38195
+ const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs42.rmdirSync, exists = import_node_fs42.existsSync } = options;
38196
+ const removeRecursive = options.removeRecursive ?? ((path2) => (0, import_node_fs42.rmSync)(path2, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }));
38015
38197
  const junction = unlinkNodeModulesJunction(wtPath);
38016
38198
  if (!junction.ok) return junction;
38017
38199
  try {
@@ -38134,31 +38316,31 @@ async function preCleanWorktreeForRemoval(wtPath, execGit) {
38134
38316
  }
38135
38317
  async function listNestedIgnoredNodeModules(wtPath, execGit) {
38136
38318
  const out = await execGit(["-C", wtPath, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory"]).catch(() => "");
38137
- return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path38.join)(wtPath, line.slice(0, -1)));
38319
+ return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path39.join)(wtPath, line.slice(0, -1)));
38138
38320
  }
38139
38321
  function safeRemoveTree(path2) {
38140
- const stat4 = (0, import_node_fs41.lstatSync)(path2);
38322
+ const stat4 = (0, import_node_fs42.lstatSync)(path2);
38141
38323
  if (stat4.isSymbolicLink()) {
38142
38324
  try {
38143
- (0, import_node_fs41.rmdirSync)(path2);
38325
+ (0, import_node_fs42.rmdirSync)(path2);
38144
38326
  } catch {
38145
- (0, import_node_fs41.unlinkSync)(path2);
38327
+ (0, import_node_fs42.unlinkSync)(path2);
38146
38328
  }
38147
38329
  return;
38148
38330
  }
38149
38331
  if (stat4.isDirectory()) {
38150
- for (const entry of (0, import_node_fs41.readdirSync)(path2)) safeRemoveTree((0, import_node_path38.join)(path2, entry));
38151
- (0, import_node_fs41.rmdirSync)(path2);
38332
+ for (const entry of (0, import_node_fs42.readdirSync)(path2)) safeRemoveTree((0, import_node_path39.join)(path2, entry));
38333
+ (0, import_node_fs42.rmdirSync)(path2);
38152
38334
  return;
38153
38335
  }
38154
- (0, import_node_fs41.unlinkSync)(path2);
38336
+ (0, import_node_fs42.unlinkSync)(path2);
38155
38337
  }
38156
38338
  function errorMessage(e) {
38157
38339
  return e instanceof Error ? e.message : String(e);
38158
38340
  }
38159
38341
  function resolvedOrRaw(path2) {
38160
38342
  try {
38161
- return normPath2((0, import_node_fs41.realpathSync)(path2));
38343
+ return normPath2((0, import_node_fs42.realpathSync)(path2));
38162
38344
  } catch {
38163
38345
  return normPath2(path2);
38164
38346
  }
@@ -38166,10 +38348,10 @@ function resolvedOrRaw(path2) {
38166
38348
  function unlinkEscapingReparsePoints(root, primaryRoot) {
38167
38349
  let realRoot;
38168
38350
  try {
38169
- if ((0, import_node_fs41.lstatSync)(root).isSymbolicLink()) {
38351
+ if ((0, import_node_fs42.lstatSync)(root).isSymbolicLink()) {
38170
38352
  return { ok: false, error: `delete root ${normPath2(root)} is a reparse point resolving to ${resolvedOrRaw(root)}` };
38171
38353
  }
38172
- realRoot = normPath2((0, import_node_fs41.realpathSync)(root));
38354
+ realRoot = normPath2((0, import_node_fs42.realpathSync)(root));
38173
38355
  } catch (e) {
38174
38356
  if (e.code === "ENOENT") return { ok: true, unlinked: [] };
38175
38357
  return { ok: false, error: `cannot resolve delete root ${normPath2(root)}: ${errorMessage(e)}` };
@@ -38185,16 +38367,16 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
38185
38367
  const dir = stack.pop();
38186
38368
  let entries;
38187
38369
  try {
38188
- entries = (0, import_node_fs41.readdirSync)(dir, { withFileTypes: true });
38370
+ entries = (0, import_node_fs42.readdirSync)(dir, { withFileTypes: true });
38189
38371
  } catch (e) {
38190
38372
  return { ok: false, error: `cannot scan ${normPath2(dir)} for reparse points: ${errorMessage(e)}` };
38191
38373
  }
38192
38374
  for (const entry of entries) {
38193
- const child2 = (0, import_node_path38.join)(dir, entry.name);
38375
+ const child2 = (0, import_node_path39.join)(dir, entry.name);
38194
38376
  if (entry.isSymbolicLink()) {
38195
38377
  let target = "";
38196
38378
  try {
38197
- target = normPath2((0, import_node_fs41.realpathSync)(child2));
38379
+ target = normPath2((0, import_node_fs42.realpathSync)(child2));
38198
38380
  } catch {
38199
38381
  target = "";
38200
38382
  }
@@ -38224,7 +38406,7 @@ async function describePreCleanFailure(wtPath, execGit, error) {
38224
38406
  const more = remaining.length > 10 ? ` (+${remaining.length - 10} more)` : "";
38225
38407
  const quote = (path2) => path2.replace(/'/g, "''");
38226
38408
  const nested = remaining.find((path2) => path2.endsWith("node_modules/") && path2 !== "node_modules/");
38227
- const remediation = remaining.includes("node_modules/") ? `jervcode worktree-node-modules-cleanup --worktree '${quote(wtPath)}'` : nested ? `Remove-Item -LiteralPath '${quote((0, import_node_path38.join)(wtPath, nested.slice(0, -1)))}' -Recurse -Force` : void 0;
38409
+ const remediation = remaining.includes("node_modules/") ? `jervcode worktree-node-modules-cleanup --worktree '${quote(wtPath)}'` : nested ? `Remove-Item -LiteralPath '${quote((0, import_node_path39.join)(wtPath, nested.slice(0, -1)))}' -Recurse -Force` : void 0;
38228
38410
  return { ok: false, error: `${error}; remaining ignored paths: ${shown}${more}`, ...remediation ? { remediation } : {} };
38229
38411
  }
38230
38412
  function formatWorktreeRemovalFailureDetail(options) {
@@ -38378,7 +38560,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
38378
38560
  return report;
38379
38561
  }
38380
38562
  const execGit = options.execGit ?? (async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
38381
- const pathExists = options.pathExists ?? import_node_fs41.existsSync;
38563
+ const pathExists = options.pathExists ?? import_node_fs42.existsSync;
38382
38564
  let afterWorktrees = [];
38383
38565
  try {
38384
38566
  afterWorktrees = parseGitWorktreePorcelain(await execGit(["worktree", "list", "--porcelain"]));
@@ -38518,7 +38700,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
38518
38700
  const preHelperGuard = unlinkEscapingReparsePoints(wtPath, options.primaryRoot);
38519
38701
  if (!preHelperGuard.ok) return refuseReparseEscape(preHelperGuard.error);
38520
38702
  unlinkedReparsePoints.push(...preHelperGuard.unlinked);
38521
- if (pathExists((0, import_node_path38.join)(wtPath, "node_modules"))) {
38703
+ if (pathExists((0, import_node_path39.join)(wtPath, "node_modules"))) {
38522
38704
  const nmRemoved = await (options.removeRealNodeModules ?? ((p) => removeWorktreeNodeModulesViaHelper(p, { cwd: mainWorktreePath })))(wtPath);
38523
38705
  if (!nmRemoved.ok) {
38524
38706
  report.worktree = {
@@ -38685,10 +38867,10 @@ function argvWantsJson2() {
38685
38867
  return process.argv.some((a) => a === "--json" || a.startsWith("--json="));
38686
38868
  }
38687
38869
  function hubRoot() {
38688
- const fromPkg = (0, import_node_path39.join)(__dirname, "..", "..");
38870
+ const fromPkg = (0, import_node_path40.join)(__dirname, "..", "..");
38689
38871
  const marker = "skills/bootstrap/seeds/manifest.json";
38690
- if ((0, import_node_fs42.existsSync)((0, import_node_path39.join)(fromPkg, marker))) return fromPkg;
38691
- if ((0, import_node_fs42.existsSync)((0, import_node_path39.join)(process.cwd(), marker))) return process.cwd();
38872
+ if ((0, import_node_fs43.existsSync)((0, import_node_path40.join)(fromPkg, marker))) return fromPkg;
38873
+ if ((0, import_node_fs43.existsSync)((0, import_node_path40.join)(process.cwd(), marker))) return process.cwd();
38692
38874
  return null;
38693
38875
  }
38694
38876
  function ciAuditDeps() {
@@ -38700,8 +38882,8 @@ function ciAuditDeps() {
38700
38882
  getProjectMeta: async (slug) => fetchProjectBySlug(slug, registryClientDeps(await cfgPromise)),
38701
38883
  readSeedFile: (path2) => {
38702
38884
  if (!root) return null;
38703
- const fullPath = (0, import_node_path39.join)(root, path2);
38704
- return (0, import_node_fs42.existsSync)(fullPath) ? (0, import_node_fs42.readFileSync)(fullPath, "utf8") : null;
38885
+ const fullPath = (0, import_node_path40.join)(root, path2);
38886
+ return (0, import_node_fs43.existsSync)(fullPath) ? (0, import_node_fs43.readFileSync)(fullPath, "utf8") : null;
38705
38887
  }
38706
38888
  };
38707
38889
  }
@@ -38783,7 +38965,7 @@ function scheduleRelatedDiscovery(o) {
38783
38965
  try {
38784
38966
  const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
38785
38967
  if (o.repo) args.push("--repo", o.repo);
38786
- spawnDetachedSelf(args, { spawn: import_node_child_process15.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
38968
+ spawnDetachedSelf(args, { spawn: import_node_child_process16.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
38787
38969
  } catch {
38788
38970
  }
38789
38971
  }
@@ -39344,11 +39526,11 @@ ${list}`);
39344
39526
  }
39345
39527
  });
39346
39528
  async function listCiWorkflowPaths(cwd = process.cwd()) {
39347
- const wfDir = (0, import_node_path39.join)(cwd, ".github", "workflows");
39348
- if (!(0, import_node_fs42.existsSync)(wfDir)) return [];
39349
- return (0, import_node_fs42.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
39529
+ const wfDir = (0, import_node_path40.join)(cwd, ".github", "workflows");
39530
+ if (!(0, import_node_fs43.existsSync)(wfDir)) return [];
39531
+ return (0, import_node_fs43.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
39350
39532
  try {
39351
- return workflowReportsPrChecks((0, import_node_fs42.readFileSync)((0, import_node_path39.join)(wfDir, name), "utf8"));
39533
+ return workflowReportsPrChecks((0, import_node_fs43.readFileSync)((0, import_node_path40.join)(wfDir, name), "utf8"));
39352
39534
  } catch {
39353
39535
  return true;
39354
39536
  }
@@ -39536,7 +39718,28 @@ ${list}`);
39536
39718
  if (result.status === "failure" || result.status === "conflicting") process.exitCode = 1;
39537
39719
  if (result.status === "timeout" || result.status === "rate-limited") process.exitCode = PR_CHECKS_TIMEOUT_EXIT_CODE;
39538
39720
  });
39539
- pr.command("land <number>").description("agent merge path (#1440): train probe \u2014 checks-wait \u2014 merge --auto \u2014 poll enqueued \u2014 development PRs only").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the PR repo)").option("--no-require-train", "skip train-authority preflight (not recommended for autonomous agents)").option("--force", "acknowledge and land past a remaining prunable/severe scratch housekeeping block (advisory kept plans/ never blocks, #3012) or a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal").action(async (number, o) => {
39721
+ jsonParity(pr.command("review-verdict <number>").description("post the ZeroCI review verdict comment the merge helper requires (#6255): computes the PR diff patch-id and head sha itself, renders the v1 contract body, posts it as a PR comment. Only a PROCEED bound to the CURRENT diff lets `pr merge` / `pr land` merge").requiredOption("--repo <owner/repo>", "target repo").requiredOption("--verdict <verdict>", `one of ${REVIEW_VERDICTS.join("|")}`).requiredOption("--scope <text>", "one line: what was reviewed").requiredOption("--risk <text>", "one line: the residual risk").option("--unverified <text>", "a claim the reviewer could not verify (repeatable)", (v, acc) => [...acc, v], []).requiredOption("--reviewer <id>", "reviewer seat or model id").option("--findings-file <path>", "Markdown findings appended after the JSON block")).action(async (number, o) => {
39722
+ if (!isReviewVerdict(o.verdict)) return fail(`pr review-verdict: --verdict must be one of ${REVIEW_VERDICTS.join("|")} (got ${o.verdict})`);
39723
+ const verdict = o.verdict;
39724
+ const findings = o.findingsFile ? (0, import_node_fs43.readFileSync)(o.findingsFile, "utf8") : void 0;
39725
+ let patch;
39726
+ let head;
39727
+ try {
39728
+ [patch, head] = await Promise.all([computePrPatchId(number, o.repo), readPrHeadSha(number, o.repo)]);
39729
+ } catch (e) {
39730
+ return failGraceful(`pr review-verdict: ${e.message}`);
39731
+ }
39732
+ const body = renderReviewVerdictComment({ v: 1, patch, head, verdict, scope: o.scope, risk: o.risk, unverified: o.unverified, reviewer: o.reviewer, findings });
39733
+ let commentUrl;
39734
+ try {
39735
+ commentUrl = await postPrCommentFromFile(number, o.repo, body);
39736
+ } catch (e) {
39737
+ const err = e;
39738
+ return failGraceful(`pr review-verdict: ${(err.stderr || err.message || String(e)).trim()}`);
39739
+ }
39740
+ printLine(JSON.stringify({ number: Number(number), repo: o.repo, verdict, patch, head, commentUrl }));
39741
+ });
39742
+ pr.command("land <number>").description("agent merge path (#1440): train probe \u2014 checks-wait \u2014 merge --auto \u2014 poll enqueued \u2014 development PRs only").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the PR repo)").option("--no-require-train", "skip train-authority preflight (not recommended for autonomous agents)").option("--force", "acknowledge and land past a remaining prunable/severe scratch housekeeping block (advisory kept plans/ never blocks, #3012) or a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal").option("--without-review <reason>", "land without a review verdict (#6255): posts a `zeroci-review-waived` comment carrying the reason and actor, then proceeds").action(async (number, o) => {
39540
39743
  if (/^(?:[^/]+\/[^/]+)?#\d+$/.test(number.trim())) {
39541
39744
  try {
39542
39745
  const parsed = parseIssueRef(number, o.repo);
@@ -39571,6 +39774,15 @@ ${list}`);
39571
39774
  return;
39572
39775
  }
39573
39776
  if (landClosingGuardVerdict.message) console.warn(landClosingGuardVerdict.message);
39777
+ {
39778
+ const gateRepo = landRepoForGuard ?? await requireRepo(o.repo);
39779
+ const refusal = await requireReviewVerdict("pr land", number, gateRepo, { withoutReview: o.withoutReview });
39780
+ if (refusal) {
39781
+ console.error(refusal);
39782
+ process.exitCode = 1;
39783
+ return;
39784
+ }
39785
+ }
39574
39786
  const result = await runPrLand(number, { repo: o.repo, requireTrain: o.requireTrain !== false }, {
39575
39787
  resolveRepo: async (prNumber, repoOpt) => {
39576
39788
  const args = repoOpt ? ["--repo", repoOpt] : repoArgs;
@@ -39705,7 +39917,7 @@ ${list}`);
39705
39917
  else printLine(`pr land: ${result.status}${result.error ? ` \u2014 ${result.error}` : ""}`);
39706
39918
  if (result.status === "failed") process.exitCode = 1;
39707
39919
  });
39708
- jsonParity(pr.command("merge <number>").description("merge a PR (squash by default); archives gitignored tmp/** before worktree teardown; on no-ci repos run pr ci-policy / checks-wait first (#1432, #5679)").option("--squash", "squash merge (default)").option("--merge", "create a merge commit").option("--rebase", "rebase merge").option("--repo <owner/repo>", "target repo (defaults to the current repo); from a foreign checkout the remote probe/delete address this repo and local cleanup runs only in the verified sibling checkout ../<repo>, else localBranch reports skipped-foreign-cwd and the receipt carries foreignCwd: true (#6148)").option("--auto", "enable auto-merge \u2014 merge once the base-branch policy is satisfied (use for policy-gated repos)").addOption(new Option("--disable-auto", "disable a queued auto-merge without merging").conflicts(["auto", "wait", "squash", "merge", "rebase", "preserveWorktree", "gc", "squashBodyFile", "force"])).option("--wait", `wait for checks to reach a terminal passing verdict before merging (default budget ${PR_CHECKS_TIMEOUT_MS / 6e4}m) \u2014 run as a background/monitor task or under a shell timeout above that budget; a short foreground timeout (e.g. 120s) kills it after checks pass and leaves the PR open (#6027)`).option("--preserve-worktree", "after merge, keep the local PR worktree/branch for an active batch (#1888); also the merge settings-proof bypass when delete_branch_on_merge cannot be proven (#6210)").option("--gc", "acknowledge deleting unarchived gitignored tmp/** evidence newer than the branch base (#5679)").option("--squash-body-file <path>", "squash commit body (overrides GitHub COMMIT_MESSAGES); use when a pushed commit mentions close/fix/resolve + #N that must not close (#5723)").option("--force", "acknowledge and merge past a remaining prunable/severe scratch housekeeping block (advisory kept plans/ never blocks, #3012) or a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal")).action(async (number, o) => {
39920
+ jsonParity(pr.command("merge <number>").description("merge a PR (squash by default); archives gitignored tmp/** before worktree teardown; on no-ci repos run pr ci-policy / checks-wait first (#1432, #5679)").option("--squash", "squash merge (default)").option("--merge", "create a merge commit").option("--rebase", "rebase merge").option("--repo <owner/repo>", "target repo (defaults to the current repo); from a foreign checkout the remote probe/delete address this repo and local cleanup runs only in the verified sibling checkout ../<repo>, else localBranch reports skipped-foreign-cwd and the receipt carries foreignCwd: true (#6148)").option("--auto", "enable auto-merge \u2014 merge once the base-branch policy is satisfied (use for policy-gated repos)").addOption(new Option("--disable-auto", "disable a queued auto-merge without merging").conflicts(["auto", "wait", "squash", "merge", "rebase", "preserveWorktree", "gc", "squashBodyFile", "force"])).option("--wait", `wait for checks to reach a terminal passing verdict before merging (default budget ${PR_CHECKS_TIMEOUT_MS / 6e4}m) \u2014 run as a background/monitor task or under a shell timeout above that budget; a short foreground timeout (e.g. 120s) kills it after checks pass and leaves the PR open (#6027)`).option("--preserve-worktree", "after merge, keep the local PR worktree/branch for an active batch (#1888); also the merge settings-proof bypass when delete_branch_on_merge cannot be proven (#6210)").option("--gc", "acknowledge deleting unarchived gitignored tmp/** evidence newer than the branch base (#5679)").option("--squash-body-file <path>", "squash commit body (overrides GitHub COMMIT_MESSAGES); use when a pushed commit mentions close/fix/resolve + #N that must not close (#5723)").option("--force", "acknowledge and merge past a remaining prunable/severe scratch housekeeping block (advisory kept plans/ never blocks, #3012) or a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal").option("--without-review <reason>", "merge without a review verdict (#6255): posts a `zeroci-review-waived` comment carrying the reason and actor, then proceeds")).action(async (number, o) => {
39709
39921
  const method = o.rebase ? "--rebase" : o.merge ? "--merge" : "--squash";
39710
39922
  const repoArgs = o.repo ? ["--repo", o.repo] : [];
39711
39923
  if (o.disableAuto) {
@@ -39743,7 +39955,7 @@ ${list}`);
39743
39955
  const mergeSquashBody = squashBodyTextForMerge(
39744
39956
  closingGuardInput,
39745
39957
  method === "--squash",
39746
- o.squashBodyFile ? (0, import_node_fs42.readFileSync)(o.squashBodyFile, "utf8") : void 0
39958
+ o.squashBodyFile ? (0, import_node_fs43.readFileSync)(o.squashBodyFile, "utf8") : void 0
39747
39959
  );
39748
39960
  if (!o.squashBodyFile) warnSquashBodyClosingStrip("pr merge", closingGuardInput, mergeSquashBody);
39749
39961
  const closingGuardVerdict = evaluateClosingGuard(closingGuardInput, {
@@ -39757,6 +39969,15 @@ ${list}`);
39757
39969
  return;
39758
39970
  }
39759
39971
  if (closingGuardVerdict.message) console.warn(closingGuardVerdict.message);
39972
+ if (prMeta.state !== "MERGED") {
39973
+ const gateRepo = repoForPostCleanup ?? await requireRepo(o.repo);
39974
+ const refusal = await requireReviewVerdict("pr merge", number, gateRepo, { withoutReview: o.withoutReview });
39975
+ if (refusal) {
39976
+ console.error(refusal);
39977
+ process.exitCode = 1;
39978
+ return;
39979
+ }
39980
+ }
39760
39981
  if (prMeta.state !== "MERGED" && !o.preserveWorktree) {
39761
39982
  if (!repoForPostCleanup) throw new Error("pr merge: repository is unreadable; cannot prove remote branch preservation");
39762
39983
  const repoSettings = await fetchRestRepoMergeSettings(repoForPostCleanup);
@@ -39775,7 +39996,7 @@ ${list}`);
39775
39996
  const remote = foreignCwd ? `https://github.com/${targetRepo2}.git` : "origin";
39776
39997
  let foreignCheckout;
39777
39998
  if (foreignCwd) {
39778
- const sibling = (0, import_node_path39.join)((0, import_node_path39.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
39999
+ const sibling = (0, import_node_path40.join)((0, import_node_path40.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
39779
40000
  const siblingRepo = repoFromRemoteUrl(await gitOut(["-C", sibling, "remote", "get-url", "origin"]).catch(() => ""));
39780
40001
  if (siblingRepo?.toLowerCase() === targetRepo2.toLowerCase()) {
39781
40002
  const siblingWorktrees = await execFileP("git", ["-C", sibling, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => parseGitWorktreePorcelain(r.stdout)).catch(() => void 0);
@@ -39867,7 +40088,7 @@ ${list}`);
39867
40088
  }
39868
40089
  if (!repoForPostCleanup) throw e;
39869
40090
  console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
39870
- const commitMessage = bodyFile ? (0, import_node_fs42.readFileSync)(bodyFile, "utf8") : void 0;
40091
+ const commitMessage = bodyFile ? (0, import_node_fs43.readFileSync)(bodyFile, "utf8") : void 0;
39871
40092
  await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
39872
40093
  body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
39873
40094
  timeoutMs: GH_MUTATION_TIMEOUT_MS
@@ -39973,7 +40194,7 @@ ${list}`);
39973
40194
  preserveWorktree: o.preserveWorktree,
39974
40195
  gcAcknowledged: o.gc,
39975
40196
  expectedHeadOid: headRefOid,
39976
- pathExists: (p) => (0, import_node_fs42.existsSync)(p),
40197
+ pathExists: (p) => (0, import_node_fs43.existsSync)(p),
39977
40198
  // #5899: pin cleanup git calls to the main checkout — the task worktree this process may be
39978
40199
  // standing in is removed mid-cleanup, so a cwd-relative invocation fails with
39979
40200
  // 'fatal: not a git repository' and leaves a spurious partial-cleanup exit.
@@ -40094,7 +40315,7 @@ ${list}`);
40094
40315
  }
40095
40316
 
40096
40317
  // src/command-register-developer.ts
40097
- var import_node_fs47 = require("node:fs");
40318
+ var import_node_fs48 = require("node:fs");
40098
40319
 
40099
40320
  // src/whoami.ts
40100
40321
  async function resolveWhoami(deps) {
@@ -40124,7 +40345,7 @@ async function resolveWhoami(deps) {
40124
40345
  }
40125
40346
 
40126
40347
  // src/command-register-developer.ts
40127
- var import_node_path44 = require("node:path");
40348
+ var import_node_path45 = require("node:path");
40128
40349
 
40129
40350
  // src/wave-land.ts
40130
40351
  function planWaveLand(prs) {
@@ -40155,7 +40376,7 @@ async function executeWaveLand(plan, deps) {
40155
40376
  }
40156
40377
 
40157
40378
  // src/box-commands.ts
40158
- var import_node_fs43 = require("node:fs");
40379
+ var import_node_fs44 = require("node:fs");
40159
40380
 
40160
40381
  // src/box.ts
40161
40382
  var BOX_KEYS = {
@@ -40357,7 +40578,7 @@ function registerBoxCommands(program3) {
40357
40578
  return;
40358
40579
  }
40359
40580
  const wroteScript = o.ssh && o.script ? o.script : null;
40360
- if (wroteScript) (0, import_node_fs43.writeFileSync)(wroteScript, sshRecipeScript(found), "utf8");
40581
+ if (wroteScript) (0, import_node_fs44.writeFileSync)(wroteScript, sshRecipeScript(found), "utf8");
40361
40582
  if (o.json) {
40362
40583
  console.log(JSON.stringify({
40363
40584
  box: found,
@@ -40377,21 +40598,21 @@ ${SSH_RECIPE_AGENT_NOTE}`);
40377
40598
  }
40378
40599
 
40379
40600
  // src/dist-drift.ts
40380
- var import_node_child_process16 = require("node:child_process");
40601
+ var import_node_child_process17 = require("node:child_process");
40381
40602
  var import_node_crypto12 = require("node:crypto");
40382
- var import_node_fs45 = require("node:fs");
40383
- var import_node_os20 = require("node:os");
40384
- var import_node_path41 = require("node:path");
40603
+ var import_node_fs46 = require("node:fs");
40604
+ var import_node_os21 = require("node:os");
40605
+ var import_node_path42 = require("node:path");
40385
40606
 
40386
40607
  // ../scripts/distribution-digest.mjs
40387
40608
  var import_node_crypto11 = require("node:crypto");
40388
- var import_node_fs44 = require("node:fs");
40389
- var import_node_path40 = require("node:path");
40609
+ var import_node_fs45 = require("node:fs");
40610
+ var import_node_path41 = require("node:path");
40390
40611
  var slash = (value) => value.replaceAll("\\", "/");
40391
40612
  function repoPath(root, declaredPath, label) {
40392
- const absoluteRoot = (0, import_node_path40.resolve)(root);
40393
- const target = (0, import_node_path40.resolve)(root, declaredPath);
40394
- if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path40.sep}`)) {
40613
+ const absoluteRoot = (0, import_node_path41.resolve)(root);
40614
+ const target = (0, import_node_path41.resolve)(root, declaredPath);
40615
+ if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path41.sep}`)) {
40395
40616
  throw new Error(`${label} ${declaredPath} escapes the repository root`);
40396
40617
  }
40397
40618
  return target;
@@ -40399,7 +40620,7 @@ function repoPath(root, declaredPath, label) {
40399
40620
  function digestFiles(files) {
40400
40621
  const hash = (0, import_node_crypto11.createHash)("sha256");
40401
40622
  for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
40402
- const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs44.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs44.readFileSync)(file.absolute);
40623
+ const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs45.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs45.readFileSync)(file.absolute);
40403
40624
  hash.update(file.relative, "utf8");
40404
40625
  hash.update("\0");
40405
40626
  hash.update(file.stat.isSymbolicLink() ? "symlink" : "file", "utf8");
@@ -40414,8 +40635,8 @@ function digestFiles(files) {
40414
40635
  function digestPackedFiles(packageRoot, packedFiles) {
40415
40636
  return digestFiles(packedFiles.map((path2) => {
40416
40637
  const absolute = repoPath(packageRoot, path2, "packed artifact identity path");
40417
- if (!(0, import_node_fs44.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
40418
- return { absolute, relative: slash(path2), stat: (0, import_node_fs44.lstatSync)(absolute) };
40638
+ if (!(0, import_node_fs45.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
40639
+ return { absolute, relative: slash(path2), stat: (0, import_node_fs45.lstatSync)(absolute) };
40419
40640
  }));
40420
40641
  }
40421
40642
 
@@ -40499,13 +40720,13 @@ function renderDistDriftReceipt(receipt) {
40499
40720
  return lines2;
40500
40721
  }
40501
40722
  function readOrNull(path2) {
40502
- return (0, import_node_fs45.existsSync)(path2) ? (0, import_node_fs45.readFileSync)(path2) : null;
40723
+ return (0, import_node_fs46.existsSync)(path2) ? (0, import_node_fs46.readFileSync)(path2) : null;
40503
40724
  }
40504
40725
  function walkFiles(root) {
40505
40726
  const files = [];
40506
40727
  const walk2 = (directory) => {
40507
- for (const entry of (0, import_node_fs45.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
40508
- const child2 = (0, import_node_path41.join)(directory, entry.name);
40728
+ for (const entry of (0, import_node_fs46.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
40729
+ const child2 = (0, import_node_path42.join)(directory, entry.name);
40509
40730
  if (entry.isDirectory()) walk2(child2);
40510
40731
  else files.push(child2);
40511
40732
  }
@@ -40515,14 +40736,14 @@ function walkFiles(root) {
40515
40736
  }
40516
40737
  function bomPathFor(root) {
40517
40738
  try {
40518
- const registry2 = JSON.parse((0, import_node_fs45.readFileSync)((0, import_node_path41.join)(root, "surfaces.json"), "utf8"));
40519
- return (0, import_node_path41.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
40739
+ const registry2 = JSON.parse((0, import_node_fs46.readFileSync)((0, import_node_path42.join)(root, "surfaces.json"), "utf8"));
40740
+ return (0, import_node_path42.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
40520
40741
  } catch {
40521
- return (0, import_node_path41.join)(root, "distribution-bom.json");
40742
+ return (0, import_node_path42.join)(root, "distribution-bom.json");
40522
40743
  }
40523
40744
  }
40524
40745
  function rebuildTo(packageRoot, outDir) {
40525
- (0, import_node_child_process16.execFileSync)(process.execPath, ["build.mjs"], {
40746
+ (0, import_node_child_process17.execFileSync)(process.execPath, ["build.mjs"], {
40526
40747
  cwd: packageRoot,
40527
40748
  env: { ...process.env, MMI_DIST_OUTDIR: outDir },
40528
40749
  windowsHide: true,
@@ -40531,35 +40752,35 @@ function rebuildTo(packageRoot, outDir) {
40531
40752
  });
40532
40753
  }
40533
40754
  function runDistStatus(root) {
40534
- const stage = (0, import_node_fs45.mkdtempSync)((0, import_node_path41.join)((0, import_node_os20.tmpdir)(), "mmi-dist-drift-"));
40755
+ const stage = (0, import_node_fs46.mkdtempSync)((0, import_node_path42.join)((0, import_node_os21.tmpdir)(), "mmi-dist-drift-"));
40535
40756
  let overlayCount = 0;
40536
40757
  try {
40537
- const cliOut = (0, import_node_path41.join)(stage, "cli-dist");
40538
- const hubOut = (0, import_node_path41.join)(stage, "hub-dist");
40539
- rebuildTo((0, import_node_path41.join)(root, "cli"), cliOut);
40540
- rebuildTo((0, import_node_path41.join)(root, "updater"), hubOut);
40758
+ const cliOut = (0, import_node_path42.join)(stage, "cli-dist");
40759
+ const hubOut = (0, import_node_path42.join)(stage, "hub-dist");
40760
+ rebuildTo((0, import_node_path42.join)(root, "cli"), cliOut);
40761
+ rebuildTo((0, import_node_path42.join)(root, "updater"), hubOut);
40541
40762
  const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
40542
40763
  const rebuilt = (path2) => {
40543
40764
  const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
40544
- return spec ? readOrNull((0, import_node_path41.join)(outDirFor(spec.packageDir), spec.output)) : null;
40765
+ return spec ? readOrNull((0, import_node_path42.join)(outDirFor(spec.packageDir), spec.output)) : null;
40545
40766
  };
40546
- const committed = (path2) => readOrNull((0, import_node_path41.join)(root, path2));
40547
- const tree = (path2) => readOrNull((0, import_node_path41.join)(root, path2));
40548
- const distRoot = (0, import_node_path41.join)(root, "cli", "dist");
40549
- const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path41.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
40550
- const bom = JSON.parse((0, import_node_fs45.readFileSync)(bomPathFor(root), "utf8"));
40767
+ const committed = (path2) => readOrNull((0, import_node_path42.join)(root, path2));
40768
+ const tree = (path2) => readOrNull((0, import_node_path42.join)(root, path2));
40769
+ const distRoot = (0, import_node_path42.join)(root, "cli", "dist");
40770
+ const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path42.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
40771
+ const bom = JSON.parse((0, import_node_fs46.readFileSync)(bomPathFor(root), "utf8"));
40551
40772
  const digest = (entries) => {
40552
- const overlay = (0, import_node_path41.join)(stage, `overlay-${overlayCount++}`);
40773
+ const overlay = (0, import_node_path42.join)(stage, `overlay-${overlayCount++}`);
40553
40774
  for (const entry of entries) {
40554
- const target = (0, import_node_path41.join)(overlay, entry.path);
40555
- (0, import_node_fs45.mkdirSync)((0, import_node_path41.dirname)(target), { recursive: true });
40556
- (0, import_node_fs45.writeFileSync)(target, entry.bytes);
40775
+ const target = (0, import_node_path42.join)(overlay, entry.path);
40776
+ (0, import_node_fs46.mkdirSync)((0, import_node_path42.dirname)(target), { recursive: true });
40777
+ (0, import_node_fs46.writeFileSync)(target, entry.bytes);
40557
40778
  }
40558
40779
  return digestPackedFiles(overlay, entries.map((entry) => entry.path));
40559
40780
  };
40560
40781
  return computeDistDriftReceipt({ committed, tree, rebuilt, distTree, bom, digest });
40561
40782
  } finally {
40562
- (0, import_node_fs45.rmSync)(stage, { recursive: true, force: true });
40783
+ (0, import_node_fs46.rmSync)(stage, { recursive: true, force: true });
40563
40784
  }
40564
40785
  }
40565
40786
 
@@ -40631,7 +40852,7 @@ function registerEdgeCommands(program3) {
40631
40852
 
40632
40853
  // src/schedules-lift-command.ts
40633
40854
  var import_promises8 = require("node:fs/promises");
40634
- var import_node_path42 = require("node:path");
40855
+ var import_node_path43 = require("node:path");
40635
40856
  var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
40636
40857
  var SCHEDULE_REPO_RE = /^[A-Za-z0-9_.-]+$/;
40637
40858
  var SchedulesLiftUsageError = class extends Error {
@@ -40658,7 +40879,7 @@ async function readWorkflowFiles(dir) {
40658
40879
  const files = [];
40659
40880
  for (const name of names.sort()) {
40660
40881
  if (!/\.ya?ml$/.test(name)) continue;
40661
- files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises8.readFile)((0, import_node_path42.join)(dir, name), "utf8") });
40882
+ files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises8.readFile)((0, import_node_path43.join)(dir, name), "utf8") });
40662
40883
  }
40663
40884
  return files;
40664
40885
  }
@@ -40740,9 +40961,9 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
40740
40961
  }
40741
40962
 
40742
40963
  // src/spawn-policy-core.ts
40743
- var import_node_child_process17 = require("node:child_process");
40744
- var import_node_fs46 = require("node:fs");
40745
- var import_node_path43 = require("node:path");
40964
+ var import_node_child_process18 = require("node:child_process");
40965
+ var import_node_fs47 = require("node:fs");
40966
+ var import_node_path44 = require("node:path");
40746
40967
  var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
40747
40968
  var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
40748
40969
  var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
@@ -40811,7 +41032,7 @@ function findViolationsInSource(raw) {
40811
41032
  return found;
40812
41033
  }
40813
41034
  function policedFiles(root) {
40814
- const r = (0, import_node_child_process17.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
41035
+ const r = (0, import_node_child_process18.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
40815
41036
  cwd: root,
40816
41037
  encoding: "utf8",
40817
41038
  windowsHide: true,
@@ -40828,7 +41049,7 @@ function runSpawnPolicy(root) {
40828
41049
  for (const file of files) {
40829
41050
  let raw;
40830
41051
  try {
40831
- raw = (0, import_node_fs46.readFileSync)((0, import_node_path43.join)(root, file), "utf8");
41052
+ raw = (0, import_node_fs47.readFileSync)((0, import_node_path44.join)(root, file), "utf8");
40832
41053
  } catch {
40833
41054
  continue;
40834
41055
  }
@@ -40848,19 +41069,19 @@ function runSpawnPolicy(root) {
40848
41069
  function registerDeveloperCommands(program3) {
40849
41070
  const rules = program3.command("rules").description("org-managed .gitignore delivery");
40850
41071
  rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").option("--json", "machine-readable output").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
40851
- const path2 = (0, import_node_path44.join)(process.cwd(), ".gitignore");
40852
- const current = (0, import_node_fs47.existsSync)(path2) ? (0, import_node_fs47.readFileSync)(path2, "utf8") : null;
41072
+ const path2 = (0, import_node_path45.join)(process.cwd(), ".gitignore");
41073
+ const current = (0, import_node_fs48.existsSync)(path2) ? (0, import_node_fs48.readFileSync)(path2, "utf8") : null;
40853
41074
  const plan = planManagedGitignore(current);
40854
41075
  const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
40855
41076
  if (opts.json) {
40856
- if (opts.write && plan.changed) (0, import_node_fs47.writeFileSync)(path2, plan.content, "utf8");
41077
+ if (opts.write && plan.changed) (0, import_node_fs48.writeFileSync)(path2, plan.content, "utf8");
40857
41078
  console.log(JSON.stringify(plan, null, 2));
40858
41079
  if (!opts.write && plan.changed) process.exitCode = 1;
40859
41080
  return;
40860
41081
  }
40861
41082
  if (opts.write) {
40862
41083
  if (plan.changed) {
40863
- (0, import_node_fs47.writeFileSync)(path2, plan.content, "utf8");
41084
+ (0, import_node_fs48.writeFileSync)(path2, plan.content, "utf8");
40864
41085
  console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
40865
41086
  } else {
40866
41087
  console.log("mmi-cli devops org rules gitignore: up to date");
@@ -42348,12 +42569,12 @@ async function findInFlightHotfixVersion(deps, ctx, latestMainTag, workflows = H
42348
42569
  }
42349
42570
 
42350
42571
  // src/hotfix-coverage.ts
42351
- var import_node_child_process18 = require("node:child_process");
42572
+ var import_node_child_process19 = require("node:child_process");
42352
42573
  var CHERRY_TRAILER = /\(cherry picked from commit ([0-9a-f]{7,40})\)/g;
42353
42574
  function checkHotfixCoverage(options = {}) {
42354
42575
  const { cwd = process.cwd(), mainRef = "origin/main", rcRef = "origin/rc", manifestPaths = [] } = options;
42355
42576
  const ack = (options.ack ?? []).filter(Boolean);
42356
- const git3 = options.git ?? ((args, opts) => (0, import_node_child_process18.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
42577
+ const git3 = options.git ?? ((args, opts) => (0, import_node_child_process19.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
42357
42578
  const revList = (range) => {
42358
42579
  const out = git3(["rev-list", "--no-merges", range]).trim();
42359
42580
  return out ? out.split("\n") : [];
@@ -42421,7 +42642,7 @@ function checkHotfixCoverage(options = {}) {
42421
42642
  }
42422
42643
  function checkHotfixCarries(options) {
42423
42644
  const { cwd = process.cwd(), branch, baseRef, targets } = options;
42424
- const git3 = options.git ?? ((args, opts) => (0, import_node_child_process18.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
42645
+ const git3 = options.git ?? ((args, opts) => (0, import_node_child_process19.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
42425
42646
  const isAncestor = (sha, ref) => {
42426
42647
  try {
42427
42648
  git3(["merge-base", "--is-ancestor", sha, ref]);
@@ -42453,8 +42674,8 @@ function checkHotfixCarries(options) {
42453
42674
  }
42454
42675
 
42455
42676
  // src/train-commands.ts
42456
- var import_node_fs48 = require("node:fs");
42457
- var import_node_path45 = require("node:path");
42677
+ var import_node_fs49 = require("node:fs");
42678
+ var import_node_path46 = require("node:path");
42458
42679
  var INVOKED_ARGV = process.argv.slice(2);
42459
42680
  var RELEASE_BUMP_INTENTS = ["major", "minor", "patch"];
42460
42681
  function resolveReleaseBumpIntent(raw) {
@@ -42466,7 +42687,7 @@ function resolveReleaseBumpIntent(raw) {
42466
42687
  }
42467
42688
  function readRepoVersion() {
42468
42689
  try {
42469
- return JSON.parse((0, import_node_fs48.readFileSync)((0, import_node_path45.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
42690
+ return JSON.parse((0, import_node_fs49.readFileSync)((0, import_node_path46.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
42470
42691
  } catch {
42471
42692
  return void 0;
42472
42693
  }
@@ -43210,12 +43431,12 @@ ${r.stderr ?? ""}`).catch(() => "");
43210
43431
  var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
43211
43432
  var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
43212
43433
  function envHealLockPath(home) {
43213
- return (0, import_node_path46.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
43434
+ return (0, import_node_path47.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
43214
43435
  }
43215
43436
  async function withEnvHealLock(what, run) {
43216
43437
  try {
43217
43438
  return await withFileLock(
43218
- envHealLockPath((0, import_node_os21.homedir)()),
43439
+ envHealLockPath((0, import_node_os22.homedir)()),
43219
43440
  { staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
43220
43441
  run
43221
43442
  );
@@ -43311,7 +43532,7 @@ function mmiDoctorDeps(opts = {}) {
43311
43532
  const configRoot = surfaceConfigRoot(surface);
43312
43533
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
43313
43534
  const plan = buildPluginCachePlan(
43314
- (0, import_node_os21.homedir)(),
43535
+ (0, import_node_os22.homedir)(),
43315
43536
  running,
43316
43537
  pluginCacheFsDeps(configRoot, () => 0),
43317
43538
  { configRoot, includeStaging: surface !== "codex" }
@@ -43339,14 +43560,14 @@ function mmiDoctorDeps(opts = {}) {
43339
43560
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
43340
43561
  const installed = installedActivePluginVersion(surface);
43341
43562
  const plan = buildPluginCachePlan(
43342
- (0, import_node_os21.homedir)(),
43563
+ (0, import_node_os22.homedir)(),
43343
43564
  running,
43344
43565
  pluginCacheFsDeps(configRoot, () => 0),
43345
43566
  { configRoot, includeStaging: surface !== "codex", installedVersion: installed }
43346
43567
  );
43347
43568
  const result = applyPluginCachePlan(
43348
43569
  plan,
43349
- (p) => (0, import_node_fs49.rmSync)(p, { recursive: true }),
43570
+ (p) => (0, import_node_fs50.rmSync)(p, { recursive: true }),
43350
43571
  stagingApplyFsGuard(configRoot)
43351
43572
  );
43352
43573
  return {
@@ -43381,7 +43602,7 @@ function mmiDoctorDeps(opts = {}) {
43381
43602
  // has no generated routing index would
43382
43603
  // get a permanent — demanding an artifact it never asked for.
43383
43604
  docsIndexState: (root) => {
43384
- if (!(0, import_node_fs49.existsSync)((0, import_node_path46.join)(root, DOCS_INDEX_PATH))) return void 0;
43605
+ if (!(0, import_node_fs50.existsSync)((0, import_node_path47.join)(root, DOCS_INDEX_PATH))) return void 0;
43385
43606
  const real = createDocsIndexDeps(root);
43386
43607
  let docs;
43387
43608
  const listDocs = () => docs ??= real.listDocs();
@@ -43390,7 +43611,7 @@ function mmiDoctorDeps(opts = {}) {
43390
43611
  },
43391
43612
  // #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
43392
43613
  healDocsIndex: (root) => {
43393
- if (!(0, import_node_fs49.existsSync)((0, import_node_path46.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
43614
+ if (!(0, import_node_fs50.existsSync)((0, import_node_path47.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
43394
43615
  const real = createDocsIndexDeps(root);
43395
43616
  let docs;
43396
43617
  const listDocs = () => docs ??= real.listDocs();
@@ -43949,7 +44170,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
43949
44170
  if (dupe) return fail(`org project set: KEY "${dupe}" was passed to both --var and --set; --set is an alias of --var, so pass each KEY once`);
43950
44171
  if (o.secretsFile) {
43951
44172
  try {
43952
- vars.push(`secrets=${(0, import_node_fs49.readFileSync)(o.secretsFile, "utf8")}`);
44173
+ vars.push(`secrets=${(0, import_node_fs50.readFileSync)(o.secretsFile, "utf8")}`);
43953
44174
  } catch (e) {
43954
44175
  return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
43955
44176
  }
@@ -44262,16 +44483,16 @@ function ciAuditDeps2() {
44262
44483
  // gate re-seed step is skipped gracefully rather than failing mid-run.
44263
44484
  readSeedFile: (path2) => {
44264
44485
  if (!root) return null;
44265
- const fullPath = (0, import_node_path46.join)(root, path2);
44266
- return (0, import_node_fs49.existsSync)(fullPath) ? (0, import_node_fs49.readFileSync)(fullPath, "utf8") : null;
44486
+ const fullPath = (0, import_node_path47.join)(root, path2);
44487
+ return (0, import_node_fs50.existsSync)(fullPath) ? (0, import_node_fs50.readFileSync)(fullPath, "utf8") : null;
44267
44488
  }
44268
44489
  };
44269
44490
  }
44270
44491
  function hubRoot2() {
44271
- const fromPkg = (0, import_node_path46.join)(__dirname, "..", "..");
44492
+ const fromPkg = (0, import_node_path47.join)(__dirname, "..", "..");
44272
44493
  const marker = "skills/bootstrap/seeds/manifest.json";
44273
- if ((0, import_node_fs49.existsSync)((0, import_node_path46.join)(fromPkg, marker))) return fromPkg;
44274
- if ((0, import_node_fs49.existsSync)((0, import_node_path46.join)(process.cwd(), marker))) return process.cwd();
44494
+ if ((0, import_node_fs50.existsSync)((0, import_node_path47.join)(fromPkg, marker))) return fromPkg;
44495
+ if ((0, import_node_fs50.existsSync)((0, import_node_path47.join)(process.cwd(), marker))) return process.cwd();
44275
44496
  return null;
44276
44497
  }
44277
44498
  registerQueryCommands(program2);
@@ -44371,10 +44592,10 @@ access.command("audit").description("audit collaborator roles + train-branch pus
44371
44592
  targets = resolution.targets;
44372
44593
  }
44373
44594
  const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
44374
- const fileMatrix = (0, import_node_fs49.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs49.readFileSync)("access-matrix.json", "utf8")) : {};
44595
+ const fileMatrix = (0, import_node_fs50.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs50.readFileSync)("access-matrix.json", "utf8")) : {};
44375
44596
  const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
44376
44597
  const dataAccess = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
44377
- const sanctioned = (0, import_node_fs49.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs49.readFileSync)("access-matrix.json", "utf8")) : {};
44598
+ const sanctioned = (0, import_node_fs50.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs50.readFileSync)("access-matrix.json", "utf8")) : {};
44378
44599
  const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
44379
44600
  console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
44380
44601
  if (!report.ok) process.exitCode = 1;
@@ -44405,16 +44626,16 @@ function directoryBytes(path2) {
44405
44626
  let total = 0;
44406
44627
  let entries;
44407
44628
  try {
44408
- entries = (0, import_node_fs49.readdirSync)(path2, { withFileTypes: true });
44629
+ entries = (0, import_node_fs50.readdirSync)(path2, { withFileTypes: true });
44409
44630
  } catch {
44410
44631
  return 0;
44411
44632
  }
44412
44633
  for (const entry of entries) {
44413
- const child2 = (0, import_node_path46.join)(path2, entry.name);
44634
+ const child2 = (0, import_node_path47.join)(path2, entry.name);
44414
44635
  if (entry.isDirectory()) total += directoryBytes(child2);
44415
44636
  else {
44416
44637
  try {
44417
- total += (0, import_node_fs49.statSync)(child2).size;
44638
+ total += (0, import_node_fs50.statSync)(child2).size;
44418
44639
  } catch {
44419
44640
  }
44420
44641
  }
@@ -44422,25 +44643,25 @@ function directoryBytes(path2) {
44422
44643
  return total;
44423
44644
  }
44424
44645
  function listDirEntries(dir) {
44425
- return (0, import_node_fs49.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
44646
+ return (0, import_node_fs50.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
44426
44647
  }
44427
44648
  function readInstalledPluginRefs(configRoot) {
44428
44649
  const p = installedPluginsPathForConfig(configRoot);
44429
- if (!(0, import_node_fs49.existsSync)(p)) return [];
44650
+ if (!(0, import_node_fs50.existsSync)(p)) return [];
44430
44651
  try {
44431
- return installedPluginPaths((0, import_node_fs49.readFileSync)(p, "utf8"));
44652
+ return installedPluginPaths((0, import_node_fs50.readFileSync)(p, "utf8"));
44432
44653
  } catch {
44433
44654
  return null;
44434
44655
  }
44435
44656
  }
44436
44657
  function pluginCacheFsDeps(configRoot, dirBytes) {
44437
44658
  return {
44438
- exists: (p) => (0, import_node_fs49.existsSync)(p),
44439
- listVersionDirs: (root) => (0, import_node_fs49.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
44659
+ exists: (p) => (0, import_node_fs50.existsSync)(p),
44660
+ listVersionDirs: (root) => (0, import_node_fs50.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
44440
44661
  dirBytes,
44441
- listStagingDirs: (root) => (0, import_node_fs49.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
44662
+ listStagingDirs: (root) => (0, import_node_fs50.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
44442
44663
  try {
44443
- return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path46.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs49.statSync)(p).mtimeMs) };
44664
+ return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path47.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs50.statSync)(p).mtimeMs) };
44444
44665
  } catch {
44445
44666
  return { name: d.name, mtimeMs: Date.now() };
44446
44667
  }
@@ -44454,10 +44675,10 @@ function stagingApplyFsGuard(configRoot) {
44454
44675
  return {
44455
44676
  referencedPaths: () => readInstalledPluginRefs(configRoot),
44456
44677
  mtimeMs: (name) => {
44457
- const p = (0, import_node_path46.join)(stagingRoot, name);
44458
- if (!(0, import_node_fs49.existsSync)(p)) return null;
44678
+ const p = (0, import_node_path47.join)(stagingRoot, name);
44679
+ if (!(0, import_node_fs50.existsSync)(p)) return null;
44459
44680
  try {
44460
- return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs49.statSync)(q).mtimeMs);
44681
+ return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs50.statSync)(q).mtimeMs);
44461
44682
  } catch {
44462
44683
  return null;
44463
44684
  }
@@ -44477,13 +44698,13 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
44477
44698
  return;
44478
44699
  }
44479
44700
  const plan = buildPluginCachePlan(
44480
- (0, import_node_os21.homedir)(),
44701
+ (0, import_node_os22.homedir)(),
44481
44702
  running,
44482
44703
  pluginCacheFsDeps(configRoot, directoryBytes),
44483
44704
  { withBytes: true, configRoot, includeStaging: surface !== "codex" }
44484
44705
  );
44485
44706
  const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
44486
- const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs49.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
44707
+ const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs50.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
44487
44708
  const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
44488
44709
  if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
44489
44710
  else console.log(renderPluginCachePlan(plan, result));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.3.15",
3
+ "version": "4.3.16",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",