@mutmutco/cli 4.4.8 → 4.4.10

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 +685 -393
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -4534,7 +4534,7 @@ function useColor() {
4534
4534
  var program = new Command();
4535
4535
 
4536
4536
  // src/command-composition.ts
4537
- var import_node_fs48 = require("node:fs");
4537
+ var import_node_fs49 = require("node:fs");
4538
4538
  init_clean_exit();
4539
4539
  init_cli_shared();
4540
4540
 
@@ -5437,7 +5437,7 @@ function resolveVerbAliasShim(argv) {
5437
5437
 
5438
5438
  // src/command-composition.ts
5439
5439
  var import_node_os21 = require("node:os");
5440
- var import_node_path46 = require("node:path");
5440
+ var import_node_path47 = require("node:path");
5441
5441
 
5442
5442
  // src/board-read.ts
5443
5443
  var import_node_fs13 = require("node:fs");
@@ -15938,10 +15938,10 @@ var rollout_plan_default = {
15938
15938
  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)."
15939
15939
  },
15940
15940
  baseline: {
15941
- version: "4.4.8",
15942
- tag: "v4.4.8",
15943
- commit: "beed56b5ec82",
15944
- npm: "@mutmutco/cli@4.4.8"
15941
+ version: "4.4.10",
15942
+ tag: "v4.4.10",
15943
+ commit: "187ab08827c9",
15944
+ npm: "@mutmutco/cli@4.4.10"
15945
15945
  },
15946
15946
  exitCriterion: "fleet-n-of-n",
15947
15947
  hubOnlyShortcut: "forbidden",
@@ -15958,14 +15958,14 @@ var rollout_plan_default = {
15958
15958
  repo: "mutmutco/mmi-hub",
15959
15959
  role: "canary",
15960
15960
  schedule: "train",
15961
- v3Target: "v4.4.8"
15961
+ v3Target: "v4.4.10"
15962
15962
  }
15963
15963
  ],
15964
15964
  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.",
15965
15965
  rollback: {
15966
15966
  independent: true,
15967
- mechanism: "npm dist-tag latest -> 4.4.8 and redeploy the Hub Lambda from tag v4.4.8 (beed56b5ec82); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15968
- v3Target: "v4.4.8 (@mutmutco/cli@4.4.8, tag commit beed56b5ec82 \u2014 last known-good release carrying the repo-index v4-only contract)"
15967
+ mechanism: "npm dist-tag latest -> 4.4.10 and redeploy the Hub Lambda from tag v4.4.10 (187ab08827c9); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15968
+ v3Target: "v4.4.10 (@mutmutco/cli@4.4.10, tag commit 187ab08827c9 \u2014 last known-good release carrying the repo-index v4-only contract)"
15969
15969
  }
15970
15970
  },
15971
15971
  {
@@ -18259,6 +18259,9 @@ function correlateTenantRun(deps, since, titleIncludes) {
18259
18259
  function correlatePublishRun(deps, since, titleIncludes) {
18260
18260
  return correlateRun(deps, { workflow: "tenant-publish.yml", since, mode: "dispatch", titleIncludes });
18261
18261
  }
18262
+ function correlateGetPublishRun(deps, since, product) {
18263
+ return correlateRun(deps, { workflow: "get-publish.yml", since, mode: "dispatch", titleIncludes: [product] });
18264
+ }
18262
18265
  function correlateControlRun(deps, since, titleIncludes) {
18263
18266
  return correlateRun(deps, { workflow: "tenant-control.yml", since, mode: "dispatch", titleIncludes });
18264
18267
  }
@@ -18801,6 +18804,10 @@ function isReleaseShaDeployEnumerationCandidate(row, nonDeployEvents) {
18801
18804
  return true;
18802
18805
  }
18803
18806
 
18807
+ // src/train-apply-deploy.ts
18808
+ var import_node_fs19 = require("node:fs");
18809
+ var import_node_path17 = require("node:path");
18810
+
18804
18811
  // src/vercel-deployments.ts
18805
18812
  var TERMINAL = /* @__PURE__ */ new Set(["success", "failure", "error", "inactive"]);
18806
18813
  function isTerminalVercelState(state) {
@@ -19009,6 +19016,189 @@ function appendPublishDispatch(deploy, publish) {
19009
19016
  deployStatus: deploy.deployStatus === "failure" || publish.deployStatus === "failure" ? "failure" : deploy.deployStatus === "pending" || publish.deployStatus === "pending" ? "pending" : "success"
19010
19017
  };
19011
19018
  }
19019
+ var HUB_SERVED_PRODUCT = "mmi";
19020
+ function getPublishDispatchCommand(repo, product = HUB_SERVED_PRODUCT) {
19021
+ return `gh workflow run get-publish.yml --repo ${repo} --ref main -f product=${product}`;
19022
+ }
19023
+ var HUB_GET_SLUG = "mmi-get";
19024
+ var HUB_GET_STAGE = "main";
19025
+ function mmiGetRedeployCommand(repo) {
19026
+ return `gh workflow run tenant-deploy.yml --repo ${repo} --ref main -f slug=${HUB_GET_SLUG} -f repo=${repo} -f ref=${HUB_GET_STAGE} -f stage=${HUB_GET_STAGE}`;
19027
+ }
19028
+ function readLocalLauncherVersion(root = process.cwd()) {
19029
+ try {
19030
+ const raw = (0, import_node_fs19.readFileSync)((0, import_node_path17.join)(root, "installer", "launcher", "package.json"), "utf8");
19031
+ const version = JSON.parse(raw).version;
19032
+ return typeof version === "string" && version.trim() ? version.trim() : null;
19033
+ } catch {
19034
+ return null;
19035
+ }
19036
+ }
19037
+ function readServedChannelUrl(product = HUB_SERVED_PRODUCT, root = process.cwd()) {
19038
+ try {
19039
+ const raw = (0, import_node_fs19.readFileSync)((0, import_node_path17.join)(root, "config", `product.${product}.json`), "utf8");
19040
+ const host = JSON.parse(raw).host;
19041
+ if (typeof host !== "string" || !host.trim()) return null;
19042
+ return `${host.trim().replace(/\/+$/, "")}/release/version`;
19043
+ } catch {
19044
+ return null;
19045
+ }
19046
+ }
19047
+ async function fetchServedChannelVersion(url, fetchImpl = fetch) {
19048
+ try {
19049
+ const res = await fetchImpl(url, { method: "GET", redirect: "follow", signal: AbortSignal.timeout(15e3) });
19050
+ if (res.status !== 200) return { ok: false, status: res.status };
19051
+ const body = (await res.text()).slice(0, 4096);
19052
+ const version = JSON.parse(body).version;
19053
+ if (typeof version !== "string" || !version.trim()) return { ok: false, status: res.status, error: "pointer carried no version" };
19054
+ return { ok: true, version: version.trim(), status: res.status };
19055
+ } catch (e) {
19056
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
19057
+ }
19058
+ }
19059
+ async function appendMmiGetRedeploy(deps, input, dispatch) {
19060
+ if (!isHubControlRepo(input.repo)) return dispatch;
19061
+ const remedy = mmiGetRedeployCommand(input.repo);
19062
+ const withNote = (note, deployStatus) => ({
19063
+ ...dispatch,
19064
+ note: `${dispatch.note}; ${note}`,
19065
+ ...deployStatus ? { deployStatus } : {}
19066
+ });
19067
+ if (input.publishStatus !== "success") {
19068
+ return withNote(`mmi-get box NOT redeployed (publish did not succeed) \u2014 after the publish is green, run \`${remedy}\``);
19069
+ }
19070
+ const since = (deps.now ?? Date.now)();
19071
+ try {
19072
+ await deps.run("gh", [
19073
+ "workflow",
19074
+ "run",
19075
+ "tenant-deploy.yml",
19076
+ "--repo",
19077
+ input.repo,
19078
+ "--ref",
19079
+ "main",
19080
+ "-f",
19081
+ `slug=${HUB_GET_SLUG}`,
19082
+ "-f",
19083
+ `repo=${input.repo}`,
19084
+ "-f",
19085
+ `ref=${HUB_GET_STAGE}`,
19086
+ "-f",
19087
+ `stage=${HUB_GET_STAGE}`
19088
+ ]);
19089
+ } catch (e) {
19090
+ const msg = e instanceof Error ? e.message : String(e);
19091
+ return withNote(`tenant-deploy dispatch FAILED: ${msg}. The release itself landed, but the MMI-GET BOX WAS NOT REDEPLOYED \u2014 recover with \`${remedy}\``, "failure");
19092
+ }
19093
+ const correlated = await correlateTenantRun(deps, since, [HUB_GET_SLUG, HUB_GET_STAGE]);
19094
+ if (correlated.read === "failed" || correlated.read === "absent") {
19095
+ const why = correlated.read === "failed" ? `its run could not be READ: ${correlated.error}` : "no matching run appeared";
19096
+ return withNote(`tenant-deploy dispatched (slug=${HUB_GET_SLUG}, stage=${HUB_GET_STAGE}) but ${why}. Check the Actions tab; if it never ran, recover with \`${remedy}\``, "failure");
19097
+ }
19098
+ const { runId, runUrl } = correlated;
19099
+ const runRow = { workflow: "tenant-deploy.yml", ...runId ? { runId } : {}, ...runUrl ? { runUrl } : {} };
19100
+ if (!input.watch) {
19101
+ return {
19102
+ ...dispatch,
19103
+ note: `${dispatch.note}; dispatched tenant-deploy.yml (slug=${HUB_GET_SLUG}, ref=${HUB_GET_STAGE}, stage=${HUB_GET_STAGE}) \u2014 NOT watched, so the mmi-get box is not yet proven redeployed`,
19104
+ workflowRuns: [...dispatch.workflowRuns ?? [], { ...runRow, conclusion: "pending" }]
19105
+ };
19106
+ }
19107
+ const runStatus = await watchTenantRun(deps, runId, input.repo);
19108
+ if (runStatus !== "success") {
19109
+ return {
19110
+ ...dispatch,
19111
+ note: `${dispatch.note}; tenant-deploy.yml ${runStatus} \u2014 the MMI-GET BOX WAS NOT REDEPLOYED; recover with \`${remedy}\``,
19112
+ workflowRuns: [...dispatch.workflowRuns ?? [], { ...runRow, conclusion: runStatus }],
19113
+ deployStatus: "failure"
19114
+ };
19115
+ }
19116
+ return {
19117
+ ...dispatch,
19118
+ note: `${dispatch.note}; mmi-get box redeployed (tenant-deploy.yml slug=${HUB_GET_SLUG} stage=${HUB_GET_STAGE})`,
19119
+ workflowRuns: [...dispatch.workflowRuns ?? [], { ...runRow, conclusion: "success" }]
19120
+ };
19121
+ }
19122
+ async function appendServedChannelPublish(deps, input, dispatch) {
19123
+ if (!isHubControlRepo(input.repo)) return dispatch;
19124
+ const channelUrl = input.channelUrl ?? readServedChannelUrl();
19125
+ const remedy = getPublishDispatchCommand(input.repo);
19126
+ const withNote = (note, deployStatus) => ({
19127
+ ...dispatch,
19128
+ note: `${dispatch.note}; ${note}`,
19129
+ ...deployStatus ? { deployStatus } : {}
19130
+ });
19131
+ if (input.publishStatus !== "success") {
19132
+ return withNote(`served channel NOT moved (publish did not succeed) \u2014 after the publish is green, run \`${remedy}\``);
19133
+ }
19134
+ const since = (deps.now ?? Date.now)();
19135
+ try {
19136
+ await deps.run("gh", ["workflow", "run", "get-publish.yml", "--repo", input.repo, "--ref", "main", "-f", `product=${HUB_SERVED_PRODUCT}`]);
19137
+ } catch (e) {
19138
+ const msg = e instanceof Error ? e.message : String(e);
19139
+ return withNote(`get-publish dispatch FAILED: ${msg}. The release itself landed, but the SERVED CHANNEL IS UNCHANGED \u2014 recover with \`${remedy}\``, "failure");
19140
+ }
19141
+ const correlated = await correlateGetPublishRun(deps, since, HUB_SERVED_PRODUCT);
19142
+ if (correlated.read === "failed") {
19143
+ return withNote(`get-publish dispatched but its run could not be READ: ${correlated.error}. Check the Actions tab; if it never ran, recover with \`${remedy}\``, "failure");
19144
+ }
19145
+ const { runId, runUrl } = correlated;
19146
+ const runRow = { workflow: "get-publish.yml", ...runId ? { runId } : {}, ...runUrl ? { runUrl } : {} };
19147
+ if (!input.watch) {
19148
+ return {
19149
+ ...dispatch,
19150
+ note: `${dispatch.note}; dispatched get-publish.yml (product=${HUB_SERVED_PRODUCT}) \u2014 NOT watched, so the served channel is not yet proven moved`,
19151
+ workflowRuns: [...dispatch.workflowRuns ?? [], { ...runRow, conclusion: "pending" }]
19152
+ };
19153
+ }
19154
+ const runStatus = await watchTenantRun(deps, runId, input.repo);
19155
+ if (runStatus !== "success") {
19156
+ return {
19157
+ ...dispatch,
19158
+ note: `${dispatch.note}; get-publish.yml ${runStatus} \u2014 the SERVED CHANNEL IS UNCHANGED; recover with \`${remedy}\``,
19159
+ workflowRuns: [...dispatch.workflowRuns ?? [], { ...runRow, conclusion: runStatus }],
19160
+ deployStatus: "failure"
19161
+ };
19162
+ }
19163
+ const expected = (deps.readLauncherVersion ?? readLocalLauncherVersion)();
19164
+ const runs = [...dispatch.workflowRuns ?? [], { ...runRow, conclusion: "success" }];
19165
+ if (!channelUrl) {
19166
+ return {
19167
+ ...dispatch,
19168
+ note: `${dispatch.note}; get-publish.yml succeeded but the channel URL could not be read from the product config \u2014 the move is unverified`,
19169
+ workflowRuns: runs
19170
+ };
19171
+ }
19172
+ const probe = await (deps.readServedChannelVersion ?? fetchServedChannelVersion)(channelUrl);
19173
+ if (!probe.ok) {
19174
+ const why = probe.error ?? `HTTP ${probe.status ?? "unknown"}`;
19175
+ return {
19176
+ ...dispatch,
19177
+ note: `${dispatch.note}; get-publish.yml succeeded but the served channel version could NOT be read at ${channelUrl} (${why}) \u2014 the move is unverified`,
19178
+ workflowRuns: runs
19179
+ };
19180
+ }
19181
+ if (expected && probe.version !== expected) {
19182
+ return {
19183
+ ...dispatch,
19184
+ note: `${dispatch.note}; get-publish.yml succeeded but the served channel still reports ${probe.version} (expected launcher ${expected}) \u2014 THE CHANNEL DID NOT MOVE; recover with \`${remedy}\``,
19185
+ workflowRuns: runs,
19186
+ deployStatus: "failure"
19187
+ };
19188
+ }
19189
+ if (!expected) {
19190
+ return {
19191
+ ...dispatch,
19192
+ note: `${dispatch.note}; get-publish.yml succeeded and ${channelUrl} reports ${probe.version}, but the local launcher version could not be read \u2014 nothing was compared, so the move is unverified`,
19193
+ workflowRuns: runs
19194
+ };
19195
+ }
19196
+ return {
19197
+ ...dispatch,
19198
+ note: `${dispatch.note}; served channel moved \u2014 ${channelUrl} now reports ${probe.version}`,
19199
+ workflowRuns: runs
19200
+ };
19201
+ }
19012
19202
  var JERV_GATEWAY_RUN_URL_NOTE = "no run URL emitted \u2014 host health leg runs outside GitHub Actions";
19013
19203
  async function appendJervGatewayReleaseDeploy(deps, repo, tag, tagSha, dispatch) {
19014
19204
  if (!isJervHubRepo(repo)) return dispatch;
@@ -19523,9 +19713,9 @@ function renderTenantControl(r) {
19523
19713
  }
19524
19714
 
19525
19715
  // src/train-apply-phases.ts
19526
- var import_node_fs23 = require("node:fs");
19716
+ var import_node_fs24 = require("node:fs");
19527
19717
  var import_promises3 = require("node:fs/promises");
19528
- var import_node_path23 = require("node:path");
19718
+ var import_node_path24 = require("node:path");
19529
19719
 
19530
19720
  // src/actions-billing-preflight.ts
19531
19721
  var CANARY_WORKFLOW = "actions-job-start-canary.yml";
@@ -19778,14 +19968,14 @@ Resume it: ${resumeCommand}
19778
19968
 
19779
19969
  // src/train-doctor.ts
19780
19970
  var import_node_child_process9 = require("node:child_process");
19781
- var import_node_fs22 = require("node:fs");
19971
+ var import_node_fs23 = require("node:fs");
19782
19972
  var import_node_os13 = require("node:os");
19783
- var import_node_path22 = require("node:path");
19973
+ var import_node_path23 = require("node:path");
19784
19974
 
19785
19975
  // src/doctor-io.ts
19786
- var import_node_fs19 = require("node:fs");
19976
+ var import_node_fs20 = require("node:fs");
19787
19977
  var import_node_os10 = require("node:os");
19788
- var import_node_path17 = require("node:path");
19978
+ var import_node_path18 = require("node:path");
19789
19979
  var import_node_child_process7 = require("node:child_process");
19790
19980
  var import_node_util4 = require("node:util");
19791
19981
 
@@ -19798,8 +19988,8 @@ function nodeDiscardSinkPath(platform2 = process.platform) {
19798
19988
  var execFileP2 = (0, import_node_util4.promisify)(import_node_child_process7.execFile);
19799
19989
  function execFileCapture(file, args, opts = {}) {
19800
19990
  const sink = nodeDiscardSinkPath();
19801
- const inFd = (0, import_node_fs19.openSync)(sink, "r");
19802
- const errFd = (0, import_node_fs19.openSync)(sink, "w");
19991
+ const inFd = (0, import_node_fs20.openSync)(sink, "r");
19992
+ const errFd = (0, import_node_fs20.openSync)(sink, "w");
19803
19993
  try {
19804
19994
  return (0, import_node_child_process7.execFileSync)(file, args, {
19805
19995
  ...opts,
@@ -19807,17 +19997,17 @@ function execFileCapture(file, args, opts = {}) {
19807
19997
  stdio: [inFd, "pipe", errFd]
19808
19998
  });
19809
19999
  } finally {
19810
- (0, import_node_fs19.closeSync)(inFd);
19811
- (0, import_node_fs19.closeSync)(errFd);
20000
+ (0, import_node_fs20.closeSync)(inFd);
20001
+ (0, import_node_fs20.closeSync)(errFd);
19812
20002
  }
19813
20003
  }
19814
20004
  var MMI_PLUGIN_ID2 = "mmi@mutmutco";
19815
20005
  function stagedClaudePluginVersion() {
19816
20006
  try {
19817
- const stagingRoot = (0, import_node_path17.join)((0, import_node_os10.homedir)(), ".claude", "plugins", "staging");
19818
- const record = JSON.parse((0, import_node_fs19.readFileSync)((0, import_node_path17.join)(stagingRoot, "launch-target.json"), "utf8"));
20007
+ const stagingRoot = (0, import_node_path18.join)((0, import_node_os10.homedir)(), ".claude", "plugins", "staging");
20008
+ const record = JSON.parse((0, import_node_fs20.readFileSync)((0, import_node_path18.join)(stagingRoot, "launch-target.json"), "utf8"));
19819
20009
  if (typeof record.target !== "string" || typeof record.incoming !== "string") return void 0;
19820
- const manifest = JSON.parse((0, import_node_fs19.readFileSync)((0, import_node_path17.join)(record.incoming, ".claude-plugin", "plugin.json"), "utf8"));
20010
+ const manifest = JSON.parse((0, import_node_fs20.readFileSync)((0, import_node_path18.join)(record.incoming, ".claude-plugin", "plugin.json"), "utf8"));
19821
20011
  if (typeof manifest.version !== "string") return void 0;
19822
20012
  return manifest.version === record.target ? record.target : void 0;
19823
20013
  } catch {
@@ -19827,7 +20017,7 @@ function stagedClaudePluginVersion() {
19827
20017
  function installedClaudePluginVersion() {
19828
20018
  try {
19829
20019
  const file = JSON.parse(
19830
- (0, import_node_fs19.readFileSync)((0, import_node_path17.join)((0, import_node_os10.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
20020
+ (0, import_node_fs20.readFileSync)((0, import_node_path18.join)((0, import_node_os10.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
19831
20021
  );
19832
20022
  const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
19833
20023
  if (versions.length === 0) return void 0;
@@ -19838,7 +20028,7 @@ function installedClaudePluginVersion() {
19838
20028
  }
19839
20029
  function manifestVersion(path2) {
19840
20030
  try {
19841
- const manifest = JSON.parse((0, import_node_fs19.readFileSync)(path2, "utf8"));
20031
+ const manifest = JSON.parse((0, import_node_fs20.readFileSync)(path2, "utf8"));
19842
20032
  return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : void 0;
19843
20033
  } catch {
19844
20034
  return void 0;
@@ -19847,12 +20037,12 @@ function manifestVersion(path2) {
19847
20037
  function readHermesPluginEvidence(env = process.env) {
19848
20038
  const host = hermesConfigRoot(env);
19849
20039
  const root = hermesPluginRoot(env);
19850
- const installRecordPresent = (0, import_node_fs19.existsSync)(root);
19851
- const manifestPath = (0, import_node_path17.join)(root, "plugin.yaml");
20040
+ const installRecordPresent = (0, import_node_fs20.existsSync)(root);
20041
+ const manifestPath = (0, import_node_path18.join)(root, "plugin.yaml");
19852
20042
  let installedVersion;
19853
20043
  let manifest = "missing";
19854
20044
  try {
19855
- const text = (0, import_node_fs19.readFileSync)(manifestPath, "utf8");
20045
+ const text = (0, import_node_fs20.readFileSync)(manifestPath, "utf8");
19856
20046
  let version;
19857
20047
  try {
19858
20048
  const parsed = JSON.parse(text).version;
@@ -19866,25 +20056,25 @@ function readHermesPluginEvidence(env = process.env) {
19866
20056
  if (version) {
19867
20057
  installedVersion = version;
19868
20058
  manifest = "valid";
19869
- } else if ((0, import_node_fs19.existsSync)(manifestPath)) manifest = "invalid";
20059
+ } else if ((0, import_node_fs20.existsSync)(manifestPath)) manifest = "invalid";
19870
20060
  } catch {
19871
- if ((0, import_node_fs19.existsSync)(manifestPath)) manifest = "invalid";
20061
+ if ((0, import_node_fs20.existsSync)(manifestPath)) manifest = "invalid";
19872
20062
  }
19873
20063
  let skills = false;
19874
20064
  try {
19875
- skills = (0, import_node_fs19.existsSync)((0, import_node_path17.join)(root, "skills")) && (0, import_node_fs19.statSync)((0, import_node_path17.join)(root, "skills")).isDirectory();
20065
+ skills = (0, import_node_fs20.existsSync)((0, import_node_path18.join)(root, "skills")) && (0, import_node_fs20.statSync)((0, import_node_path18.join)(root, "skills")).isDirectory();
19876
20066
  } catch {
19877
20067
  }
19878
20068
  let provisioned = false;
19879
- const provisionedRoot = (0, import_node_path17.join)(host, "skills", "mmi");
20069
+ const provisionedRoot = (0, import_node_path18.join)(host, "skills", "mmi");
19880
20070
  try {
19881
- provisioned = (0, import_node_fs19.existsSync)(provisionedRoot) && (0, import_node_fs19.statSync)(provisionedRoot).isDirectory();
20071
+ provisioned = (0, import_node_fs20.existsSync)(provisionedRoot) && (0, import_node_fs20.statSync)(provisionedRoot).isDirectory();
19882
20072
  } catch {
19883
20073
  }
19884
- const treeOk = (0, import_node_fs19.existsSync)((0, import_node_path17.join)(root, "__init__.py")) && skills && manifest === "valid";
20074
+ const treeOk = (0, import_node_fs20.existsSync)((0, import_node_path18.join)(root, "__init__.py")) && skills && manifest === "valid";
19885
20075
  const receipt = treeOk && !provisioned ? `hermes-skills-unprovisioned:${provisionedRoot}` : void 0;
19886
20076
  return {
19887
- hostPresent: (0, import_node_fs19.existsSync)(host),
20077
+ hostPresent: (0, import_node_fs20.existsSync)(host),
19888
20078
  installRecordPresent,
19889
20079
  manifest,
19890
20080
  payloadPresent: treeOk && provisioned,
@@ -19896,7 +20086,7 @@ function installedSurfacePluginVersion(surface) {
19896
20086
  const token = surfaceToken(surface);
19897
20087
  if (token === "hermes") return readHermesPluginEvidence().installedVersion;
19898
20088
  if (token === "cursor") {
19899
- return manifestVersion((0, import_node_path17.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
20089
+ return manifestVersion((0, import_node_path18.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
19900
20090
  }
19901
20091
  if (token === "jervcode") {
19902
20092
  return installedJervCodePackageVersion();
@@ -19933,13 +20123,13 @@ function worktreeRootSync() {
19933
20123
  }
19934
20124
  var gitignorePath = () => {
19935
20125
  const root = worktreeRootSync();
19936
- return root === null ? null : (0, import_node_path17.join)(root, ".gitignore");
20126
+ return root === null ? null : (0, import_node_path18.join)(root, ".gitignore");
19937
20127
  };
19938
20128
  function readGitignore() {
19939
20129
  const path2 = gitignorePath();
19940
20130
  if (path2 === null) return null;
19941
20131
  try {
19942
- return (0, import_node_fs19.readFileSync)(path2, "utf8");
20132
+ return (0, import_node_fs20.readFileSync)(path2, "utf8");
19943
20133
  } catch {
19944
20134
  return null;
19945
20135
  }
@@ -19948,14 +20138,14 @@ function writeGitignore(content) {
19948
20138
  const path2 = gitignorePath();
19949
20139
  if (path2 === null) return false;
19950
20140
  try {
19951
- (0, import_node_fs19.writeFileSync)(path2, content, "utf8");
20141
+ (0, import_node_fs20.writeFileSync)(path2, content, "utf8");
19952
20142
  return true;
19953
20143
  } catch {
19954
20144
  return false;
19955
20145
  }
19956
20146
  }
19957
20147
  function lineEndingState(root) {
19958
- const attributesPresent = (0, import_node_fs19.existsSync)((0, import_node_path17.join)(root, ".gitattributes"));
20148
+ const attributesPresent = (0, import_node_fs20.existsSync)((0, import_node_path18.join)(root, ".gitattributes"));
19959
20149
  try {
19960
20150
  const output = execFileCapture("git", ["-C", root, "ls-files", "--eol", "--", ":(glob)**/*.sh"], {
19961
20151
  windowsHide: true
@@ -19998,18 +20188,18 @@ function compatHolds(range, version) {
19998
20188
  }
19999
20189
 
20000
20190
  // src/claude-binary-doctor.ts
20001
- var import_node_fs20 = require("node:fs");
20191
+ var import_node_fs21 = require("node:fs");
20002
20192
  var import_node_os12 = require("node:os");
20003
- var import_node_path19 = require("node:path");
20193
+ var import_node_path20 = require("node:path");
20004
20194
 
20005
20195
  // src/jerv-cli-spawn.ts
20006
20196
  var import_node_os11 = require("node:os");
20007
- var import_node_path18 = require("node:path");
20197
+ var import_node_path19 = require("node:path");
20008
20198
  init_cli_shared();
20009
- var JERV_CLI_ENTRY = (0, import_node_path18.join)("node_modules", "@jervaise", "jerv-cli", "dist", "index.cjs");
20199
+ var JERV_CLI_ENTRY = (0, import_node_path19.join)("node_modules", "@jervaise", "jerv-cli", "dist", "index.cjs");
20010
20200
  function pathEnvEntries(pathEnv, platform2 = process.platform) {
20011
20201
  if (platform2 !== "win32") {
20012
- return pathEnv.split(import_node_path18.delimiter).map((e) => e.trim()).filter(Boolean);
20202
+ return pathEnv.split(import_node_path19.delimiter).map((e) => e.trim()).filter(Boolean);
20013
20203
  }
20014
20204
  if (pathEnv.includes(";")) {
20015
20205
  return pathEnv.split(";").map((e) => e.trim()).filter(Boolean);
@@ -20042,10 +20232,10 @@ function jervCliCandidateDirs(env = process.env, home = (0, import_node_os11.hom
20042
20232
  push(normalizeSpawnPathEntry(entry, platform2));
20043
20233
  }
20044
20234
  if (platform2 === "win32") {
20045
- if (env.APPDATA) push((0, import_node_path18.join)(env.APPDATA, "npm"));
20046
- if (env.LOCALAPPDATA) push((0, import_node_path18.join)(env.LOCALAPPDATA, "npm"));
20235
+ if (env.APPDATA) push((0, import_node_path19.join)(env.APPDATA, "npm"));
20236
+ if (env.LOCALAPPDATA) push((0, import_node_path19.join)(env.LOCALAPPDATA, "npm"));
20047
20237
  } else {
20048
- push((0, import_node_path18.join)(home, ".local", "bin"));
20238
+ push((0, import_node_path19.join)(home, ".local", "bin"));
20049
20239
  }
20050
20240
  return out;
20051
20241
  }
@@ -20098,26 +20288,26 @@ function globalNodeModulesRoots(host) {
20098
20288
  out.push(dir);
20099
20289
  };
20100
20290
  const prefix = env.npm_config_prefix?.trim();
20101
- if (prefix) push(platform2 === "win32" ? (0, import_node_path19.join)(prefix, "node_modules") : (0, import_node_path19.join)(prefix, "lib", "node_modules"));
20291
+ if (prefix) push(platform2 === "win32" ? (0, import_node_path20.join)(prefix, "node_modules") : (0, import_node_path20.join)(prefix, "lib", "node_modules"));
20102
20292
  for (const dir of jervCliCandidateDirs(env, host.home ?? (0, import_node_os12.homedir)(), platform2)) {
20103
- push((0, import_node_path19.join)(dir, "node_modules"));
20104
- push((0, import_node_path19.join)((0, import_node_path19.dirname)(dir), "lib", "node_modules"));
20293
+ push((0, import_node_path20.join)(dir, "node_modules"));
20294
+ push((0, import_node_path20.join)((0, import_node_path20.dirname)(dir), "lib", "node_modules"));
20105
20295
  }
20106
20296
  return out;
20107
20297
  }
20108
20298
  function readHead(path2) {
20109
20299
  let fd;
20110
20300
  try {
20111
- fd = (0, import_node_fs20.openSync)(path2, "r");
20301
+ fd = (0, import_node_fs21.openSync)(path2, "r");
20112
20302
  const buffer = new Uint8Array(MAGIC_HEAD_BYTES);
20113
- const read = (0, import_node_fs20.readSync)(fd, buffer, 0, MAGIC_HEAD_BYTES, 0);
20303
+ const read = (0, import_node_fs21.readSync)(fd, buffer, 0, MAGIC_HEAD_BYTES, 0);
20114
20304
  return buffer.subarray(0, read);
20115
20305
  } catch {
20116
20306
  return void 0;
20117
20307
  } finally {
20118
20308
  if (fd !== void 0) {
20119
20309
  try {
20120
- (0, import_node_fs20.closeSync)(fd);
20310
+ (0, import_node_fs21.closeSync)(fd);
20121
20311
  } catch {
20122
20312
  }
20123
20313
  }
@@ -20125,7 +20315,7 @@ function readHead(path2) {
20125
20315
  }
20126
20316
  function fileBytes(path2) {
20127
20317
  try {
20128
- return (0, import_node_fs20.statSync)(path2).size;
20318
+ return (0, import_node_fs21.statSync)(path2).size;
20129
20319
  } catch {
20130
20320
  return void 0;
20131
20321
  }
@@ -20139,17 +20329,17 @@ function readClaudeBinaryState(host = {}) {
20139
20329
  const arch = host.arch ?? process.arch;
20140
20330
  const magic = EXECUTABLE_MAGIC[platform2];
20141
20331
  if (!magic) return void 0;
20142
- const packageRoot = globalNodeModulesRoots(host).map((root) => (0, import_node_path19.join)(root, ...PACKAGE.split("/"))).find((dir) => (0, import_node_fs20.existsSync)((0, import_node_path19.join)(dir, "package.json")));
20332
+ const packageRoot = globalNodeModulesRoots(host).map((root) => (0, import_node_path20.join)(root, ...PACKAGE.split("/"))).find((dir) => (0, import_node_fs21.existsSync)((0, import_node_path20.join)(dir, "package.json")));
20143
20333
  if (!packageRoot) return void 0;
20144
20334
  const keys = platformPackageKeys(platform2, arch);
20145
20335
  const fallbackPackage = `${PACKAGE}-${keys[0]}`;
20146
20336
  let manifest;
20147
20337
  try {
20148
- manifest = JSON.parse((0, import_node_fs20.readFileSync)((0, import_node_path19.join)(packageRoot, "package.json"), "utf8"));
20338
+ manifest = JSON.parse((0, import_node_fs21.readFileSync)((0, import_node_path20.join)(packageRoot, "package.json"), "utf8"));
20149
20339
  } catch (e) {
20150
20340
  return {
20151
20341
  state: "unreadable",
20152
- binPath: (0, import_node_path19.join)(packageRoot, "package.json"),
20342
+ binPath: (0, import_node_path20.join)(packageRoot, "package.json"),
20153
20343
  expectedMagic: magic.name,
20154
20344
  platformPackage: fallbackPackage,
20155
20345
  error: `package.json could not be read \u2014 ${e.message}`
@@ -20166,17 +20356,17 @@ function readClaudeBinaryState(host = {}) {
20166
20356
  error: "package.json declares no `bin` entry \u2014 the executed file cannot be resolved"
20167
20357
  };
20168
20358
  }
20169
- const binPath = (0, import_node_path19.join)(packageRoot, binRelative);
20170
- const binName = (0, import_node_path19.basename)(binRelative);
20359
+ const binPath = (0, import_node_path20.join)(packageRoot, binRelative);
20360
+ const binName = (0, import_node_path20.basename)(binRelative);
20171
20361
  const optional = manifest.optionalDependencies;
20172
20362
  const publishes = (name) => typeof optional === "object" && optional !== null && Object.prototype.hasOwnProperty.call(optional, name);
20173
20363
  const published = keys.map((key) => `${PACKAGE}-${key}`).filter(publishes);
20174
20364
  if (published.length === 0) return void 0;
20175
20365
  const binIn = (name) => [
20176
- (0, import_node_path19.join)(packageRoot, "node_modules", ...name.split("/"), binName),
20177
- (0, import_node_path19.join)((0, import_node_path19.dirname)((0, import_node_path19.dirname)(packageRoot)), ...name.split("/"), binName)
20366
+ (0, import_node_path20.join)(packageRoot, "node_modules", ...name.split("/"), binName),
20367
+ (0, import_node_path20.join)((0, import_node_path20.dirname)((0, import_node_path20.dirname)(packageRoot)), ...name.split("/"), binName)
20178
20368
  ];
20179
- const found = published.map((name) => ({ name, path: binIn(name).find((file) => (0, import_node_fs20.existsSync)(file)) })).find((c) => c.path);
20369
+ const found = published.map((name) => ({ name, path: binIn(name).find((file) => (0, import_node_fs21.existsSync)(file)) })).find((c) => c.path);
20180
20370
  const platformPackage = found?.name ?? published[0];
20181
20371
  let source;
20182
20372
  let sourceProblem;
@@ -20187,7 +20377,7 @@ function readClaudeBinaryState(host = {}) {
20187
20377
  } else {
20188
20378
  source = { path: found.path, bytes: fileBytes(found.path) ?? 0 };
20189
20379
  }
20190
- if (!(0, import_node_fs20.existsSync)(binPath)) {
20380
+ if (!(0, import_node_fs21.existsSync)(binPath)) {
20191
20381
  return { state: "missing", binPath, expectedMagic: magic.name, platformPackage, ...source ? { source } : {}, ...sourceProblem ? { sourceProblem } : {} };
20192
20382
  }
20193
20383
  const head = readHead(binPath);
@@ -20230,9 +20420,9 @@ function healClaudeBinary(host = {}, onStep) {
20230
20420
  const platform2 = host.platform ?? process.platform;
20231
20421
  const aside = `${probe.binPath}.stub-${Date.now()}`;
20232
20422
  let renamed = false;
20233
- if ((0, import_node_fs20.existsSync)(probe.binPath)) {
20423
+ if ((0, import_node_fs21.existsSync)(probe.binPath)) {
20234
20424
  try {
20235
- (0, import_node_fs20.renameSync)(probe.binPath, aside);
20425
+ (0, import_node_fs21.renameSync)(probe.binPath, aside);
20236
20426
  renamed = true;
20237
20427
  onStep?.(`renamed the stub aside: ${aside}`);
20238
20428
  } catch (e) {
@@ -20241,12 +20431,12 @@ function healClaudeBinary(host = {}, onStep) {
20241
20431
  }
20242
20432
  try {
20243
20433
  onStep?.(`copying ${probe.source.path} \u2192 ${probe.binPath} (${(probe.source.bytes / 1e6).toFixed(0)} MB)`);
20244
- (0, import_node_fs20.copyFileSync)(probe.source.path, probe.binPath);
20245
- if (platform2 !== "win32") (0, import_node_fs20.chmodSync)(probe.binPath, 493);
20434
+ (0, import_node_fs21.copyFileSync)(probe.source.path, probe.binPath);
20435
+ if (platform2 !== "win32") (0, import_node_fs21.chmodSync)(probe.binPath, 493);
20246
20436
  } catch (e) {
20247
20437
  if (renamed) {
20248
20438
  try {
20249
- (0, import_node_fs20.renameSync)(aside, probe.binPath);
20439
+ (0, import_node_fs21.renameSync)(aside, probe.binPath);
20250
20440
  } catch {
20251
20441
  return { ok: false, detail: `copy failed (${e.message}) and the stub could not be restored \u2014 the original is at ${aside}` };
20252
20442
  }
@@ -20260,7 +20450,7 @@ function healClaudeBinary(host = {}, onStep) {
20260
20450
  let kept = false;
20261
20451
  if (renamed) {
20262
20452
  try {
20263
- (0, import_node_fs20.rmSync)(aside);
20453
+ (0, import_node_fs21.rmSync)(aside);
20264
20454
  } catch {
20265
20455
  kept = true;
20266
20456
  }
@@ -20619,13 +20809,13 @@ function checkLineEndings(probe) {
20619
20809
  }
20620
20810
 
20621
20811
  // src/release-ledger.ts
20622
- var import_node_fs21 = require("node:fs");
20623
- var import_node_path21 = require("node:path");
20812
+ var import_node_fs22 = require("node:fs");
20813
+ var import_node_path22 = require("node:path");
20624
20814
  var import_node_crypto5 = require("node:crypto");
20625
20815
 
20626
20816
  // src/file-lock.ts
20627
20817
  var import_promises2 = require("node:fs/promises");
20628
- var import_node_path20 = require("node:path");
20818
+ var import_node_path21 = require("node:path");
20629
20819
  var sleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
20630
20820
  var IMMEDIATE_RETRY_BUDGET = 3;
20631
20821
  var FileLockBusyError = class extends Error {
@@ -20710,7 +20900,7 @@ async function releaseFileLock(lockPath, guard) {
20710
20900
  }
20711
20901
  async function withFileLock(lockPath, opts, fn) {
20712
20902
  const resolved = resolveFileLockOpts(opts);
20713
- await (0, import_promises2.mkdir)((0, import_node_path20.dirname)(lockPath), { recursive: true }).catch(() => void 0);
20903
+ await (0, import_promises2.mkdir)((0, import_node_path21.dirname)(lockPath), { recursive: true }).catch(() => void 0);
20714
20904
  const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
20715
20905
  try {
20716
20906
  return await fn();
@@ -20879,37 +21069,37 @@ function resolvePrimaryGitDir(cwd) {
20879
21069
  }
20880
21070
  let common;
20881
21071
  try {
20882
- common = (0, import_node_fs21.readFileSync)((0, import_node_path21.join)(gitDir, "commondir"), "utf8").trim();
21072
+ common = (0, import_node_fs22.readFileSync)((0, import_node_path22.join)(gitDir, "commondir"), "utf8").trim();
20883
21073
  } catch (e) {
20884
21074
  if (e.code !== "ENOENT") {
20885
21075
  throw new ReleaseLedgerError("unreadable", `release ledger: the worktree gitdir's commondir pointer could not be READ (${e instanceof Error ? e.message : String(e)}) \u2014 a failed read is unverified, not primary (#5987/#4713)`);
20886
21076
  }
20887
21077
  }
20888
21078
  if (!common) return gitDir;
20889
- const commonDir = (0, import_node_path21.isAbsolute)(common) ? common : (0, import_node_path21.resolve)(gitDir, common);
21079
+ const commonDir = (0, import_node_path22.isAbsolute)(common) ? common : (0, import_node_path22.resolve)(gitDir, common);
20890
21080
  try {
20891
- (0, import_node_fs21.readFileSync)((0, import_node_path21.join)(commonDir, "HEAD"), "utf8");
21081
+ (0, import_node_fs22.readFileSync)((0, import_node_path22.join)(commonDir, "HEAD"), "utf8");
20892
21082
  } catch (e) {
20893
21083
  throw new ReleaseLedgerError("no-checkout", `release ledger: the worktree's commondir pointer names ${commonDir}, which has no readable HEAD (${e instanceof Error ? e.message : String(e)}) \u2014 the primary checkout may have been removed; refusing to write the ledger into a doomed location`);
20894
21084
  }
20895
21085
  return commonDir;
20896
21086
  }
20897
21087
  function releaseLedgerPath(cwd) {
20898
- return (0, import_node_path21.join)(resolvePrimaryGitDir(cwd), "mmi-runtime", "release", "phases.json");
21088
+ return (0, import_node_path22.join)(resolvePrimaryGitDir(cwd), "mmi-runtime", "release", "phases.json");
20899
21089
  }
20900
21090
  function atomicWriteJson(path2, ledger) {
20901
- (0, import_node_fs21.mkdirSync)((0, import_node_path21.dirname)(path2), { recursive: true });
21091
+ (0, import_node_fs22.mkdirSync)((0, import_node_path22.dirname)(path2), { recursive: true });
20902
21092
  const tmp = `${path2}.tmp-${process.pid}-${(0, import_node_crypto5.randomBytes)(4).toString("hex")}`;
20903
- (0, import_node_fs21.writeFileSync)(tmp, `${JSON.stringify(ledger, null, 2)}
21093
+ (0, import_node_fs22.writeFileSync)(tmp, `${JSON.stringify(ledger, null, 2)}
20904
21094
  `, "utf8");
20905
- (0, import_node_fs21.renameSync)(tmp, path2);
21095
+ (0, import_node_fs22.renameSync)(tmp, path2);
20906
21096
  }
20907
21097
  async function withReleaseLedger(path2, expected, fn, opts = {}) {
20908
21098
  return withFileLock(`${path2}.lock`, { label: "release ledger", maxWaitMs: opts.maxWaitMs ?? 5e3 }, async () => {
20909
21099
  let current;
20910
21100
  let raw;
20911
21101
  try {
20912
- raw = (0, import_node_fs21.readFileSync)(path2, "utf8");
21102
+ raw = (0, import_node_fs22.readFileSync)(path2, "utf8");
20913
21103
  } catch (e) {
20914
21104
  if (e.code !== "ENOENT") {
20915
21105
  throw new ReleaseLedgerError("unreadable", `release ledger at ${path2} could not be READ (${e instanceof Error ? e.message : String(e)}) \u2014 a failed read is unverified, not absent (#5987)`);
@@ -21543,7 +21733,7 @@ async function clearLedgerFile(path2, anchors, maxWaitMs) {
21543
21733
  return withFileLock(`${path2}.lock`, { label: "release ledger", maxWaitMs }, async () => {
21544
21734
  let raw;
21545
21735
  try {
21546
- raw = (0, import_node_fs21.readFileSync)(path2, "utf8");
21736
+ raw = (0, import_node_fs22.readFileSync)(path2, "utf8");
21547
21737
  } catch (e) {
21548
21738
  if (e.code === "ENOENT") return { cleared: false };
21549
21739
  return { cleared: false, note: `release ledger at ${path2} could not be read for clearing (${e instanceof Error ? e.message : String(e)}) \u2014 left in place` };
@@ -21559,7 +21749,7 @@ async function clearLedgerFile(path2, anchors, maxWaitMs) {
21559
21749
  } catch (e) {
21560
21750
  return { cleared: false, note: `release ledger at ${path2} belongs to ${ledger.tag} @ ${ledger.tagSha.slice(0, 12)} \u2014 not this release; left in place (${e instanceof Error ? e.message : String(e)})` };
21561
21751
  }
21562
- (0, import_node_fs21.unlinkSync)(path2);
21752
+ (0, import_node_fs22.unlinkSync)(path2);
21563
21753
  return { cleared: true, note: `cleared the release ledger at ${path2} for the live-proven aborted ${anchors.tag}` };
21564
21754
  });
21565
21755
  }
@@ -21662,7 +21852,7 @@ async function healStaleAlignmentLeg(deps, cwd, expected) {
21662
21852
  return withFileLock(`${path2}.lock`, { label: "release ledger" }, async () => {
21663
21853
  let prior;
21664
21854
  try {
21665
- prior = parseReleaseLedger((0, import_node_fs21.readFileSync)(path2, "utf8"));
21855
+ prior = parseReleaseLedger((0, import_node_fs22.readFileSync)(path2, "utf8"));
21666
21856
  } catch {
21667
21857
  return void 0;
21668
21858
  }
@@ -21687,7 +21877,7 @@ async function supersedeContentCausedDeployLeg(deps, cwd) {
21687
21877
  return withFileLock(`${path2}.lock`, { label: "release ledger" }, async () => {
21688
21878
  let prior;
21689
21879
  try {
21690
- prior = parseReleaseLedger((0, import_node_fs21.readFileSync)(path2, "utf8"));
21880
+ prior = parseReleaseLedger((0, import_node_fs22.readFileSync)(path2, "utf8"));
21691
21881
  } catch (e) {
21692
21882
  throw new ReleaseLedgerError(
21693
21883
  "unreadable",
@@ -21783,11 +21973,11 @@ async function proveOriginPromotionOrArchived(deps, prior, path2, label, ref) {
21783
21973
  if (!releaseAbsent) {
21784
21974
  throw new Error(`${label}: origin no longer has ${prior.tag} but its GitHub Release still exists \u2014 resolve the mixed external state before closing the ledger; nothing was written (${ref})`);
21785
21975
  }
21786
- const archivePath = (0, import_node_path21.join)((0, import_node_path21.dirname)(path2), `phases.archive-${prior.tag}.json`);
21787
- if ((0, import_node_fs21.existsSync)(archivePath)) {
21976
+ const archivePath = (0, import_node_path22.join)((0, import_node_path22.dirname)(path2), `phases.archive-${prior.tag}.json`);
21977
+ if ((0, import_node_fs22.existsSync)(archivePath)) {
21788
21978
  throw new Error(`${label}: archive already exists at ${archivePath} \u2014 preserving both ledger records; resolve the collision by hand; nothing was written (${ref})`);
21789
21979
  }
21790
- (0, import_node_fs21.renameSync)(path2, archivePath);
21980
+ (0, import_node_fs22.renameSync)(path2, archivePath);
21791
21981
  return "deleted";
21792
21982
  }
21793
21983
  async function supersedeContentCausedPublishLeg(deps, cwd) {
@@ -21795,7 +21985,7 @@ async function supersedeContentCausedPublishLeg(deps, cwd) {
21795
21985
  return withFileLock(`${path2}.lock`, { label: "release ledger" }, async () => {
21796
21986
  let prior;
21797
21987
  try {
21798
- prior = parseReleaseLedger((0, import_node_fs21.readFileSync)(path2, "utf8"));
21988
+ prior = parseReleaseLedger((0, import_node_fs22.readFileSync)(path2, "utf8"));
21799
21989
  } catch (e) {
21800
21990
  throw new ReleaseLedgerError(
21801
21991
  "unreadable",
@@ -21877,7 +22067,7 @@ async function supersedeContentCausedDeployAndPublishLegs(deps, cwd) {
21877
22067
  return withFileLock(`${path2}.lock`, { label: "release ledger" }, async () => {
21878
22068
  let prior;
21879
22069
  try {
21880
- prior = parseReleaseLedger((0, import_node_fs21.readFileSync)(path2, "utf8"));
22070
+ prior = parseReleaseLedger((0, import_node_fs22.readFileSync)(path2, "utf8"));
21881
22071
  } catch (e) {
21882
22072
  throw new ReleaseLedgerError(
21883
22073
  "unreadable",
@@ -21982,7 +22172,7 @@ async function supersedeContentCausedDeployAndPublishLegs(deps, cwd) {
21982
22172
  async function reverifyPriorRunLegs(deps, path2, targetTag) {
21983
22173
  let prior;
21984
22174
  try {
21985
- prior = parseReleaseLedger((0, import_node_fs21.readFileSync)(path2, "utf8"));
22175
+ prior = parseReleaseLedger((0, import_node_fs22.readFileSync)(path2, "utf8"));
21986
22176
  } catch {
21987
22177
  return;
21988
22178
  }
@@ -22002,7 +22192,7 @@ async function beginReleaseLedger(deps, cwd, targetTag) {
22002
22192
  return withFileLock(`${path2}.lock`, { label: "release ledger" }, async () => {
22003
22193
  let raw;
22004
22194
  try {
22005
- raw = (0, import_node_fs21.readFileSync)(path2, "utf8");
22195
+ raw = (0, import_node_fs22.readFileSync)(path2, "utf8");
22006
22196
  } catch (e) {
22007
22197
  if (e.code === "ENOENT") return { ok: true };
22008
22198
  return { ok: false, error: `the release ledger at ${path2} could not be READ (${e instanceof Error ? e.message : String(e)}) \u2014 a failed read is unverified, not absent (#5987/#4713)` };
@@ -22039,15 +22229,15 @@ async function beginReleaseLedger(deps, cwd, targetTag) {
22039
22229
  if (liveSha.toLowerCase() !== prior.tagSha.toLowerCase()) {
22040
22230
  return { ok: false, error: `the completed ${prior.tag} ledger says phases-green but origin reports ${liveSha.slice(0, 12) || "no tag"} \u2260 ${prior.tagSha.slice(0, 12)} \u2014 refusing to archive on unproven state; resolve by hand (#5987)` };
22041
22231
  }
22042
- const archivePath = (0, import_node_path21.join)((0, import_node_path21.dirname)(path2), `phases.archive-${prior.tag}.json`);
22232
+ const archivePath = (0, import_node_path22.join)((0, import_node_path22.dirname)(path2), `phases.archive-${prior.tag}.json`);
22043
22233
  try {
22044
- (0, import_node_fs21.unlinkSync)(archivePath);
22234
+ (0, import_node_fs22.unlinkSync)(archivePath);
22045
22235
  } catch (e) {
22046
22236
  if (e.code !== "ENOENT") {
22047
22237
  return { ok: false, error: `could not clear the stale archive at ${archivePath} (${e instanceof Error ? e.message : String(e)}) \u2014 the ledger for ${prior.tag} stays active (#5987)` };
22048
22238
  }
22049
22239
  }
22050
- (0, import_node_fs21.renameSync)(path2, archivePath);
22240
+ (0, import_node_fs22.renameSync)(path2, archivePath);
22051
22241
  return { ok: true, note: `archived the completed release ledger ${prior.tag} to ${archivePath} (live-proven at ${prior.tagSha.slice(0, 12)})` };
22052
22242
  });
22053
22243
  }
@@ -22148,10 +22338,10 @@ function readOriginWorkflowsDefault(root, ref) {
22148
22338
  }
22149
22339
  var NPM_REGISTRY = "https://registry.npmjs.org";
22150
22340
  async function probeRegistryWhoami(train) {
22151
- const dir = (0, import_node_fs22.mkdtempSync)((0, import_node_path22.join)((0, import_node_os13.tmpdir)(), "mmi-npmrc-"));
22152
- const rc = (0, import_node_path22.join)(dir, "npmrc");
22341
+ const dir = (0, import_node_fs23.mkdtempSync)((0, import_node_path23.join)((0, import_node_os13.tmpdir)(), "mmi-npmrc-"));
22342
+ const rc = (0, import_node_path23.join)(dir, "npmrc");
22153
22343
  try {
22154
- (0, import_node_fs22.writeFileSync)(rc, "//registry.npmjs.org/:_authToken=${NPM_TOKEN}\n");
22344
+ (0, import_node_fs23.writeFileSync)(rc, "//registry.npmjs.org/:_authToken=${NPM_TOKEN}\n");
22155
22345
  await train.runSelf(["secrets", "use", "npm/NPM_TOKEN", "--slug", "_org", "--", "npm", "whoami", "--registry", NPM_REGISTRY, "--userconfig", rc]);
22156
22346
  return { kind: "ok", detail: "the registry answered a login" };
22157
22347
  } catch (e) {
@@ -22162,7 +22352,7 @@ async function probeRegistryWhoami(train) {
22162
22352
  if (/\bE401\b|401 Unauthorized/.test(stderr)) return { kind: "rejected", detail: "npm whoami answered 401 Unauthorized" };
22163
22353
  return { kind: "unverified", detail: first };
22164
22354
  } finally {
22165
- (0, import_node_fs22.rmSync)(dir, { recursive: true, force: true });
22355
+ (0, import_node_fs23.rmSync)(dir, { recursive: true, force: true });
22166
22356
  }
22167
22357
  }
22168
22358
  var REGISTRY_TOKEN_ENV = /\b(NODE_AUTH_TOKEN|NPM_TOKEN)\b/;
@@ -22180,7 +22370,7 @@ var defaultDeps2 = {
22180
22370
  ledgerPath: releaseLedgerPath,
22181
22371
  readLedgerRaw: (path2) => {
22182
22372
  try {
22183
- return (0, import_node_fs22.readFileSync)(path2, "utf8");
22373
+ return (0, import_node_fs23.readFileSync)(path2, "utf8");
22184
22374
  } catch (e) {
22185
22375
  if (e.code === "ENOENT") return void 0;
22186
22376
  throw e;
@@ -22189,7 +22379,7 @@ var defaultDeps2 = {
22189
22379
  healAlignmentLeg: healStaleAlignmentLeg,
22190
22380
  readSurfacesRaw: (cwd) => {
22191
22381
  try {
22192
- return (0, import_node_fs22.readFileSync)((0, import_node_path22.join)(cwd, "surfaces.json"), "utf8");
22382
+ return (0, import_node_fs23.readFileSync)((0, import_node_path23.join)(cwd, "surfaces.json"), "utf8");
22193
22383
  } catch {
22194
22384
  return void 0;
22195
22385
  }
@@ -22594,6 +22784,53 @@ async function runTrainDoctor(input) {
22594
22784
  regenerate the projections (\`node scripts/check-skill-payload.mjs\`) and commit the canonical fix; never hand-edit a projection` });
22595
22785
  }
22596
22786
  }
22787
+ if (isHubControlRepo(repo)) {
22788
+ const verifyDeps = async () => {
22789
+ try {
22790
+ await train.run("node", [`${cwd}/scripts/release-distribution.mjs`, "verify-deps"]);
22791
+ return null;
22792
+ } catch (e) {
22793
+ return message(e);
22794
+ }
22795
+ };
22796
+ const failure = await verifyDeps();
22797
+ if (failure) {
22798
+ const firstDir = /in (cli|updater)\/node_modules/u.exec(failure)?.[1] ?? null;
22799
+ if (!heal || !firstDir) {
22800
+ add({
22801
+ code: "hub-build-deps",
22802
+ severity: "blocker",
22803
+ source: "local",
22804
+ title: `the Hub distribution build dependencies do not resolve${firstDir ? ` in ${firstDir}/node_modules` : ""} \u2014 the release refuses at preflight`,
22805
+ remedy: firstDir ? `run \`mmi-cli devops train doctor --heal\` (installs ${firstDir} from its lockfile), or \`npm --prefix ${firstDir} ci\`` : "run `npm --prefix cli ci` and `npm --prefix updater ci`, then rerun"
22806
+ });
22807
+ } else {
22808
+ const installed = [];
22809
+ let pending = firstDir;
22810
+ let outstanding = failure;
22811
+ let failed = null;
22812
+ while (pending && !installed.includes(pending)) {
22813
+ const dir = pending;
22814
+ try {
22815
+ await train.run("npm", ["--prefix", dir, "ci"]);
22816
+ } catch (e) {
22817
+ failed = { dir, detail: `\`npm --prefix ${dir} ci\` failed: ${message(e)}` };
22818
+ break;
22819
+ }
22820
+ installed.push(dir);
22821
+ outstanding = await verifyDeps();
22822
+ pending = outstanding ? /in (cli|updater)\/node_modules/u.exec(outstanding)?.[1] ?? null : null;
22823
+ }
22824
+ if (failed) {
22825
+ add({ code: "hub-build-deps", severity: "blocker", source: "local", title: failed.detail, remedy: `repair ${failed.dir}/node_modules by hand, then rerun` });
22826
+ } else if (outstanding) {
22827
+ add({ code: "hub-build-deps", severity: "blocker", source: "local", title: `installing ${installed.join(", ")} from ${installed.length === 1 ? "its lockfile" : "their lockfiles"} did not resolve the build dependencies`, remedy: outstanding });
22828
+ } else {
22829
+ add({ code: "hub-build-deps", severity: "healed", source: "local", title: `installed ${installed.join(", ")} from ${installed.length === 1 ? "its lockfile" : "their lockfiles"} \u2014 the Hub distribution build dependencies resolve`, remedy: "nothing \u2014 the release preflight passes" });
22830
+ }
22831
+ }
22832
+ }
22833
+ }
22597
22834
  const branches = TRAIN_BRANCHES.filter((b) => b !== "rc" || track === "full");
22598
22835
  if (heal) {
22599
22836
  const sync = await syncLocalTrainBranches(train.run, { branches, fetch: false });
@@ -22689,34 +22926,34 @@ function formatTrainDoctor(v) {
22689
22926
  // src/train-apply-phases.ts
22690
22927
  var TRAIN_BUMP_INTENTS = ["major", "minor", "patch"];
22691
22928
  function readLocalGateWorkflows() {
22692
- const dir = (0, import_node_path23.join)(".github", "workflows");
22929
+ const dir = (0, import_node_path24.join)(".github", "workflows");
22693
22930
  let names;
22694
22931
  try {
22695
- names = (0, import_node_fs23.readdirSync)(dir);
22932
+ names = (0, import_node_fs24.readdirSync)(dir);
22696
22933
  } catch {
22697
22934
  return null;
22698
22935
  }
22699
22936
  const files = [];
22700
22937
  for (const name of names.filter(isGateWorkflowPath)) {
22701
22938
  try {
22702
- files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs23.readFileSync)((0, import_node_path23.join)(dir, name), "utf8") });
22939
+ files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs24.readFileSync)((0, import_node_path24.join)(dir, name), "utf8") });
22703
22940
  } catch {
22704
22941
  }
22705
22942
  }
22706
22943
  return files;
22707
22944
  }
22708
22945
  function readLocalWorkflows() {
22709
- const dir = (0, import_node_path23.join)(".github", "workflows");
22946
+ const dir = (0, import_node_path24.join)(".github", "workflows");
22710
22947
  let names;
22711
22948
  try {
22712
- names = (0, import_node_fs23.readdirSync)(dir);
22949
+ names = (0, import_node_fs24.readdirSync)(dir);
22713
22950
  } catch {
22714
22951
  return null;
22715
22952
  }
22716
22953
  const files = [];
22717
22954
  for (const name of names.filter((n) => /\.ya?ml$/i.test(n))) {
22718
22955
  try {
22719
- files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs23.readFileSync)((0, import_node_path23.join)(dir, name), "utf8") });
22956
+ files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs24.readFileSync)((0, import_node_path24.join)(dir, name), "utf8") });
22720
22957
  } catch {
22721
22958
  }
22722
22959
  }
@@ -23506,6 +23743,16 @@ ${recovery.note}`), recoveryInput);
23506
23743
  });
23507
23744
  let dispatch = appendPublishDispatch(deployDispatch, publishDispatch);
23508
23745
  if (publishSkipNote) dispatch = { ...dispatch, note: `${dispatch.note}; tenant-publish.yml skipped (${publishSkipNote})` };
23746
+ dispatch = await appendMmiGetRedeploy(
23747
+ deps,
23748
+ { repo: ctx.repo, publishStatus: publishLeg.state === "complete" ? "success" : "failure", watch },
23749
+ dispatch
23750
+ );
23751
+ dispatch = await appendServedChannelPublish(
23752
+ deps,
23753
+ { repo: ctx.repo, publishStatus: publishLeg.state === "complete" ? "success" : "failure", watch },
23754
+ dispatch
23755
+ );
23509
23756
  const announcing = announceShippedRelease(
23510
23757
  deps,
23511
23758
  { repo: ctx.repo, tag, summaryFile: options.announceSummaryFile },
@@ -23562,7 +23809,7 @@ async function activeRcandLedgerCandidate(deps, cwd) {
23562
23809
  }
23563
23810
  let prior;
23564
23811
  try {
23565
- prior = parseReleaseLedger((0, import_node_fs23.readFileSync)(path2, "utf8"));
23812
+ prior = parseReleaseLedger((0, import_node_fs24.readFileSync)(path2, "utf8"));
23566
23813
  } catch {
23567
23814
  return void 0;
23568
23815
  }
@@ -26988,9 +27235,9 @@ function registerBoardCommands(program3) {
26988
27235
  }
26989
27236
 
26990
27237
  // src/bootstrap-commands.ts
26991
- var import_node_fs25 = require("node:fs");
27238
+ var import_node_fs26 = require("node:fs");
26992
27239
  var import_node_os14 = require("node:os");
26993
- var import_node_path25 = require("node:path");
27240
+ var import_node_path26 = require("node:path");
26994
27241
  init_cli_shared();
26995
27242
  init_clean_exit();
26996
27243
 
@@ -27170,8 +27417,8 @@ init_github_client();
27170
27417
  init_cli_shared();
27171
27418
 
27172
27419
  // src/port-registry.ts
27173
- var import_node_fs24 = require("node:fs");
27174
- var import_node_path24 = require("node:path");
27420
+ var import_node_fs25 = require("node:fs");
27421
+ var import_node_path25 = require("node:path");
27175
27422
 
27176
27423
  // ../infra/port-geometry.mjs
27177
27424
  var PORT_BLOCK = 100;
@@ -27185,8 +27432,8 @@ function nextPortBlock(registry2) {
27185
27432
  return [base, base + PORT_SPAN];
27186
27433
  }
27187
27434
  function loadPortRegistry(path2) {
27188
- if (!(0, import_node_fs24.existsSync)(path2)) return {};
27189
- const raw = JSON.parse((0, import_node_fs24.readFileSync)(path2, "utf8"));
27435
+ if (!(0, import_node_fs25.existsSync)(path2)) return {};
27436
+ const raw = JSON.parse((0, import_node_fs25.readFileSync)(path2, "utf8"));
27190
27437
  const out = {};
27191
27438
  for (const [key, value] of Object.entries(raw)) {
27192
27439
  if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
@@ -27200,9 +27447,9 @@ function ensurePortRange(repo, path2) {
27200
27447
  const existing = registry2[repo];
27201
27448
  if (existing) return existing;
27202
27449
  const range = nextPortBlock(registry2);
27203
- const raw = (0, import_node_fs24.existsSync)(path2) ? JSON.parse((0, import_node_fs24.readFileSync)(path2, "utf8")) : {};
27450
+ const raw = (0, import_node_fs25.existsSync)(path2) ? JSON.parse((0, import_node_fs25.readFileSync)(path2, "utf8")) : {};
27204
27451
  raw[repo] = range;
27205
- (0, import_node_fs24.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
27452
+ (0, import_node_fs25.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
27206
27453
  return range;
27207
27454
  }
27208
27455
  function portCursorSeed(registry2) {
@@ -27224,22 +27471,22 @@ function existingPortRange(repo, registry2) {
27224
27471
  return registry2[repo] ?? null;
27225
27472
  }
27226
27473
  function portRangeInfraAt(root, source) {
27227
- const registryPath = (0, import_node_path24.join)(root, "infra", "port-ranges.json");
27228
- const ddbScriptPath = (0, import_node_path24.join)(root, "infra", "port-ddb.mjs");
27229
- if (!(0, import_node_fs24.existsSync)(registryPath) || !(0, import_node_fs24.existsSync)(ddbScriptPath)) return null;
27474
+ const registryPath = (0, import_node_path25.join)(root, "infra", "port-ranges.json");
27475
+ const ddbScriptPath = (0, import_node_path25.join)(root, "infra", "port-ddb.mjs");
27476
+ if (!(0, import_node_fs25.existsSync)(registryPath) || !(0, import_node_fs25.existsSync)(ddbScriptPath)) return null;
27230
27477
  return { root, source, registryPath, ddbScriptPath };
27231
27478
  }
27232
27479
  function resolvePortRangeInfra(cwd, packageDir) {
27233
27480
  const direct = portRangeInfraAt(cwd, "cwd");
27234
27481
  if (direct) return direct;
27235
- for (let dir = cwd; ; dir = (0, import_node_path24.dirname)(dir)) {
27236
- const sibling = portRangeInfraAt((0, import_node_path24.join)(dir, "MMI-Hub"), "sibling-hub");
27482
+ for (let dir = cwd; ; dir = (0, import_node_path25.dirname)(dir)) {
27483
+ const sibling = portRangeInfraAt((0, import_node_path25.join)(dir, "MMI-Hub"), "sibling-hub");
27237
27484
  if (sibling) return sibling;
27238
- const parent = (0, import_node_path24.dirname)(dir);
27485
+ const parent = (0, import_node_path25.dirname)(dir);
27239
27486
  if (parent === dir) break;
27240
27487
  }
27241
27488
  if (packageDir) {
27242
- const pkgRoot = (0, import_node_path24.join)(packageDir, "..", "..");
27489
+ const pkgRoot = (0, import_node_path25.join)(packageDir, "..", "..");
27243
27490
  const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
27244
27491
  if (pkgFrom) return pkgFrom;
27245
27492
  }
@@ -28725,13 +28972,13 @@ function registerBootstrapCommands(program3) {
28725
28972
  deployFactsRead: deployFacts !== null,
28726
28973
  pushAllowlistOwners: verifyOwners,
28727
28974
  pushAllowlistOwnersRead: verifyOwnersRead,
28728
- readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs25.existsSync)(path2) ? (0, import_node_fs25.readFileSync)(path2, "utf8") : null,
28975
+ readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs26.existsSync)(path2) ? (0, import_node_fs26.readFileSync)(path2, "utf8") : null,
28729
28976
  // requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
28730
28977
  // comma-string — accept either so the seeded value verifies regardless of how it was written.
28731
28978
  // #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
28732
28979
  // permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
28733
28980
  // sanction, which is the pre-#3664 behaviour.
28734
- sanctionedAdmins: (0, import_node_fs25.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs25.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
28981
+ sanctionedAdmins: (0, import_node_fs26.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs26.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
28735
28982
  requiredGcpApis: (() => {
28736
28983
  const v = meta?.requiredGcpApis;
28737
28984
  if (Array.isArray(v)) return v;
@@ -28809,14 +29056,14 @@ function registerBootstrapCommands(program3) {
28809
29056
  bootstrap.command("drift").description("#3818: compare every org-owned whole-file seed against MMI-Hub's copy across the registry roster; read-only").option("--repo <owner/repo>", "audit one repo instead of the roster (never a fleet verdict)").option("--json", "machine-readable output").action(async () => {
28810
29057
  const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
28811
29058
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
28812
- if (!(0, import_node_fs25.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
29059
+ if (!(0, import_node_fs26.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
28813
29060
  const seedSource = await resolveHubSeedSource(execGitForSeedSource);
28814
29061
  if (!seedSource.ok) return fail(`bootstrap drift: ${seedSource.reason}`);
28815
- const manifest = loadBootstrapSeeds((0, import_node_fs25.readFileSync)(manifestPath, "utf8"));
29062
+ const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
28816
29063
  const hubContents = /* @__PURE__ */ new Map();
28817
29064
  for (const s of manifest.seeds) {
28818
29065
  if (s.ownership !== "org" || s.source !== "self") continue;
28819
- hubContents.set(s.target, (0, import_node_fs25.existsSync)(s.target) ? (0, import_node_fs25.readFileSync)(s.target, "utf8") : null);
29066
+ hubContents.set(s.target, (0, import_node_fs26.existsSync)(s.target) ? (0, import_node_fs26.readFileSync)(s.target, "utf8") : null);
28820
29067
  }
28821
29068
  let targets;
28822
29069
  let classOf = (_repo) => "deployable";
@@ -28979,10 +29226,10 @@ function registerBootstrapCommands(program3) {
28979
29226
  return;
28980
29227
  }
28981
29228
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
28982
- if (!(0, import_node_fs25.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
29229
+ if (!(0, import_node_fs26.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
28983
29230
  const seedSource = await resolveHubSeedSource(execGitForSeedSource);
28984
29231
  if (!seedSource.ok) return fail(`bootstrap apply: ${seedSource.reason}`);
28985
- const manifest = loadBootstrapSeeds((0, import_node_fs25.readFileSync)(manifestPath, "utf8"));
29232
+ const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
28986
29233
  const baseBranch = o.class === "content" ? "main" : "development";
28987
29234
  const slug = parsedRepo.slug;
28988
29235
  const onlyTarget = o.only.trim();
@@ -28994,16 +29241,16 @@ function registerBootstrapCommands(program3) {
28994
29241
  }
28995
29242
  const onlyManagedBlock = onlyTarget ? seedsToApply[0]?.managedBlock != null : false;
28996
29243
  const gh = async (args) => execFileP("gh", args, { timeout: 2e4 });
28997
- const readFile9 = (p) => (0, import_node_fs25.existsSync)(p) ? (0, import_node_fs25.readFileSync)(p, "utf8") : null;
29244
+ const readFile9 = (p) => (0, import_node_fs26.existsSync)(p) ? (0, import_node_fs26.readFileSync)(p, "utf8") : null;
28998
29245
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
28999
29246
  const putSeed = async (target, content, ref, sha) => {
29000
- const tmp = (0, import_node_path25.join)((0, import_node_os14.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
29001
- (0, import_node_fs25.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
29247
+ const tmp = (0, import_node_path26.join)((0, import_node_os14.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
29248
+ (0, import_node_fs26.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
29002
29249
  try {
29003
29250
  await gh(contentPutInputArgs(repo, target, tmp));
29004
29251
  } finally {
29005
29252
  try {
29006
- (0, import_node_fs25.unlinkSync)(tmp);
29253
+ (0, import_node_fs26.unlinkSync)(tmp);
29007
29254
  } catch {
29008
29255
  }
29009
29256
  }
@@ -29452,10 +29699,10 @@ LIVE apply to ${repo}:
29452
29699
  bootstrap.command("propagate").description("#4238: re-entrant canary\u2192wave tick \u2014 plan (or, with --execute, open) per-repo PRs fanning an org-owned seed out to the fleet").option("--target <path>", "the manifest target to propagate (an org-owned whole file or declared Hub-managed block)").option("--execute", "LIVE tick via gh (master-gated) \u2014 opens/reuses per-repo seed-propagate PRs; dry-run prints the plan only").option("--json", "machine-readable output").action(async () => {
29453
29700
  const o = { target: rawValue("--target", ""), execute: rawFlag("--execute"), json: rawFlag("--json") };
29454
29701
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
29455
- if (!(0, import_node_fs25.existsSync)(manifestPath)) return fail(`bootstrap propagate: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the desired state this tick propagates`);
29702
+ if (!(0, import_node_fs26.existsSync)(manifestPath)) return fail(`bootstrap propagate: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the desired state this tick propagates`);
29456
29703
  const seedSource = await resolveHubSeedSource(execGitForSeedSource);
29457
29704
  if (!seedSource.ok) return fail(`bootstrap propagate: ${seedSource.reason}`);
29458
- const manifest = loadBootstrapSeeds((0, import_node_fs25.readFileSync)(manifestPath, "utf8"));
29705
+ const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
29459
29706
  const propagatable = manifest.seeds.filter(isPropagatableSeed);
29460
29707
  if (!o.target) {
29461
29708
  return fail(`bootstrap propagate: --target <path> is required \u2014 one of:
@@ -29464,9 +29711,9 @@ LIVE apply to ${repo}:
29464
29711
  const seed = propagatable.find((s) => s.target === o.target);
29465
29712
  if (!seed) return fail(`bootstrap propagate: --target '${o.target}' names no centrally propagatable seed in ${manifestPath}. Propagatable targets:
29466
29713
  ${propagatable.map((s) => s.target).join("\n ")}`);
29467
- if (!seed.managedBlock && !(0, import_node_fs25.existsSync)(seed.target)) return fail(`bootstrap propagate: the Hub's own copy of '${seed.target}' is missing \u2014 nothing to propagate`);
29468
- const hubContent = seed.managedBlock ? null : (0, import_node_fs25.readFileSync)(seed.target, "utf8");
29469
- const readSeedFile2 = (path2) => (0, import_node_fs25.existsSync)(path2) ? (0, import_node_fs25.readFileSync)(path2, "utf8") : null;
29714
+ if (!seed.managedBlock && !(0, import_node_fs26.existsSync)(seed.target)) return fail(`bootstrap propagate: the Hub's own copy of '${seed.target}' is missing \u2014 nothing to propagate`);
29715
+ const hubContent = seed.managedBlock ? null : (0, import_node_fs26.readFileSync)(seed.target, "utf8");
29716
+ const readSeedFile2 = (path2) => (0, import_node_fs26.existsSync)(path2) ? (0, import_node_fs26.readFileSync)(path2, "utf8") : null;
29470
29717
  const isWorkflowSeed = seed.target.startsWith(".github/workflows/");
29471
29718
  const cfg = await loadConfig();
29472
29719
  const projects = await fetchProjectsList(registryClientDeps(cfg));
@@ -29475,9 +29722,9 @@ LIVE apply to ${repo}:
29475
29722
  }
29476
29723
  const rosterRepos = collectRegistryRepos(projects).filter((r) => r.toLowerCase() !== "mutmutco/mmi-hub");
29477
29724
  let independentCount = rosterRepos.length;
29478
- if ((0, import_node_fs25.existsSync)("projects.json")) {
29725
+ if ((0, import_node_fs26.existsSync)("projects.json")) {
29479
29726
  try {
29480
- const local = JSON.parse((0, import_node_fs25.readFileSync)("projects.json", "utf8"));
29727
+ const local = JSON.parse((0, import_node_fs26.readFileSync)("projects.json", "utf8"));
29481
29728
  const localRepos = /* @__PURE__ */ new Set();
29482
29729
  for (const p of local.projects ?? []) for (const r of p.repos ?? []) {
29483
29730
  const full = (r.includes("/") ? r : `mutmutco/${r}`).toLowerCase();
@@ -29638,15 +29885,15 @@ LIVE apply to ${repo}:
29638
29885
  } catch {
29639
29886
  existingSha = void 0;
29640
29887
  }
29641
- const tmp = (0, import_node_path25.join)((0, import_node_os14.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
29888
+ const tmp = (0, import_node_path26.join)((0, import_node_os14.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
29642
29889
  const desiredContent = desiredByRepo.get(rec.repo);
29643
29890
  if (desiredContent == null) return fail(`bootstrap propagate: no resolved content for ${rec.repo} ${seed.target} \u2014 refusing to write`);
29644
- (0, import_node_fs25.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, desiredContent, branch, existingSha)), "utf8");
29891
+ (0, import_node_fs26.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, desiredContent, branch, existingSha)), "utf8");
29645
29892
  try {
29646
29893
  await gh(contentPutInputArgs(rec.repo, seed.target, tmp));
29647
29894
  } finally {
29648
29895
  try {
29649
- (0, import_node_fs25.unlinkSync)(tmp);
29896
+ (0, import_node_fs26.unlinkSync)(tmp);
29650
29897
  } catch {
29651
29898
  }
29652
29899
  }
@@ -29713,10 +29960,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
29713
29960
  return fail(`bootstrap rollback: ${e.message}`);
29714
29961
  }
29715
29962
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
29716
- if (!(0, import_node_fs25.existsSync)(manifestPath)) return fail(`bootstrap rollback: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the manifest names which targets are org-owned and therefore propagated (and rollback-able)`);
29963
+ if (!(0, import_node_fs26.existsSync)(manifestPath)) return fail(`bootstrap rollback: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the manifest names which targets are org-owned and therefore propagated (and rollback-able)`);
29717
29964
  const seedSource = await resolveHubSeedSource(execGitForSeedSource);
29718
29965
  if (!seedSource.ok) return fail(`bootstrap rollback: ${seedSource.reason}`);
29719
- const manifest = loadBootstrapSeeds((0, import_node_fs25.readFileSync)(manifestPath, "utf8"));
29966
+ const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
29720
29967
  const propagatable = manifest.seeds.filter(isPropagatableSeed);
29721
29968
  if (!o.target) {
29722
29969
  return fail(`bootstrap rollback: --target <path> is required \u2014 one of:
@@ -29733,10 +29980,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
29733
29980
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
29734
29981
  let candidates;
29735
29982
  if (o.record) {
29736
- if (!(0, import_node_fs25.existsSync)(o.record)) return fail(`bootstrap rollback: --record '${o.record}' not found`);
29983
+ if (!(0, import_node_fs26.existsSync)(o.record)) return fail(`bootstrap rollback: --record '${o.record}' not found`);
29737
29984
  let parsed;
29738
29985
  try {
29739
- parsed = JSON.parse((0, import_node_fs25.readFileSync)(o.record, "utf8"));
29986
+ parsed = JSON.parse((0, import_node_fs26.readFileSync)(o.record, "utf8"));
29740
29987
  } catch (e) {
29741
29988
  return fail(`bootstrap rollback: --record '${o.record}' is not valid JSON: ${e.message}`);
29742
29989
  }
@@ -29813,13 +30060,13 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
29813
30060
  } catch {
29814
30061
  existingSha = void 0;
29815
30062
  }
29816
- const tmp = (0, import_node_path25.join)((0, import_node_os14.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
29817
- (0, import_node_fs25.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
30063
+ const tmp = (0, import_node_path26.join)((0, import_node_os14.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
30064
+ (0, import_node_fs26.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
29818
30065
  try {
29819
30066
  await gh(contentPutInputArgs(repo, seed.target, tmp));
29820
30067
  } finally {
29821
30068
  try {
29822
- (0, import_node_fs25.unlinkSync)(tmp);
30069
+ (0, import_node_fs26.unlinkSync)(tmp);
29823
30070
  } catch {
29824
30071
  }
29825
30072
  }
@@ -30886,7 +31133,7 @@ function renderDeployPortDoctor(report) {
30886
31133
  init_cli_shared();
30887
31134
 
30888
31135
  // src/wave-status.ts
30889
- var import_node_fs28 = require("node:fs");
31136
+ var import_node_fs29 = require("node:fs");
30890
31137
 
30891
31138
  // src/git-worktree-observation.ts
30892
31139
  function parseGitWorktreePorcelain(text) {
@@ -30917,8 +31164,8 @@ function deriveComposeProjectName(worktreePath) {
30917
31164
 
30918
31165
  // src/stage-runner.ts
30919
31166
  var import_node_child_process10 = require("node:child_process");
30920
- var import_node_fs27 = require("node:fs");
30921
- var import_node_path27 = require("node:path");
31167
+ var import_node_fs28 = require("node:fs");
31168
+ var import_node_path28 = require("node:path");
30922
31169
  var import_node_net = require("node:net");
30923
31170
  var import_node_util5 = require("node:util");
30924
31171
 
@@ -30928,8 +31175,8 @@ function normalizeEol2(s) {
30928
31175
  }
30929
31176
 
30930
31177
  // src/env-detection.ts
30931
- var import_node_fs26 = require("node:fs");
30932
- var import_node_path26 = require("node:path");
31178
+ var import_node_fs27 = require("node:fs");
31179
+ var import_node_path27 = require("node:path");
30933
31180
  var SKIPPED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".jerv", ".pi", ".venv", "venv"]);
30934
31181
  function isEnvBaseName(name) {
30935
31182
  return name.toLowerCase() === ".env";
@@ -30939,7 +31186,7 @@ function findEnvFiles(root) {
30939
31186
  const walk2 = (dir, rel) => {
30940
31187
  let entries;
30941
31188
  try {
30942
- entries = (0, import_node_fs26.readdirSync)(dir, { withFileTypes: true });
31189
+ entries = (0, import_node_fs27.readdirSync)(dir, { withFileTypes: true });
30943
31190
  } catch {
30944
31191
  return;
30945
31192
  }
@@ -30947,13 +31194,13 @@ function findEnvFiles(root) {
30947
31194
  const relPath = rel ? `${rel}/${entry.name}` : entry.name;
30948
31195
  if (entry.isDirectory()) {
30949
31196
  if (SKIPPED_DIRS.has(entry.name)) continue;
30950
- walk2((0, import_node_path26.join)(dir, entry.name), relPath);
31197
+ walk2((0, import_node_path27.join)(dir, entry.name), relPath);
30951
31198
  continue;
30952
31199
  }
30953
31200
  if (isEnvBaseName(entry.name)) {
30954
31201
  let isDir = false;
30955
31202
  try {
30956
- isDir = (0, import_node_fs26.lstatSync)((0, import_node_path26.join)(dir, entry.name)).isDirectory();
31203
+ isDir = (0, import_node_fs27.lstatSync)((0, import_node_path27.join)(dir, entry.name)).isDirectory();
30957
31204
  } catch {
30958
31205
  }
30959
31206
  if (!isDir) found.push(relPath);
@@ -31015,12 +31262,12 @@ var DOCKER_TIMEOUT_MS = 15e3;
31015
31262
  function stageBash() {
31016
31263
  if (process.platform !== "win32") return "bash";
31017
31264
  const configured = process.env.SHELL;
31018
- if (configured && (0, import_node_path27.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs27.existsSync)(configured)) return configured;
31265
+ if (configured && (0, import_node_path28.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs28.existsSync)(configured)) return configured;
31019
31266
  const candidates = [
31020
- (0, import_node_path27.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
31021
- ...process.env.LOCALAPPDATA ? [(0, import_node_path27.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
31267
+ (0, import_node_path28.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
31268
+ ...process.env.LOCALAPPDATA ? [(0, import_node_path28.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
31022
31269
  ];
31023
- const bash = candidates.find((path2) => (0, import_node_fs27.existsSync)(path2));
31270
+ const bash = candidates.find((path2) => (0, import_node_fs28.existsSync)(path2));
31024
31271
  if (!bash) throw new Error("Local stages require Git Bash on Windows. Install Git for Windows or set SHELL to its absolute bash.exe path.");
31025
31272
  return bash;
31026
31273
  }
@@ -31151,7 +31398,7 @@ function appendForceRecreate(up) {
31151
31398
  return `${up.trimEnd()} --force-recreate`;
31152
31399
  }
31153
31400
  function stageStatePath(cwd = process.cwd()) {
31154
- return (0, import_node_path27.join)(cwd, "tmp", "stage", "state.json");
31401
+ return (0, import_node_path28.join)(cwd, "tmp", "stage", "state.json");
31155
31402
  }
31156
31403
  function normPath(path2) {
31157
31404
  return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
@@ -31368,8 +31615,8 @@ function stageProcessEnv(stagePort, extraEnv) {
31368
31615
  }
31369
31616
  function composeResolvesPort(cwd) {
31370
31617
  if (process.env.PORT) return true;
31371
- const envFile = (0, import_node_path27.join)(cwd, ".env");
31372
- return (0, import_node_fs27.existsSync)(envFile) && envFileKeys((0, import_node_fs27.readFileSync)(envFile, "utf8")).has("PORT");
31618
+ const envFile = (0, import_node_path28.join)(cwd, ".env");
31619
+ return (0, import_node_fs28.existsSync)(envFile) && envFileKeys((0, import_node_fs28.readFileSync)(envFile, "utf8")).has("PORT");
31373
31620
  }
31374
31621
  function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
31375
31622
  return {
@@ -31382,13 +31629,13 @@ function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
31382
31629
  function stageComposeFiles(cwd) {
31383
31630
  const files = ["docker-compose.yml"];
31384
31631
  for (const conventional of ["docker-compose.override.yml", "docker-compose.override.yaml"]) {
31385
- if ((0, import_node_fs27.existsSync)((0, import_node_path27.join)(cwd, conventional))) files.push(conventional);
31632
+ if ((0, import_node_fs28.existsSync)((0, import_node_path28.join)(cwd, conventional))) files.push(conventional);
31386
31633
  }
31387
31634
  return [...files, RUNTIME_ENV_OVERRIDE_RELPATH];
31388
31635
  }
31389
31636
  async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
31390
31637
  const keys = Object.keys(vaultEnvMerge ?? {});
31391
- if (!keys.length || !(0, import_node_fs27.existsSync)((0, import_node_path27.join)(cwd, "docker-compose.yml"))) return {};
31638
+ if (!keys.length || !(0, import_node_fs28.existsSync)((0, import_node_path28.join)(cwd, "docker-compose.yml"))) return {};
31392
31639
  let configJson;
31393
31640
  try {
31394
31641
  const { stdout } = await execFileP3("docker", ["compose", "config", "--format", "json"], {
@@ -31404,21 +31651,21 @@ async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
31404
31651
  }
31405
31652
  const body = renderRuntimeEnvOverride(runtimeEnvMarkedServices(configJson), keys);
31406
31653
  if (!body) return {};
31407
- const overridePath = (0, import_node_path27.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
31408
- (0, import_node_fs27.mkdirSync)((0, import_node_path27.join)(cwd, "tmp", "stage"), { recursive: true });
31409
- (0, import_node_fs27.writeFileSync)(overridePath, body, "utf8");
31654
+ const overridePath = (0, import_node_path28.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
31655
+ (0, import_node_fs28.mkdirSync)((0, import_node_path28.join)(cwd, "tmp", "stage"), { recursive: true });
31656
+ (0, import_node_fs28.writeFileSync)(overridePath, body, "utf8");
31410
31657
  return stageComposeFileEnv(stageComposeFiles(cwd));
31411
31658
  }
31412
31659
  async function ensureStageRuntimeEnv(config, opts, cwd) {
31413
31660
  if (!config.ensureEnv) return;
31414
- const target = (0, import_node_path27.join)(cwd, config.ensureEnv.target);
31415
- const example = (0, import_node_path27.join)(cwd, config.ensureEnv.example);
31416
- if (!(0, import_node_fs27.existsSync)(target) && (0, import_node_fs27.existsSync)(example)) {
31417
- (0, import_node_fs27.copyFileSync)(example, target);
31418
- } else if ((0, import_node_fs27.existsSync)(target) && (0, import_node_fs27.existsSync)(example)) {
31419
- const stale = detectStaleEnvFile((0, import_node_fs27.readFileSync)(example, "utf8"), (0, import_node_fs27.readFileSync)(target, "utf8"), {
31420
- exampleMtimeMs: (0, import_node_fs27.statSync)(example).mtimeMs,
31421
- targetMtimeMs: (0, import_node_fs27.statSync)(target).mtimeMs
31661
+ const target = (0, import_node_path28.join)(cwd, config.ensureEnv.target);
31662
+ const example = (0, import_node_path28.join)(cwd, config.ensureEnv.example);
31663
+ if (!(0, import_node_fs28.existsSync)(target) && (0, import_node_fs28.existsSync)(example)) {
31664
+ (0, import_node_fs28.copyFileSync)(example, target);
31665
+ } else if ((0, import_node_fs28.existsSync)(target) && (0, import_node_fs28.existsSync)(example)) {
31666
+ const stale = detectStaleEnvFile((0, import_node_fs28.readFileSync)(example, "utf8"), (0, import_node_fs28.readFileSync)(target, "utf8"), {
31667
+ exampleMtimeMs: (0, import_node_fs28.statSync)(example).mtimeMs,
31668
+ targetMtimeMs: (0, import_node_fs28.statSync)(target).mtimeMs
31422
31669
  });
31423
31670
  if (stale) {
31424
31671
  const msg = `stale ${config.ensureEnv.target} (${stale}) \u2014 delete it or refresh from ${config.ensureEnv.example} before re-running /stage`;
@@ -31426,8 +31673,8 @@ async function ensureStageRuntimeEnv(config, opts, cwd) {
31426
31673
  console.error(`mmi-cli stage: ${msg} (allowed via --allow-stale-env)`);
31427
31674
  }
31428
31675
  }
31429
- if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0, import_node_fs27.existsSync)(target)) {
31430
- (0, import_node_fs27.writeFileSync)(target, mergeEnvSecretsIntoFile((0, import_node_fs27.readFileSync)(target, "utf8"), opts.vaultEnvMerge), "utf8");
31676
+ if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0, import_node_fs28.existsSync)(target)) {
31677
+ (0, import_node_fs28.writeFileSync)(target, mergeEnvSecretsIntoFile((0, import_node_fs28.readFileSync)(target, "utf8"), opts.vaultEnvMerge), "utf8");
31431
31678
  }
31432
31679
  }
31433
31680
  async function gitText(cwd, args) {
@@ -31457,20 +31704,20 @@ async function resolveGlobalStatePath(cwd, explicit) {
31457
31704
  return void 0;
31458
31705
  }
31459
31706
  function readState(path2) {
31460
- if (!(0, import_node_fs27.existsSync)(path2)) return null;
31707
+ if (!(0, import_node_fs28.existsSync)(path2)) return null;
31461
31708
  try {
31462
- return JSON.parse((0, import_node_fs27.readFileSync)(path2, "utf8"));
31709
+ return JSON.parse((0, import_node_fs28.readFileSync)(path2, "utf8"));
31463
31710
  } catch {
31464
31711
  return null;
31465
31712
  }
31466
31713
  }
31467
31714
  function mkdirFor(path2) {
31468
31715
  const dir = path2.slice(0, Math.max(path2.lastIndexOf("/"), path2.lastIndexOf("\\")));
31469
- (0, import_node_fs27.mkdirSync)(dir, { recursive: true });
31716
+ (0, import_node_fs28.mkdirSync)(dir, { recursive: true });
31470
31717
  }
31471
31718
  function writeState(path2, state) {
31472
31719
  mkdirFor(path2);
31473
- (0, import_node_fs27.writeFileSync)(path2, JSON.stringify(state, null, 2), "utf8");
31720
+ (0, import_node_fs28.writeFileSync)(path2, JSON.stringify(state, null, 2), "utf8");
31474
31721
  }
31475
31722
  function writeStagePortReservation(port, cwd, statePath, globalStatePath, now) {
31476
31723
  const reservation = {
@@ -31499,7 +31746,7 @@ async function cleanupStageState(state, paths, timeoutMs, fallbackCwd, currentEn
31499
31746
  );
31500
31747
  }
31501
31748
  for (const path2 of [...new Set(paths.filter((p) => Boolean(p)))]) {
31502
- (0, import_node_fs27.rmSync)(path2, { force: true });
31749
+ (0, import_node_fs28.rmSync)(path2, { force: true });
31503
31750
  }
31504
31751
  }
31505
31752
  async function killTree(pid) {
@@ -31686,8 +31933,8 @@ async function runStage(config = {}, opts = {}) {
31686
31933
  });
31687
31934
  }
31688
31935
  } catch (e) {
31689
- (0, import_node_fs27.rmSync)(statePath, { force: true });
31690
- if (globalStatePath && globalStatePath !== statePath) (0, import_node_fs27.rmSync)(globalStatePath, { force: true });
31936
+ (0, import_node_fs28.rmSync)(statePath, { force: true });
31937
+ if (globalStatePath && globalStatePath !== statePath) (0, import_node_fs28.rmSync)(globalStatePath, { force: true });
31691
31938
  throw e;
31692
31939
  }
31693
31940
  const started = await startStage(config, {
@@ -31704,9 +31951,9 @@ async function runStage(config = {}, opts = {}) {
31704
31951
  // src/wave-status.ts
31705
31952
  function readStageSummary(worktreePath) {
31706
31953
  const statePath = stageStatePath(worktreePath);
31707
- if (!(0, import_node_fs28.existsSync)(statePath)) return void 0;
31954
+ if (!(0, import_node_fs29.existsSync)(statePath)) return void 0;
31708
31955
  try {
31709
- const state = JSON.parse((0, import_node_fs28.readFileSync)(statePath, "utf8"));
31956
+ const state = JSON.parse((0, import_node_fs29.readFileSync)(statePath, "utf8"));
31710
31957
  const port = typeof state.port === "number" ? state.port : void 0;
31711
31958
  if (port == null || !Number.isInteger(port) || port <= 0) return void 0;
31712
31959
  return { port, url: typeof state.url === "string" ? state.url : void 0 };
@@ -31763,9 +32010,9 @@ async function collectWaveStatus(deps) {
31763
32010
  // src/discovery-commands.ts
31764
32011
  init_github_client();
31765
32012
  init_cli_shared();
31766
- var import_node_fs29 = require("node:fs");
32013
+ var import_node_fs30 = require("node:fs");
31767
32014
  var import_node_os15 = require("node:os");
31768
- var import_node_path28 = require("node:path");
32015
+ var import_node_path29 = require("node:path");
31769
32016
  var GC_GH_TIMEOUT_MS = 2e4;
31770
32017
  async function collectStatus() {
31771
32018
  const repo = await resolveRepo();
@@ -31955,8 +32202,8 @@ async function collectOnboardStatus(opts = {}) {
31955
32202
  }
31956
32203
  const home = (0, import_node_os15.homedir)();
31957
32204
  const plugin = onboardPluginGate({
31958
- readKnown: () => readFileSyncSafe((0, import_node_path28.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs29.readFileSync),
31959
- readSettings: () => readFileSyncSafe((0, import_node_path28.join)(home, ".claude", "settings.json"), import_node_fs29.readFileSync)
32205
+ readKnown: () => readFileSyncSafe((0, import_node_path29.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs30.readFileSync),
32206
+ readSettings: () => readFileSyncSafe((0, import_node_path29.join)(home, ".claude", "settings.json"), import_node_fs30.readFileSync)
31960
32207
  });
31961
32208
  return {
31962
32209
  guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
@@ -33973,7 +34220,7 @@ function parseOriginRepo(remoteUrl) {
33973
34220
  init_github_client();
33974
34221
 
33975
34222
  // src/issue-commands.ts
33976
- var import_node_fs30 = require("node:fs");
34223
+ var import_node_fs31 = require("node:fs");
33977
34224
  var import_node_crypto10 = require("node:crypto");
33978
34225
  init_cli_shared();
33979
34226
  init_clean_exit();
@@ -34230,7 +34477,7 @@ async function editIssue(client, options, deps = {}) {
34230
34477
  const url = `https://github.com/${repo}/issues/${parsed.number}`;
34231
34478
  const patch = {};
34232
34479
  let bodyChanged = false;
34233
- const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs30.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
34480
+ const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs31.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
34234
34481
  if (options.titleFile !== void 0) {
34235
34482
  patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
34236
34483
  } else if (options.title !== void 0) {
@@ -34886,7 +35133,7 @@ function extendCreateCommand(issue, batchAttach) {
34886
35133
  if (opts.batch) {
34887
35134
  let specs;
34888
35135
  try {
34889
- const raw = (0, import_node_fs30.readFileSync)(opts.batch, "utf8");
35136
+ const raw = (0, import_node_fs31.readFileSync)(opts.batch, "utf8");
34890
35137
  specs = JSON.parse(raw);
34891
35138
  if (!Array.isArray(specs)) {
34892
35139
  const top = specs === null ? "null" : typeof specs === "object" ? `an object with keys: ${Object.keys(specs).join(", ") || "(none)"}` : `a ${typeof specs}`;
@@ -36050,8 +36297,8 @@ function registerPrLifecycleCommands(program3) {
36050
36297
  }
36051
36298
 
36052
36299
  // src/project-info-sync.ts
36053
- var import_node_fs31 = require("node:fs");
36054
- var import_node_path29 = require("node:path");
36300
+ var import_node_fs32 = require("node:fs");
36301
+ var import_node_path30 = require("node:path");
36055
36302
  var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
36056
36303
  updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
36057
36304
  projectV2 { id }
@@ -36098,14 +36345,14 @@ function sharedName(entries, fallback) {
36098
36345
  }
36099
36346
  function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
36100
36347
  if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
36101
- const readmePath = (0, import_node_path29.join)(repoRoot2, "README.md");
36102
- if (!(0, import_node_fs31.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
36348
+ const readmePath = (0, import_node_path30.join)(repoRoot2, "README.md");
36349
+ if (!(0, import_node_fs32.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
36103
36350
  const entries = entriesFor(project2, projects);
36104
36351
  const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
36105
36352
  const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
36106
36353
  if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
36107
36354
  const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
36108
- const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs31.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
36355
+ const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs32.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
36109
36356
  const lines2 = [
36110
36357
  `# ${projectName}`,
36111
36358
  "",
@@ -36124,8 +36371,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
36124
36371
  const targetBase = `https://github.com/${targetRepo2}`;
36125
36372
  const targetBranch = branchFor(targetRepo2, projects);
36126
36373
  const orgDocs = [
36127
- (0, import_node_fs31.existsSync)((0, import_node_path29.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
36128
- (0, import_node_fs31.existsSync)((0, import_node_path29.join)(repoRoot2, "docs", "Architecture", "agentic-dev-environment.md")) ? `- [Agentic development environment](${targetBase}/blob/${targetBranch}/docs/Architecture/agentic-dev-environment.md)` : ""
36374
+ (0, import_node_fs32.existsSync)((0, import_node_path30.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
36375
+ (0, import_node_fs32.existsSync)((0, import_node_path30.join)(repoRoot2, "docs", "Architecture", "agentic-dev-environment.md")) ? `- [Agentic development environment](${targetBase}/blob/${targetBranch}/docs/Architecture/agentic-dev-environment.md)` : ""
36129
36376
  ].filter(Boolean);
36130
36377
  if (orgDocs.length) lines2.push("", "## Organisation docs", "", ...orgDocs);
36131
36378
  return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines2.join("\n")}
@@ -36507,8 +36754,8 @@ function registerSchedulesCommands(program3) {
36507
36754
  }
36508
36755
 
36509
36756
  // src/secrets-commands.ts
36510
- var import_node_fs32 = require("node:fs");
36511
- var import_node_path30 = require("node:path");
36757
+ var import_node_fs33 = require("node:fs");
36758
+ var import_node_path31 = require("node:path");
36512
36759
  var import_node_os17 = require("node:os");
36513
36760
  init_cli_shared();
36514
36761
  init_hub_auth();
@@ -36614,18 +36861,18 @@ function collectMap(value, previous = []) {
36614
36861
  return [...previous, value];
36615
36862
  }
36616
36863
  async function decryptRailsCredentials(input) {
36617
- const appDir = (0, import_node_path30.resolve)(input.appDir ?? process.cwd());
36864
+ const appDir = (0, import_node_path31.resolve)(input.appDir ?? process.cwd());
36618
36865
  const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
36619
36866
  const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
36620
- const credentialsPath = (0, import_node_path30.resolve)(appDir, credentialsFile);
36621
- const masterKeyPath = (0, import_node_path30.resolve)(appDir, masterKeyFile);
36867
+ const credentialsPath = (0, import_node_path31.resolve)(appDir, credentialsFile);
36868
+ const masterKeyPath = (0, import_node_path31.resolve)(appDir, masterKeyFile);
36622
36869
  const env = {
36623
36870
  ...process.env,
36624
36871
  MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
36625
36872
  MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
36626
36873
  };
36627
- if ((0, import_node_fs32.existsSync)(masterKeyPath)) {
36628
- env.RAILS_MASTER_KEY = (0, import_node_fs32.readFileSync)(masterKeyPath, "utf8").trim();
36874
+ if ((0, import_node_fs33.existsSync)(masterKeyPath)) {
36875
+ env.RAILS_MASTER_KEY = (0, import_node_fs33.readFileSync)(masterKeyPath, "utf8").trim();
36629
36876
  }
36630
36877
  const script = [
36631
36878
  'require "json"',
@@ -36635,9 +36882,9 @@ async function decryptRailsCredentials(input) {
36635
36882
  'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
36636
36883
  "puts JSON.generate(config.config)"
36637
36884
  ].join("\n");
36638
- const scriptDir = (0, import_node_fs32.mkdtempSync)((0, import_node_path30.join)((0, import_node_os17.tmpdir)(), "mmi-rails-decrypt-"));
36639
- const scriptPath = (0, import_node_path30.join)(scriptDir, "decrypt.rb");
36640
- (0, import_node_fs32.writeFileSync)(scriptPath, script, "utf8");
36885
+ const scriptDir = (0, import_node_fs33.mkdtempSync)((0, import_node_path31.join)((0, import_node_os17.tmpdir)(), "mmi-rails-decrypt-"));
36886
+ const scriptPath = (0, import_node_path31.join)(scriptDir, "decrypt.rb");
36887
+ (0, import_node_fs33.writeFileSync)(scriptPath, script, "utf8");
36641
36888
  try {
36642
36889
  const args = ["exec", "ruby", scriptPath];
36643
36890
  const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
@@ -36649,7 +36896,7 @@ async function decryptRailsCredentials(input) {
36649
36896
  });
36650
36897
  return JSON.parse(stdout);
36651
36898
  } finally {
36652
- (0, import_node_fs32.rmSync)(scriptDir, { recursive: true, force: true });
36899
+ (0, import_node_fs33.rmSync)(scriptDir, { recursive: true, force: true });
36653
36900
  }
36654
36901
  }
36655
36902
  async function readSecretStdin() {
@@ -36739,7 +36986,7 @@ function registerSecretsCommands(program3) {
36739
36986
  let body;
36740
36987
  if (o.file) {
36741
36988
  try {
36742
- body = (0, import_node_fs32.readFileSync)((0, import_node_path30.resolve)(o.file), "utf8");
36989
+ body = (0, import_node_fs33.readFileSync)((0, import_node_path31.resolve)(o.file), "utf8");
36743
36990
  } catch (e) {
36744
36991
  return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
36745
36992
  }
@@ -36871,7 +37118,7 @@ function registerSecretsCommands(program3) {
36871
37118
  {
36872
37119
  ...d,
36873
37120
  decryptRailsCredentials,
36874
- removeFile: (path2) => (0, import_node_fs32.unlinkSync)((0, import_node_path30.resolve)(o.appDir ?? process.cwd(), path2))
37121
+ removeFile: (path2) => (0, import_node_fs33.unlinkSync)((0, import_node_path31.resolve)(o.appDir ?? process.cwd(), path2))
36875
37122
  },
36876
37123
  {
36877
37124
  repo: o.repo,
@@ -37034,8 +37281,8 @@ function registerSessionReport(program3) {
37034
37281
  }
37035
37282
 
37036
37283
  // src/stage-commands.ts
37037
- var import_node_fs33 = require("node:fs");
37038
- var import_node_path31 = require("node:path");
37284
+ var import_node_fs34 = require("node:fs");
37285
+ var import_node_path32 = require("node:path");
37039
37286
  init_cli_shared();
37040
37287
  init_clean_exit();
37041
37288
 
@@ -37281,8 +37528,8 @@ function registerStageCommands(program3) {
37281
37528
  return {
37282
37529
  resolution: decideStage({
37283
37530
  registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
37284
- hasCompose: (0, import_node_fs33.existsSync)((0, import_node_path31.join)(process.cwd(), "docker-compose.yml")),
37285
- hasEnvExample: (0, import_node_fs33.existsSync)((0, import_node_path31.join)(process.cwd(), ".env.example")),
37531
+ hasCompose: (0, import_node_fs34.existsSync)((0, import_node_path32.join)(process.cwd(), "docker-compose.yml")),
37532
+ hasEnvExample: (0, import_node_fs34.existsSync)((0, import_node_path32.join)(process.cwd(), ".env.example")),
37286
37533
  repo
37287
37534
  }),
37288
37535
  project: project2,
@@ -37566,19 +37813,19 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
37566
37813
 
37567
37814
  // src/tenant-artifact.ts
37568
37815
  var import_node_crypto11 = require("node:crypto");
37569
- var import_node_fs34 = require("node:fs");
37816
+ var import_node_fs35 = require("node:fs");
37570
37817
  var import_promises6 = require("node:fs/promises");
37571
- var import_node_path32 = require("node:path");
37818
+ var import_node_path33 = require("node:path");
37572
37819
  var ARTIFACT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
37573
37820
  var MAX_BYTES = 5 * 1024 * 1024 * 1024;
37574
37821
  async function sha256File(path2) {
37575
37822
  const hash = (0, import_node_crypto11.createHash)("sha256");
37576
- for await (const chunk of (0, import_node_fs34.createReadStream)(path2)) hash.update(chunk);
37823
+ for await (const chunk of (0, import_node_fs35.createReadStream)(path2)) hash.update(chunk);
37577
37824
  return hash.digest("hex");
37578
37825
  }
37579
37826
  async function putTenantArtifact(repo, stage, inputPath, deps) {
37580
37827
  if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
37581
- const path2 = (0, import_node_path32.resolve)(inputPath);
37828
+ const path2 = (0, import_node_path33.resolve)(inputPath);
37582
37829
  const info = await (0, import_promises6.stat)(path2);
37583
37830
  if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
37584
37831
  if (!Number.isSafeInteger(info.size) || info.size < 1 || info.size > MAX_BYTES) throw new Error(`tenant artifact put: file must be 1..${MAX_BYTES} bytes`);
@@ -37596,7 +37843,7 @@ async function putTenantArtifact(repo, stage, inputPath, deps) {
37596
37843
  return [key, value];
37597
37844
  }));
37598
37845
  headers["content-length"] = String(info.size);
37599
- const stream = (0, import_node_fs34.createReadStream)(path2);
37846
+ const stream = (0, import_node_fs35.createReadStream)(path2);
37600
37847
  let uploaded;
37601
37848
  try {
37602
37849
  uploaded = await fetch(body.uploadUrl, {
@@ -37736,7 +37983,7 @@ function renderVerifySecrets(body) {
37736
37983
 
37737
37984
  // src/command-register-collaboration.ts
37738
37985
  var import_node_child_process15 = require("node:child_process");
37739
- var import_node_fs41 = require("node:fs");
37986
+ var import_node_fs42 = require("node:fs");
37740
37987
  var import_promises7 = require("node:fs/promises");
37741
37988
  init_clean_exit();
37742
37989
  init_cli_shared();
@@ -37760,7 +38007,7 @@ function spawnDetachedSelf(args, deps, opts = {}) {
37760
38007
  }
37761
38008
 
37762
38009
  // src/command-register-collaboration.ts
37763
- var import_node_path39 = require("node:path");
38010
+ var import_node_path40 = require("node:path");
37764
38011
 
37765
38012
  // src/attach-to-project.ts
37766
38013
  async function attachViaHubApp(cfg, contentNodeId) {
@@ -37851,6 +38098,9 @@ function prHeadBehindBase(input) {
37851
38098
  function prHeadUpdateRemedy(head, base) {
37852
38099
  return `git fetch origin ${base} && git merge origin/${base} (on ${head}), resolve the conflicts, push, then rerun \u2014 docs/Guides/train-troubleshooting.md#head-behind-base`;
37853
38100
  }
38101
+ function prLandedBranchFinality(prNumber, base) {
38102
+ return `PR #${prNumber} is merged, and that is final \u2014 stopping a local waiter or monitor does not unmerge it. The head branch is now landed; further commits pushed to it reach nothing. If more work is needed, run \`git fetch origin ${base} && git switch -c <new-branch> origin/${base}\`, then cherry-pick anything already committed \u2014 docs/Guides/train-troubleshooting.md#merged-pr-head-reuse`;
38103
+ }
37854
38104
  var PROTECTED_PR_HEAD_BRANCHES = ["main", "master", "development", "rc"];
37855
38105
  function isProtectedPrHead(head) {
37856
38106
  return PROTECTED_PR_HEAD_BRANCHES.includes(head);
@@ -37946,8 +38196,9 @@ async function readGhPrStateWithRetry(fetchState, options) {
37946
38196
  return { ok: false, error: lastError };
37947
38197
  }
37948
38198
  async function runPrLand(prNumber, options, deps) {
37949
- const repo = await deps.resolveRepo(prNumber, options.repo);
37950
- const base = { status: "failed", repo, pr: prNumber };
38199
+ const resolved = await deps.resolveRepo(prNumber, options.repo);
38200
+ const { repo } = resolved;
38201
+ const base = { status: "failed", repo, pr: prNumber, base: resolved.base };
37951
38202
  if (options.requireTrain !== false) {
37952
38203
  const verdict = await deps.fetchTrainAuthority(repo);
37953
38204
  if (!verdict.ok) {
@@ -37999,12 +38250,21 @@ async function runPrLand(prNumber, options, deps) {
37999
38250
  return { ...base, error: merge.error ?? "merge failed" };
38000
38251
  }
38001
38252
  if (merge.mergeStatus === "merged") {
38002
- return { ...base, status: "merged" };
38253
+ return {
38254
+ ...base,
38255
+ status: "merged",
38256
+ branchFinality: prLandedBranchFinality(prNumber, resolved.base)
38257
+ };
38003
38258
  }
38004
38259
  const now = deps.now ?? (() => Date.now());
38005
38260
  const merged = await deps.pollMerged(prNumber, repo, now() + PR_LAND_ENQUEUE_TIMEOUT_MS);
38006
38261
  if (merged) {
38007
- return { ...base, status: "auto-merge-enqueued-then-merged", mergeStatus: "merged" };
38262
+ return {
38263
+ ...base,
38264
+ status: "auto-merge-enqueued-then-merged",
38265
+ mergeStatus: "merged",
38266
+ branchFinality: prLandedBranchFinality(prNumber, resolved.base)
38267
+ };
38008
38268
  }
38009
38269
  return {
38010
38270
  ...base,
@@ -38016,8 +38276,8 @@ async function runPrLand(prNumber, options, deps) {
38016
38276
  init_github_client();
38017
38277
 
38018
38278
  // src/merge-cleanup.ts
38019
- var import_node_fs36 = require("node:fs");
38020
- var import_node_path34 = require("node:path");
38279
+ var import_node_fs37 = require("node:fs");
38280
+ var import_node_path35 = require("node:path");
38021
38281
  var import_node_os18 = require("node:os");
38022
38282
  init_cli_shared();
38023
38283
  init_github_client();
@@ -38126,8 +38386,8 @@ function boardAdvanceFailureMessage(result) {
38126
38386
 
38127
38387
  // src/test-policy-core.ts
38128
38388
  var import_node_child_process12 = require("node:child_process");
38129
- var import_node_fs35 = require("node:fs");
38130
- var import_node_path33 = require("node:path");
38389
+ var import_node_fs36 = require("node:fs");
38390
+ var import_node_path34 = require("node:path");
38131
38391
 
38132
38392
  // src/test-command-policy-shared.mjs
38133
38393
  var import_node_child_process11 = require("node:child_process");
@@ -38603,7 +38863,7 @@ function isMeaningfulRow(file) {
38603
38863
  return file.meaningful !== false;
38604
38864
  }
38605
38865
  function loadPolicy(root, readFile9 = readFileOrNull2) {
38606
- const raw = readFile9((0, import_node_path33.join)(root, POLICY_FILE));
38866
+ const raw = readFile9((0, import_node_path34.join)(root, POLICY_FILE));
38607
38867
  if (raw == null) return { mandatory: [], declared: false };
38608
38868
  return parsePolicy(raw, POLICY_FILE);
38609
38869
  }
@@ -38680,7 +38940,7 @@ function loadPolicySource(root, explicitRef, diffBase) {
38680
38940
  }
38681
38941
  function readFileOrNull2(path2) {
38682
38942
  try {
38683
- return (0, import_node_fs35.readFileSync)(path2, "utf8");
38943
+ return (0, import_node_fs36.readFileSync)(path2, "utf8");
38684
38944
  } catch {
38685
38945
  return null;
38686
38946
  }
@@ -38708,12 +38968,12 @@ function classify(changed, policy, present = () => false) {
38708
38968
  const removedProtected = [...removed].filter((p) => protectedBy.has(p)).map((p) => ({ path: p, why: protectedBy.get(p) ?? "" }));
38709
38969
  return { mandatoryHits, untestedHits, testChanges, meaningfulTestChanges, addedTests, removedProtected };
38710
38970
  }
38711
- function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs35.existsSync)(path2)) {
38712
- return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path33.join)(root, p)));
38971
+ function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs36.existsSync)(path2)) {
38972
+ return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path34.join)(root, p)));
38713
38973
  }
38714
- function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs35.existsSync)(path2)) {
38974
+ function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs36.existsSync)(path2)) {
38715
38975
  const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
38716
- return [...new Set(declared)].filter((p) => !exists((0, import_node_path33.join)(root, p)));
38976
+ return [...new Set(declared)].filter((p) => !exists((0, import_node_path34.join)(root, p)));
38717
38977
  }
38718
38978
  function evaluate(changed, policy, present = () => false) {
38719
38979
  const { mandatoryHits, untestedHits, meaningfulTestChanges, addedTests, removedProtected } = classify(changed, policy, present);
@@ -38852,14 +39112,14 @@ function runTestPolicy(root, deps = {}) {
38852
39112
  }
38853
39113
  const loaded = deps.policy ? { policy: deps.policy, source: { ref: null, sha: null, path: POLICY_FILE } } : loadPolicySource(root, deps.policyRef, deps.base);
38854
39114
  const { policy, source: policySource } = loaded;
38855
- const exists = deps.exists ?? ((path2) => (0, import_node_fs35.existsSync)(path2));
39115
+ const exists = deps.exists ?? ((path2) => (0, import_node_fs36.existsSync)(path2));
38856
39116
  const counts = { mandatoryCount: (policy.mandatory ?? []).length, protectedCount: (policy.protected ?? []).length };
38857
39117
  const base = deps.changed ? "(injected)" : deps.policyRef ? resolveHotfixBase(root) : resolveBase(root, deps.base);
38858
39118
  const refusal = deps.changed ? null : untrustworthyRange(root, base);
38859
39119
  const raw = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
38860
39120
  const changed = deps.changed ? raw : annotateChangeMeaning(raw, policy, (path2) => ({
38861
39121
  before: blobAt(base, path2, root),
38862
- after: readFileOrNull2((0, import_node_path33.join)(root, path2))
39122
+ after: readFileOrNull2((0, import_node_path34.join)(root, path2))
38863
39123
  }));
38864
39124
  const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride2(base, root) : { override: null, refusals: [] };
38865
39125
  const policyRefusals = policy.declared === false ? [{
@@ -38869,8 +39129,8 @@ function runTestPolicy(root, deps = {}) {
38869
39129
  A gate with no policy cannot decide which test commands are required or permitted.
38870
39130
  ` + (deps.base === HOTFIX_DIFF_BASE ? ` Hotfix lane (--base ${HOTFIX_DIFF_BASE}): pass --policy-ref <exact-${HOTFIX_POLICY_REF}-commit> so the current policy is read from ${HOTFIX_POLICY_REF}.` : ` Restore ${POLICY_FILE} on this branch; the policy file belongs on every non-hotfix lane.`)
38871
39131
  }] : [];
38872
- const present = (path2) => exists((0, import_node_path33.join)(root, path2));
38873
- const policyTree = policySource.sha ? new Set(git2(["ls-tree", "-r", "-z", "--name-only", policySource.sha], root).split("\0").filter(Boolean).map((p) => (0, import_node_path33.join)(root, p))) : null;
39132
+ const present = (path2) => exists((0, import_node_path34.join)(root, path2));
39133
+ const policyTree = policySource.sha ? new Set(git2(["ls-tree", "-r", "-z", "--name-only", policySource.sha], root).split("\0").filter(Boolean).map((p) => (0, import_node_path34.join)(root, p))) : null;
38874
39134
  const policyTreeExists = policyTree ? (abs) => policyTree.has(abs) : exists;
38875
39135
  const where = policySource.sha ? `${policySource.ref}@${policySource.sha}` : "this worktree";
38876
39136
  const removedByThisDiff = removedPaths(changed);
@@ -39088,13 +39348,13 @@ function resolveSquashMergeBodyText(commits, allowedClosing, cwd) {
39088
39348
  return rewritten === base ? null : rewritten;
39089
39349
  }
39090
39350
  function writeSquashBodyFile(body) {
39091
- const dir = (0, import_node_fs36.mkdtempSync)((0, import_node_path34.join)((0, import_node_os18.tmpdir)(), "mmi-squash-body-"));
39092
- const path2 = (0, import_node_path34.join)(dir, "body.txt");
39093
- (0, import_node_fs36.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
39351
+ const dir = (0, import_node_fs37.mkdtempSync)((0, import_node_path35.join)((0, import_node_os18.tmpdir)(), "mmi-squash-body-"));
39352
+ const path2 = (0, import_node_path35.join)(dir, "body.txt");
39353
+ (0, import_node_fs37.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
39094
39354
  `, "utf8");
39095
39355
  return { path: path2, cleanup: () => {
39096
39356
  try {
39097
- (0, import_node_fs36.rmSync)(dir, { recursive: true, force: true });
39357
+ (0, import_node_fs37.rmSync)(dir, { recursive: true, force: true });
39098
39358
  } catch {
39099
39359
  }
39100
39360
  } };
@@ -39301,8 +39561,8 @@ async function deleteMergedRemoteBranch(options) {
39301
39561
  }
39302
39562
 
39303
39563
  // src/post-merge-recon.ts
39304
- var import_node_fs37 = require("node:fs");
39305
- var import_node_path35 = require("node:path");
39564
+ var import_node_fs38 = require("node:fs");
39565
+ var import_node_path36 = require("node:path");
39306
39566
 
39307
39567
  // src/cross-repo-filing-issue.ts
39308
39568
  init_github_client();
@@ -39469,16 +39729,16 @@ function buildPostMergeReconRecovery(input) {
39469
39729
  }
39470
39730
  function writePostMergeReconRecovery(cwd, recovery) {
39471
39731
  const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
39472
- (0, import_node_fs37.mkdirSync)((0, import_node_path35.dirname)(path2), { recursive: true });
39473
- (0, import_node_fs37.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
39732
+ (0, import_node_fs38.mkdirSync)((0, import_node_path36.dirname)(path2), { recursive: true });
39733
+ (0, import_node_fs38.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
39474
39734
  `, "utf8");
39475
39735
  return path2;
39476
39736
  }
39477
39737
  function clearPostMergeReconRecovery(cwd, repo, pr) {
39478
39738
  const path2 = postMergeReconStatePath(cwd, repo, pr);
39479
- if (!(0, import_node_fs37.existsSync)(path2)) return;
39739
+ if (!(0, import_node_fs38.existsSync)(path2)) return;
39480
39740
  try {
39481
- (0, import_node_fs37.unlinkSync)(path2);
39741
+ (0, import_node_fs38.unlinkSync)(path2);
39482
39742
  } catch {
39483
39743
  }
39484
39744
  }
@@ -39510,9 +39770,9 @@ function postMergeReconWarnings(input) {
39510
39770
 
39511
39771
  // src/review-verdict.ts
39512
39772
  var import_node_child_process13 = require("node:child_process");
39513
- var import_node_fs38 = require("node:fs");
39773
+ var import_node_fs39 = require("node:fs");
39514
39774
  var import_node_os19 = require("node:os");
39515
- var import_node_path36 = require("node:path");
39775
+ var import_node_path37 = require("node:path");
39516
39776
  init_cli_shared();
39517
39777
  var REVIEW_VERDICT_MARKER = "<!-- zeroci-review v1 -->";
39518
39778
  var REVIEW_VERDICTS = ["PROCEED", "CORRECT", "ESCALATE"];
@@ -39677,15 +39937,15 @@ async function readPrIssueComments(number, repo) {
39677
39937
  return comments;
39678
39938
  }
39679
39939
  async function postPrCommentFromFile(number, repo, body) {
39680
- const dir = (0, import_node_fs38.mkdtempSync)((0, import_node_path36.join)((0, import_node_os19.tmpdir)(), "mmi-review-verdict-"));
39681
- const path2 = (0, import_node_path36.join)(dir, "body.md");
39940
+ const dir = (0, import_node_fs39.mkdtempSync)((0, import_node_path37.join)((0, import_node_os19.tmpdir)(), "mmi-review-verdict-"));
39941
+ const path2 = (0, import_node_path37.join)(dir, "body.md");
39682
39942
  try {
39683
- (0, import_node_fs38.writeFileSync)(path2, body, "utf8");
39943
+ (0, import_node_fs39.writeFileSync)(path2, body, "utf8");
39684
39944
  const { stdout } = await execFileP("gh", ["pr", "comment", number, "--repo", repo, "--body-file", path2], { timeout: GH_MUTATION_TIMEOUT_MS });
39685
39945
  return stdout.trim();
39686
39946
  } finally {
39687
39947
  try {
39688
- (0, import_node_fs38.rmSync)(dir, { recursive: true, force: true });
39948
+ (0, import_node_fs39.rmSync)(dir, { recursive: true, force: true });
39689
39949
  } catch {
39690
39950
  }
39691
39951
  }
@@ -39839,6 +40099,32 @@ function withRateLimitRetry(client, seams = {}) {
39839
40099
  throttledPaths
39840
40100
  };
39841
40101
  }
40102
+ async function prCreateMergedHeadWarning(input, deps = {}) {
40103
+ const guide = "docs/Guides/train-troubleshooting.md#merged-pr-head-reuse";
40104
+ if (!input.head) {
40105
+ return `pr create: WARNING \u2014 could not check whether the current head branch already landed (the current branch could not be resolved); continuing with PR creation \u2014 ${guide}`;
40106
+ }
40107
+ try {
40108
+ const repo = await requireRepo(input.repo);
40109
+ const owner = repo.split("/")[0];
40110
+ if (!owner) throw new Error(`invalid repository '${repo}'`);
40111
+ const qualifiedHead = input.head.includes(":") ? input.head : `${owner}:${input.head}`;
40112
+ const query = new URLSearchParams({ state: "closed", head: qualifiedHead, sort: "updated", direction: "desc" });
40113
+ const pulls = await (deps.client ?? defaultGitHubClient()).restPaginate(
40114
+ `repos/${repo}/pulls?${query.toString()}`
40115
+ );
40116
+ const merged = pulls.find((pull) => typeof pull.merged_at === "string" && pull.merged_at.length > 0);
40117
+ if (!merged) return void 0;
40118
+ if (typeof merged.number !== "number" || typeof merged.base?.ref !== "string" || !merged.base.ref) {
40119
+ throw new Error("GitHub returned a merged PR without its number or base branch");
40120
+ }
40121
+ const base = merged.base.ref;
40122
+ return `pr create: WARNING \u2014 head branch '${input.head}' already landed in merged PR #${merged.number}. If you meant to continue that landed work, cut a fresh branch from origin/${base}: \`git fetch origin ${base} && git switch -c <new-branch> origin/${base}\`, then cherry-pick anything already committed. Creating another PR from a deliberately reused branch name remains allowed \u2014 ${guide}`;
40123
+ } catch (e) {
40124
+ const detail = String(e.message || e).replace(/\s+/g, " ").slice(0, 240);
40125
+ return `pr create: WARNING \u2014 could not check whether head branch '${input.head}' already landed (${detail}); continuing with PR creation \u2014 ${guide}`;
40126
+ }
40127
+ }
39842
40128
  async function prCreateClaimRefusal(body, repoOption, deps = {}) {
39843
40129
  const issues = [...new Set(findClosingMentions(body).map((mention) => mention.issue))];
39844
40130
  if (!issues.length) return void 0;
@@ -39871,13 +40157,13 @@ async function prCreateClaimRefusal(body, repoOption, deps = {}) {
39871
40157
  }
39872
40158
 
39873
40159
  // src/worktree-merge-cleanup.ts
39874
- var import_node_fs40 = require("node:fs");
39875
- var import_node_path38 = require("node:path");
40160
+ var import_node_fs41 = require("node:fs");
40161
+ var import_node_path39 = require("node:path");
39876
40162
  init_cli_shared();
39877
40163
 
39878
40164
  // src/worktree-evidence-archive.ts
39879
- var import_node_fs39 = require("node:fs");
39880
- var import_node_path37 = require("node:path");
40165
+ var import_node_fs40 = require("node:fs");
40166
+ var import_node_path38 = require("node:path");
39881
40167
  var JERV_ARTIFACT_RUN_SCOPE_ENV_VARS = ["JERV_RUN_ID", ...SESSION_ID_ENV_VARS];
39882
40168
  function sanitizeArchiveSegment(value, max = 80) {
39883
40169
  const scrubbed = value.replace(/[^A-Za-z0-9._@+-]+/g, "-").replace(/^-+|-+$/g, "");
@@ -39892,24 +40178,24 @@ function resolveArtifactRunScope(env = process.env) {
39892
40178
  }
39893
40179
  function defaultIsDirectory(path2) {
39894
40180
  try {
39895
- return (0, import_node_fs39.lstatSync)(path2).isDirectory();
40181
+ return (0, import_node_fs40.lstatSync)(path2).isDirectory();
39896
40182
  } catch {
39897
40183
  return false;
39898
40184
  }
39899
40185
  }
39900
40186
  function archiveWorktreeJervArtifacts(args, deps = {}) {
39901
- const exists = deps.exists ?? import_node_fs39.existsSync;
40187
+ const exists = deps.exists ?? import_node_fs40.existsSync;
39902
40188
  const isDirectory = deps.isDirectory ?? defaultIsDirectory;
39903
- const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs39.cpSync)(from, to, { recursive: true, force: true }));
40189
+ const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs40.cpSync)(from, to, { recursive: true, force: true }));
39904
40190
  const mkdirp = deps.mkdirp ?? ((path2) => {
39905
- (0, import_node_fs39.mkdirSync)(path2, { recursive: true });
40191
+ (0, import_node_fs40.mkdirSync)(path2, { recursive: true });
39906
40192
  });
39907
40193
  const env = deps.env ?? process.env;
39908
40194
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
39909
40195
  const resolveRoot = deps.resolveArchiveRoot ?? repoRuntimeStatePath;
39910
40196
  if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
39911
40197
  if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
39912
- const source = (0, import_node_path37.join)(args.worktreePath, ".jerv");
40198
+ const source = (0, import_node_path38.join)(args.worktreePath, ".jerv");
39913
40199
  if (!exists(source)) return { status: "absent" };
39914
40200
  if (!isDirectory(source)) return { status: "skipped", reason: "jerv-not-a-directory" };
39915
40201
  const runScope = resolveArtifactRunScope(env);
@@ -39917,7 +40203,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
39917
40203
  const stamp = now().toISOString().replace(/[:.]/g, "-");
39918
40204
  const dest = resolveRoot(args.primaryRoot, "jerv-artifacts", runScope, branchSlug, stamp, ".jerv");
39919
40205
  try {
39920
- mkdirp((0, import_node_path37.dirname)(dest));
40206
+ mkdirp((0, import_node_path38.dirname)(dest));
39921
40207
  copyDir(source, dest);
39922
40208
  if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
39923
40209
  return { status: "archived", path: dest, runScope };
@@ -39926,14 +40212,14 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
39926
40212
  }
39927
40213
  }
39928
40214
  function defaultStat(path2) {
39929
- const st = (0, import_node_fs39.statSync)(path2);
40215
+ const st = (0, import_node_fs40.statSync)(path2);
39930
40216
  return { mtimeMs: st.mtimeMs, size: st.size, isDirectory: () => st.isDirectory() };
39931
40217
  }
39932
40218
  function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
39933
- const exists = deps.exists ?? import_node_fs39.existsSync;
40219
+ const exists = deps.exists ?? import_node_fs40.existsSync;
39934
40220
  const stat4 = deps.stat ?? defaultStat;
39935
- const readdir2 = deps.readdir ?? import_node_fs39.readdirSync;
39936
- const tmpRoot = (0, import_node_path37.join)(worktreePath, "tmp");
40221
+ const readdir2 = deps.readdir ?? import_node_fs40.readdirSync;
40222
+ const tmpRoot = (0, import_node_path38.join)(worktreePath, "tmp");
39937
40223
  if (!exists(tmpRoot)) return [];
39938
40224
  const entries = [];
39939
40225
  const walk2 = (dir) => {
@@ -39944,14 +40230,14 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
39944
40230
  return;
39945
40231
  }
39946
40232
  for (const name of names) {
39947
- const full = (0, import_node_path37.join)(dir, name);
40233
+ const full = (0, import_node_path38.join)(dir, name);
39948
40234
  let st;
39949
40235
  try {
39950
40236
  st = stat4(full);
39951
40237
  } catch {
39952
40238
  continue;
39953
40239
  }
39954
- const relPath = (0, import_node_path37.relative)(worktreePath, full).replace(/\\/g, "/");
40240
+ const relPath = (0, import_node_path38.relative)(worktreePath, full).replace(/\\/g, "/");
39955
40241
  if (st.isDirectory()) {
39956
40242
  if (st.mtimeMs > newerThanMs) entries.push({ relPath, bytes: 0, mtimeMs: st.mtimeMs });
39957
40243
  walk2(full);
@@ -39965,10 +40251,10 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
39965
40251
  return entries;
39966
40252
  }
39967
40253
  function archiveWorktreeTmpArtifacts(args, deps = {}) {
39968
- const exists = deps.exists ?? import_node_fs39.existsSync;
39969
- const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs39.cpSync)(from, to, { recursive: true, force: true }));
40254
+ const exists = deps.exists ?? import_node_fs40.existsSync;
40255
+ const copyDir = deps.copyDir ?? ((from, to) => (0, import_node_fs40.cpSync)(from, to, { recursive: true, force: true }));
39970
40256
  const mkdirp = deps.mkdirp ?? ((path2) => {
39971
- (0, import_node_fs39.mkdirSync)(path2, { recursive: true });
40257
+ (0, import_node_fs40.mkdirSync)(path2, { recursive: true });
39972
40258
  });
39973
40259
  const env = deps.env ?? process.env;
39974
40260
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
@@ -39976,7 +40262,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
39976
40262
  if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
39977
40263
  if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
39978
40264
  const scanned = scanWorktreeTmpEvidence(args.worktreePath, args.newerThanMs, deps);
39979
- const source = (0, import_node_path37.join)(args.worktreePath, "tmp");
40265
+ const source = (0, import_node_path38.join)(args.worktreePath, "tmp");
39980
40266
  if (!scanned.length) return { status: "absent" };
39981
40267
  if (!exists(source)) return { status: "absent" };
39982
40268
  const runScope = resolveArtifactRunScope(env);
@@ -39984,7 +40270,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
39984
40270
  const stamp = now().toISOString().replace(/[:.]/g, "-");
39985
40271
  const dest = resolveRoot(args.primaryRoot, "worktree-artifacts", runScope, branchSlug, stamp, "tmp");
39986
40272
  try {
39987
- mkdirp((0, import_node_path37.dirname)(dest));
40273
+ mkdirp((0, import_node_path38.dirname)(dest));
39988
40274
  copyDir(source, dest);
39989
40275
  if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
39990
40276
  const bytes = scanned.reduce((sum, e) => sum + e.bytes, 0);
@@ -40053,9 +40339,9 @@ function normPath2(p) {
40053
40339
  return p.replace(/\\/g, "/").replace(/\/+$/, "");
40054
40340
  }
40055
40341
  function unlinkNodeModulesJunction(wtPath) {
40056
- const nm = (0, import_node_path38.join)(wtPath, "node_modules");
40342
+ const nm = (0, import_node_path39.join)(wtPath, "node_modules");
40057
40343
  try {
40058
- if ((0, import_node_fs40.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs40.rmdirSync)(nm);
40344
+ if ((0, import_node_fs41.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs41.rmdirSync)(nm);
40059
40345
  return { ok: true };
40060
40346
  } catch (e) {
40061
40347
  if (e.code === "ENOENT") return { ok: true };
@@ -40066,8 +40352,8 @@ function defaultSleep2(ms) {
40066
40352
  return new Promise((resolve7) => setTimeout(resolve7, ms));
40067
40353
  }
40068
40354
  async function removeResidueDirectory(wtPath, options = {}) {
40069
- const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs40.rmdirSync, exists = import_node_fs40.existsSync } = options;
40070
- const removeRecursive = options.removeRecursive ?? ((path2) => (0, import_node_fs40.rmSync)(path2, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }));
40355
+ const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs41.rmdirSync, exists = import_node_fs41.existsSync } = options;
40356
+ const removeRecursive = options.removeRecursive ?? ((path2) => (0, import_node_fs41.rmSync)(path2, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }));
40071
40357
  const junction = unlinkNodeModulesJunction(wtPath);
40072
40358
  if (!junction.ok) return junction;
40073
40359
  try {
@@ -40190,28 +40476,28 @@ async function preCleanWorktreeForRemoval(wtPath, execGit) {
40190
40476
  }
40191
40477
  async function listNestedIgnoredNodeModules(wtPath, execGit) {
40192
40478
  const out = await execGit(["-C", wtPath, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory"]).catch(() => "");
40193
- 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)));
40479
+ 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)));
40194
40480
  }
40195
40481
  function safeRemoveTree(path2) {
40196
- const stat4 = (0, import_node_fs40.lstatSync)(path2);
40482
+ const stat4 = (0, import_node_fs41.lstatSync)(path2);
40197
40483
  if (stat4.isSymbolicLink()) {
40198
40484
  try {
40199
- (0, import_node_fs40.rmdirSync)(path2);
40485
+ (0, import_node_fs41.rmdirSync)(path2);
40200
40486
  } catch {
40201
- (0, import_node_fs40.unlinkSync)(path2);
40487
+ (0, import_node_fs41.unlinkSync)(path2);
40202
40488
  }
40203
40489
  return;
40204
40490
  }
40205
40491
  if (stat4.isDirectory()) {
40206
- for (const entry of (0, import_node_fs40.readdirSync)(path2)) safeRemoveTree((0, import_node_path38.join)(path2, entry));
40207
- (0, import_node_fs40.rmdirSync)(path2);
40492
+ for (const entry of (0, import_node_fs41.readdirSync)(path2)) safeRemoveTree((0, import_node_path39.join)(path2, entry));
40493
+ (0, import_node_fs41.rmdirSync)(path2);
40208
40494
  return;
40209
40495
  }
40210
- (0, import_node_fs40.unlinkSync)(path2);
40496
+ (0, import_node_fs41.unlinkSync)(path2);
40211
40497
  }
40212
40498
  async function removeWorktreeNodeModules(wtPath) {
40213
40499
  try {
40214
- safeRemoveTree((0, import_node_path38.join)(wtPath, "node_modules"));
40500
+ safeRemoveTree((0, import_node_path39.join)(wtPath, "node_modules"));
40215
40501
  return { ok: true };
40216
40502
  } catch (e) {
40217
40503
  if (e.code === "ENOENT") return { ok: true };
@@ -40223,7 +40509,7 @@ function errorMessage(e) {
40223
40509
  }
40224
40510
  function resolvedOrRaw(path2) {
40225
40511
  try {
40226
- return normPath2((0, import_node_fs40.realpathSync)(path2));
40512
+ return normPath2((0, import_node_fs41.realpathSync)(path2));
40227
40513
  } catch {
40228
40514
  return normPath2(path2);
40229
40515
  }
@@ -40231,10 +40517,10 @@ function resolvedOrRaw(path2) {
40231
40517
  function unlinkEscapingReparsePoints(root, primaryRoot) {
40232
40518
  let realRoot;
40233
40519
  try {
40234
- if ((0, import_node_fs40.lstatSync)(root).isSymbolicLink()) {
40520
+ if ((0, import_node_fs41.lstatSync)(root).isSymbolicLink()) {
40235
40521
  return { ok: false, error: `delete root ${normPath2(root)} is a reparse point resolving to ${resolvedOrRaw(root)}` };
40236
40522
  }
40237
- realRoot = normPath2((0, import_node_fs40.realpathSync)(root));
40523
+ realRoot = normPath2((0, import_node_fs41.realpathSync)(root));
40238
40524
  } catch (e) {
40239
40525
  if (e.code === "ENOENT") return { ok: true, unlinked: [] };
40240
40526
  return { ok: false, error: `cannot resolve delete root ${normPath2(root)}: ${errorMessage(e)}` };
@@ -40250,16 +40536,16 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
40250
40536
  const dir = stack.pop();
40251
40537
  let entries;
40252
40538
  try {
40253
- entries = (0, import_node_fs40.readdirSync)(dir, { withFileTypes: true });
40539
+ entries = (0, import_node_fs41.readdirSync)(dir, { withFileTypes: true });
40254
40540
  } catch (e) {
40255
40541
  return { ok: false, error: `cannot scan ${normPath2(dir)} for reparse points: ${errorMessage(e)}` };
40256
40542
  }
40257
40543
  for (const entry of entries) {
40258
- const child2 = (0, import_node_path38.join)(dir, entry.name);
40544
+ const child2 = (0, import_node_path39.join)(dir, entry.name);
40259
40545
  if (entry.isSymbolicLink()) {
40260
40546
  let target = "";
40261
40547
  try {
40262
- target = normPath2((0, import_node_fs40.realpathSync)(child2));
40548
+ target = normPath2((0, import_node_fs41.realpathSync)(child2));
40263
40549
  } catch {
40264
40550
  target = "";
40265
40551
  }
@@ -40442,7 +40728,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
40442
40728
  return report;
40443
40729
  }
40444
40730
  const execGit = options.execGit ?? (async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
40445
- const pathExists = options.pathExists ?? import_node_fs40.existsSync;
40731
+ const pathExists = options.pathExists ?? import_node_fs41.existsSync;
40446
40732
  let afterWorktrees = [];
40447
40733
  try {
40448
40734
  afterWorktrees = parseGitWorktreePorcelain(await execGit(["worktree", "list", "--porcelain"]));
@@ -40582,7 +40868,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
40582
40868
  const preHelperGuard = unlinkEscapingReparsePoints(wtPath, options.primaryRoot);
40583
40869
  if (!preHelperGuard.ok) return refuseReparseEscape(preHelperGuard.error);
40584
40870
  unlinkedReparsePoints.push(...preHelperGuard.unlinked);
40585
- if (pathExists((0, import_node_path38.join)(wtPath, "node_modules"))) {
40871
+ if (pathExists((0, import_node_path39.join)(wtPath, "node_modules"))) {
40586
40872
  const nmRemoved = await (options.removeRealNodeModules ?? removeWorktreeNodeModules)(wtPath);
40587
40873
  if (!nmRemoved.ok) {
40588
40874
  report.worktree = {
@@ -40749,10 +41035,10 @@ function argvWantsJson2() {
40749
41035
  return process.argv.some((a) => a === "--json" || a.startsWith("--json="));
40750
41036
  }
40751
41037
  function hubRoot() {
40752
- const fromPkg = (0, import_node_path39.join)(__dirname, "..", "..");
41038
+ const fromPkg = (0, import_node_path40.join)(__dirname, "..", "..");
40753
41039
  const marker = "skills/bootstrap/seeds/manifest.json";
40754
- if ((0, import_node_fs41.existsSync)((0, import_node_path39.join)(fromPkg, marker))) return fromPkg;
40755
- if ((0, import_node_fs41.existsSync)((0, import_node_path39.join)(process.cwd(), marker))) return process.cwd();
41040
+ if ((0, import_node_fs42.existsSync)((0, import_node_path40.join)(fromPkg, marker))) return fromPkg;
41041
+ if ((0, import_node_fs42.existsSync)((0, import_node_path40.join)(process.cwd(), marker))) return process.cwd();
40756
41042
  return null;
40757
41043
  }
40758
41044
  function ciAuditDeps() {
@@ -40764,8 +41050,8 @@ function ciAuditDeps() {
40764
41050
  getProjectMeta: async (slug) => fetchProjectBySlug(slug, registryClientDeps(await cfgPromise)),
40765
41051
  readSeedFile: (path2) => {
40766
41052
  if (!root) return null;
40767
- const fullPath = (0, import_node_path39.join)(root, path2);
40768
- return (0, import_node_fs41.existsSync)(fullPath) ? (0, import_node_fs41.readFileSync)(fullPath, "utf8") : null;
41053
+ const fullPath = (0, import_node_path40.join)(root, path2);
41054
+ return (0, import_node_fs42.existsSync)(fullPath) ? (0, import_node_fs42.readFileSync)(fullPath, "utf8") : null;
40769
41055
  }
40770
41056
  };
40771
41057
  }
@@ -41443,6 +41729,10 @@ ${list}`);
41443
41729
  }
41444
41730
  const claimRefusal = await prCreateClaimRefusal(body, o.repo);
41445
41731
  if (claimRefusal) return fail(claimRefusal);
41732
+ const createHead = o.head ?? await gitOut(["symbolic-ref", "--quiet", "--short", "HEAD"]).catch(() => "");
41733
+ const mergedHeadWarning = await prCreateMergedHeadWarning({ head: createHead || void 0, repo: o.repo });
41734
+ if (mergedHeadWarning) process.stderr.write(`${mergedHeadWarning}
41735
+ `);
41446
41736
  const created = await ghCreate(buildPrArgs({ title, body, base: o.base, head: o.head, repo: o.repo, draft: o.draft }));
41447
41737
  if (isGhCreateRateLimited(created)) {
41448
41738
  console.log(JSON.stringify(created));
@@ -41486,11 +41776,11 @@ ${list}`);
41486
41776
  }
41487
41777
  });
41488
41778
  async function listCiWorkflowPaths(cwd = process.cwd()) {
41489
- const wfDir = (0, import_node_path39.join)(cwd, ".github", "workflows");
41490
- if (!(0, import_node_fs41.existsSync)(wfDir)) return [];
41491
- return (0, import_node_fs41.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
41779
+ const wfDir = (0, import_node_path40.join)(cwd, ".github", "workflows");
41780
+ if (!(0, import_node_fs42.existsSync)(wfDir)) return [];
41781
+ return (0, import_node_fs42.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
41492
41782
  try {
41493
- return workflowReportsPrChecks((0, import_node_fs41.readFileSync)((0, import_node_path39.join)(wfDir, name), "utf8"));
41783
+ return workflowReportsPrChecks((0, import_node_fs42.readFileSync)((0, import_node_path40.join)(wfDir, name), "utf8"));
41494
41784
  } catch {
41495
41785
  return true;
41496
41786
  }
@@ -41686,7 +41976,7 @@ ${list}`);
41686
41976
  jsonParity(pr.command("review-verdict <number>").description("post an explicit review verdict comment: computes the PR diff patch-id and head sha, renders the v1 contract body, and posts it as a PR comment").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) => {
41687
41977
  if (!isReviewVerdict(o.verdict)) return fail(`pr review-verdict: --verdict must be one of ${REVIEW_VERDICTS.join("|")} (got ${o.verdict})`);
41688
41978
  const verdict = o.verdict;
41689
- const findings = o.findingsFile ? (0, import_node_fs41.readFileSync)(o.findingsFile, "utf8") : void 0;
41979
+ const findings = o.findingsFile ? (0, import_node_fs42.readFileSync)(o.findingsFile, "utf8") : void 0;
41690
41980
  let patch;
41691
41981
  let head;
41692
41982
  try {
@@ -41801,7 +42091,7 @@ ${list}`);
41801
42091
  const shape = track ? `${track} track` : "unresolved track \u2014 strict reading";
41802
42092
  throw new Error(`pr land: base branch ${base} is a promotion target (${shape}) \u2014 promotion merges stay human-only`);
41803
42093
  }
41804
- return repo;
42094
+ return { repo, base };
41805
42095
  },
41806
42096
  fetchTrainAuthority: async (repo) => fetchTrainAuthority(repo, registryClientDeps(await loadConfig())),
41807
42097
  resolveCiPolicy: (repo) => resolveRepoMergeCiPolicy(repo, ciAuditDeps()),
@@ -41916,7 +42206,9 @@ ${list}`);
41916
42206
  }
41917
42207
  const credential = await pollToken().catch(() => void 0) ? "app_installation" : "user";
41918
42208
  if (o.json) printLine(JSON.stringify({ ...result, credential }));
41919
- else printLine(`pr land: ${result.status}${result.error ? ` \u2014 ${result.error}` : ""} (credential: ${credential})`);
42209
+ else printLine(
42210
+ `pr land: ${result.status}${result.error ? ` \u2014 ${result.error}` : ""} (credential: ${credential})` + (result.branchFinality ? ` \u2014 ${result.branchFinality}` : "")
42211
+ );
41920
42212
  if (result.status === "failed") process.exitCode = 1;
41921
42213
  });
41922
42214
  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>", "compatibility option; shared merge helpers do not require a review verdict")).action(async (number, o) => {
@@ -41958,7 +42250,7 @@ ${list}`);
41958
42250
  const mergeSquashBody = squashBodyTextForMerge(
41959
42251
  closingGuardInput,
41960
42252
  method === "--squash",
41961
- o.squashBodyFile ? (0, import_node_fs41.readFileSync)(o.squashBodyFile, "utf8") : void 0
42253
+ o.squashBodyFile ? (0, import_node_fs42.readFileSync)(o.squashBodyFile, "utf8") : void 0
41962
42254
  );
41963
42255
  if (!o.squashBodyFile) warnSquashBodyClosingStrip("pr merge", closingGuardInput, mergeSquashBody);
41964
42256
  const closingGuardVerdict = evaluateClosingGuard(closingGuardInput, {
@@ -41990,7 +42282,7 @@ ${list}`);
41990
42282
  const remote = foreignCwd ? `https://github.com/${targetRepo2}.git` : "origin";
41991
42283
  let foreignCheckout;
41992
42284
  if (foreignCwd) {
41993
- const sibling = (0, import_node_path39.join)((0, import_node_path39.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
42285
+ const sibling = (0, import_node_path40.join)((0, import_node_path40.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
41994
42286
  const siblingRepo = repoFromRemoteUrl(await gitOut(["-C", sibling, "remote", "get-url", "origin"]).catch(() => ""));
41995
42287
  if (siblingRepo?.toLowerCase() === targetRepo2.toLowerCase()) {
41996
42288
  const siblingWorktrees = await execFileP("git", ["-C", sibling, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => parseGitWorktreePorcelain(r.stdout)).catch(() => void 0);
@@ -42088,7 +42380,7 @@ ${list}`);
42088
42380
  }
42089
42381
  if (!repoForPostCleanup) throw e;
42090
42382
  console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
42091
- const commitMessage = bodyFile ? (0, import_node_fs41.readFileSync)(bodyFile, "utf8") : void 0;
42383
+ const commitMessage = bodyFile ? (0, import_node_fs42.readFileSync)(bodyFile, "utf8") : void 0;
42092
42384
  await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
42093
42385
  body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
42094
42386
  timeoutMs: GH_MUTATION_TIMEOUT_MS
@@ -42208,7 +42500,7 @@ ${list}`);
42208
42500
  preserveWorktree: o.preserveWorktree,
42209
42501
  gcAcknowledged: o.gc,
42210
42502
  expectedHeadOid: headRefOid,
42211
- pathExists: (p) => (0, import_node_fs41.existsSync)(p),
42503
+ pathExists: (p) => (0, import_node_fs42.existsSync)(p),
42212
42504
  // #5899: pin cleanup git calls to the main checkout — the task worktree this process may be
42213
42505
  // standing in is removed mid-cleanup, so a cwd-relative invocation fails with
42214
42506
  // 'fatal: not a git repository' and leaves a spurious partial-cleanup exit.
@@ -42329,7 +42621,7 @@ ${list}`);
42329
42621
  }
42330
42622
 
42331
42623
  // src/command-register-developer.ts
42332
- var import_node_fs46 = require("node:fs");
42624
+ var import_node_fs47 = require("node:fs");
42333
42625
  init_clean_exit();
42334
42626
  init_cli_shared();
42335
42627
  init_error_codes();
@@ -42362,7 +42654,7 @@ async function resolveWhoami(deps) {
42362
42654
  }
42363
42655
 
42364
42656
  // src/command-register-developer.ts
42365
- var import_node_path44 = require("node:path");
42657
+ var import_node_path45 = require("node:path");
42366
42658
  init_house_map();
42367
42659
  init_hub_url();
42368
42660
 
@@ -42395,7 +42687,7 @@ async function executeWaveLand(plan, deps) {
42395
42687
  }
42396
42688
 
42397
42689
  // src/box-commands.ts
42398
- var import_node_fs42 = require("node:fs");
42690
+ var import_node_fs43 = require("node:fs");
42399
42691
  init_clean_exit();
42400
42692
 
42401
42693
  // src/box.ts
@@ -42607,7 +42899,7 @@ function registerBoxCommands(program3) {
42607
42899
  }
42608
42900
  const remote = command && command.length ? command.join(" ") : void 0;
42609
42901
  const wroteScript = o.ssh && o.script ? o.script : null;
42610
- if (wroteScript) (0, import_node_fs42.writeFileSync)(wroteScript, sshRecipeScript(found, remote), "utf8");
42902
+ if (wroteScript) (0, import_node_fs43.writeFileSync)(wroteScript, sshRecipeScript(found, remote), "utf8");
42611
42903
  if (o.json) {
42612
42904
  console.log(JSON.stringify({
42613
42905
  box: found,
@@ -42629,19 +42921,19 @@ ${SSH_RECIPE_AGENT_NOTE}`);
42629
42921
  // src/dist-drift.ts
42630
42922
  var import_node_child_process16 = require("node:child_process");
42631
42923
  var import_node_crypto13 = require("node:crypto");
42632
- var import_node_fs44 = require("node:fs");
42924
+ var import_node_fs45 = require("node:fs");
42633
42925
  var import_node_os20 = require("node:os");
42634
- var import_node_path41 = require("node:path");
42926
+ var import_node_path42 = require("node:path");
42635
42927
 
42636
42928
  // ../scripts/distribution-digest.mjs
42637
42929
  var import_node_crypto12 = require("node:crypto");
42638
- var import_node_fs43 = require("node:fs");
42639
- var import_node_path40 = require("node:path");
42930
+ var import_node_fs44 = require("node:fs");
42931
+ var import_node_path41 = require("node:path");
42640
42932
  var slash = (value) => value.replaceAll("\\", "/");
42641
42933
  function repoPath(root, declaredPath, label) {
42642
- const absoluteRoot = (0, import_node_path40.resolve)(root);
42643
- const target = (0, import_node_path40.resolve)(root, declaredPath);
42644
- if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path40.sep}`)) {
42934
+ const absoluteRoot = (0, import_node_path41.resolve)(root);
42935
+ const target = (0, import_node_path41.resolve)(root, declaredPath);
42936
+ if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path41.sep}`)) {
42645
42937
  throw new Error(`${label} ${declaredPath} escapes the repository root`);
42646
42938
  }
42647
42939
  return target;
@@ -42649,7 +42941,7 @@ function repoPath(root, declaredPath, label) {
42649
42941
  function digestFiles(files) {
42650
42942
  const hash = (0, import_node_crypto12.createHash)("sha256");
42651
42943
  for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
42652
- const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs43.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs43.readFileSync)(file.absolute);
42944
+ const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs44.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs44.readFileSync)(file.absolute);
42653
42945
  hash.update(file.relative, "utf8");
42654
42946
  hash.update("\0");
42655
42947
  hash.update(file.stat.isSymbolicLink() ? "symlink" : "file", "utf8");
@@ -42664,8 +42956,8 @@ function digestFiles(files) {
42664
42956
  function digestPackedFiles(packageRoot, packedFiles) {
42665
42957
  return digestFiles(packedFiles.map((path2) => {
42666
42958
  const absolute = repoPath(packageRoot, path2, "packed artifact identity path");
42667
- if (!(0, import_node_fs43.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
42668
- return { absolute, relative: slash(path2), stat: (0, import_node_fs43.lstatSync)(absolute) };
42959
+ if (!(0, import_node_fs44.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
42960
+ return { absolute, relative: slash(path2), stat: (0, import_node_fs44.lstatSync)(absolute) };
42669
42961
  }));
42670
42962
  }
42671
42963
 
@@ -42749,13 +43041,13 @@ function renderDistDriftReceipt(receipt) {
42749
43041
  return lines2;
42750
43042
  }
42751
43043
  function readOrNull(path2) {
42752
- return (0, import_node_fs44.existsSync)(path2) ? (0, import_node_fs44.readFileSync)(path2) : null;
43044
+ return (0, import_node_fs45.existsSync)(path2) ? (0, import_node_fs45.readFileSync)(path2) : null;
42753
43045
  }
42754
43046
  function walkFiles(root) {
42755
43047
  const files = [];
42756
43048
  const walk2 = (directory) => {
42757
- for (const entry of (0, import_node_fs44.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
42758
- const child2 = (0, import_node_path41.join)(directory, entry.name);
43049
+ for (const entry of (0, import_node_fs45.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
43050
+ const child2 = (0, import_node_path42.join)(directory, entry.name);
42759
43051
  if (entry.isDirectory()) walk2(child2);
42760
43052
  else files.push(child2);
42761
43053
  }
@@ -42765,10 +43057,10 @@ function walkFiles(root) {
42765
43057
  }
42766
43058
  function bomPathFor(root) {
42767
43059
  try {
42768
- const registry2 = JSON.parse((0, import_node_fs44.readFileSync)((0, import_node_path41.join)(root, "surfaces.json"), "utf8"));
42769
- return (0, import_node_path41.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
43060
+ const registry2 = JSON.parse((0, import_node_fs45.readFileSync)((0, import_node_path42.join)(root, "surfaces.json"), "utf8"));
43061
+ return (0, import_node_path42.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
42770
43062
  } catch {
42771
- return (0, import_node_path41.join)(root, "distribution-bom.json");
43063
+ return (0, import_node_path42.join)(root, "distribution-bom.json");
42772
43064
  }
42773
43065
  }
42774
43066
  function rebuildTo(packageRoot, outDir) {
@@ -42781,35 +43073,35 @@ function rebuildTo(packageRoot, outDir) {
42781
43073
  });
42782
43074
  }
42783
43075
  function runDistStatus(root) {
42784
- const stage = (0, import_node_fs44.mkdtempSync)((0, import_node_path41.join)((0, import_node_os20.tmpdir)(), "mmi-dist-drift-"));
43076
+ const stage = (0, import_node_fs45.mkdtempSync)((0, import_node_path42.join)((0, import_node_os20.tmpdir)(), "mmi-dist-drift-"));
42785
43077
  let overlayCount = 0;
42786
43078
  try {
42787
- const cliOut = (0, import_node_path41.join)(stage, "cli-dist");
42788
- const hubOut = (0, import_node_path41.join)(stage, "hub-dist");
42789
- rebuildTo((0, import_node_path41.join)(root, "cli"), cliOut);
42790
- rebuildTo((0, import_node_path41.join)(root, "updater"), hubOut);
43079
+ const cliOut = (0, import_node_path42.join)(stage, "cli-dist");
43080
+ const hubOut = (0, import_node_path42.join)(stage, "hub-dist");
43081
+ rebuildTo((0, import_node_path42.join)(root, "cli"), cliOut);
43082
+ rebuildTo((0, import_node_path42.join)(root, "updater"), hubOut);
42791
43083
  const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
42792
43084
  const rebuilt = (path2) => {
42793
43085
  const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
42794
- return spec ? readOrNull((0, import_node_path41.join)(outDirFor(spec.packageDir), spec.output)) : null;
43086
+ return spec ? readOrNull((0, import_node_path42.join)(outDirFor(spec.packageDir), spec.output)) : null;
42795
43087
  };
42796
- const committed = (path2) => readOrNull((0, import_node_path41.join)(root, path2));
42797
- const tree = (path2) => readOrNull((0, import_node_path41.join)(root, path2));
42798
- const distRoot = (0, import_node_path41.join)(root, "cli", "dist");
42799
- const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path41.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
42800
- const bom = JSON.parse((0, import_node_fs44.readFileSync)(bomPathFor(root), "utf8"));
43088
+ const committed = (path2) => readOrNull((0, import_node_path42.join)(root, path2));
43089
+ const tree = (path2) => readOrNull((0, import_node_path42.join)(root, path2));
43090
+ const distRoot = (0, import_node_path42.join)(root, "cli", "dist");
43091
+ const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path42.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
43092
+ const bom = JSON.parse((0, import_node_fs45.readFileSync)(bomPathFor(root), "utf8"));
42801
43093
  const digest = (entries) => {
42802
- const overlay = (0, import_node_path41.join)(stage, `overlay-${overlayCount++}`);
43094
+ const overlay = (0, import_node_path42.join)(stage, `overlay-${overlayCount++}`);
42803
43095
  for (const entry of entries) {
42804
- const target = (0, import_node_path41.join)(overlay, entry.path);
42805
- (0, import_node_fs44.mkdirSync)((0, import_node_path41.dirname)(target), { recursive: true });
42806
- (0, import_node_fs44.writeFileSync)(target, entry.bytes);
43096
+ const target = (0, import_node_path42.join)(overlay, entry.path);
43097
+ (0, import_node_fs45.mkdirSync)((0, import_node_path42.dirname)(target), { recursive: true });
43098
+ (0, import_node_fs45.writeFileSync)(target, entry.bytes);
42807
43099
  }
42808
43100
  return digestPackedFiles(overlay, entries.map((entry) => entry.path));
42809
43101
  };
42810
43102
  return computeDistDriftReceipt({ committed, tree, rebuilt, distTree, bom, digest });
42811
43103
  } finally {
42812
- (0, import_node_fs44.rmSync)(stage, { recursive: true, force: true });
43104
+ (0, import_node_fs45.rmSync)(stage, { recursive: true, force: true });
42813
43105
  }
42814
43106
  }
42815
43107
 
@@ -42888,7 +43180,7 @@ init_hub_auth();
42888
43180
 
42889
43181
  // src/schedules-lift-command.ts
42890
43182
  var import_promises8 = require("node:fs/promises");
42891
- var import_node_path42 = require("node:path");
43183
+ var import_node_path43 = require("node:path");
42892
43184
  init_clean_exit();
42893
43185
  init_cli_shared();
42894
43186
  var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
@@ -42917,7 +43209,7 @@ async function readWorkflowFiles(dir) {
42917
43209
  const files = [];
42918
43210
  for (const name of names.sort()) {
42919
43211
  if (!/\.ya?ml$/.test(name)) continue;
42920
- files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises8.readFile)((0, import_node_path42.join)(dir, name), "utf8") });
43212
+ files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises8.readFile)((0, import_node_path43.join)(dir, name), "utf8") });
42921
43213
  }
42922
43214
  return files;
42923
43215
  }
@@ -43000,8 +43292,8 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
43000
43292
 
43001
43293
  // src/spawn-policy-core.ts
43002
43294
  var import_node_child_process17 = require("node:child_process");
43003
- var import_node_fs45 = require("node:fs");
43004
- var import_node_path43 = require("node:path");
43295
+ var import_node_fs46 = require("node:fs");
43296
+ var import_node_path44 = require("node:path");
43005
43297
  var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
43006
43298
  var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
43007
43299
  var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
@@ -43087,7 +43379,7 @@ function runSpawnPolicy(root) {
43087
43379
  for (const file of files) {
43088
43380
  let raw;
43089
43381
  try {
43090
- raw = (0, import_node_fs45.readFileSync)((0, import_node_path43.join)(root, file), "utf8");
43382
+ raw = (0, import_node_fs46.readFileSync)((0, import_node_path44.join)(root, file), "utf8");
43091
43383
  } catch {
43092
43384
  continue;
43093
43385
  }
@@ -43107,19 +43399,19 @@ function runSpawnPolicy(root) {
43107
43399
  function registerDeveloperCommands(program3) {
43108
43400
  const rules = program3.command("rules").description("org-managed .gitignore delivery");
43109
43401
  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) => {
43110
- const path2 = (0, import_node_path44.join)(process.cwd(), ".gitignore");
43111
- const current = (0, import_node_fs46.existsSync)(path2) ? (0, import_node_fs46.readFileSync)(path2, "utf8") : null;
43402
+ const path2 = (0, import_node_path45.join)(process.cwd(), ".gitignore");
43403
+ const current = (0, import_node_fs47.existsSync)(path2) ? (0, import_node_fs47.readFileSync)(path2, "utf8") : null;
43112
43404
  const plan = planManagedGitignore(current);
43113
43405
  const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
43114
43406
  if (opts.json) {
43115
- if (opts.write && plan.changed) (0, import_node_fs46.writeFileSync)(path2, plan.content, "utf8");
43407
+ if (opts.write && plan.changed) (0, import_node_fs47.writeFileSync)(path2, plan.content, "utf8");
43116
43408
  console.log(JSON.stringify(plan, null, 2));
43117
43409
  if (!opts.write && plan.changed) process.exitCode = 1;
43118
43410
  return;
43119
43411
  }
43120
43412
  if (opts.write) {
43121
43413
  if (plan.changed) {
43122
- (0, import_node_fs46.writeFileSync)(path2, plan.content, "utf8");
43414
+ (0, import_node_fs47.writeFileSync)(path2, plan.content, "utf8");
43123
43415
  console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
43124
43416
  } else {
43125
43417
  console.log("mmi-cli devops org rules gitignore: up to date");
@@ -44753,8 +45045,8 @@ function checkHotfixCarries(options) {
44753
45045
  }
44754
45046
 
44755
45047
  // src/train-commands.ts
44756
- var import_node_fs47 = require("node:fs");
44757
- var import_node_path45 = require("node:path");
45048
+ var import_node_fs48 = require("node:fs");
45049
+ var import_node_path46 = require("node:path");
44758
45050
  init_cli_shared();
44759
45051
  init_clean_exit();
44760
45052
  init_client_version();
@@ -44769,7 +45061,7 @@ function resolveReleaseBumpIntent(raw) {
44769
45061
  }
44770
45062
  function readRepoVersion() {
44771
45063
  try {
44772
- return JSON.parse((0, import_node_fs47.readFileSync)((0, import_node_path45.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
45064
+ return JSON.parse((0, import_node_fs48.readFileSync)((0, import_node_path46.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
44773
45065
  } catch {
44774
45066
  return void 0;
44775
45067
  }
@@ -45735,7 +46027,7 @@ ${r.stderr ?? ""}`).catch(() => "");
45735
46027
  var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
45736
46028
  var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
45737
46029
  function envHealLockPath(home) {
45738
- return (0, import_node_path46.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
46030
+ return (0, import_node_path47.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
45739
46031
  }
45740
46032
  async function withEnvHealLock(what, run) {
45741
46033
  try {
@@ -45872,7 +46164,7 @@ function mmiDoctorDeps(opts = {}) {
45872
46164
  );
45873
46165
  const result = applyPluginCachePlan(
45874
46166
  plan,
45875
- (p) => (0, import_node_fs48.rmSync)(p, { recursive: true }),
46167
+ (p) => (0, import_node_fs49.rmSync)(p, { recursive: true }),
45876
46168
  stagingApplyFsGuard(configRoot)
45877
46169
  );
45878
46170
  return {
@@ -45907,7 +46199,7 @@ function mmiDoctorDeps(opts = {}) {
45907
46199
  // has no generated routing index would
45908
46200
  // get a permanent — demanding an artifact it never asked for.
45909
46201
  docsIndexState: (root) => {
45910
- if (!(0, import_node_fs48.existsSync)((0, import_node_path46.join)(root, DOCS_INDEX_PATH))) return void 0;
46202
+ if (!(0, import_node_fs49.existsSync)((0, import_node_path47.join)(root, DOCS_INDEX_PATH))) return void 0;
45911
46203
  const real = createDocsIndexDeps(root);
45912
46204
  let docs;
45913
46205
  const listDocs = () => docs ??= real.listDocs();
@@ -45916,7 +46208,7 @@ function mmiDoctorDeps(opts = {}) {
45916
46208
  },
45917
46209
  // #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
45918
46210
  healDocsIndex: (root) => {
45919
- if (!(0, import_node_fs48.existsSync)((0, import_node_path46.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
46211
+ if (!(0, import_node_fs49.existsSync)((0, import_node_path47.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
45920
46212
  const real = createDocsIndexDeps(root);
45921
46213
  let docs;
45922
46214
  const listDocs = () => docs ??= real.listDocs();
@@ -46542,7 +46834,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
46542
46834
  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`);
46543
46835
  if (o.secretsFile) {
46544
46836
  try {
46545
- vars.push(`secrets=${(0, import_node_fs48.readFileSync)(o.secretsFile, "utf8")}`);
46837
+ vars.push(`secrets=${(0, import_node_fs49.readFileSync)(o.secretsFile, "utf8")}`);
46546
46838
  } catch (e) {
46547
46839
  return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
46548
46840
  }
@@ -46875,16 +47167,16 @@ function ciAuditDeps2() {
46875
47167
  // gate re-seed step is skipped gracefully rather than failing mid-run.
46876
47168
  readSeedFile: (path2) => {
46877
47169
  if (!root) return null;
46878
- const fullPath = (0, import_node_path46.join)(root, path2);
46879
- return (0, import_node_fs48.existsSync)(fullPath) ? (0, import_node_fs48.readFileSync)(fullPath, "utf8") : null;
47170
+ const fullPath = (0, import_node_path47.join)(root, path2);
47171
+ return (0, import_node_fs49.existsSync)(fullPath) ? (0, import_node_fs49.readFileSync)(fullPath, "utf8") : null;
46880
47172
  }
46881
47173
  };
46882
47174
  }
46883
47175
  function hubRoot2() {
46884
- const fromPkg = (0, import_node_path46.join)(__dirname, "..", "..");
47176
+ const fromPkg = (0, import_node_path47.join)(__dirname, "..", "..");
46885
47177
  const marker = "skills/bootstrap/seeds/manifest.json";
46886
- if ((0, import_node_fs48.existsSync)((0, import_node_path46.join)(fromPkg, marker))) return fromPkg;
46887
- if ((0, import_node_fs48.existsSync)((0, import_node_path46.join)(process.cwd(), marker))) return process.cwd();
47178
+ if ((0, import_node_fs49.existsSync)((0, import_node_path47.join)(fromPkg, marker))) return fromPkg;
47179
+ if ((0, import_node_fs49.existsSync)((0, import_node_path47.join)(process.cwd(), marker))) return process.cwd();
46888
47180
  return null;
46889
47181
  }
46890
47182
  registerQueryCommands(program2);
@@ -46984,10 +47276,10 @@ access.command("audit").description("audit collaborator roles + train-branch pus
46984
47276
  targets = resolution.targets;
46985
47277
  }
46986
47278
  const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
46987
- const fileMatrix = (0, import_node_fs48.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs48.readFileSync)("access-matrix.json", "utf8")) : {};
47279
+ const fileMatrix = (0, import_node_fs49.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs49.readFileSync)("access-matrix.json", "utf8")) : {};
46988
47280
  const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
46989
47281
  const dataAccess = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
46990
- const sanctioned = (0, import_node_fs48.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs48.readFileSync)("access-matrix.json", "utf8")) : {};
47282
+ const sanctioned = (0, import_node_fs49.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs49.readFileSync)("access-matrix.json", "utf8")) : {};
46991
47283
  const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
46992
47284
  console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
46993
47285
  if (!report.ok) process.exitCode = 1;
@@ -47018,16 +47310,16 @@ function directoryBytes(path2) {
47018
47310
  let total = 0;
47019
47311
  let entries;
47020
47312
  try {
47021
- entries = (0, import_node_fs48.readdirSync)(path2, { withFileTypes: true });
47313
+ entries = (0, import_node_fs49.readdirSync)(path2, { withFileTypes: true });
47022
47314
  } catch {
47023
47315
  return 0;
47024
47316
  }
47025
47317
  for (const entry of entries) {
47026
- const child2 = (0, import_node_path46.join)(path2, entry.name);
47318
+ const child2 = (0, import_node_path47.join)(path2, entry.name);
47027
47319
  if (entry.isDirectory()) total += directoryBytes(child2);
47028
47320
  else {
47029
47321
  try {
47030
- total += (0, import_node_fs48.statSync)(child2).size;
47322
+ total += (0, import_node_fs49.statSync)(child2).size;
47031
47323
  } catch {
47032
47324
  }
47033
47325
  }
@@ -47035,25 +47327,25 @@ function directoryBytes(path2) {
47035
47327
  return total;
47036
47328
  }
47037
47329
  function listDirEntries(dir) {
47038
- return (0, import_node_fs48.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
47330
+ return (0, import_node_fs49.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
47039
47331
  }
47040
47332
  function readInstalledPluginRefs(configRoot) {
47041
47333
  const p = installedPluginsPathForConfig(configRoot);
47042
- if (!(0, import_node_fs48.existsSync)(p)) return [];
47334
+ if (!(0, import_node_fs49.existsSync)(p)) return [];
47043
47335
  try {
47044
- return installedPluginPaths((0, import_node_fs48.readFileSync)(p, "utf8"));
47336
+ return installedPluginPaths((0, import_node_fs49.readFileSync)(p, "utf8"));
47045
47337
  } catch {
47046
47338
  return null;
47047
47339
  }
47048
47340
  }
47049
47341
  function pluginCacheFsDeps(configRoot, dirBytes) {
47050
47342
  return {
47051
- exists: (p) => (0, import_node_fs48.existsSync)(p),
47052
- listVersionDirs: (root) => (0, import_node_fs48.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
47343
+ exists: (p) => (0, import_node_fs49.existsSync)(p),
47344
+ listVersionDirs: (root) => (0, import_node_fs49.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
47053
47345
  dirBytes,
47054
- listStagingDirs: (root) => (0, import_node_fs48.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
47346
+ listStagingDirs: (root) => (0, import_node_fs49.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
47055
47347
  try {
47056
- return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path46.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs48.statSync)(p).mtimeMs) };
47348
+ return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path47.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs49.statSync)(p).mtimeMs) };
47057
47349
  } catch {
47058
47350
  return { name: d.name, mtimeMs: Date.now() };
47059
47351
  }
@@ -47067,10 +47359,10 @@ function stagingApplyFsGuard(configRoot) {
47067
47359
  return {
47068
47360
  referencedPaths: () => readInstalledPluginRefs(configRoot),
47069
47361
  mtimeMs: (name) => {
47070
- const p = (0, import_node_path46.join)(stagingRoot, name);
47071
- if (!(0, import_node_fs48.existsSync)(p)) return null;
47362
+ const p = (0, import_node_path47.join)(stagingRoot, name);
47363
+ if (!(0, import_node_fs49.existsSync)(p)) return null;
47072
47364
  try {
47073
- return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs48.statSync)(q).mtimeMs);
47365
+ return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs49.statSync)(q).mtimeMs);
47074
47366
  } catch {
47075
47367
  return null;
47076
47368
  }
@@ -47096,7 +47388,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
47096
47388
  { withBytes: true, configRoot, includeStaging: surface !== "codex" }
47097
47389
  );
47098
47390
  const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
47099
- const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs48.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
47391
+ const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs49.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
47100
47392
  const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
47101
47393
  if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
47102
47394
  else console.log(renderPluginCachePlan(plan, result));