@mutmutco/cli 4.5.84 → 4.5.86

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 +111 -20
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -17268,10 +17268,10 @@ var rollout_plan_default = {
17268
17268
  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)."
17269
17269
  },
17270
17270
  baseline: {
17271
- version: "4.5.84",
17272
- tag: "v4.5.84",
17273
- commit: "86b566fd9857",
17274
- npm: "@mutmutco/cli@4.5.84"
17271
+ version: "4.5.86",
17272
+ tag: "v4.5.86",
17273
+ commit: "6485afaf5af8",
17274
+ npm: "@mutmutco/cli@4.5.86"
17275
17275
  },
17276
17276
  exitCriterion: "fleet-n-of-n",
17277
17277
  hubOnlyShortcut: "forbidden",
@@ -17288,14 +17288,14 @@ var rollout_plan_default = {
17288
17288
  repo: "mutmutco/mmi-hub",
17289
17289
  role: "canary",
17290
17290
  schedule: "train",
17291
- v3Target: "v4.5.84"
17291
+ v3Target: "v4.5.86"
17292
17292
  }
17293
17293
  ],
17294
17294
  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.",
17295
17295
  rollback: {
17296
17296
  independent: true,
17297
- mechanism: "npm dist-tag latest -> 4.5.84 and redeploy the Hub Lambda from tag v4.5.84 (86b566fd9857); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
17298
- v3Target: "v4.5.84 (@mutmutco/cli@4.5.84, tag commit 86b566fd9857 \u2014 last known-good release carrying the repo-index v4-only contract)"
17297
+ mechanism: "npm dist-tag latest -> 4.5.86 and redeploy the Hub Lambda from tag v4.5.86 (6485afaf5af8); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
17298
+ v3Target: "v4.5.86 (@mutmutco/cli@4.5.86, tag commit 6485afaf5af8 \u2014 last known-good release carrying the repo-index v4-only contract)"
17299
17299
  }
17300
17300
  },
17301
17301
  {
@@ -19607,6 +19607,7 @@ var TRAIN_COMMIT_STATUS_JQ = "[.statuses[]|{context:.context,state:.state,id:.id
19607
19607
  var TRAIN_PROTECTION_CONTEXTS_JQ = "[.contexts[]]";
19608
19608
  var TRAIN_RULES_CONTEXTS_JQ = '[.[]|select(.type=="required_status_checks")|.parameters.required_status_checks[].context]';
19609
19609
  var TRAIN_CHECK_ATTEMPTS = 40;
19610
+ var TRAIN_QUEUED_CHECK_ATTEMPTS = 80;
19610
19611
  var TRAIN_CHECK_DELAY_MS = 15e3;
19611
19612
  var TRAIN_PR_ONLY_CONTEXTS = /* @__PURE__ */ new Set(["add-to-project", "mark-merged-pr-done", "guard", "verdict"]);
19612
19613
  var TRAIN_PR_AUTOMATION_GRACE_ATTEMPTS = 3;
@@ -20011,10 +20012,14 @@ async function waitForRequiredTrainChecks(deps, ctx, sha, required, freshSince)
20011
20012
  const sleep2 = resolveSleep(deps);
20012
20013
  let lastStatus = "not checked";
20013
20014
  let lastError;
20015
+ let workflowRunRead = "not-read";
20016
+ let workflowRunNote = "";
20017
+ let workflowRunEverSeen = false;
20018
+ let hadQueuedWorkflowRun = false;
20014
20019
  const everObserved = /* @__PURE__ */ new Set();
20015
20020
  const autoSatisfied = /* @__PURE__ */ new Set();
20016
20021
  let fleetForeignStreak = 0;
20017
- for (let attempt = 0; attempt < TRAIN_CHECK_ATTEMPTS; attempt++) {
20022
+ for (let attempt = 0; attempt < TRAIN_CHECK_ATTEMPTS || hadQueuedWorkflowRun && attempt < TRAIN_QUEUED_CHECK_ATTEMPTS; attempt++) {
20018
20023
  if (attempt > 0) await sleep2(TRAIN_CHECK_DELAY_MS);
20019
20024
  let checkRuns;
20020
20025
  let statuses;
@@ -20032,6 +20037,40 @@ async function waitForRequiredTrainChecks(deps, ctx, sha, required, freshSince)
20032
20037
  lastError = e.message || String(e);
20033
20038
  continue;
20034
20039
  }
20040
+ try {
20041
+ const workflowOut = await deps.run("gh", [
20042
+ "run",
20043
+ "list",
20044
+ "--repo",
20045
+ ctx.repo,
20046
+ "--commit",
20047
+ sha,
20048
+ "--limit",
20049
+ "100",
20050
+ "--json",
20051
+ "databaseId,status,conclusion,headSha"
20052
+ ]);
20053
+ const parsedWorkflowRuns = JSON.parse(workflowOut);
20054
+ if (!Array.isArray(parsedWorkflowRuns)) throw new Error("workflow-runs response was not an array");
20055
+ const matchingRuns = parsedWorkflowRuns.filter((run) => !run.headSha || run.headSha === sha);
20056
+ if (matchingRuns.length) {
20057
+ workflowRunRead = "found";
20058
+ workflowRunEverSeen = true;
20059
+ const activeRun = matchingRuns.find((run) => run.status === "queued" || run.status === "in_progress");
20060
+ if (activeRun) {
20061
+ if (activeRun.status === "queued") hadQueuedWorkflowRun = true;
20062
+ workflowRunNote = activeRun.status === "queued" ? "Actions run is queued behind busy runners" : "Actions run is in progress";
20063
+ } else {
20064
+ workflowRunNote = "Actions run exists but has not produced the required check-run";
20065
+ }
20066
+ } else {
20067
+ workflowRunRead = "empty";
20068
+ workflowRunNote = "";
20069
+ }
20070
+ } catch (e) {
20071
+ workflowRunRead = "failed";
20072
+ workflowRunNote = `workflow-run lookup failed: ${e.message || String(e)}`;
20073
+ }
20035
20074
  for (const c of required) {
20036
20075
  if (checkRuns.some((r) => r.name === c) || statuses.some((s) => s.context === c)) everObserved.add(c);
20037
20076
  }
@@ -20069,11 +20108,14 @@ async function waitForRequiredTrainChecks(deps, ctx, sha, required, freshSince)
20069
20108
  return `required checks passed: ${pending.join(", ") || "(none pending)"}${satisfiedNote}${fleetNote}`;
20070
20109
  }
20071
20110
  }
20072
- const waitedMin = Math.round((TRAIN_CHECK_ATTEMPTS - 1) * TRAIN_CHECK_DELAY_MS / 6e4);
20111
+ const attempts = hadQueuedWorkflowRun ? TRAIN_QUEUED_CHECK_ATTEMPTS : TRAIN_CHECK_ATTEMPTS;
20112
+ const waitedMin = Math.round((attempts - 1) * TRAIN_CHECK_DELAY_MS / 6e4);
20073
20113
  const neverMaterialized = required.filter((c) => !everObserved.has(c) && !autoSatisfied.has(c));
20074
- const neverNote = neverMaterialized.length ? ` Never materialized on ${sha} (no check-run or commit status ever appeared \u2014 the workflow that produces them was likely not triggered by the tag event): ${neverMaterialized.join(", ")}.` : "";
20114
+ const noWorkflowRun = workflowRunRead === "empty" && !workflowRunEverSeen;
20115
+ const neverNote = neverMaterialized.length && noWorkflowRun ? ` No workflow run was found on ${sha} after a successful lookup, and no check-run or commit status appeared: ${neverMaterialized.join(", ")}.` : "";
20116
+ const runNote = workflowRunNote ? ` ${workflowRunNote}.` : "";
20075
20117
  throw new Error(
20076
- `timed out after ~${waitedMin}m (${TRAIN_CHECK_ATTEMPTS} attempts) waiting for required train checks on ${sha}.${neverNote} Last observed: ${lastError ? `error: ${lastError}` : lastStatus}`
20118
+ `timed out after ~${waitedMin}m (${attempts} attempts) waiting for required train checks on ${sha}.${neverNote}${runNote}${lastError ? ` Last read error: ${lastError}` : ` Last observed: ${lastStatus}`}`
20077
20119
  );
20078
20120
  }
20079
20121
  async function probeRemoteTag(deps, tag) {
@@ -20297,6 +20339,10 @@ function validSuiteCutContext(repo, context) {
20297
20339
  function authorisedSuiteCut(repo, meta, context) {
20298
20340
  return validSuiteCutContext(repo, context) && !!meta?.suite && context.lead.toLowerCase() === suiteLeadRepo(repo, meta.suite).toLowerCase();
20299
20341
  }
20342
+ function acceptedSuiteContext(repo, meta, context) {
20343
+ if (authorisedSuiteCut(repo, meta, context)) return true;
20344
+ return !!context && context.token === SUITE_LEAD_TOKEN && !!meta?.suite && !isBundleOnlySuiteMember(repo, meta.suite) && context.repo.toLowerCase() === repo.toLowerCase();
20345
+ }
20300
20346
  var BUNDLE_ONLY_RC_EXCEPTION = "suite member: rc is bundle-only (no rc runtime)";
20301
20347
  function isBundleOnlySuiteMember(repo, suite) {
20302
20348
  if (!suite) return false;
@@ -21747,7 +21793,7 @@ function partialTrainRecoveryError(cause, input) {
21747
21793
  const causeMessage = cause instanceof Error ? cause.message : String(cause);
21748
21794
  const branch = input.stage;
21749
21795
  const releaseState = input.stage === "rc" ? "GitHub Release n/a" : "GitHub Release not created";
21750
- const resumeCommand = input.stage === "rc" ? "mmi-cli devops rcand --resume" : "mmi-cli devops release --resume";
21796
+ const resumeCommand = input.stage === "rc" ? "mmi-cli devops rcand --resume" : `mmi-cli devops release --resume${input.suiteSolo ? " --suite-solo --owner-go" : ""}`;
21751
21797
  const tenant2 = input.deployModel === "tenant-container";
21752
21798
  const deployLine = tenant2 ? ` - deploy: dispatched by the train after the branch push; recover with \`mmi-cli devops runtime tenant redeploy ${input.repo} ${input.stage} --watch\` once the branch and release exist.` : ` - deploy: this repo is \`${input.deployModel ?? "not tenant-container"}\` \u2014 it has NO tenant runtime. Its own release/main-push workflows publish and deploy once the branch and Release exist. Do NOT run the tenant redeployer.`;
21753
21799
  const stateBlock = `partial train state for ${input.tag}:
@@ -24767,7 +24813,7 @@ async function runTrainDoctor(input) {
24767
24813
  const dev = input.dev === true && lane === "release";
24768
24814
  const suiteRefusal = suiteMemberRefusal(repo, meta, lane, input.suiteSolo === true, input.suiteCut);
24769
24815
  if (suiteRefusal) add({ code: "suite-member-solo", severity: "blocker", source: "origin", title: suiteRefusal, remedy: suiteRefusal });
24770
- if (input.suiteCut && !authorisedSuiteCut(repo, meta, input.suiteCut))
24816
+ if (input.suiteCut && !acceptedSuiteContext(repo, meta, input.suiteCut))
24771
24817
  add({ code: "suite-cut-context-invalid", severity: "blocker", source: "origin", title: "suite cut context does not match this member and its registered lead", remedy: "run from the MM-Work suite command with the member registered in META" });
24772
24818
  if (lane === "rcand" && track === "direct") {
24773
24819
  add({ code: "lane-mismatch", severity: "blocker", source: "origin", title: `${repo} is direct-track (no rc) \u2014 rcand does not apply`, remedy: "run `mmi-cli devops release --apply` from development instead of rcand" });
@@ -25485,7 +25531,7 @@ function enforceGateBudget(deps, repo) {
25485
25531
  async function preflight(deps, ctx, stage, meta, lane) {
25486
25532
  assertGitBashOnWindowsForJervHubRelease(ctx.repo);
25487
25533
  deps.warn?.("Release: Checking release prerequisites");
25488
- if (deps.suiteCut && !authorisedSuiteCut(ctx.repo, meta, deps.suiteCut))
25534
+ if (deps.suiteCut && !acceptedSuiteContext(ctx.repo, meta, deps.suiteCut))
25489
25535
  throw new Error(`${ctx.repo}: suite cut context does not match registry META; no deployment or tag was changed`);
25490
25536
  const model = requireDeployModel(meta, ctx.repo);
25491
25537
  if (model === "content") {
@@ -26154,7 +26200,7 @@ async function completeMainRelease(deps, ctx, meta, deployModel, watch, options,
26154
26200
  }
26155
26201
  let tagPush;
26156
26202
  let tagLedgerStarted = false;
26157
- const recoveryInput = { repo: ctx.repo, tag, stage: "main", deployModel, meta, branchHints };
26203
+ const recoveryInput = { repo: ctx.repo, tag, stage: "main", deployModel, suiteSolo: options.suiteSolo, meta, branchHints };
26158
26204
  try {
26159
26205
  await assertComputedReleaseTagStillNext(deps, tag, preOneZeroInForce(meta));
26160
26206
  if (deployModel === "hub-serverless") {
@@ -26745,7 +26791,7 @@ Nothing was written. Inspect ${ledger.path} (or clear it only after proving the
26745
26791
  }
26746
26792
  const steps = [];
26747
26793
  let requiredChecks;
26748
- const recoveryInput = { repo: ctx.repo, tag, stage: "main", deployModel, meta, branchHints };
26794
+ const recoveryInput = { repo: ctx.repo, tag, stage: "main", deployModel, suiteSolo: options.suiteSolo, meta, branchHints };
26749
26795
  try {
26750
26796
  requiredChecks = await discoverRequiredCheckContexts(deps, ctx, "main");
26751
26797
  } catch (e) {
@@ -46729,6 +46775,17 @@ async function prepareLaneCheckout(git3, repo, branch, lane) {
46729
46775
  const [head, origin] = (await git3(["rev-parse", "HEAD", `origin/${branch}`])).trim().split(/\s+/);
46730
46776
  if (!head || head !== origin) throw new Error(`${repo}: local ${branch} ${head?.slice(0, 12)} is not origin/${branch} ${origin?.slice(0, 12)}; stop`);
46731
46777
  }
46778
+ async function refreshSuiteCheckout(git3) {
46779
+ await git3(["fetch", "--prune", "origin"]);
46780
+ if ((await git3(["branch", "--remotes", "--list", "origin/rc"])).trim()) return;
46781
+ if (!(await git3(["branch", "--list", "rc"])).trim()) return;
46782
+ const upstream = (await git3(["for-each-ref", "--format=%(upstream:short)", "refs/heads/rc"])).trim();
46783
+ if (upstream !== "origin/rc") return;
46784
+ const sha = (await git3(["rev-parse", "--short=12", "refs/heads/rc"])).trim();
46785
+ let stale = `stale-rc-${sha}`;
46786
+ for (let suffix = 1; (await git3(["branch", "--list", stale])).trim(); suffix++) stale = `stale-rc-${sha}-${suffix}`;
46787
+ await git3(["branch", "--move", "rc", stale]);
46788
+ }
46732
46789
  function rcRunCauseFromLog(log) {
46733
46790
  const causes = [...new Set([...log.matchAll(/##\[error\](?!Process completed with exit code)([^\r\n]+)/g)].map((m) => m[1].trim()).filter(Boolean))];
46734
46791
  return causes.length ? `: ${causes.join("; ")}` : "";
@@ -46750,6 +46807,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}) {
46750
46807
  const origin = await exec("git", ["remote", "get-url", "origin"], target);
46751
46808
  if (!origin.toLowerCase().endsWith(`/${repo.toLowerCase()}.git`) && !origin.toLowerCase().endsWith(`/${repo.toLowerCase()}`))
46752
46809
  throw new Error(`${repo}: suite checkout origin mismatch at ${target}; stop`);
46810
+ await refreshSuiteCheckout((args) => exec("git", args, target, WATCH_TIMEOUT));
46753
46811
  return target;
46754
46812
  };
46755
46813
  const inTrain = async (repo, action, solo = false) => {
@@ -47766,6 +47824,16 @@ var surfaces_default = {
47766
47824
  "mmi-cli.cmd"
47767
47825
  ]
47768
47826
  },
47827
+ {
47828
+ mode: "files",
47829
+ sourcePath: "cli",
47830
+ targetPath: "packages/claude-plugin/cli",
47831
+ include: [
47832
+ "dist/index.cjs",
47833
+ "dist/main.cjs",
47834
+ "package.json"
47835
+ ]
47836
+ },
47769
47837
  {
47770
47838
  mode: "files",
47771
47839
  sourcePath: ".claude-plugin",
@@ -47841,6 +47909,16 @@ var surfaces_default = {
47841
47909
  "mmi-cli.cmd"
47842
47910
  ]
47843
47911
  },
47912
+ {
47913
+ mode: "files",
47914
+ sourcePath: "cli",
47915
+ targetPath: "packages/codex-plugin/cli",
47916
+ include: [
47917
+ "dist/index.cjs",
47918
+ "dist/main.cjs",
47919
+ "package.json"
47920
+ ]
47921
+ },
47844
47922
  {
47845
47923
  mode: "files",
47846
47924
  sourcePath: ".codex-plugin",
@@ -47916,6 +47994,16 @@ var surfaces_default = {
47916
47994
  "mmi-cli.cmd"
47917
47995
  ]
47918
47996
  },
47997
+ {
47998
+ mode: "files",
47999
+ sourcePath: "cli",
48000
+ targetPath: "packages/cursor-plugin/cli",
48001
+ include: [
48002
+ "dist/index.cjs",
48003
+ "dist/main.cjs",
48004
+ "package.json"
48005
+ ]
48006
+ },
47919
48007
  {
47920
48008
  mode: "files",
47921
48009
  sourcePath: ".cursor-plugin",
@@ -48132,7 +48220,7 @@ var surfaces_default = {
48132
48220
  ownerPath: "packages/claude-plugin/package.json",
48133
48221
  deliveryPath: "packages/claude-plugin/package.json",
48134
48222
  delivery: "npm",
48135
- applicability: "Claude Code plugin; npm package @mutmutco/claude-plugin carrying the plugin manifest, skills, hooks, gate scripts, and launchers. The thin git catalog stays in .claude-plugin/marketplace.json (#4945).",
48223
+ applicability: "Claude Code plugin; npm package @mutmutco/claude-plugin carrying the plugin manifest, skills, hooks, gate scripts, launchers, and the bundled mmi-cli engine the launchers run. The thin git catalog stays in .claude-plugin/marketplace.json (#4945).",
48136
48224
  versionCoordinated: true,
48137
48225
  surfaceToken: "claude",
48138
48226
  versionPaths: [
@@ -48148,7 +48236,8 @@ var surfaces_default = {
48148
48236
  additionalPaths: [
48149
48237
  "packages/claude-plugin/.claude-plugin",
48150
48238
  "packages/claude-plugin/skills",
48151
- "packages/claude-plugin/bin"
48239
+ "packages/claude-plugin/bin",
48240
+ "packages/claude-plugin/cli"
48152
48241
  ],
48153
48242
  artifactIdentity: {
48154
48243
  kind: "npm-pack",
@@ -48183,7 +48272,8 @@ var surfaces_default = {
48183
48272
  additionalPaths: [
48184
48273
  "packages/codex-plugin/.codex-plugin",
48185
48274
  "packages/codex-plugin/skills",
48186
- "packages/codex-plugin/bin"
48275
+ "packages/codex-plugin/bin",
48276
+ "packages/codex-plugin/cli"
48187
48277
  ],
48188
48278
  artifactIdentity: {
48189
48279
  kind: "npm-pack",
@@ -48214,7 +48304,8 @@ var surfaces_default = {
48214
48304
  additionalPaths: [
48215
48305
  "packages/cursor-plugin/.cursor-plugin",
48216
48306
  "packages/cursor-plugin/skills",
48217
- "packages/cursor-plugin/bin"
48307
+ "packages/cursor-plugin/bin",
48308
+ "packages/cursor-plugin/cli"
48218
48309
  ],
48219
48310
  artifactIdentity: {
48220
48311
  kind: "npm-pack",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.5.84",
3
+ "version": "4.5.86",
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",