@mutmutco/cli 3.67.0 → 3.69.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.
- package/dist/main.cjs +138 -64
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -5003,6 +5003,7 @@ function buildPrMergeResultPayload(input) {
|
|
|
5003
5003
|
function buildPrMergeArgs(input) {
|
|
5004
5004
|
const args = ["pr", "merge", input.number, ...input.repoArgs, input.method];
|
|
5005
5005
|
if (input.deleteBranch !== false) args.push("--delete-branch");
|
|
5006
|
+
if (input.bodyFile) args.push("--body-file", input.bodyFile);
|
|
5006
5007
|
if (input.auto) args.push("--auto");
|
|
5007
5008
|
return args;
|
|
5008
5009
|
}
|
|
@@ -7235,7 +7236,7 @@ function checkGateBudget(files, opts = {}) {
|
|
|
7235
7236
|
}
|
|
7236
7237
|
|
|
7237
7238
|
// src/project-model.ts
|
|
7238
|
-
var PROJECT_TYPES = ["web-app", "hub-service", "content", "desktop-app", "desktop-game", "non-deployable", "cli-tool", "worker"];
|
|
7239
|
+
var PROJECT_TYPES = ["web-app", "hub-service", "content", "desktop-app", "desktop-game", "mobile-app", "non-deployable", "cli-tool", "worker"];
|
|
7239
7240
|
var DEPLOY_MODELS = ["hub-serverless", "serverless", "tenant-container", "solo-container", "registry-publish", "content", "none"];
|
|
7240
7241
|
var RELEASE_TRACKS = ["full", "direct", "trunk"];
|
|
7241
7242
|
var PROJECT_TYPE_SET = new Set(PROJECT_TYPES);
|
|
@@ -7272,7 +7273,7 @@ function resolveDeployModel(meta, repo) {
|
|
|
7272
7273
|
const projectType = resolveProjectType(meta, repo);
|
|
7273
7274
|
if (projectType === "content" || meta?.class === "content") return "content";
|
|
7274
7275
|
if (projectType === "hub-service" || repoIsHub(repo)) return "hub-serverless";
|
|
7275
|
-
if (projectType === "desktop-app" || projectType === "desktop-game" || projectType === "non-deployable") return "none";
|
|
7276
|
+
if (projectType === "desktop-app" || projectType === "desktop-game" || projectType === "mobile-app" || projectType === "non-deployable") return "none";
|
|
7276
7277
|
if (projectType === "cli-tool") return "registry-publish";
|
|
7277
7278
|
return "tenant-container";
|
|
7278
7279
|
}
|
|
@@ -8970,7 +8971,7 @@ async function executeWaveLand(plan, deps, opts = { preserveWorktree: true }) {
|
|
|
8970
8971
|
}
|
|
8971
8972
|
|
|
8972
8973
|
// src/index.ts
|
|
8973
|
-
var
|
|
8974
|
+
var import_node_os11 = require("node:os");
|
|
8974
8975
|
|
|
8975
8976
|
// src/board.ts
|
|
8976
8977
|
var import_node_child_process7 = require("node:child_process");
|
|
@@ -12517,7 +12518,6 @@ for (const [helpGroup, names] of PRIMARY_GROUPS) {
|
|
|
12517
12518
|
HELP_GROUP_ORDER.set("Operations", HELP_GROUP_ORDER.size);
|
|
12518
12519
|
for (const name of OPERATIONAL_TOP_LEVEL) TOP_LEVEL_ORDER.set(name, topLevelPosition++);
|
|
12519
12520
|
var PATH_OVERRIDES = {
|
|
12520
|
-
"board slice-refresh": { category: "internal", discovery: "hidden", help_group: "Operations" },
|
|
12521
12521
|
"secrets find": { category: "admin", discovery: "all-only", help_group: "Operations" },
|
|
12522
12522
|
"secrets catalog": { category: "admin", discovery: "all-only", help_group: "Operations" },
|
|
12523
12523
|
"secrets doctor": { category: "admin", discovery: "all-only", help_group: "Operations" },
|
|
@@ -16549,6 +16549,43 @@ function evaluate(changed, policy, present = () => false) {
|
|
|
16549
16549
|
function git(args, cwd) {
|
|
16550
16550
|
return (0, import_node_child_process10.execFileSync)("git", args, { cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
|
|
16551
16551
|
}
|
|
16552
|
+
var COAUTHOR_KEY = "Co-authored-by";
|
|
16553
|
+
var GH_MESSAGE_SEPARATOR = /^-{5,}$/;
|
|
16554
|
+
var LIFTED_KEYS = new RegExp(`^(?:${TRAILER_KEY}|${COAUTHOR_KEY}):`, "i");
|
|
16555
|
+
function parseTrailers(message, cwd) {
|
|
16556
|
+
try {
|
|
16557
|
+
return (0, import_node_child_process10.execFileSync)("git", ["interpret-trailers", "--parse", "--unfold"], {
|
|
16558
|
+
cwd,
|
|
16559
|
+
input: message,
|
|
16560
|
+
encoding: "utf8",
|
|
16561
|
+
maxBuffer: 4 * 1024 * 1024
|
|
16562
|
+
}).split("\n").map((l) => l.trim()).filter(Boolean);
|
|
16563
|
+
} catch {
|
|
16564
|
+
return [];
|
|
16565
|
+
}
|
|
16566
|
+
}
|
|
16567
|
+
function squashBodyWithOverride(commits, cwd) {
|
|
16568
|
+
const overrides = [];
|
|
16569
|
+
const coauthors = [];
|
|
16570
|
+
const bodies = [];
|
|
16571
|
+
for (const commit of commits) {
|
|
16572
|
+
const message = `${commit.headline}
|
|
16573
|
+
|
|
16574
|
+
${commit.body}`.trim();
|
|
16575
|
+
for (const trailer of parseTrailers(message, cwd)) {
|
|
16576
|
+
if (trailer.toLowerCase().startsWith(`${TRAILER_KEY.toLowerCase()}:`)) {
|
|
16577
|
+
if (!overrides.includes(trailer)) overrides.push(trailer);
|
|
16578
|
+
} else if (trailer.toLowerCase().startsWith(`${COAUTHOR_KEY.toLowerCase()}:`)) {
|
|
16579
|
+
if (!coauthors.some((c) => c.toLowerCase() === trailer.toLowerCase())) coauthors.push(trailer);
|
|
16580
|
+
}
|
|
16581
|
+
}
|
|
16582
|
+
const source = commits.length > 1 ? message : commit.body;
|
|
16583
|
+
const kept = source.split("\n").filter((line) => !LIFTED_KEYS.test(line.trim()) && !GH_MESSAGE_SEPARATOR.test(line.trim())).join("\n").trim();
|
|
16584
|
+
if (kept) bodies.push(kept);
|
|
16585
|
+
}
|
|
16586
|
+
if (!overrides.length) return null;
|
|
16587
|
+
return [...bodies, [...overrides, ...coauthors].join("\n")].join("\n\n");
|
|
16588
|
+
}
|
|
16552
16589
|
function resolveBase(cwd, explicit) {
|
|
16553
16590
|
const candidates = [explicit, process.env.TEST_POLICY_BASE, "origin/development", "origin/main"].filter(Boolean);
|
|
16554
16591
|
for (const ref of candidates) {
|
|
@@ -16608,7 +16645,9 @@ function invisibleTrailer(sha, line) {
|
|
|
16608
16645
|
AUDITED, and a reason the auditor cannot see is not a reason this gate accepts (#3628).
|
|
16609
16646
|
Git reads trailers from the LAST paragraph only, and every line of that paragraph must be a
|
|
16610
16647
|
trailer or an indented continuation \u2014 one bare line such as \`Closes #123\` disqualifies the whole
|
|
16611
|
-
block. Indent the continuation lines, and leave nothing but trailers in that paragraph
|
|
16648
|
+
block. Indent the continuation lines, and leave nothing but trailers in that paragraph.
|
|
16649
|
+
This is fixable forward: push a LATER commit on this branch carrying a well-formed trailer.
|
|
16650
|
+
The nearest waiver wins, and it supersedes this one \u2014 no force-push, no re-filed branch.`
|
|
16612
16651
|
};
|
|
16613
16652
|
}
|
|
16614
16653
|
function unknownScope(sha, unknown) {
|
|
@@ -16629,13 +16668,13 @@ function readOverride(base, cwd) {
|
|
|
16629
16668
|
for (const record of git(["log", `${base}..HEAD`, `--format=${format}`], cwd).split(REC)) {
|
|
16630
16669
|
const [sha, trailer, body] = record.replace(/^\s+/, "").split(FLD);
|
|
16631
16670
|
if (!sha) continue;
|
|
16671
|
+
if (override) break;
|
|
16632
16672
|
const value = (trailer ?? "").trim();
|
|
16633
16673
|
if (!value) {
|
|
16634
16674
|
const shaped = OVERRIDE_RE.exec(body ?? "");
|
|
16635
16675
|
if (shaped) refusals.push(invisibleTrailer(sha, shaped[0].trim()));
|
|
16636
16676
|
continue;
|
|
16637
16677
|
}
|
|
16638
|
-
if (override) continue;
|
|
16639
16678
|
const { kinds, reason, unknown } = parseScope(value);
|
|
16640
16679
|
if (unknown.length > 0) refusals.push(unknownScope(sha, unknown));
|
|
16641
16680
|
else override = { sha, reason, kinds };
|
|
@@ -21349,6 +21388,7 @@ function registerBoardCommands(program3) {
|
|
|
21349
21388
|
var import_node_fs27 = require("node:fs");
|
|
21350
21389
|
var import_promises6 = require("node:fs/promises");
|
|
21351
21390
|
var import_node_path26 = require("node:path");
|
|
21391
|
+
var import_node_os8 = require("node:os");
|
|
21352
21392
|
var import_node_child_process12 = require("node:child_process");
|
|
21353
21393
|
|
|
21354
21394
|
// src/board-advance.ts
|
|
@@ -22257,8 +22297,36 @@ ${err?.stdout ?? ""}`.split("\n").map((line) => line.trim()).find((line) => line
|
|
|
22257
22297
|
var defaultGhMergeAutoIo = {
|
|
22258
22298
|
gh: async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout
|
|
22259
22299
|
};
|
|
22300
|
+
async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
|
|
22301
|
+
try {
|
|
22302
|
+
const raw = await gh(["pr", "view", prNumber, ...repoArgs, "--json", "commits"], GC_GH_TIMEOUT_MS2);
|
|
22303
|
+
const commits = JSON.parse(raw).commits ?? [];
|
|
22304
|
+
const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
|
|
22305
|
+
if (!body) return void 0;
|
|
22306
|
+
const dir = (0, import_node_fs27.mkdtempSync)((0, import_node_path26.join)((0, import_node_os8.tmpdir)(), "mmi-squash-body-"));
|
|
22307
|
+
const path2 = (0, import_node_path26.join)(dir, "body.txt");
|
|
22308
|
+
(0, import_node_fs27.writeFileSync)(path2, `${body}
|
|
22309
|
+
`, "utf8");
|
|
22310
|
+
return { path: path2, cleanup: () => {
|
|
22311
|
+
try {
|
|
22312
|
+
(0, import_node_fs27.rmSync)(dir, { recursive: true, force: true });
|
|
22313
|
+
} catch {
|
|
22314
|
+
}
|
|
22315
|
+
} };
|
|
22316
|
+
} catch {
|
|
22317
|
+
return void 0;
|
|
22318
|
+
}
|
|
22319
|
+
}
|
|
22260
22320
|
async function ghMergeAutoEnqueue(prNumber, repo, method, io = defaultGhMergeAutoIo) {
|
|
22261
22321
|
const args = repo ? ["--repo", repo] : [];
|
|
22322
|
+
const overrideBody = await composeOverrideBodyFile(prNumber, args, io.gh);
|
|
22323
|
+
try {
|
|
22324
|
+
return await mergeAutoEnqueueWithBody(prNumber, args, method, io, overrideBody?.path);
|
|
22325
|
+
} finally {
|
|
22326
|
+
overrideBody?.cleanup();
|
|
22327
|
+
}
|
|
22328
|
+
}
|
|
22329
|
+
async function mergeAutoEnqueueWithBody(prNumber, args, method, io, bodyFile) {
|
|
22262
22330
|
const headRef = (await io.gh(["pr", "view", prNumber, ...args, "--json", "headRefName", "--jq", ".headRefName"], GC_GH_TIMEOUT_MS2).catch(() => "")).trim();
|
|
22263
22331
|
const deleteBranch = !isProtectedBranch(headRef);
|
|
22264
22332
|
const readMergeState = () => readGhPrStateWithRetry(
|
|
@@ -22277,7 +22345,7 @@ async function ghMergeAutoEnqueue(prNumber, repo, method, io = defaultGhMergeAut
|
|
|
22277
22345
|
return s.ok && s.state === "MERGED";
|
|
22278
22346
|
},
|
|
22279
22347
|
reEnqueue: async () => {
|
|
22280
|
-
await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch }), GH_MUTATION_TIMEOUT_MS);
|
|
22348
|
+
await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch, bodyFile }), GH_MUTATION_TIMEOUT_MS);
|
|
22281
22349
|
}
|
|
22282
22350
|
});
|
|
22283
22351
|
if (confirmation === "merged") return { mergeStatus: "merged" };
|
|
@@ -22285,7 +22353,7 @@ async function ghMergeAutoEnqueue(prNumber, repo, method, io = defaultGhMergeAut
|
|
|
22285
22353
|
return { mergeStatus: "failed", error: "auto-merge did not stick \u2014 GitHub reported no autoMergeRequest after enqueue; retry once the PR has a pending check" };
|
|
22286
22354
|
};
|
|
22287
22355
|
try {
|
|
22288
|
-
await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch }), GH_MUTATION_TIMEOUT_MS);
|
|
22356
|
+
await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch, bodyFile }), GH_MUTATION_TIMEOUT_MS);
|
|
22289
22357
|
} catch (e) {
|
|
22290
22358
|
const message = String(e.message || "");
|
|
22291
22359
|
if (/already been merged/i.test(message)) return { mergeStatus: "merged" };
|
|
@@ -22293,7 +22361,7 @@ async function ghMergeAutoEnqueue(prNumber, repo, method, io = defaultGhMergeAut
|
|
|
22293
22361
|
if (note) return { mergeStatus: "failed", error: note };
|
|
22294
22362
|
if (mergeAutoRejectedPrAlreadyClean(message)) {
|
|
22295
22363
|
try {
|
|
22296
|
-
await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: false, deleteBranch }), GH_MUTATION_TIMEOUT_MS);
|
|
22364
|
+
await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: false, deleteBranch, bodyFile }), GH_MUTATION_TIMEOUT_MS);
|
|
22297
22365
|
} catch (e2) {
|
|
22298
22366
|
const m2 = String(e2.message || "");
|
|
22299
22367
|
if (/already been merged/i.test(m2)) return { mergeStatus: "merged" };
|
|
@@ -22304,7 +22372,7 @@ async function ghMergeAutoEnqueue(prNumber, repo, method, io = defaultGhMergeAut
|
|
|
22304
22372
|
return { mergeStatus: "failed", error: `could not confirm PR state after clean-status fallback: ${afterDirect.error}` };
|
|
22305
22373
|
}
|
|
22306
22374
|
try {
|
|
22307
|
-
await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch }), GH_MUTATION_TIMEOUT_MS);
|
|
22375
|
+
await io.gh(buildPrMergeArgs({ number: prNumber, repoArgs: args, method, auto: true, deleteBranch, bodyFile }), GH_MUTATION_TIMEOUT_MS);
|
|
22308
22376
|
} catch (e3) {
|
|
22309
22377
|
const m3 = String(e3.message || "");
|
|
22310
22378
|
if (/already been merged/i.test(m3)) return { mergeStatus: "merged" };
|
|
@@ -23828,7 +23896,7 @@ function registerDeployCommands(program3) {
|
|
|
23828
23896
|
|
|
23829
23897
|
// src/discovery-commands.ts
|
|
23830
23898
|
var import_node_fs31 = require("node:fs");
|
|
23831
|
-
var
|
|
23899
|
+
var import_node_os9 = require("node:os");
|
|
23832
23900
|
var import_node_path29 = require("node:path");
|
|
23833
23901
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
23834
23902
|
async function collectStatus() {
|
|
@@ -24004,7 +24072,7 @@ async function collectOnboardStatus() {
|
|
|
24004
24072
|
else if (top) nextCommand = `mmi-cli board claim ${top.number} # ${top.title}`;
|
|
24005
24073
|
else nextCommand = "mmi-cli board read \u2014 no claimable items found";
|
|
24006
24074
|
}
|
|
24007
|
-
const home = (0,
|
|
24075
|
+
const home = (0, import_node_os9.homedir)();
|
|
24008
24076
|
const plugin = onboardPluginGate({
|
|
24009
24077
|
readKnown: () => readFileSyncSafe((0, import_node_path29.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs31.readFileSync),
|
|
24010
24078
|
readSettings: () => readFileSyncSafe((0, import_node_path29.join)(home, ".claude", "settings.json"), import_node_fs31.readFileSync)
|
|
@@ -25387,17 +25455,17 @@ function parseOriginRepo(remoteUrl) {
|
|
|
25387
25455
|
}
|
|
25388
25456
|
function ghHostsConfigPath(env, platform2) {
|
|
25389
25457
|
const sep2 = platform2 === "win32" ? "\\" : "/";
|
|
25390
|
-
const
|
|
25458
|
+
const join27 = (...parts) => parts.join(sep2);
|
|
25391
25459
|
const explicit = env.GH_CONFIG_DIR?.trim();
|
|
25392
|
-
if (explicit) return
|
|
25460
|
+
if (explicit) return join27(explicit, "hosts.yml");
|
|
25393
25461
|
if (platform2 === "win32") {
|
|
25394
25462
|
const appData = (env.AppData ?? env.APPDATA)?.trim();
|
|
25395
|
-
return appData ?
|
|
25463
|
+
return appData ? join27(appData, "GitHub CLI", "hosts.yml") : void 0;
|
|
25396
25464
|
}
|
|
25397
25465
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
25398
|
-
if (xdg) return
|
|
25466
|
+
if (xdg) return join27(xdg, "gh", "hosts.yml");
|
|
25399
25467
|
const home = env.HOME?.trim();
|
|
25400
|
-
return home ?
|
|
25468
|
+
return home ? join27(home, ".config", "gh", "hosts.yml") : void 0;
|
|
25401
25469
|
}
|
|
25402
25470
|
function parseGhHostsAccounts(yaml, host = "github.com") {
|
|
25403
25471
|
let hostIndent = null;
|
|
@@ -25448,7 +25516,7 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
25448
25516
|
|
|
25449
25517
|
// src/doctor-io.ts
|
|
25450
25518
|
var import_node_fs32 = require("node:fs");
|
|
25451
|
-
var
|
|
25519
|
+
var import_node_os10 = require("node:os");
|
|
25452
25520
|
var import_node_path30 = require("node:path");
|
|
25453
25521
|
var import_node_child_process13 = require("node:child_process");
|
|
25454
25522
|
var import_node_util8 = require("node:util");
|
|
@@ -25457,7 +25525,7 @@ var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
|
25457
25525
|
function installedClaudePluginVersion() {
|
|
25458
25526
|
try {
|
|
25459
25527
|
const file = JSON.parse(
|
|
25460
|
-
(0, import_node_fs32.readFileSync)((0, import_node_path30.join)((0,
|
|
25528
|
+
(0, import_node_fs32.readFileSync)((0, import_node_path30.join)((0, import_node_os10.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
25461
25529
|
);
|
|
25462
25530
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
25463
25531
|
if (versions.length === 0) return void 0;
|
|
@@ -25566,7 +25634,7 @@ function envHealLockPath(home) {
|
|
|
25566
25634
|
async function withEnvHealLock(what, run) {
|
|
25567
25635
|
try {
|
|
25568
25636
|
return await withFileLock(
|
|
25569
|
-
envHealLockPath((0,
|
|
25637
|
+
envHealLockPath((0, import_node_os11.homedir)()),
|
|
25570
25638
|
{ staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
|
|
25571
25639
|
run
|
|
25572
25640
|
);
|
|
@@ -25661,9 +25729,9 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
25661
25729
|
},
|
|
25662
25730
|
pluginCache: () => {
|
|
25663
25731
|
const plan = buildPluginCachePlan(
|
|
25664
|
-
(0,
|
|
25732
|
+
(0, import_node_os11.homedir)(),
|
|
25665
25733
|
runningPluginVersion(process.env, resolveClientVersion()),
|
|
25666
|
-
pluginCacheFsDeps((0,
|
|
25734
|
+
pluginCacheFsDeps((0, import_node_os11.homedir)(), () => 0)
|
|
25667
25735
|
);
|
|
25668
25736
|
return {
|
|
25669
25737
|
stale: plan.prune,
|
|
@@ -25712,7 +25780,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
25712
25780
|
// which branch it would pick one up from. Two local file reads, no network, fail-soft to no rows.
|
|
25713
25781
|
marketplaceRows: () => {
|
|
25714
25782
|
try {
|
|
25715
|
-
const home = (0,
|
|
25783
|
+
const home = (0, import_node_os11.homedir)();
|
|
25716
25784
|
return marketplaceRows(
|
|
25717
25785
|
MMI_MARKETPLACE_NAME,
|
|
25718
25786
|
readFileSyncSafe((0, import_node_path31.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs33.readFileSync),
|
|
@@ -27526,44 +27594,50 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
27526
27594
|
let remoteDeleteAttempted = false;
|
|
27527
27595
|
let upgradedToAuto = false;
|
|
27528
27596
|
let remoteNotAttemptedReason = headIsProtected ? "protected-branch" : void 0;
|
|
27529
|
-
await
|
|
27530
|
-
|
|
27531
|
-
|
|
27532
|
-
|
|
27533
|
-
|
|
27534
|
-
|
|
27535
|
-
|
|
27536
|
-
|
|
27537
|
-
|
|
27538
|
-
|
|
27539
|
-
|
|
27540
|
-
|
|
27541
|
-
|
|
27542
|
-
|
|
27543
|
-
|
|
27544
|
-
|
|
27545
|
-
|
|
27546
|
-
|
|
27547
|
-
|
|
27548
|
-
|
|
27549
|
-
|
|
27550
|
-
|
|
27551
|
-
|
|
27552
|
-
|
|
27553
|
-
|
|
27554
|
-
|
|
27555
|
-
|
|
27556
|
-
|
|
27557
|
-
|
|
27558
|
-
|
|
27559
|
-
|
|
27560
|
-
|
|
27561
|
-
|
|
27562
|
-
|
|
27563
|
-
|
|
27564
|
-
|
|
27565
|
-
|
|
27566
|
-
|
|
27597
|
+
const overrideBody = await composeOverrideBodyFile(number, repoArgs, async (a, t) => (await execFileP2("gh", a, { timeout: t })).stdout);
|
|
27598
|
+
const bodyFile = overrideBody?.path;
|
|
27599
|
+
try {
|
|
27600
|
+
await execFileP2("gh", buildPrMergeArgs({ number, repoArgs, method, auto: o.auto, deleteBranch: !headIsProtected, bodyFile }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch(async (e) => {
|
|
27601
|
+
const message = String(e.message || "");
|
|
27602
|
+
if (/already been merged/i.test(message)) {
|
|
27603
|
+
remoteNotAttemptedReason = "pr-already-merged";
|
|
27604
|
+
return;
|
|
27605
|
+
}
|
|
27606
|
+
const note = timeoutKillNote(e, GH_MUTATION_TIMEOUT_MS);
|
|
27607
|
+
if (note) throw new Error(`gh pr merge ${number}: ${note}`);
|
|
27608
|
+
if (o.auto && mergeAutoRejectedPrAlreadyClean(message)) {
|
|
27609
|
+
await execFileP2("gh", buildPrMergeArgs({ number, repoArgs, method, auto: false, deleteBranch: !headIsProtected, bodyFile }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch((e2) => {
|
|
27610
|
+
const m2 = String(e2.message || "");
|
|
27611
|
+
if (/already been merged/i.test(m2)) {
|
|
27612
|
+
remoteNotAttemptedReason = "pr-already-merged";
|
|
27613
|
+
return;
|
|
27614
|
+
}
|
|
27615
|
+
const note2 = timeoutKillNote(e2, GH_MUTATION_TIMEOUT_MS);
|
|
27616
|
+
if (note2) throw new Error(`gh pr merge ${number}: ${note2}`);
|
|
27617
|
+
if (!ghPrMergeLocalBranchDeleteWarning(m2)) throw e2;
|
|
27618
|
+
});
|
|
27619
|
+
return;
|
|
27620
|
+
}
|
|
27621
|
+
if (!o.auto && basePolicyBlocksImmediateMerge(message)) {
|
|
27622
|
+
console.warn(`pr merge: the base-branch policy blocks an immediate merge \u2014 upgrading to --auto (merges once required checks pass).`);
|
|
27623
|
+
upgradedToAuto = true;
|
|
27624
|
+
await execFileP2("gh", buildPrMergeArgs({ number, repoArgs, method, auto: true, deleteBranch: !headIsProtected, bodyFile }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch((e2) => {
|
|
27625
|
+
const m2 = String(e2.message || "");
|
|
27626
|
+
if (/already been merged/i.test(m2)) {
|
|
27627
|
+
remoteNotAttemptedReason = "pr-already-merged";
|
|
27628
|
+
return;
|
|
27629
|
+
}
|
|
27630
|
+
const note2 = timeoutKillNote(e2, GH_MUTATION_TIMEOUT_MS);
|
|
27631
|
+
if (note2) throw new Error(`gh pr merge ${number}: ${note2}`);
|
|
27632
|
+
if (!ghPrMergeLocalBranchDeleteWarning(m2)) throw e2;
|
|
27633
|
+
});
|
|
27634
|
+
return;
|
|
27635
|
+
}
|
|
27636
|
+
if (!ghPrMergeLocalBranchDeleteWarning(message)) throw e;
|
|
27637
|
+
});
|
|
27638
|
+
} finally {
|
|
27639
|
+
overrideBody?.cleanup();
|
|
27640
|
+
}
|
|
27567
27641
|
const stateRead = await readGhPrStateWithRetry(
|
|
27568
27642
|
() => execFileP2("gh", ["pr", "view", number, ...repoArgs, "--json", "state", "--jq", ".state"], { timeout: GC_GH_TIMEOUT_MS2 }).then((r) => r.stdout)
|
|
27569
27643
|
);
|
|
@@ -28051,13 +28125,13 @@ function stagingApplyFsGuard(home) {
|
|
|
28051
28125
|
}
|
|
28052
28126
|
program2.command("plugin-prune").description(`prune stale cached MMI plugin versions (keeps running + newest, ${PLUGIN_CACHE_KEEP} total) and orphaned temp_git_* staging dirs; dry-run unless --apply (#2903, #2990)`).option("--apply", "actually delete the stale version dirs + orphaned staging dirs (default: report only)").option("--json", "machine-readable output").action((o) => {
|
|
28053
28127
|
const plan = buildPluginCachePlan(
|
|
28054
|
-
(0,
|
|
28128
|
+
(0, import_node_os11.homedir)(),
|
|
28055
28129
|
runningPluginVersion(process.env, resolveClientVersion()),
|
|
28056
|
-
pluginCacheFsDeps((0,
|
|
28130
|
+
pluginCacheFsDeps((0, import_node_os11.homedir)(), directoryBytes),
|
|
28057
28131
|
{ withBytes: true }
|
|
28058
28132
|
);
|
|
28059
28133
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
28060
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs33.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard((0,
|
|
28134
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs33.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard((0, import_node_os11.homedir)())) : void 0;
|
|
28061
28135
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
28062
28136
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
28063
28137
|
else console.log(renderPluginCachePlan(plan, result));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutmutco/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.69.0",
|
|
4
4
|
"description": "MMI Future CLI — the org dev toolbox (board, registry, keyless secrets, release train, bootstrap, doctor) and the cross-IDE engine the plugin's session-start hook drives.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|