@mutmutco/cli 4.3.10 → 4.3.12

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 +77 -13
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -10515,7 +10515,14 @@ var SESSION_ID_ENV_VARS = [
10515
10515
  "CODEX_THREAD_ID",
10516
10516
  "CURSOR_SESSION_ID",
10517
10517
  "HERMES_SESSION_ID",
10518
- "PI_SESSION_ID"
10518
+ "PI_SESSION_ID",
10519
+ // #6193: lane runners spawn the CLI through the host's extension shell operations, which strip
10520
+ // the PI_* session variables; the JervCode launcher's durable lane id (JERV_SESSION_ID) is what
10521
+ // survives in that env. Read it LAST so interactive invocations keep their own PI_SESSION_ID and
10522
+ // a stripped spawn still resolves to its owning lane — otherwise the actor degrades to a
10523
+ // synth/shell identity and the claim guard contests the caller's OWN lane (the #3414/#3438
10524
+ // refusals of jerv_worktree_land's `devops pr create` from the claimed task worktree).
10525
+ "JERV_SESSION_ID"
10519
10526
  ];
10520
10527
  var SYNTH_SESSION_PREFIX = "synth-";
10521
10528
  function isHostSessionId(session) {
@@ -12048,6 +12055,14 @@ async function postIssueComment(client, input) {
12048
12055
 
12049
12056
  // src/board-claim-move.ts
12050
12057
  var CLAIM_CONCURRENCY = 5;
12058
+ function withPowerShellChainHint(message2, platform2 = process.platform) {
12059
+ if (platform2 !== "win32") return message2;
12060
+ return `${message2}
12061
+ note: in PowerShell a ';' chain runs every statement regardless of the previous exit code, so the chain ran past this refusal \u2014 rerun the refused write solo or chain gated writes with '&&'`;
12062
+ }
12063
+ function isArchivedItemRefusal(message2) {
12064
+ return /The item is archived and cannot be updated/i.test(message2);
12065
+ }
12051
12066
  async function moveBoardItem(options, deps = {}) {
12052
12067
  if (!BOARD_STATUSES.includes(options.status)) {
12053
12068
  throw new Error(`unknown status '${options.status}'; expected one of ${BOARD_STATUSES.join(", ")}`);
@@ -12065,6 +12080,17 @@ async function moveBoardItem(options, deps = {}) {
12065
12080
  try {
12066
12081
  await updateItemSingleSelect(client, cfg.projectId, item.itemId, cfg.statusFieldId, optionId);
12067
12082
  } catch (e) {
12083
+ if (options.status === "Done" && isArchivedItemRefusal(ghError(e))) {
12084
+ return {
12085
+ item,
12086
+ viewer: lookup.viewer,
12087
+ repo: currentRepo,
12088
+ status: item.status,
12089
+ partial: false,
12090
+ archived: true,
12091
+ warning: `${item.ref} is archived (already terminal \u2014 its close archived the card); no ${options.status} move was performed`
12092
+ };
12093
+ }
12068
12094
  const warning = `partial move: ${item.ref} status was not changed to ${options.status} (${ghError(e)})`;
12069
12095
  if (!options.allowPartial) throw new Error(warning);
12070
12096
  return { item, viewer: lookup.viewer, repo: currentRepo, status: item.status, partial: true, warning };
@@ -12209,7 +12235,7 @@ async function claimOneBoardItem(ctx, selector, options) {
12209
12235
  }
12210
12236
  return;
12211
12237
  }
12212
- if (!options.force) throw new Error(laneContestMessage(item.ref, contest, "claim"));
12238
+ if (!options.force) throw new Error(withPowerShellChainHint(laneContestMessage(item.ref, contest, "claim")));
12213
12239
  previousHolder = displaced();
12214
12240
  };
12215
12241
  await refuseIfContested();
@@ -12392,7 +12418,7 @@ async function unclaimBoardIssue(options, deps = {}) {
12392
12418
  const toStatus = options.toStatus ?? "Todo";
12393
12419
  if (!options.force && item.contentType === "Issue") {
12394
12420
  const contest = await checkLaneContest(client, item);
12395
- if (contest.contested) throw new Error(laneContestMessage(item.ref, contest, "unclaim"));
12421
+ if (contest.contested) throw new Error(withPowerShellChainHint(laneContestMessage(item.ref, contest, "unclaim")));
12396
12422
  }
12397
12423
  try {
12398
12424
  await client.rest("DELETE", `repos/${item.repository}/issues/${item.number}/assignees`, {
@@ -15450,10 +15476,10 @@ var rollout_plan_default = {
15450
15476
  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)."
15451
15477
  },
15452
15478
  baseline: {
15453
- version: "4.3.10",
15454
- tag: "v4.3.10",
15455
- commit: "6fe2720dc6ef",
15456
- npm: "@mutmutco/cli@4.3.10"
15479
+ version: "4.3.12",
15480
+ tag: "v4.3.12",
15481
+ commit: "355e861fa08f",
15482
+ npm: "@mutmutco/cli@4.3.12"
15457
15483
  },
15458
15484
  exitCriterion: "fleet-n-of-n",
15459
15485
  hubOnlyShortcut: "forbidden",
@@ -15470,14 +15496,14 @@ var rollout_plan_default = {
15470
15496
  repo: "mutmutco/mmi-hub",
15471
15497
  role: "canary",
15472
15498
  schedule: "train",
15473
- v3Target: "v4.3.10"
15499
+ v3Target: "v4.3.12"
15474
15500
  }
15475
15501
  ],
15476
15502
  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.",
15477
15503
  rollback: {
15478
15504
  independent: true,
15479
- mechanism: "npm dist-tag latest -> 4.3.10 and redeploy the Hub Lambda from tag v4.3.10 (6fe2720dc6ef); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15480
- v3Target: "v4.3.10 (@mutmutco/cli@4.3.10, tag commit 6fe2720dc6ef \u2014 last known-good release carrying the repo-index v4-only contract)"
15505
+ mechanism: "npm dist-tag latest -> 4.3.12 and redeploy the Hub Lambda from tag v4.3.12 (355e861fa08f); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15506
+ v3Target: "v4.3.12 (@mutmutco/cli@4.3.12, tag commit 355e861fa08f \u2014 last known-good release carrying the repo-index v4-only contract)"
15481
15507
  }
15482
15508
  },
15483
15509
  {
@@ -25520,7 +25546,7 @@ function registerBoardCommands(program3) {
25520
25546
  const result = await moveBoardItem({ config: await loadConfigForBoardSelector2(issueRefs[0], o.repo), selector: issueRefs[0], status: canonicalStatus, repo: o.repo, allowPartial: o.allowPartial });
25521
25547
  invalidateStatuslineBoardCache();
25522
25548
  if (o.json) return console.log(JSON.stringify(result));
25523
- console.log(result.partial ? `Partially moved ${result.item.ref}: ${result.warning}` : `Moved ${result.item.ref} -> ${result.status}`);
25549
+ console.log(result.archived ? `Already terminal ${result.item.ref}: ${result.warning}` : result.partial ? `Partially moved ${result.item.ref}: ${result.warning}` : `Moved ${result.item.ref} -> ${result.status}`);
25524
25550
  } catch (e) {
25525
25551
  return failGraceful(`board move failed: ${e.message}`);
25526
25552
  }
@@ -33482,6 +33508,36 @@ async function fetchBranchTipSha(repo, ref, gh = defaultGhApi) {
33482
33508
  async function fetchRestPrSnapshot(prNumber, repo, gh = defaultGhApi) {
33483
33509
  return parseRestPrSnapshot(JSON.parse(await gh([`repos/${repo}/pulls/${prNumber}`])));
33484
33510
  }
33511
+ async function readRepoMergeSettingsOnce(repo, gh) {
33512
+ let raw;
33513
+ try {
33514
+ raw = await gh([`repos/${repo}`]);
33515
+ } catch (e) {
33516
+ return { proven: false, reason: `read failed: ${e.message}` };
33517
+ }
33518
+ let payload;
33519
+ try {
33520
+ payload = JSON.parse(raw);
33521
+ } catch {
33522
+ return { proven: false, reason: "payload was not JSON" };
33523
+ }
33524
+ if (typeof payload.delete_branch_on_merge === "boolean") {
33525
+ return { proven: true, deleteBranchOnMerge: payload.delete_branch_on_merge };
33526
+ }
33527
+ const apiMessage = typeof payload.message === "string" ? ` (${payload.message})` : "";
33528
+ return { proven: false, reason: `payload carried no delete_branch_on_merge${apiMessage}` };
33529
+ }
33530
+ async function ambientGhApi(args) {
33531
+ const { stdout } = await execFileP("gh", ["api", ...args], { timeout: REST_GH_TIMEOUT_MS });
33532
+ return stdout;
33533
+ }
33534
+ async function fetchRestRepoMergeSettings(repo, gh = defaultGhApi, ambientGh = ambientGhApi) {
33535
+ const primary = await readRepoMergeSettingsOnce(repo, gh);
33536
+ if (primary.proven) return primary;
33537
+ const fallback = await readRepoMergeSettingsOnce(repo, ambientGh);
33538
+ if (fallback.proven) return fallback;
33539
+ return { proven: false, reason: `${primary.reason}; ambient fallback: ${fallback.reason}` };
33540
+ }
33485
33541
  var PR_SNAPSHOT_READ_RETRIES = 3;
33486
33542
  var PR_SNAPSHOT_READ_DELAY_MS = 2e3;
33487
33543
  async function readRestPrSnapshotWithRetry(prNumber, repo, gh = defaultGhApi, options) {
@@ -36108,6 +36164,10 @@ async function advanceClosedIssuesToDone(deps) {
36108
36164
  const issue = `#${ref.number}`;
36109
36165
  try {
36110
36166
  const outcome = await deps.moveIssueToDone(issue);
36167
+ if (outcome.archived) {
36168
+ entries.push({ issue, moved: true, status: "archived-terminal" });
36169
+ continue;
36170
+ }
36111
36171
  if (!outcome.moved) {
36112
36172
  entries.push({ issue, moved: false, status: "failed", error: outcome.error });
36113
36173
  continue;
@@ -37015,6 +37075,7 @@ async function resolveBoardAdvanceForPr(prNumber, repoOption) {
37015
37075
  },
37016
37076
  moveIssueToDone: async (ref) => {
37017
37077
  const moved = await moveBoardItem({ config: await loadConfigForBoardSelector2(ref, repoOption), selector: ref, status: "Done", repo: repoOption, allowPartial: true });
37078
+ if (moved.archived) return { moved: true, archived: true };
37018
37079
  return moved.partial ? { moved: false, error: moved.warning } : { moved: true };
37019
37080
  },
37020
37081
  // #5317: after a successful move, re-read the board to verify Done actually holds — a move the API
@@ -39650,8 +39711,11 @@ ${list}`);
39650
39711
  if (closingGuardVerdict.message) console.warn(closingGuardVerdict.message);
39651
39712
  if (prMeta.state !== "MERGED" && !o.preserveWorktree) {
39652
39713
  if (!repoForPostCleanup) throw new Error("pr merge: repository is unreadable; cannot prove remote branch preservation");
39653
- const repoSettings = await defaultGitHubClient().rest("GET", `repos/${repoForPostCleanup}`);
39654
- assertRemoteBranchPreservedOnMerge(repoForPostCleanup, repoSettings.delete_branch_on_merge);
39714
+ const repoSettings = await fetchRestRepoMergeSettings(repoForPostCleanup);
39715
+ if (!repoSettings.proven) {
39716
+ throw new Error(`pr merge: ${repoForPostCleanup} cannot prove delete_branch_on_merge=false \u2014 ${repoSettings.reason}. Automatic deletion can irreversibly close stacked PRs (#5789).`);
39717
+ }
39718
+ assertRemoteBranchPreservedOnMerge(repoForPostCleanup, repoSettings.deleteBranchOnMerge);
39655
39719
  }
39656
39720
  const startingPath = (await execFileP("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
39657
39721
  const housekeeping = assertPrMergeHousekeepingClean(startingPath || process.cwd(), "pr merge", { force: o.force });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.3.10",
3
+ "version": "4.3.12",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",