@mutmutco/cli 4.3.14 → 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 (3) hide show
  1. package/README.md +4 -2
  2. package/dist/main.cjs +407 -178
  3. 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");
@@ -7917,10 +7917,12 @@ function buildIssueArgs({ type, title, body, priority, repo, labels }) {
7917
7917
  for (const label of labels ?? []) args.push("--label", label);
7918
7918
  return args;
7919
7919
  }
7920
+ var LEARNING_LABEL = "learning";
7921
+ var LEARNING_LABEL_COLOR = "d93f0b";
7920
7922
  async function ensureLabelsExist(labels, repo, deps = {}) {
7921
7923
  const run = deps.run ?? execFileP;
7922
7924
  for (const label of new Set(labels)) {
7923
- const args = ["label", "create", label, "--color", "ededed"];
7925
+ const args = ["label", "create", label, "--color", label === LEARNING_LABEL ? LEARNING_LABEL_COLOR : "ededed"];
7924
7926
  if (repo) args.push("--repo", repo);
7925
7927
  try {
7926
7928
  await run("gh", args, { timeout: GH_MUTATION_TIMEOUT_MS });
@@ -9334,7 +9336,7 @@ async function fetchSecretForUse(deps, { repo, key, slug }) {
9334
9336
  // src/report.ts
9335
9337
  var HUB_REPO = "mutmutco/MMI-Hub";
9336
9338
  var REPORT_LABEL = "report";
9337
- var LEARNING_LABEL = "learning";
9339
+ var LEARNING_LABEL2 = "learning";
9338
9340
  var REPORT_LOOP_KIND = "friction";
9339
9341
  var REPORT_SURFACE_WAIVER_REASON = "Hub-only friction filing; attribution repo is footer-only; no honest single surface";
9340
9342
  function preflightReportSurface() {
@@ -9516,7 +9518,7 @@ async function loadConfigForBoardSelector(selector, repoOption) {
9516
9518
 
9517
9519
  // src/skill-lesson.ts
9518
9520
  var SKILL_LESSON_LABEL = "skill-lesson";
9519
- var SKILL_LESSON_FILE_LABELS = [SKILL_LESSON_LABEL, LEARNING_LABEL];
9521
+ var SKILL_LESSON_FILE_LABELS = [SKILL_LESSON_LABEL, LEARNING_LABEL2];
9520
9522
  var SKILL_LESSON_LOOP_KIND = "lesson";
9521
9523
  var SKILL_NAMES = ["bootstrap", "epic", "hotfix", "rcand", "release", "secrets", "stage"];
9522
9524
  function assertSkillName(name) {
@@ -9547,6 +9549,11 @@ ${buildSkillLessonBody(body, sourceRepo, pluginSha)}`;
9547
9549
  function findDuplicateLesson(source, openLessons) {
9548
9550
  return findDuplicateReport(source, openLessons);
9549
9551
  }
9552
+ var SKILL_LESSON_DEPRECATION_NOTICE = "mmi-cli learning skill-lesson is deprecated \u2014 file the retro with the jerv_learning_note tool in-session (origin retro), or `jervcode learn --origin retro --surface skills` on a machine path. This verb still files.";
9553
+ function warnSkillLessonDeprecated(write = (line) => void process.stderr.write(line)) {
9554
+ write(`${SKILL_LESSON_DEPRECATION_NOTICE}
9555
+ `);
9556
+ }
9550
9557
 
9551
9558
  // src/session-identity.ts
9552
9559
  var import_node_crypto4 = require("node:crypto");
@@ -13699,7 +13706,7 @@ function createDocsIndexDeps(repoRoot2) {
13699
13706
  }
13700
13707
 
13701
13708
  // src/gate-budget.ts
13702
- var BLESSED_RUN_WITH_BUDGET_SHA = "ea2cf8710949dec53566c29836e29cd7515a4e23";
13709
+ var BLESSED_RUN_WITH_BUDGET_SHA = "5ce541add569e54e7ca96de67a768577d3d604e4";
13703
13710
  var BLESSED_RUNNER_GATE_SHA = "a6ee756d94fd95884451381e94408f0348eb11db";
13704
13711
  var REMOTE_USE = /^mutmutco\/MMI-Hub\/\.github\/actions\/run-with-budget@(\S+)$/;
13705
13712
  var LOCAL_USE = "./.github/actions/run-with-budget";
@@ -14037,6 +14044,8 @@ function gateSeedVars(cls, releaseTrack, runtime = "node", requiredCheckBranches
14037
14044
  const runtimeVars = {
14038
14045
  GATE_RUNTIME: runtime,
14039
14046
  GATE_CMD: rt.cmd,
14047
+ // #6254: affected-only PR command; empty means "same as GATE_CMD" (withDerivedRepoVars fills it).
14048
+ GATE_AFFECTED_CMD: "",
14040
14049
  GATE_INSTALL_CMD: rt.install,
14041
14050
  GATE_WORKDIR: ".",
14042
14051
  GATE_CACHE_DEP_PATH: "package-lock.json",
@@ -14084,6 +14093,7 @@ function withDerivedRepoVars(vars, parsed, cls, releaseTrack, requiredCheckBranc
14084
14093
  for (const [key, value] of Object.entries(gateSeedVars(cls, track, runtime, requiredCheckBranches))) {
14085
14094
  out[key] ??= value;
14086
14095
  }
14096
+ if (!out.GATE_AFFECTED_CMD.trim()) out.GATE_AFFECTED_CMD = out.GATE_CMD;
14087
14097
  return out;
14088
14098
  }
14089
14099
  function gateConfigToVars(gate) {
@@ -14091,6 +14101,7 @@ function gateConfigToVars(gate) {
14091
14101
  if (!gate || typeof gate !== "object") return out;
14092
14102
  if (gate.runtime === "node" || gate.runtime === "python") out.GATE_RUNTIME = gate.runtime;
14093
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;
14094
14105
  if (typeof gate.workdir === "string" && gate.workdir.trim()) out.GATE_WORKDIR = gate.workdir;
14095
14106
  if (typeof gate.cacheDepPath === "string" && gate.cacheDepPath.trim()) out.GATE_CACHE_DEP_PATH = gate.cacheDepPath;
14096
14107
  if (typeof gate.pyVersion === "string" && gate.pyVersion.trim()) out.GATE_PY_VERSION = gate.pyVersion;
@@ -15476,10 +15487,10 @@ var rollout_plan_default = {
15476
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)."
15477
15488
  },
15478
15489
  baseline: {
15479
- version: "4.3.14",
15480
- tag: "v4.3.14",
15481
- commit: "1382ade8602e",
15482
- npm: "@mutmutco/cli@4.3.14"
15490
+ version: "4.3.16",
15491
+ tag: "v4.3.16",
15492
+ commit: "5a4b8e29fd1a",
15493
+ npm: "@mutmutco/cli@4.3.16"
15483
15494
  },
15484
15495
  exitCriterion: "fleet-n-of-n",
15485
15496
  hubOnlyShortcut: "forbidden",
@@ -15496,14 +15507,14 @@ var rollout_plan_default = {
15496
15507
  repo: "mutmutco/mmi-hub",
15497
15508
  role: "canary",
15498
15509
  schedule: "train",
15499
- v3Target: "v4.3.14"
15510
+ v3Target: "v4.3.16"
15500
15511
  }
15501
15512
  ],
15502
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.",
15503
15514
  rollback: {
15504
15515
  independent: true,
15505
- mechanism: "npm dist-tag latest -> 4.3.14 and redeploy the Hub Lambda from tag v4.3.14 (1382ade8602e); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15506
- v3Target: "v4.3.14 (@mutmutco/cli@4.3.14, tag commit 1382ade8602e \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)"
15507
15518
  }
15508
15519
  },
15509
15520
  {
@@ -28771,7 +28782,7 @@ function parseGateVar(raw) {
28771
28782
  throw new Error('org project set: gate must be JSON, e.g. {"runtime":"python","cmd":"pytest","workdir":"app"}');
28772
28783
  }
28773
28784
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
28774
- 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");
28775
28786
  }
28776
28787
  const map = parsed;
28777
28788
  const out = {};
@@ -28779,11 +28790,11 @@ function parseGateVar(raw) {
28779
28790
  if (key === "runtime") {
28780
28791
  if (value !== "node" && value !== "python") throw new Error('org project set: gate.runtime must be "node" or "python"');
28781
28792
  out.runtime = value;
28782
- } else if (key === "cmd" || key === "workdir" || key === "cacheDepPath" || key === "pyVersion") {
28793
+ } else if (key === "cmd" || key === "affectedCmd" || key === "workdir" || key === "cacheDepPath" || key === "pyVersion") {
28783
28794
  if (typeof value !== "string" || !value.trim()) throw new Error(`org project set: gate.${key} must be a non-empty string`);
28784
28795
  out[key] = value.trim();
28785
28796
  } else {
28786
- 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`);
28787
28798
  }
28788
28799
  }
28789
28800
  return out;
@@ -28884,7 +28895,7 @@ var SETTABLE_VAR_HINTS = {
28884
28895
  requiredChecks: 'JSON array, e.g. ["gate"] or [] for no-ci',
28885
28896
  requiredCheckBranches: "non-empty JSON branch-name array; exact product-ruleset scope (#5210)",
28886
28897
  ciExemptReason: "non-empty string \u2014 declares this deployable repo has no CI surface (#4265)",
28887
- gate: "JSON {runtime,cmd,workdir,cacheDepPath,pyVersion}"
28898
+ gate: "JSON {runtime,cmd,affectedCmd,workdir,cacheDepPath,pyVersion}"
28888
28899
  };
28889
28900
  function settableVarHelp() {
28890
28901
  const keys = SETTABLE_VAR_KEYS.map((k) => {
@@ -33124,7 +33135,7 @@ ${lines2}`, {
33124
33135
  var LEARNING_PICKUP_CMD = ["mmi-cli", "learning", "pickup"].join(" ");
33125
33136
  var LEARNING_CLAIM_MARKER_LIVE_MS = 24 * 60 * 6e4;
33126
33137
  function isLearningChannelItem(labels) {
33127
- return labels.includes(LEARNING_LABEL) || loopKindOf(labels) !== void 0;
33138
+ return labels.includes(LEARNING_LABEL2) || loopKindOf(labels) !== void 0;
33128
33139
  }
33129
33140
  function claimEvidenceBlocksLearningAdoption(evidence) {
33130
33141
  if (evidence.failed.includes("comments")) return true;
@@ -35901,8 +35912,8 @@ function renderVerifySecrets(body) {
35901
35912
  }
35902
35913
 
35903
35914
  // src/command-register-collaboration.ts
35904
- var import_node_child_process15 = require("node:child_process");
35905
- 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");
35906
35917
  var import_promises7 = require("node:fs/promises");
35907
35918
 
35908
35919
  // src/session-runtime.ts
@@ -35922,7 +35933,7 @@ function spawnDetachedSelf(args, deps, opts = {}) {
35922
35933
  }
35923
35934
 
35924
35935
  // src/command-register-collaboration.ts
35925
- var import_node_path39 = require("node:path");
35936
+ var import_node_path40 = require("node:path");
35926
35937
 
35927
35938
  // src/attach-to-project.ts
35928
35939
  function boardAttachRateLimitedReceipt(resetEpochSeconds) {
@@ -37574,15 +37585,193 @@ function postMergeReconWarnings(input) {
37574
37585
  return lines2;
37575
37586
  }
37576
37587
 
37577
- // src/pr-create-docs-check.ts
37588
+ // src/review-verdict.ts
37578
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");
37579
37768
  var GIT_TIMEOUT_MS2 = 15e3;
37580
37769
  function catFileBatch(root, ref, paths) {
37581
37770
  if (paths.length === 0) return Promise.resolve([]);
37582
37771
  return new Promise((resolve7) => {
37583
37772
  const chunks = [];
37584
37773
  let settled = false;
37585
- 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 });
37586
37775
  const finish = () => {
37587
37776
  if (settled) return;
37588
37777
  settled = true;
@@ -37752,26 +37941,26 @@ async function prCreateClaimRefusal(body, repoOption, deps = {}) {
37752
37941
  }
37753
37942
 
37754
37943
  // src/worktree-merge-cleanup.ts
37755
- var import_node_fs41 = require("node:fs");
37756
- var import_node_path38 = require("node:path");
37944
+ var import_node_fs42 = require("node:fs");
37945
+ var import_node_path39 = require("node:path");
37757
37946
 
37758
37947
  // src/jervcode-node-modules-cleanup.ts
37759
- var import_node_fs39 = require("node:fs");
37760
- var import_node_os19 = require("node:os");
37761
- var import_node_path36 = require("node:path");
37762
- 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");
37763
37952
  var WIN_NAMES2 = ["jervcode.cmd", "jervcode"];
37764
37953
  var POSIX_NAMES2 = ["jervcode"];
37765
37954
  var NODE_MODULES_CLEANUP_TIMEOUT_MS = 3e5;
37766
- 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) {
37767
37956
  const names = platform2 === "win32" ? WIN_NAMES2 : POSIX_NAMES2;
37768
37957
  const out = [];
37769
37958
  for (const dir of jervCliCandidateDirs(env, home, platform2)) {
37770
- 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));
37771
37960
  }
37772
37961
  return out;
37773
37962
  }
37774
- 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) {
37775
37964
  for (const candidate of jervcodeCandidatePaths(env, home, platform2)) {
37776
37965
  if (exists(candidate)) return candidate;
37777
37966
  }
@@ -37779,10 +37968,10 @@ function resolveJervcodePath(env = process.env, home = (0, import_node_os19.home
37779
37968
  }
37780
37969
  function jervcodeExecFileArgs(args, opts = {}) {
37781
37970
  const platform2 = opts.platform ?? process.platform;
37782
- const exists = opts.exists ?? import_node_fs39.existsSync;
37783
- 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);
37784
37973
  if (resolved) {
37785
- 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);
37786
37975
  if (exists(entry)) {
37787
37976
  return { file: opts.execPath ?? process.execPath, args: [entry, ...args], via: "node-entry" };
37788
37977
  }
@@ -37813,8 +38002,8 @@ async function removeWorktreeNodeModulesViaHelper(wtPath, opts = {}) {
37813
38002
  }
37814
38003
 
37815
38004
  // src/worktree-evidence-archive.ts
37816
- var import_node_fs40 = require("node:fs");
37817
- var import_node_path37 = require("node:path");
38005
+ var import_node_fs41 = require("node:fs");
38006
+ var import_node_path38 = require("node:path");
37818
38007
  var JERV_ARTIFACT_RUN_SCOPE_ENV_VARS = ["JERV_RUN_ID", ...SESSION_ID_ENV_VARS];
37819
38008
  function sanitizeArchiveSegment(value, max = 80) {
37820
38009
  const scrubbed = value.replace(/[^A-Za-z0-9._@+-]+/g, "-").replace(/^-+|-+$/g, "");
@@ -37829,24 +38018,24 @@ function resolveArtifactRunScope(env = process.env) {
37829
38018
  }
37830
38019
  function defaultIsDirectory(path2) {
37831
38020
  try {
37832
- return (0, import_node_fs40.lstatSync)(path2).isDirectory();
38021
+ return (0, import_node_fs41.lstatSync)(path2).isDirectory();
37833
38022
  } catch {
37834
38023
  return false;
37835
38024
  }
37836
38025
  }
37837
38026
  function archiveWorktreeJervArtifacts(args, deps = {}) {
37838
- const exists = deps.exists ?? import_node_fs40.existsSync;
38027
+ const exists = deps.exists ?? import_node_fs41.existsSync;
37839
38028
  const isDirectory = deps.isDirectory ?? defaultIsDirectory;
37840
- 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 }));
37841
38030
  const mkdirp = deps.mkdirp ?? ((path2) => {
37842
- (0, import_node_fs40.mkdirSync)(path2, { recursive: true });
38031
+ (0, import_node_fs41.mkdirSync)(path2, { recursive: true });
37843
38032
  });
37844
38033
  const env = deps.env ?? process.env;
37845
38034
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
37846
38035
  const resolveRoot = deps.resolveArchiveRoot ?? repoRuntimeStatePath;
37847
38036
  if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
37848
38037
  if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
37849
- const source = (0, import_node_path37.join)(args.worktreePath, ".jerv");
38038
+ const source = (0, import_node_path38.join)(args.worktreePath, ".jerv");
37850
38039
  if (!exists(source)) return { status: "absent" };
37851
38040
  if (!isDirectory(source)) return { status: "skipped", reason: "jerv-not-a-directory" };
37852
38041
  const runScope = resolveArtifactRunScope(env);
@@ -37854,7 +38043,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
37854
38043
  const stamp = now().toISOString().replace(/[:.]/g, "-");
37855
38044
  const dest = resolveRoot(args.primaryRoot, "jerv-artifacts", runScope, branchSlug, stamp, ".jerv");
37856
38045
  try {
37857
- mkdirp((0, import_node_path37.dirname)(dest));
38046
+ mkdirp((0, import_node_path38.dirname)(dest));
37858
38047
  copyDir(source, dest);
37859
38048
  if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
37860
38049
  return { status: "archived", path: dest, runScope };
@@ -37863,14 +38052,14 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
37863
38052
  }
37864
38053
  }
37865
38054
  function defaultStat(path2) {
37866
- const st = (0, import_node_fs40.statSync)(path2);
38055
+ const st = (0, import_node_fs41.statSync)(path2);
37867
38056
  return { mtimeMs: st.mtimeMs, size: st.size, isDirectory: () => st.isDirectory() };
37868
38057
  }
37869
38058
  function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
37870
- const exists = deps.exists ?? import_node_fs40.existsSync;
38059
+ const exists = deps.exists ?? import_node_fs41.existsSync;
37871
38060
  const stat4 = deps.stat ?? defaultStat;
37872
- const readdir2 = deps.readdir ?? import_node_fs40.readdirSync;
37873
- 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");
37874
38063
  if (!exists(tmpRoot)) return [];
37875
38064
  const entries = [];
37876
38065
  const walk2 = (dir) => {
@@ -37881,14 +38070,14 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
37881
38070
  return;
37882
38071
  }
37883
38072
  for (const name of names) {
37884
- const full = (0, import_node_path37.join)(dir, name);
38073
+ const full = (0, import_node_path38.join)(dir, name);
37885
38074
  let st;
37886
38075
  try {
37887
38076
  st = stat4(full);
37888
38077
  } catch {
37889
38078
  continue;
37890
38079
  }
37891
- const relPath = (0, import_node_path37.relative)(worktreePath, full).replace(/\\/g, "/");
38080
+ const relPath = (0, import_node_path38.relative)(worktreePath, full).replace(/\\/g, "/");
37892
38081
  if (st.isDirectory()) {
37893
38082
  if (st.mtimeMs > newerThanMs) entries.push({ relPath, bytes: 0, mtimeMs: st.mtimeMs });
37894
38083
  walk2(full);
@@ -37902,10 +38091,10 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
37902
38091
  return entries;
37903
38092
  }
37904
38093
  function archiveWorktreeTmpArtifacts(args, deps = {}) {
37905
- const exists = deps.exists ?? import_node_fs40.existsSync;
37906
- 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 }));
37907
38096
  const mkdirp = deps.mkdirp ?? ((path2) => {
37908
- (0, import_node_fs40.mkdirSync)(path2, { recursive: true });
38097
+ (0, import_node_fs41.mkdirSync)(path2, { recursive: true });
37909
38098
  });
37910
38099
  const env = deps.env ?? process.env;
37911
38100
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
@@ -37913,7 +38102,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
37913
38102
  if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
37914
38103
  if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
37915
38104
  const scanned = scanWorktreeTmpEvidence(args.worktreePath, args.newerThanMs, deps);
37916
- const source = (0, import_node_path37.join)(args.worktreePath, "tmp");
38105
+ const source = (0, import_node_path38.join)(args.worktreePath, "tmp");
37917
38106
  if (!scanned.length) return { status: "absent" };
37918
38107
  if (!exists(source)) return { status: "absent" };
37919
38108
  const runScope = resolveArtifactRunScope(env);
@@ -37921,7 +38110,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
37921
38110
  const stamp = now().toISOString().replace(/[:.]/g, "-");
37922
38111
  const dest = resolveRoot(args.primaryRoot, "worktree-artifacts", runScope, branchSlug, stamp, "tmp");
37923
38112
  try {
37924
- mkdirp((0, import_node_path37.dirname)(dest));
38113
+ mkdirp((0, import_node_path38.dirname)(dest));
37925
38114
  copyDir(source, dest);
37926
38115
  if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
37927
38116
  const bytes = scanned.reduce((sum, e) => sum + e.bytes, 0);
@@ -37990,9 +38179,9 @@ function normPath2(p) {
37990
38179
  return p.replace(/\\/g, "/").replace(/\/+$/, "");
37991
38180
  }
37992
38181
  function unlinkNodeModulesJunction(wtPath) {
37993
- const nm = (0, import_node_path38.join)(wtPath, "node_modules");
38182
+ const nm = (0, import_node_path39.join)(wtPath, "node_modules");
37994
38183
  try {
37995
- 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);
37996
38185
  return { ok: true };
37997
38186
  } catch (e) {
37998
38187
  if (e.code === "ENOENT") return { ok: true };
@@ -38003,8 +38192,8 @@ function defaultSleep2(ms) {
38003
38192
  return new Promise((resolve7) => setTimeout(resolve7, ms));
38004
38193
  }
38005
38194
  async function removeResidueDirectory(wtPath, options = {}) {
38006
- const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs41.rmdirSync, exists = import_node_fs41.existsSync } = options;
38007
- 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 }));
38008
38197
  const junction = unlinkNodeModulesJunction(wtPath);
38009
38198
  if (!junction.ok) return junction;
38010
38199
  try {
@@ -38127,31 +38316,31 @@ async function preCleanWorktreeForRemoval(wtPath, execGit) {
38127
38316
  }
38128
38317
  async function listNestedIgnoredNodeModules(wtPath, execGit) {
38129
38318
  const out = await execGit(["-C", wtPath, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory"]).catch(() => "");
38130
- 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)));
38131
38320
  }
38132
38321
  function safeRemoveTree(path2) {
38133
- const stat4 = (0, import_node_fs41.lstatSync)(path2);
38322
+ const stat4 = (0, import_node_fs42.lstatSync)(path2);
38134
38323
  if (stat4.isSymbolicLink()) {
38135
38324
  try {
38136
- (0, import_node_fs41.rmdirSync)(path2);
38325
+ (0, import_node_fs42.rmdirSync)(path2);
38137
38326
  } catch {
38138
- (0, import_node_fs41.unlinkSync)(path2);
38327
+ (0, import_node_fs42.unlinkSync)(path2);
38139
38328
  }
38140
38329
  return;
38141
38330
  }
38142
38331
  if (stat4.isDirectory()) {
38143
- for (const entry of (0, import_node_fs41.readdirSync)(path2)) safeRemoveTree((0, import_node_path38.join)(path2, entry));
38144
- (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);
38145
38334
  return;
38146
38335
  }
38147
- (0, import_node_fs41.unlinkSync)(path2);
38336
+ (0, import_node_fs42.unlinkSync)(path2);
38148
38337
  }
38149
38338
  function errorMessage(e) {
38150
38339
  return e instanceof Error ? e.message : String(e);
38151
38340
  }
38152
38341
  function resolvedOrRaw(path2) {
38153
38342
  try {
38154
- return normPath2((0, import_node_fs41.realpathSync)(path2));
38343
+ return normPath2((0, import_node_fs42.realpathSync)(path2));
38155
38344
  } catch {
38156
38345
  return normPath2(path2);
38157
38346
  }
@@ -38159,10 +38348,10 @@ function resolvedOrRaw(path2) {
38159
38348
  function unlinkEscapingReparsePoints(root, primaryRoot) {
38160
38349
  let realRoot;
38161
38350
  try {
38162
- if ((0, import_node_fs41.lstatSync)(root).isSymbolicLink()) {
38351
+ if ((0, import_node_fs42.lstatSync)(root).isSymbolicLink()) {
38163
38352
  return { ok: false, error: `delete root ${normPath2(root)} is a reparse point resolving to ${resolvedOrRaw(root)}` };
38164
38353
  }
38165
- realRoot = normPath2((0, import_node_fs41.realpathSync)(root));
38354
+ realRoot = normPath2((0, import_node_fs42.realpathSync)(root));
38166
38355
  } catch (e) {
38167
38356
  if (e.code === "ENOENT") return { ok: true, unlinked: [] };
38168
38357
  return { ok: false, error: `cannot resolve delete root ${normPath2(root)}: ${errorMessage(e)}` };
@@ -38178,16 +38367,16 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
38178
38367
  const dir = stack.pop();
38179
38368
  let entries;
38180
38369
  try {
38181
- entries = (0, import_node_fs41.readdirSync)(dir, { withFileTypes: true });
38370
+ entries = (0, import_node_fs42.readdirSync)(dir, { withFileTypes: true });
38182
38371
  } catch (e) {
38183
38372
  return { ok: false, error: `cannot scan ${normPath2(dir)} for reparse points: ${errorMessage(e)}` };
38184
38373
  }
38185
38374
  for (const entry of entries) {
38186
- const child2 = (0, import_node_path38.join)(dir, entry.name);
38375
+ const child2 = (0, import_node_path39.join)(dir, entry.name);
38187
38376
  if (entry.isSymbolicLink()) {
38188
38377
  let target = "";
38189
38378
  try {
38190
- target = normPath2((0, import_node_fs41.realpathSync)(child2));
38379
+ target = normPath2((0, import_node_fs42.realpathSync)(child2));
38191
38380
  } catch {
38192
38381
  target = "";
38193
38382
  }
@@ -38217,7 +38406,7 @@ async function describePreCleanFailure(wtPath, execGit, error) {
38217
38406
  const more = remaining.length > 10 ? ` (+${remaining.length - 10} more)` : "";
38218
38407
  const quote = (path2) => path2.replace(/'/g, "''");
38219
38408
  const nested = remaining.find((path2) => path2.endsWith("node_modules/") && path2 !== "node_modules/");
38220
- 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;
38221
38410
  return { ok: false, error: `${error}; remaining ignored paths: ${shown}${more}`, ...remediation ? { remediation } : {} };
38222
38411
  }
38223
38412
  function formatWorktreeRemovalFailureDetail(options) {
@@ -38371,7 +38560,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
38371
38560
  return report;
38372
38561
  }
38373
38562
  const execGit = options.execGit ?? (async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
38374
- const pathExists = options.pathExists ?? import_node_fs41.existsSync;
38563
+ const pathExists = options.pathExists ?? import_node_fs42.existsSync;
38375
38564
  let afterWorktrees = [];
38376
38565
  try {
38377
38566
  afterWorktrees = parseGitWorktreePorcelain(await execGit(["worktree", "list", "--porcelain"]));
@@ -38511,7 +38700,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
38511
38700
  const preHelperGuard = unlinkEscapingReparsePoints(wtPath, options.primaryRoot);
38512
38701
  if (!preHelperGuard.ok) return refuseReparseEscape(preHelperGuard.error);
38513
38702
  unlinkedReparsePoints.push(...preHelperGuard.unlinked);
38514
- if (pathExists((0, import_node_path38.join)(wtPath, "node_modules"))) {
38703
+ if (pathExists((0, import_node_path39.join)(wtPath, "node_modules"))) {
38515
38704
  const nmRemoved = await (options.removeRealNodeModules ?? ((p) => removeWorktreeNodeModulesViaHelper(p, { cwd: mainWorktreePath })))(wtPath);
38516
38705
  if (!nmRemoved.ok) {
38517
38706
  report.worktree = {
@@ -38678,10 +38867,10 @@ function argvWantsJson2() {
38678
38867
  return process.argv.some((a) => a === "--json" || a.startsWith("--json="));
38679
38868
  }
38680
38869
  function hubRoot() {
38681
- const fromPkg = (0, import_node_path39.join)(__dirname, "..", "..");
38870
+ const fromPkg = (0, import_node_path40.join)(__dirname, "..", "..");
38682
38871
  const marker = "skills/bootstrap/seeds/manifest.json";
38683
- if ((0, import_node_fs42.existsSync)((0, import_node_path39.join)(fromPkg, marker))) return fromPkg;
38684
- 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();
38685
38874
  return null;
38686
38875
  }
38687
38876
  function ciAuditDeps() {
@@ -38693,8 +38882,8 @@ function ciAuditDeps() {
38693
38882
  getProjectMeta: async (slug) => fetchProjectBySlug(slug, registryClientDeps(await cfgPromise)),
38694
38883
  readSeedFile: (path2) => {
38695
38884
  if (!root) return null;
38696
- const fullPath = (0, import_node_path39.join)(root, path2);
38697
- 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;
38698
38887
  }
38699
38888
  };
38700
38889
  }
@@ -38776,7 +38965,7 @@ function scheduleRelatedDiscovery(o) {
38776
38965
  try {
38777
38966
  const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
38778
38967
  if (o.repo) args.push("--repo", o.repo);
38779
- 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() });
38780
38969
  } catch {
38781
38970
  }
38782
38971
  }
@@ -39199,6 +39388,7 @@ ${list}`);
39199
39388
  }
39200
39389
  }
39201
39390
  program3.command("skill-lesson").description("file a skill-lesson on the Hub board (GitHub auth, dedups open lessons) and print {number,url} JSON").addOption(new Option("--skill <name>", `which skill misfired (${SKILL_NAMES.join(" | ")})`).makeOptionMandatory().choices([...SKILL_NAMES])).option("--title <title>", "one-line summary of what misfired").option("--title-file <path|->", "read the one-line summary from a UTF-8 file, or from stdin with -").option("--body <body>", "lesson body: what misfired, the evidence, and the proposed amendment (markdown)").option("--body-file <path|->", "read the lesson body from a UTF-8 file, or from stdin with -").option("--priority <priority>", "urgent | high | medium | low (defaults to medium; sets the board Priority field only, #416)").option("--repo <owner/repo>", `target repo (defaults to the org Hub: ${HUB_REPO})`).option("--force", "file a new issue even when an open lesson looks like a duplicate").option("--json", "machine-readable output (already the default \u2014 skill-lesson always prints JSON)").action(async (o) => {
39391
+ warnSkillLessonDeprecated();
39202
39392
  const targetRepo2 = o.repo ?? HUB_REPO;
39203
39393
  const sourceRepo = await resolveRepo(void 0);
39204
39394
  const pluginSha = await resolvePluginSha();
@@ -39257,7 +39447,7 @@ ${list}`);
39257
39447
  });
39258
39448
  if (surfaceWarn) process.stderr.write(`${surfaceWarn}
39259
39449
  `);
39260
- for (const [label, color] of [[SKILL_LESSON_LABEL, "c2e0c6"], [LEARNING_LABEL, "ededed"]]) {
39450
+ for (const [label, color] of [[SKILL_LESSON_LABEL, "c2e0c6"], [LEARNING_LABEL2, "ededed"]]) {
39261
39451
  try {
39262
39452
  await execFileP("gh", ["label", "create", label, "--color", color, "--repo", targetRepo2], { timeout: GH_MUTATION_TIMEOUT_MS });
39263
39453
  } catch {
@@ -39336,11 +39526,11 @@ ${list}`);
39336
39526
  }
39337
39527
  });
39338
39528
  async function listCiWorkflowPaths(cwd = process.cwd()) {
39339
- const wfDir = (0, import_node_path39.join)(cwd, ".github", "workflows");
39340
- if (!(0, import_node_fs42.existsSync)(wfDir)) return [];
39341
- 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) => {
39342
39532
  try {
39343
- 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"));
39344
39534
  } catch {
39345
39535
  return true;
39346
39536
  }
@@ -39528,7 +39718,28 @@ ${list}`);
39528
39718
  if (result.status === "failure" || result.status === "conflicting") process.exitCode = 1;
39529
39719
  if (result.status === "timeout" || result.status === "rate-limited") process.exitCode = PR_CHECKS_TIMEOUT_EXIT_CODE;
39530
39720
  });
39531
- 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) => {
39532
39743
  if (/^(?:[^/]+\/[^/]+)?#\d+$/.test(number.trim())) {
39533
39744
  try {
39534
39745
  const parsed = parseIssueRef(number, o.repo);
@@ -39563,6 +39774,15 @@ ${list}`);
39563
39774
  return;
39564
39775
  }
39565
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
+ }
39566
39786
  const result = await runPrLand(number, { repo: o.repo, requireTrain: o.requireTrain !== false }, {
39567
39787
  resolveRepo: async (prNumber, repoOpt) => {
39568
39788
  const args = repoOpt ? ["--repo", repoOpt] : repoArgs;
@@ -39697,7 +39917,7 @@ ${list}`);
39697
39917
  else printLine(`pr land: ${result.status}${result.error ? ` \u2014 ${result.error}` : ""}`);
39698
39918
  if (result.status === "failed") process.exitCode = 1;
39699
39919
  });
39700
- 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) => {
39701
39921
  const method = o.rebase ? "--rebase" : o.merge ? "--merge" : "--squash";
39702
39922
  const repoArgs = o.repo ? ["--repo", o.repo] : [];
39703
39923
  if (o.disableAuto) {
@@ -39735,7 +39955,7 @@ ${list}`);
39735
39955
  const mergeSquashBody = squashBodyTextForMerge(
39736
39956
  closingGuardInput,
39737
39957
  method === "--squash",
39738
- 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
39739
39959
  );
39740
39960
  if (!o.squashBodyFile) warnSquashBodyClosingStrip("pr merge", closingGuardInput, mergeSquashBody);
39741
39961
  const closingGuardVerdict = evaluateClosingGuard(closingGuardInput, {
@@ -39749,6 +39969,15 @@ ${list}`);
39749
39969
  return;
39750
39970
  }
39751
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
+ }
39752
39981
  if (prMeta.state !== "MERGED" && !o.preserveWorktree) {
39753
39982
  if (!repoForPostCleanup) throw new Error("pr merge: repository is unreadable; cannot prove remote branch preservation");
39754
39983
  const repoSettings = await fetchRestRepoMergeSettings(repoForPostCleanup);
@@ -39767,7 +39996,7 @@ ${list}`);
39767
39996
  const remote = foreignCwd ? `https://github.com/${targetRepo2}.git` : "origin";
39768
39997
  let foreignCheckout;
39769
39998
  if (foreignCwd) {
39770
- 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]);
39771
40000
  const siblingRepo = repoFromRemoteUrl(await gitOut(["-C", sibling, "remote", "get-url", "origin"]).catch(() => ""));
39772
40001
  if (siblingRepo?.toLowerCase() === targetRepo2.toLowerCase()) {
39773
40002
  const siblingWorktrees = await execFileP("git", ["-C", sibling, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => parseGitWorktreePorcelain(r.stdout)).catch(() => void 0);
@@ -39859,7 +40088,7 @@ ${list}`);
39859
40088
  }
39860
40089
  if (!repoForPostCleanup) throw e;
39861
40090
  console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
39862
- 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;
39863
40092
  await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
39864
40093
  body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
39865
40094
  timeoutMs: GH_MUTATION_TIMEOUT_MS
@@ -39965,7 +40194,7 @@ ${list}`);
39965
40194
  preserveWorktree: o.preserveWorktree,
39966
40195
  gcAcknowledged: o.gc,
39967
40196
  expectedHeadOid: headRefOid,
39968
- pathExists: (p) => (0, import_node_fs42.existsSync)(p),
40197
+ pathExists: (p) => (0, import_node_fs43.existsSync)(p),
39969
40198
  // #5899: pin cleanup git calls to the main checkout — the task worktree this process may be
39970
40199
  // standing in is removed mid-cleanup, so a cwd-relative invocation fails with
39971
40200
  // 'fatal: not a git repository' and leaves a spurious partial-cleanup exit.
@@ -40086,7 +40315,7 @@ ${list}`);
40086
40315
  }
40087
40316
 
40088
40317
  // src/command-register-developer.ts
40089
- var import_node_fs47 = require("node:fs");
40318
+ var import_node_fs48 = require("node:fs");
40090
40319
 
40091
40320
  // src/whoami.ts
40092
40321
  async function resolveWhoami(deps) {
@@ -40116,7 +40345,7 @@ async function resolveWhoami(deps) {
40116
40345
  }
40117
40346
 
40118
40347
  // src/command-register-developer.ts
40119
- var import_node_path44 = require("node:path");
40348
+ var import_node_path45 = require("node:path");
40120
40349
 
40121
40350
  // src/wave-land.ts
40122
40351
  function planWaveLand(prs) {
@@ -40147,7 +40376,7 @@ async function executeWaveLand(plan, deps) {
40147
40376
  }
40148
40377
 
40149
40378
  // src/box-commands.ts
40150
- var import_node_fs43 = require("node:fs");
40379
+ var import_node_fs44 = require("node:fs");
40151
40380
 
40152
40381
  // src/box.ts
40153
40382
  var BOX_KEYS = {
@@ -40349,7 +40578,7 @@ function registerBoxCommands(program3) {
40349
40578
  return;
40350
40579
  }
40351
40580
  const wroteScript = o.ssh && o.script ? o.script : null;
40352
- if (wroteScript) (0, import_node_fs43.writeFileSync)(wroteScript, sshRecipeScript(found), "utf8");
40581
+ if (wroteScript) (0, import_node_fs44.writeFileSync)(wroteScript, sshRecipeScript(found), "utf8");
40353
40582
  if (o.json) {
40354
40583
  console.log(JSON.stringify({
40355
40584
  box: found,
@@ -40369,21 +40598,21 @@ ${SSH_RECIPE_AGENT_NOTE}`);
40369
40598
  }
40370
40599
 
40371
40600
  // src/dist-drift.ts
40372
- var import_node_child_process16 = require("node:child_process");
40601
+ var import_node_child_process17 = require("node:child_process");
40373
40602
  var import_node_crypto12 = require("node:crypto");
40374
- var import_node_fs45 = require("node:fs");
40375
- var import_node_os20 = require("node:os");
40376
- 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");
40377
40606
 
40378
40607
  // ../scripts/distribution-digest.mjs
40379
40608
  var import_node_crypto11 = require("node:crypto");
40380
- var import_node_fs44 = require("node:fs");
40381
- var import_node_path40 = require("node:path");
40609
+ var import_node_fs45 = require("node:fs");
40610
+ var import_node_path41 = require("node:path");
40382
40611
  var slash = (value) => value.replaceAll("\\", "/");
40383
40612
  function repoPath(root, declaredPath, label) {
40384
- const absoluteRoot = (0, import_node_path40.resolve)(root);
40385
- const target = (0, import_node_path40.resolve)(root, declaredPath);
40386
- 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}`)) {
40387
40616
  throw new Error(`${label} ${declaredPath} escapes the repository root`);
40388
40617
  }
40389
40618
  return target;
@@ -40391,7 +40620,7 @@ function repoPath(root, declaredPath, label) {
40391
40620
  function digestFiles(files) {
40392
40621
  const hash = (0, import_node_crypto11.createHash)("sha256");
40393
40622
  for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
40394
- 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);
40395
40624
  hash.update(file.relative, "utf8");
40396
40625
  hash.update("\0");
40397
40626
  hash.update(file.stat.isSymbolicLink() ? "symlink" : "file", "utf8");
@@ -40406,8 +40635,8 @@ function digestFiles(files) {
40406
40635
  function digestPackedFiles(packageRoot, packedFiles) {
40407
40636
  return digestFiles(packedFiles.map((path2) => {
40408
40637
  const absolute = repoPath(packageRoot, path2, "packed artifact identity path");
40409
- if (!(0, import_node_fs44.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
40410
- 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) };
40411
40640
  }));
40412
40641
  }
40413
40642
 
@@ -40491,13 +40720,13 @@ function renderDistDriftReceipt(receipt) {
40491
40720
  return lines2;
40492
40721
  }
40493
40722
  function readOrNull(path2) {
40494
- 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;
40495
40724
  }
40496
40725
  function walkFiles(root) {
40497
40726
  const files = [];
40498
40727
  const walk2 = (directory) => {
40499
- for (const entry of (0, import_node_fs45.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
40500
- 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);
40501
40730
  if (entry.isDirectory()) walk2(child2);
40502
40731
  else files.push(child2);
40503
40732
  }
@@ -40507,14 +40736,14 @@ function walkFiles(root) {
40507
40736
  }
40508
40737
  function bomPathFor(root) {
40509
40738
  try {
40510
- const registry2 = JSON.parse((0, import_node_fs45.readFileSync)((0, import_node_path41.join)(root, "surfaces.json"), "utf8"));
40511
- 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");
40512
40741
  } catch {
40513
- return (0, import_node_path41.join)(root, "distribution-bom.json");
40742
+ return (0, import_node_path42.join)(root, "distribution-bom.json");
40514
40743
  }
40515
40744
  }
40516
40745
  function rebuildTo(packageRoot, outDir) {
40517
- (0, import_node_child_process16.execFileSync)(process.execPath, ["build.mjs"], {
40746
+ (0, import_node_child_process17.execFileSync)(process.execPath, ["build.mjs"], {
40518
40747
  cwd: packageRoot,
40519
40748
  env: { ...process.env, MMI_DIST_OUTDIR: outDir },
40520
40749
  windowsHide: true,
@@ -40523,35 +40752,35 @@ function rebuildTo(packageRoot, outDir) {
40523
40752
  });
40524
40753
  }
40525
40754
  function runDistStatus(root) {
40526
- 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-"));
40527
40756
  let overlayCount = 0;
40528
40757
  try {
40529
- const cliOut = (0, import_node_path41.join)(stage, "cli-dist");
40530
- const hubOut = (0, import_node_path41.join)(stage, "hub-dist");
40531
- rebuildTo((0, import_node_path41.join)(root, "cli"), cliOut);
40532
- 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);
40533
40762
  const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
40534
40763
  const rebuilt = (path2) => {
40535
40764
  const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
40536
- 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;
40537
40766
  };
40538
- const committed = (path2) => readOrNull((0, import_node_path41.join)(root, path2));
40539
- const tree = (path2) => readOrNull((0, import_node_path41.join)(root, path2));
40540
- const distRoot = (0, import_node_path41.join)(root, "cli", "dist");
40541
- const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path41.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
40542
- 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"));
40543
40772
  const digest = (entries) => {
40544
- const overlay = (0, import_node_path41.join)(stage, `overlay-${overlayCount++}`);
40773
+ const overlay = (0, import_node_path42.join)(stage, `overlay-${overlayCount++}`);
40545
40774
  for (const entry of entries) {
40546
- const target = (0, import_node_path41.join)(overlay, entry.path);
40547
- (0, import_node_fs45.mkdirSync)((0, import_node_path41.dirname)(target), { recursive: true });
40548
- (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);
40549
40778
  }
40550
40779
  return digestPackedFiles(overlay, entries.map((entry) => entry.path));
40551
40780
  };
40552
40781
  return computeDistDriftReceipt({ committed, tree, rebuilt, distTree, bom, digest });
40553
40782
  } finally {
40554
- (0, import_node_fs45.rmSync)(stage, { recursive: true, force: true });
40783
+ (0, import_node_fs46.rmSync)(stage, { recursive: true, force: true });
40555
40784
  }
40556
40785
  }
40557
40786
 
@@ -40623,7 +40852,7 @@ function registerEdgeCommands(program3) {
40623
40852
 
40624
40853
  // src/schedules-lift-command.ts
40625
40854
  var import_promises8 = require("node:fs/promises");
40626
- var import_node_path42 = require("node:path");
40855
+ var import_node_path43 = require("node:path");
40627
40856
  var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
40628
40857
  var SCHEDULE_REPO_RE = /^[A-Za-z0-9_.-]+$/;
40629
40858
  var SchedulesLiftUsageError = class extends Error {
@@ -40650,7 +40879,7 @@ async function readWorkflowFiles(dir) {
40650
40879
  const files = [];
40651
40880
  for (const name of names.sort()) {
40652
40881
  if (!/\.ya?ml$/.test(name)) continue;
40653
- 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") });
40654
40883
  }
40655
40884
  return files;
40656
40885
  }
@@ -40732,9 +40961,9 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
40732
40961
  }
40733
40962
 
40734
40963
  // src/spawn-policy-core.ts
40735
- var import_node_child_process17 = require("node:child_process");
40736
- var import_node_fs46 = require("node:fs");
40737
- 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");
40738
40967
  var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
40739
40968
  var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
40740
40969
  var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
@@ -40803,7 +41032,7 @@ function findViolationsInSource(raw) {
40803
41032
  return found;
40804
41033
  }
40805
41034
  function policedFiles(root) {
40806
- 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"], {
40807
41036
  cwd: root,
40808
41037
  encoding: "utf8",
40809
41038
  windowsHide: true,
@@ -40820,7 +41049,7 @@ function runSpawnPolicy(root) {
40820
41049
  for (const file of files) {
40821
41050
  let raw;
40822
41051
  try {
40823
- 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");
40824
41053
  } catch {
40825
41054
  continue;
40826
41055
  }
@@ -40840,19 +41069,19 @@ function runSpawnPolicy(root) {
40840
41069
  function registerDeveloperCommands(program3) {
40841
41070
  const rules = program3.command("rules").description("org-managed .gitignore delivery");
40842
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) => {
40843
- const path2 = (0, import_node_path44.join)(process.cwd(), ".gitignore");
40844
- 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;
40845
41074
  const plan = planManagedGitignore(current);
40846
41075
  const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
40847
41076
  if (opts.json) {
40848
- 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");
40849
41078
  console.log(JSON.stringify(plan, null, 2));
40850
41079
  if (!opts.write && plan.changed) process.exitCode = 1;
40851
41080
  return;
40852
41081
  }
40853
41082
  if (opts.write) {
40854
41083
  if (plan.changed) {
40855
- (0, import_node_fs47.writeFileSync)(path2, plan.content, "utf8");
41084
+ (0, import_node_fs48.writeFileSync)(path2, plan.content, "utf8");
40856
41085
  console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
40857
41086
  } else {
40858
41087
  console.log("mmi-cli devops org rules gitignore: up to date");
@@ -42340,12 +42569,12 @@ async function findInFlightHotfixVersion(deps, ctx, latestMainTag, workflows = H
42340
42569
  }
42341
42570
 
42342
42571
  // src/hotfix-coverage.ts
42343
- var import_node_child_process18 = require("node:child_process");
42572
+ var import_node_child_process19 = require("node:child_process");
42344
42573
  var CHERRY_TRAILER = /\(cherry picked from commit ([0-9a-f]{7,40})\)/g;
42345
42574
  function checkHotfixCoverage(options = {}) {
42346
42575
  const { cwd = process.cwd(), mainRef = "origin/main", rcRef = "origin/rc", manifestPaths = [] } = options;
42347
42576
  const ack = (options.ack ?? []).filter(Boolean);
42348
- 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"] }));
42349
42578
  const revList = (range) => {
42350
42579
  const out = git3(["rev-list", "--no-merges", range]).trim();
42351
42580
  return out ? out.split("\n") : [];
@@ -42413,7 +42642,7 @@ function checkHotfixCoverage(options = {}) {
42413
42642
  }
42414
42643
  function checkHotfixCarries(options) {
42415
42644
  const { cwd = process.cwd(), branch, baseRef, targets } = options;
42416
- 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"] }));
42417
42646
  const isAncestor = (sha, ref) => {
42418
42647
  try {
42419
42648
  git3(["merge-base", "--is-ancestor", sha, ref]);
@@ -42445,8 +42674,8 @@ function checkHotfixCarries(options) {
42445
42674
  }
42446
42675
 
42447
42676
  // src/train-commands.ts
42448
- var import_node_fs48 = require("node:fs");
42449
- var import_node_path45 = require("node:path");
42677
+ var import_node_fs49 = require("node:fs");
42678
+ var import_node_path46 = require("node:path");
42450
42679
  var INVOKED_ARGV = process.argv.slice(2);
42451
42680
  var RELEASE_BUMP_INTENTS = ["major", "minor", "patch"];
42452
42681
  function resolveReleaseBumpIntent(raw) {
@@ -42458,7 +42687,7 @@ function resolveReleaseBumpIntent(raw) {
42458
42687
  }
42459
42688
  function readRepoVersion() {
42460
42689
  try {
42461
- 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;
42462
42691
  } catch {
42463
42692
  return void 0;
42464
42693
  }
@@ -43202,12 +43431,12 @@ ${r.stderr ?? ""}`).catch(() => "");
43202
43431
  var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
43203
43432
  var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
43204
43433
  function envHealLockPath(home) {
43205
- 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");
43206
43435
  }
43207
43436
  async function withEnvHealLock(what, run) {
43208
43437
  try {
43209
43438
  return await withFileLock(
43210
- envHealLockPath((0, import_node_os21.homedir)()),
43439
+ envHealLockPath((0, import_node_os22.homedir)()),
43211
43440
  { staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
43212
43441
  run
43213
43442
  );
@@ -43303,7 +43532,7 @@ function mmiDoctorDeps(opts = {}) {
43303
43532
  const configRoot = surfaceConfigRoot(surface);
43304
43533
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
43305
43534
  const plan = buildPluginCachePlan(
43306
- (0, import_node_os21.homedir)(),
43535
+ (0, import_node_os22.homedir)(),
43307
43536
  running,
43308
43537
  pluginCacheFsDeps(configRoot, () => 0),
43309
43538
  { configRoot, includeStaging: surface !== "codex" }
@@ -43331,14 +43560,14 @@ function mmiDoctorDeps(opts = {}) {
43331
43560
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
43332
43561
  const installed = installedActivePluginVersion(surface);
43333
43562
  const plan = buildPluginCachePlan(
43334
- (0, import_node_os21.homedir)(),
43563
+ (0, import_node_os22.homedir)(),
43335
43564
  running,
43336
43565
  pluginCacheFsDeps(configRoot, () => 0),
43337
43566
  { configRoot, includeStaging: surface !== "codex", installedVersion: installed }
43338
43567
  );
43339
43568
  const result = applyPluginCachePlan(
43340
43569
  plan,
43341
- (p) => (0, import_node_fs49.rmSync)(p, { recursive: true }),
43570
+ (p) => (0, import_node_fs50.rmSync)(p, { recursive: true }),
43342
43571
  stagingApplyFsGuard(configRoot)
43343
43572
  );
43344
43573
  return {
@@ -43373,7 +43602,7 @@ function mmiDoctorDeps(opts = {}) {
43373
43602
  // has no generated routing index would
43374
43603
  // get a permanent — demanding an artifact it never asked for.
43375
43604
  docsIndexState: (root) => {
43376
- 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;
43377
43606
  const real = createDocsIndexDeps(root);
43378
43607
  let docs;
43379
43608
  const listDocs = () => docs ??= real.listDocs();
@@ -43382,7 +43611,7 @@ function mmiDoctorDeps(opts = {}) {
43382
43611
  },
43383
43612
  // #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
43384
43613
  healDocsIndex: (root) => {
43385
- 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 };
43386
43615
  const real = createDocsIndexDeps(root);
43387
43616
  let docs;
43388
43617
  const listDocs = () => docs ??= real.listDocs();
@@ -43941,7 +44170,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
43941
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`);
43942
44171
  if (o.secretsFile) {
43943
44172
  try {
43944
- vars.push(`secrets=${(0, import_node_fs49.readFileSync)(o.secretsFile, "utf8")}`);
44173
+ vars.push(`secrets=${(0, import_node_fs50.readFileSync)(o.secretsFile, "utf8")}`);
43945
44174
  } catch (e) {
43946
44175
  return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
43947
44176
  }
@@ -44254,16 +44483,16 @@ function ciAuditDeps2() {
44254
44483
  // gate re-seed step is skipped gracefully rather than failing mid-run.
44255
44484
  readSeedFile: (path2) => {
44256
44485
  if (!root) return null;
44257
- const fullPath = (0, import_node_path46.join)(root, path2);
44258
- 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;
44259
44488
  }
44260
44489
  };
44261
44490
  }
44262
44491
  function hubRoot2() {
44263
- const fromPkg = (0, import_node_path46.join)(__dirname, "..", "..");
44492
+ const fromPkg = (0, import_node_path47.join)(__dirname, "..", "..");
44264
44493
  const marker = "skills/bootstrap/seeds/manifest.json";
44265
- if ((0, import_node_fs49.existsSync)((0, import_node_path46.join)(fromPkg, marker))) return fromPkg;
44266
- 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();
44267
44496
  return null;
44268
44497
  }
44269
44498
  registerQueryCommands(program2);
@@ -44363,10 +44592,10 @@ access.command("audit").description("audit collaborator roles + train-branch pus
44363
44592
  targets = resolution.targets;
44364
44593
  }
44365
44594
  const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
44366
- 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")) : {};
44367
44596
  const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
44368
44597
  const dataAccess = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
44369
- 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")) : {};
44370
44599
  const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
44371
44600
  console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
44372
44601
  if (!report.ok) process.exitCode = 1;
@@ -44397,16 +44626,16 @@ function directoryBytes(path2) {
44397
44626
  let total = 0;
44398
44627
  let entries;
44399
44628
  try {
44400
- entries = (0, import_node_fs49.readdirSync)(path2, { withFileTypes: true });
44629
+ entries = (0, import_node_fs50.readdirSync)(path2, { withFileTypes: true });
44401
44630
  } catch {
44402
44631
  return 0;
44403
44632
  }
44404
44633
  for (const entry of entries) {
44405
- const child2 = (0, import_node_path46.join)(path2, entry.name);
44634
+ const child2 = (0, import_node_path47.join)(path2, entry.name);
44406
44635
  if (entry.isDirectory()) total += directoryBytes(child2);
44407
44636
  else {
44408
44637
  try {
44409
- total += (0, import_node_fs49.statSync)(child2).size;
44638
+ total += (0, import_node_fs50.statSync)(child2).size;
44410
44639
  } catch {
44411
44640
  }
44412
44641
  }
@@ -44414,25 +44643,25 @@ function directoryBytes(path2) {
44414
44643
  return total;
44415
44644
  }
44416
44645
  function listDirEntries(dir) {
44417
- 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() }));
44418
44647
  }
44419
44648
  function readInstalledPluginRefs(configRoot) {
44420
44649
  const p = installedPluginsPathForConfig(configRoot);
44421
- if (!(0, import_node_fs49.existsSync)(p)) return [];
44650
+ if (!(0, import_node_fs50.existsSync)(p)) return [];
44422
44651
  try {
44423
- return installedPluginPaths((0, import_node_fs49.readFileSync)(p, "utf8"));
44652
+ return installedPluginPaths((0, import_node_fs50.readFileSync)(p, "utf8"));
44424
44653
  } catch {
44425
44654
  return null;
44426
44655
  }
44427
44656
  }
44428
44657
  function pluginCacheFsDeps(configRoot, dirBytes) {
44429
44658
  return {
44430
- exists: (p) => (0, import_node_fs49.existsSync)(p),
44431
- 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),
44432
44661
  dirBytes,
44433
- 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) => {
44434
44663
  try {
44435
- 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) };
44436
44665
  } catch {
44437
44666
  return { name: d.name, mtimeMs: Date.now() };
44438
44667
  }
@@ -44446,10 +44675,10 @@ function stagingApplyFsGuard(configRoot) {
44446
44675
  return {
44447
44676
  referencedPaths: () => readInstalledPluginRefs(configRoot),
44448
44677
  mtimeMs: (name) => {
44449
- const p = (0, import_node_path46.join)(stagingRoot, name);
44450
- 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;
44451
44680
  try {
44452
- 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);
44453
44682
  } catch {
44454
44683
  return null;
44455
44684
  }
@@ -44469,13 +44698,13 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
44469
44698
  return;
44470
44699
  }
44471
44700
  const plan = buildPluginCachePlan(
44472
- (0, import_node_os21.homedir)(),
44701
+ (0, import_node_os22.homedir)(),
44473
44702
  running,
44474
44703
  pluginCacheFsDeps(configRoot, directoryBytes),
44475
44704
  { withBytes: true, configRoot, includeStaging: surface !== "codex" }
44476
44705
  );
44477
44706
  const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
44478
- 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;
44479
44708
  const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
44480
44709
  if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
44481
44710
  else console.log(renderPluginCachePlan(plan, result));