@mutmutco/cli 3.128.0 → 3.130.0

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 +52 -168
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -16037,10 +16037,10 @@ var rollout_plan_default = {
16037
16037
  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)."
16038
16038
  },
16039
16039
  baseline: {
16040
- version: "3.128.0",
16041
- tag: "v3.128.0",
16042
- commit: "95241fa22be9",
16043
- npm: "@mutmutco/cli@3.128.0"
16040
+ version: "3.130.0",
16041
+ tag: "v3.130.0",
16042
+ commit: "4092383c82be",
16043
+ npm: "@mutmutco/cli@3.130.0"
16044
16044
  },
16045
16045
  exitCriterion: "fleet-n-of-n",
16046
16046
  hubOnlyShortcut: "forbidden",
@@ -16057,14 +16057,14 @@ var rollout_plan_default = {
16057
16057
  repo: "mutmutco/mmi-hub",
16058
16058
  role: "canary",
16059
16059
  schedule: "train",
16060
- v3Target: "v3.128.0"
16060
+ v3Target: "v3.130.0"
16061
16061
  }
16062
16062
  ],
16063
16063
  rollbackTrigger: "Any red inside the post-cut soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a v3 client refused while the compat window must still admit it (SUPPORTED_MINOR_WINDOW=2, MIN_CLIENT_VERSION 0.0.0 \u2014 D6a), or npm consumer install/doctor failure on the v4 dist.",
16064
16064
  rollback: {
16065
16065
  independent: true,
16066
- mechanism: "npm dist-tag latest -> 3.128.0 and redeploy the Hub Lambda from tag v3.128.0 (95241fa22be9); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
16067
- v3Target: "v3.128.0 (@mutmutco/cli@3.128.0, tag commit 95241fa22be9 \u2014 the preserved latest-v3 distribution, D6b)"
16066
+ mechanism: "npm dist-tag latest -> 3.130.0 and redeploy the Hub Lambda from tag v3.130.0 (4092383c82be); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
16067
+ v3Target: "v3.130.0 (@mutmutco/cli@3.130.0, tag commit 4092383c82be \u2014 the preserved latest-v3 distribution, D6b)"
16068
16068
  }
16069
16069
  },
16070
16070
  {
@@ -16253,7 +16253,7 @@ function formatTrainStatus(r) {
16253
16253
  "",
16254
16254
  `release track: ${r.releaseTrack} \u2014 stages: ${r.branches.join(" -> ")}`,
16255
16255
  `cli version: ${r.cliVersion}${r.repoVersion ? ` (repo: ${r.repoVersion})` : ""}${r.releasedVersion ? ` (released: ${r.releasedVersion})` : ""}`,
16256
- `release bump intent: ${r.bumpIntent ?? "not declared \u2014 set MMI_BUMP_INTENT=major|minor|patch before an ordinary train"}`
16256
+ `release bump intent: ${r.bumpIntent ?? "not declared \u2014 an ordinary train defaults to patch (#4929); set MMI_BUMP_INTENT=major|minor to override"}`
16257
16257
  ];
16258
16258
  if (r.versionLag.ok) {
16259
16259
  lines.push("version lag: none \u2014 CLI is current");
@@ -17065,7 +17065,7 @@ async function runIssueViewContext(deps, opts) {
17065
17065
  }
17066
17066
  async function runPrViewContext(deps, opts) {
17067
17067
  let fields = opts.jsonFields;
17068
- if (opts.comments || opts.context) fields = ensureField(fields, "comments");
17068
+ if (opts.comments || opts.context) fields = ensureField(ensureField(fields, "body"), "comments");
17069
17069
  if (opts.context) fields = ensureField(fields, "closingIssuesReferences");
17070
17070
  const base = await deps.ghJson(["pr", "view", String(opts.number), "--repo", opts.repo, "--json", fields], GH_LIST_TIMEOUT_MS);
17071
17071
  if (!opts.context) return base;
@@ -20417,21 +20417,7 @@ async function resolveTrainBumpIntent(deps, env = process.env) {
20417
20417
  }
20418
20418
  return explicit.major > latest.major ? "major" : explicit.minor > latest.minor ? "minor" : "patch";
20419
20419
  }
20420
- try {
20421
- return requireValue(
20422
- clean2(await deps.run("node", ["scripts/next-version.mjs", "intent"])),
20423
- "release bump intent"
20424
- );
20425
- } catch (e) {
20426
- const message = e instanceof Error ? e.message : String(e);
20427
- const usage = /usage: next-version\.mjs <([^>]+)>/.exec(message);
20428
- if (usage && !usage[1].split("|").includes("intent")) {
20429
- throw new Error(
20430
- `release bump intent: this repo's seeded scripts/next-version.mjs predates the \`intent\` verb (its usage lists <${usage[1]}>), so unset-intent derivation cannot run here. Declare the intent explicitly and rerun \u2014 MMI_BUMP_INTENT=major|minor|patch (or MMI_RELEASE_VERSION=X.Y.Z for an exact target); the train then resolves it in the CLI without shelling this script. The durable fix is refreshing the seeded script from the Hub via seed propagation.`
20431
- );
20432
- }
20433
- throw e;
20434
- }
20420
+ return "patch";
20435
20421
  }
20436
20422
  async function runTrainApply(command, deps, options = {}) {
20437
20423
  const watch = options.watch ?? false;
@@ -22817,9 +22803,6 @@ var BOARD_GIT_TIMEOUT_MS = 1e4;
22817
22803
  var WRITE_PROBE_CONCURRENCY = 8;
22818
22804
  var CLAIM_CONCURRENCY = 5;
22819
22805
  var BOARD_ACTIVE_READ_MAX_PAGES = 100;
22820
- var BOARD_PROJECTION_STALE_MS = 24 * 60 * 6e4;
22821
- var BOARD_PROJECTION_RECONCILE_STALE_MS = 3 * 60 * 6e4;
22822
- var BOARD_PROJECTION_TIMEOUT_MS = 8e3;
22823
22806
  var execFileP4 = (file, args, options = {}) => (
22824
22807
  // encoding 'utf8' guarantees string output at runtime; the cast pins the type (promisify's
22825
22808
  // overloads widen to string|Buffer when options is spread in).
@@ -23087,7 +23070,7 @@ function renderBoardItem(item) {
23087
23070
  return lines.join("\n");
23088
23071
  }
23089
23072
  function renderBoardReport(report) {
23090
- const lines = [`Board \xB7 ${report.project.title} \xB7 @${report.viewer}`, renderBoardSource(report)];
23073
+ const lines = [`Board \xB7 ${report.project.title} \xB7 @${report.viewer}`, renderBoardSource()];
23091
23074
  renderScope(lines, "PRIMARY", report.repo, report.primary, report.viewer);
23092
23075
  renderScope(lines, "SECONDARY", "Other repos on this project", report.secondary, report.viewer);
23093
23076
  if (report.warnings.length) {
@@ -23191,132 +23174,13 @@ async function resolveWritableReposForClaimables(items, client) {
23191
23174
  function writableOrUnknown(writable) {
23192
23175
  return /* @__PURE__ */ new Set([...writable.repos, ...writable.unknown]);
23193
23176
  }
23194
- async function defaultFetchBoardProjection(owner, number, baseUrl) {
23195
- if (!baseUrl) throw new Error("Hub API URL not configured");
23196
- const token = await hubAuthToken({ baseUrl, githubToken });
23197
- if (!token) throw new Error("no Hub session token");
23198
- const url = new URL("/board-projection", `${baseUrl.replace(/\/$/, "")}/`);
23199
- url.searchParams.set("owner", owner);
23200
- url.searchParams.set("number", String(number));
23201
- const res = await fetch(url, {
23202
- method: "GET",
23203
- headers: { ...clientVersionHeaders(), Authorization: `Bearer ${token}` },
23204
- signal: AbortSignal.timeout(BOARD_PROJECTION_TIMEOUT_MS)
23205
- });
23206
- if (res.status === 404) return null;
23207
- if (res.status === 426) throw new Error(upgradeRequiredError(res, await res.json().catch(() => null)));
23208
- if (!res.ok) throw new Error(`board projection HTTP ${res.status}`);
23209
- return await res.json();
23210
- }
23211
- function projectionItems(row) {
23212
- if (!row.items || typeof row.items !== "object" || Array.isArray(row.items)) {
23213
- throw new Error("malformed projection items");
23214
- }
23215
- const items = [];
23216
- for (const raw of Object.values(row.items)) {
23217
- if (!raw || typeof raw !== "object") throw new Error("malformed projection item");
23218
- if (raw.tombstone === true) continue;
23219
- const item = raw;
23220
- if (typeof item.itemId !== "string" || typeof item.contentId !== "string" || item.contentType !== "Issue" && item.contentType !== "PullRequest" || typeof item.repository !== "string" || typeof item.number !== "number" || !Number.isFinite(item.number) || typeof item.ref !== "string" || typeof item.url !== "string" || typeof item.title !== "string" || typeof item.state !== "string" || !BOARD_STATUSES.includes(item.status) || !Array.isArray(item.assignees) || item.assignees.some((value) => typeof value !== "string") || !Array.isArray(item.labels) || item.labels.some((value) => typeof value !== "string") || typeof item.sourceTimestamp !== "string") {
23221
- throw new Error("malformed projection item");
23222
- }
23223
- if (item.state.toUpperCase() === "CLOSED" || item.status === "Done") continue;
23224
- items.push(withLoopState({
23225
- itemId: item.itemId,
23226
- contentId: item.contentId,
23227
- contentType: item.contentType,
23228
- repository: item.repository,
23229
- number: item.number,
23230
- ref: item.ref,
23231
- url: item.url,
23232
- title: item.title,
23233
- state: item.state,
23234
- // #4441: the reconciler stamps each item's content updatedAt as sourceTimestamp — the same
23235
- // idle-age evidence the live read carries, so projection reads derive staleness too.
23236
- updatedAt: item.sourceTimestamp,
23237
- status: item.status,
23238
- statusOptionId: item.statusOptionId,
23239
- priority: item.priority,
23240
- priorityOptionId: item.priorityOptionId,
23241
- assignees: [...item.assignees],
23242
- labels: [...item.labels],
23243
- type: item.type
23244
- }));
23245
- }
23246
- return items;
23247
- }
23248
- async function readViewerLogin(client) {
23249
- const user = await client.rest("GET", "user");
23250
- if (!user?.login) throw new Error("viewer login unavailable");
23251
- return user.login;
23252
- }
23253
- function projectionFreshness(row, now) {
23254
- const projectedAtMs = Date.parse(row.watermark?.projectedAt);
23255
- const sourceTimestampMs = Date.parse(row.watermark?.sourceTimestamp);
23256
- if (!Number.isFinite(projectedAtMs) || !Number.isFinite(sourceTimestampMs)) {
23257
- throw new Error("malformed projection watermark");
23258
- }
23259
- const projectedAgeMs = Math.max(0, now - projectedAtMs);
23260
- const sourceAgeMs = Math.max(0, now - sourceTimestampMs);
23261
- return {
23262
- projectedAt: row.watermark.projectedAt,
23263
- projectedAgeMs,
23264
- sourceTimestamp: row.watermark.sourceTimestamp,
23265
- sourceAgeMs,
23266
- // #4774: health must agree with BOTH ages a caller can see on the source line — a projection whose
23267
- // rebuild (projectedAt) went stale is exactly as unreliable as one whose source events did, even
23268
- // while recent webhook activity keeps sourceAgeMs looking fresh.
23269
- health: row.watermark.health === "stale" || sourceAgeMs > BOARD_PROJECTION_STALE_MS || projectedAgeMs > BOARD_PROJECTION_RECONCILE_STALE_MS ? "stale" : "fresh"
23270
- };
23271
- }
23272
- function renderBoardSource(report) {
23273
- if (report.source === "projection" && report.freshness) {
23274
- return `source: projection \xB7 projected ${formatClaimAge(report.freshness.projectedAgeMs)} ago \xB7 source events through ${report.freshness.sourceTimestamp} \xB7 health ${report.freshness.health}`;
23275
- }
23276
- return `source: live${report.sourceReason ? ` (${report.sourceReason})` : ""}`;
23177
+ function renderBoardSource() {
23178
+ return "source: live";
23277
23179
  }
23278
23180
  async function readBoard(options, deps = {}) {
23279
23181
  const cfg = resolveBoardConfig(options.config);
23280
23182
  const client = deps.client ?? defaultGitHubClient();
23281
- let collected;
23282
- let source = "live";
23283
- let freshness = null;
23284
- let sourceReason;
23285
- if (!options.live) {
23286
- try {
23287
- const baseUrl = options.config.sagaApiUrl;
23288
- const row = await (deps.fetchProjection ?? defaultFetchBoardProjection)(cfg.projectOwner, cfg.projectNumber, baseUrl);
23289
- if (!row) {
23290
- sourceReason = "projection absent";
23291
- } else if (row.schemaVersion !== 1) {
23292
- sourceReason = `unsupported projection schema ${String(row.schemaVersion)}`;
23293
- } else if (row.watermark?.health === "oversize") {
23294
- sourceReason = "projection oversize";
23295
- } else {
23296
- const [viewer, repo] = await Promise.all([
23297
- readViewerLogin(client),
23298
- resolveCurrentRepo(options, deps)
23299
- ]);
23300
- const now = (deps.now ?? Date.now)();
23301
- collected = {
23302
- items: projectionItems(row),
23303
- viewer,
23304
- repo,
23305
- projectId: cfg.projectId,
23306
- projectTitle: String(cfg.projectNumber),
23307
- warnings: [],
23308
- partial: false
23309
- };
23310
- freshness = projectionFreshness(row, now);
23311
- source = "projection";
23312
- }
23313
- } catch {
23314
- sourceReason = "projection read error";
23315
- }
23316
- }
23317
- if (!collected) {
23318
- collected = await collectBoardItems(cfg, { repo: options.repo, allowPartial: options.allowPartial, activeOnly: true }, deps);
23319
- }
23183
+ const collected = await collectBoardItems(cfg, { repo: options.repo, allowPartial: options.allowPartial, activeOnly: true }, deps);
23320
23184
  const writable = await resolveWritableReposForClaimables(collected.items, client);
23321
23185
  collected.warnings.push(...writable.warnings);
23322
23186
  collected.partial = collected.partial || writable.partial;
@@ -23328,9 +23192,7 @@ async function readBoard(options, deps = {}) {
23328
23192
  ...groups,
23329
23193
  warnings: collected.warnings,
23330
23194
  partial: collected.partial,
23331
- source,
23332
- freshness,
23333
- ...sourceReason ? { sourceReason } : {}
23195
+ source: "live"
23334
23196
  };
23335
23197
  if (options.includeBundleDetails || options.includeAllBodies) {
23336
23198
  await attachBundleDetails(report, client, options.allowPartial ?? false, { all: options.includeAllBodies });
@@ -32691,8 +32553,7 @@ function registerBoardCommands(program3) {
32691
32553
  repo: o.repo,
32692
32554
  includeBundleDetails: o.bundleDetails,
32693
32555
  includeAllBodies: o.bodies,
32694
- allowPartial: o.allowPartial,
32695
- live: o.live
32556
+ allowPartial: o.allowPartial
32696
32557
  });
32697
32558
  console.log(o.json ? JSON.stringify(report) : renderBoardReport(report));
32698
32559
  } catch (e) {
@@ -32703,7 +32564,7 @@ function registerBoardCommands(program3) {
32703
32564
  return alreadyClaimed ? `Check ${ref}: claimed and In Progress, no live contest - claim would renew the lease (nothing written)` : `Check ${ref}: free - claim would proceed (nothing written)`;
32704
32565
  }
32705
32566
  const board = program3.command("board").description("read, claim, show, and move Project v2 work items for the current repo");
32706
- board.command("read", { isDefault: true }).description("read the board and print user-owned, claimable, and taken items").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo (defaults to git origin)").option("--bundle-details", "fetch body/comments only for user-owned and claimable issues").option("--bodies", "fetch body/comments for EVERY scoped row, including taken and unowned in-flight ones \u2014 for consumers that scope by Status rather than ownership (#4861); implies --bundle-details and costs one extra read per row").option("--allow-partial", "return partial board results when later page/detail reads fail").option("--live", "bypass the projection and read the authoritative GitHub Project v2 board").addHelpText("after", "\nBy default, read uses the explicitly-stale Hub projection and prints its watermark. --live is authoritative.\n--allow-partial applies to the live paginated path and detail reads; the projection row itself is atomic.\n").action((o) => runBoardRead(o));
32567
+ board.command("read", { isDefault: true }).description("read the board and print user-owned, claimable, and taken items").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo (defaults to git origin)").option("--bundle-details", "fetch body/comments only for user-owned and claimable issues").option("--bodies", "fetch body/comments for EVERY scoped row, including taken and unowned in-flight ones \u2014 for consumers that scope by Status rather than ownership (#4861); implies --bundle-details and costs one extra read per row").option("--allow-partial", "return partial board results when later page/detail reads fail").addHelpText("after", "\nread is always the authoritative live GitHub Project v2 board (#4926).\n--allow-partial applies to the paginated path and detail reads.\n").action((o) => runBoardRead(o));
32707
32568
  withExamples(mutating(
32708
32569
  board.command("claim <issues...>").description("claim issues: assign them and move their Project v2 Status to In Progress \u2014 idempotent, so an item already yours and In Progress succeeds unchanged (one or more refs)").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo for local issue numbers (defaults to git origin)").option("--for <login>", "assign to this login instead of @me \u2014 agent claims on behalf of the master").option("--force", "take an item already claimed by another lane that shows live evidence of active work (#3727)").option("--check", "read-only: run every claim gate and report the verdict, writing nothing \u2014 exits 1 with the same refusal a real claim would raise (#4511)").option("--allow-partial", "return success JSON if assignment succeeds but the status move fails"),
32709
32570
  (_opts, args) => ({ command: "board claim", issues: args[0] ?? [] })
@@ -36286,12 +36147,8 @@ var LOOP_PLAYBOOKS = {
36286
36147
  ]
36287
36148
  }
36288
36149
  };
36289
- function formatExplainCommand(cmd, rootName) {
36150
+ function formatOwnDetail(cmd) {
36290
36151
  const lines = [];
36291
- if (cmd.description) {
36292
- lines.push(cmd.description);
36293
- lines.push("");
36294
- }
36295
36152
  const args = cmd.arguments;
36296
36153
  if (args.length) {
36297
36154
  lines.push("Arguments:");
@@ -36302,10 +36159,10 @@ function formatExplainCommand(cmd, rootName) {
36302
36159
  }
36303
36160
  lines.push("");
36304
36161
  }
36305
- if (cmd.options.length) {
36162
+ const ownOptions = cmd.options.filter((opt) => opt.flags !== "--help" && opt.flags !== "-V, --version");
36163
+ if (ownOptions.length) {
36306
36164
  lines.push("Options:");
36307
- for (const opt of cmd.options) {
36308
- if (opt.flags === "--help" || opt.flags === "-V, --version") continue;
36165
+ for (const opt of ownOptions) {
36309
36166
  const tags = [];
36310
36167
  if (opt.mandatory) tags.push("required");
36311
36168
  if (opt.default !== void 0) tags.push(`default: ${JSON.stringify(opt.default)}`);
@@ -36330,6 +36187,15 @@ function formatExplainCommand(cmd, rootName) {
36330
36187
  }
36331
36188
  lines.push("");
36332
36189
  }
36190
+ return lines;
36191
+ }
36192
+ function formatExplainCommand(cmd, rootName) {
36193
+ const lines = [];
36194
+ if (cmd.description) {
36195
+ lines.push(cmd.description);
36196
+ lines.push("");
36197
+ }
36198
+ lines.push(...formatOwnDetail(cmd));
36333
36199
  return lines.join("\n").trimEnd();
36334
36200
  }
36335
36201
  function formatExplainGroup(cmd, rootName) {
@@ -36337,9 +36203,10 @@ function formatExplainGroup(cmd, rootName) {
36337
36203
  const lines = [
36338
36204
  `${rootName} ${canonical(cmd)}${cmd.description ? ` \u2014 ${cmd.description}` : ""}`,
36339
36205
  `Category: ${cmd.category} \xB7 Discovery: ${cmd.discovery}`,
36340
- "",
36341
- "Commands:"
36206
+ ""
36342
36207
  ];
36208
+ lines.push(...formatOwnDetail(cmd));
36209
+ lines.push("Commands:");
36343
36210
  for (const child2 of cmd.subcommands) {
36344
36211
  const args = child2.arguments.map((arg) => {
36345
36212
  const name = arg.variadic ? `${arg.name}...` : arg.name;
@@ -43860,6 +43727,23 @@ installProcessBackstop();
43860
43727
  consolidateCommandNamespaces(program2);
43861
43728
  registerOrgHealthQuery(program2);
43862
43729
  applyCommandTaxonomy(program2);
43730
+ function registeredPathPrefix(path2) {
43731
+ const tokens2 = path2.split(" ");
43732
+ let node = program2;
43733
+ let depth = 0;
43734
+ while (depth < tokens2.length) {
43735
+ const next = node.commands.find((c) => c.name() === tokens2[depth]);
43736
+ if (!next) break;
43737
+ node = next;
43738
+ depth += 1;
43739
+ }
43740
+ return tokens2.slice(0, Math.max(depth, 1)).join(" ");
43741
+ }
43742
+ function recoveryPointerFor(path2) {
43743
+ const runnable = registeredPathPrefix(path2);
43744
+ if (runnable === path2) return `run: mmi-cli ${canonicalPathFor(runnable)} \u2026`;
43745
+ return `'${path2}' is not a registered command \u2014 run \`mmi-cli ${canonicalPathFor(runnable)} --help\` for what lives under it`;
43746
+ }
43863
43747
  function resolveHouseShim(argv) {
43864
43748
  const houseToken = argv[2];
43865
43749
  if (!houseToken) return;
@@ -43869,7 +43753,7 @@ function resolveHouseShim(argv) {
43869
43753
  const flatHouse = houseForPath(flatPath);
43870
43754
  if (!flatHouse || flatHouse === "core") return;
43871
43755
  process.stderr.write(
43872
- `mmi-cli: the flat '${flatPath}' alias was removed in ${COMPAT_SHIM_DEATH_WAVE} (#4316) \u2014 run: mmi-cli ${canonicalPathFor(flatPath)} \u2026
43756
+ `mmi-cli: the flat '${flatPath}' alias was removed in ${COMPAT_SHIM_DEATH_WAVE} (#4316) \u2014 ${recoveryPointerFor(flatPath)}
43873
43757
  `
43874
43758
  );
43875
43759
  hardExit(2);
@@ -43893,7 +43777,7 @@ function resolveHouseShim(argv) {
43893
43777
  }
43894
43778
  if (actual) {
43895
43779
  process.stderr.write(
43896
- `mmi-cli ${houseToken}: '${lookupPath}' lives in house '${actual}' \u2014 run: mmi-cli ${canonicalPathFor(lookupPath)} \u2026
43780
+ `mmi-cli ${houseToken}: '${lookupPath}' lives in house '${actual}' \u2014 ${recoveryPointerFor(lookupPath)}
43897
43781
  `
43898
43782
  );
43899
43783
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "3.128.0",
3
+ "version": "3.130.0",
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",