@mutmutco/cli 3.83.0 → 3.84.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 +72 -16
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -8624,16 +8624,33 @@ function partialTrainRecoveryError(cause, input) {
8624
8624
  const resumeCommand = input.stage === "rc" ? "mmi-cli rcand --resume" : "mmi-cli release --resume";
8625
8625
  const tenant2 = input.deployModel === "tenant-container";
8626
8626
  const deployLine = tenant2 ? ` - deploy: dispatched by the train after the branch push; recover with \`mmi-cli 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.`;
8627
- return new Error(
8628
- `${causeMessage}
8629
-
8630
- partial train state for ${input.tag}:
8627
+ const stateBlock = `partial train state for ${input.tag}:
8631
8628
  - tag ${input.tag}: PUSHED to origin
8632
8629
  - origin/${branch}: NOT pushed
8633
8630
  - ${releaseState}
8634
- ` + deployLine + `
8631
+ ` + deployLine;
8632
+ const failedHubMainGate = input.stage === "main" && input.repo.toLowerCase() === HUB_REPO.toLowerCase() && /^required train check failed:/i.test(causeMessage);
8633
+ if (failedHubMainGate) {
8634
+ return new Error(
8635
+ `${causeMessage}
8636
+
8637
+ ` + stateBlock + `
8635
8638
 
8636
- The tag is public and correct \u2014 do not delete or force-move it.
8639
+ The tag is public and immutable, but it FAILED a required gate. Do not resume it unchanged: \`${resumeCommand}\` cannot alter tagged code or make the failed check pass.
8640
+
8641
+ Diagnose the failed check, then choose the proven recovery:
8642
+ - transient workflow failure: rerun the failed tag workflow; only after it passes, use \`${resumeCommand}\`.
8643
+ - tagged-code defect: obtain fresh destructive approval to remove ${input.tag}, run \`mmi-cli release --abort --apply\`, repair development, and cut the release again through the guarded train.
8644
+
8645
+ Do not push main or create the GitHub Release by hand.`
8646
+ );
8647
+ }
8648
+ return new Error(
8649
+ `${causeMessage}
8650
+
8651
+ ` + stateBlock + `
8652
+
8653
+ The tag is public and immutable \u2014 do not delete or force-move it.
8637
8654
 
8638
8655
  Resume it: ${resumeCommand}
8639
8656
 
@@ -23049,7 +23066,7 @@ function renderBootstrapVerifyReport(report) {
23049
23066
  // src/bootstrap-org-ruleset.ts
23050
23067
  var ORG_NO_AGENT_FILES_RULESET_NAME = "mmi-no-agent-files-org";
23051
23068
  var ORG_LOGIN = "mutmutco";
23052
- var ORG_NO_AGENT_FILES_EXCLUDED_REPOS = ["Jerv-PowerTools"];
23069
+ var ORG_NO_AGENT_FILES_EXCLUDED_REPOS = ["Jerv-PowerTools", "Jerv-JervCode"];
23053
23070
  var NO_AGENT_FILES_GLOBS = [
23054
23071
  "AGENTS.md",
23055
23072
  "**/AGENTS.md",
@@ -23126,8 +23143,8 @@ OK \u2014 live ruleset matches the codified IaC (${NO_AGENT_FILES_GLOBS.length}
23126
23143
  `${head}`,
23127
23144
  verb,
23128
23145
  ...plan.drift.map((d) => ` - ${d}`),
23129
- "This command is read-only; it never mutates the live ruleset. Recreate/repair via the GitHub UI or a",
23130
- "deliberate POST/PUT of orgNoAgentFilesRulesetDesired() \u2014 see cli/src/bootstrap-org-ruleset.ts."
23146
+ "Read-only preview \u2014 a master-admin may reconcile this exact desired state with",
23147
+ "`mmi-cli bootstrap org-ruleset --apply`."
23131
23148
  ].join("\n");
23132
23149
  }
23133
23150
  async function fetchOrgNoAgentFilesRuleset(client, org = ORG_LOGIN) {
@@ -23136,6 +23153,27 @@ async function fetchOrgNoAgentFilesRuleset(client, org = ORG_LOGIN) {
23136
23153
  if (summary?.id == null) return null;
23137
23154
  return await client.rest("GET", `orgs/${org}/rulesets/${summary.id}`, { timeoutMs: 2e4 }) ?? null;
23138
23155
  }
23156
+ async function reconcileOrgNoAgentFilesRuleset(plan, client, org = ORG_LOGIN) {
23157
+ if (plan.action === "noop") {
23158
+ return { action: "noop", id: plan.id, verified: true, desired: plan.desired };
23159
+ }
23160
+ if (plan.action === "create") {
23161
+ await client.rest("POST", `orgs/${org}/rulesets`, { body: plan.desired, timeoutMs: 2e4 });
23162
+ } else {
23163
+ if (plan.id == null) throw new Error(`cannot update '${plan.desired.name}': live ruleset id is missing`);
23164
+ await client.rest("PUT", `orgs/${org}/rulesets/${plan.id}`, { body: plan.desired, timeoutMs: 2e4 });
23165
+ }
23166
+ const verified = planOrgNoAgentFilesRuleset(await fetchOrgNoAgentFilesRuleset(client, org));
23167
+ if (verified.action !== "noop") {
23168
+ throw new Error(`org ruleset reconcile did not settle: ${verified.drift.join("; ")}`);
23169
+ }
23170
+ return {
23171
+ action: plan.action === "create" ? "created" : "updated",
23172
+ id: verified.id,
23173
+ verified: true,
23174
+ desired: verified.desired
23175
+ };
23176
+ }
23139
23177
 
23140
23178
  // src/bootstrap-commands.ts
23141
23179
  function registerBootstrapCommands(program3) {
@@ -23190,13 +23228,28 @@ function registerBootstrapCommands(program3) {
23190
23228
  console.log(o.json ? JSON.stringify(report, null, 2) : renderBootstrapVerifyReport(report));
23191
23229
  if (!report.ok) process.exitCode = 1;
23192
23230
  });
23193
- bootstrap.command("org-ruleset").description("drift-check the codified org no-agent-files push ruleset (mmi-no-agent-files-org) against live; read-only, never mutates (#2196)").option("--json", "machine-readable output").action(async () => {
23231
+ bootstrap.command("org-ruleset").description("drift-check the codified org no-agent-files push ruleset; optionally reconcile it under master authority (#2196/#4001)").option("--json", "machine-readable output").option("--apply", "create or update the one named org ruleset to exact desired state (master-admin required)").action(async () => {
23194
23232
  const json = rawFlag("--json");
23195
- const live = await fetchOrgNoAgentFilesRuleset(defaultGitHubClient());
23233
+ const apply = rawFlag("--apply");
23234
+ const client = defaultGitHubClient();
23235
+ const live = await fetchOrgNoAgentFilesRuleset(client);
23196
23236
  const plan = planOrgNoAgentFilesRuleset(live);
23197
- if (json) console.log(JSON.stringify({ action: plan.action, id: plan.id, drift: plan.drift, desired: plan.desired }, null, 2));
23198
- else console.log(renderOrgRulesetDriftReport(plan));
23199
- if (plan.action !== "noop") process.exitCode = 1;
23237
+ if (!apply) {
23238
+ if (json) console.log(JSON.stringify({ action: plan.action, id: plan.id, drift: plan.drift, desired: plan.desired }, null, 2));
23239
+ else console.log(renderOrgRulesetDriftReport(plan));
23240
+ if (plan.action !== "noop") process.exitCode = 1;
23241
+ return;
23242
+ }
23243
+ const verdict = await fetchTrainAuthority("mutmutco/MMI-Hub", registryClientDeps(await loadConfig()));
23244
+ if (!verdict.ok || verdict.authority.role !== "master") {
23245
+ return fail("bootstrap org-ruleset --apply: master-admin required");
23246
+ }
23247
+ const result = await reconcileOrgNoAgentFilesRuleset(plan, client);
23248
+ if (json) console.log(JSON.stringify(result, null, 2));
23249
+ else {
23250
+ console.log(`mmi-cli bootstrap org-ruleset --apply: ${result.action}`);
23251
+ console.log(renderOrgRulesetDriftReport(planOrgNoAgentFilesRuleset(await fetchOrgNoAgentFilesRuleset(client))));
23252
+ }
23200
23253
  });
23201
23254
  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 () => {
23202
23255
  const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
@@ -25622,10 +25675,13 @@ async function bestEffortGit(args, cwd, step, timeoutMs = GIT_TIMEOUT_MS) {
25622
25675
  await execFileP2("git", fullArgs, { timeout: timeoutMs });
25623
25676
  return { step, status: "done" };
25624
25677
  } catch (e) {
25625
- const msg = e.message.split("\n")[0];
25626
- if (/not found|already|no such|does not exist|not a valid|couldn't|unable to resolve/i.test(msg)) {
25678
+ const err = e;
25679
+ const detail = `${err.message}
25680
+ ${err.stderr ?? ""}`;
25681
+ if (/not found|already|no such|does not exist|not a valid|couldn't|unable to resolve/i.test(detail)) {
25627
25682
  return { step, status: "already gone" };
25628
25683
  }
25684
+ const msg = (err.stderr?.trim() || err.message).split("\n")[0];
25629
25685
  return { step, status: `failed: ${msg}` };
25630
25686
  }
25631
25687
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "3.83.0",
3
+ "version": "3.84.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",