@mutmutco/cli 4.1.20 → 4.2.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 +297 -88
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -3416,7 +3416,7 @@ var program = new Command();
|
|
|
3416
3416
|
// src/index.ts
|
|
3417
3417
|
var import_promises8 = require("node:fs/promises");
|
|
3418
3418
|
var import_node_fs42 = require("node:fs");
|
|
3419
|
-
var
|
|
3419
|
+
var import_node_child_process18 = require("node:child_process");
|
|
3420
3420
|
|
|
3421
3421
|
// src/cli-shared.ts
|
|
3422
3422
|
var import_node_child_process3 = require("node:child_process");
|
|
@@ -5538,7 +5538,7 @@ function commandLadderHint() {
|
|
|
5538
5538
|
}
|
|
5539
5539
|
|
|
5540
5540
|
// src/index.ts
|
|
5541
|
-
var
|
|
5541
|
+
var import_node_path39 = require("node:path");
|
|
5542
5542
|
|
|
5543
5543
|
// src/merge-ci-policy.ts
|
|
5544
5544
|
function resolveMergeCiPolicy(input) {
|
|
@@ -7287,6 +7287,7 @@ function parseOwnerRepo(repo) {
|
|
|
7287
7287
|
if (owner.includes("\\") || name.includes("\\")) throw new Error("repo must be owner/repo");
|
|
7288
7288
|
return { owner, name, slug: name.toLowerCase(), fullName: `${owner}/${name}` };
|
|
7289
7289
|
}
|
|
7290
|
+
var MANAGED_DELETE_BRANCH_ON_MERGE = false;
|
|
7290
7291
|
var DEFAULT_GATE_CMD = "npm run check";
|
|
7291
7292
|
var DEFAULT_GATE_MAX_SECONDS = "300";
|
|
7292
7293
|
var DEFAULT_GATE_PY_VERSION = "3.11";
|
|
@@ -11761,10 +11762,10 @@ var rollout_plan_default = {
|
|
|
11761
11762
|
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)."
|
|
11762
11763
|
},
|
|
11763
11764
|
baseline: {
|
|
11764
|
-
version: "4.
|
|
11765
|
-
tag: "v4.
|
|
11766
|
-
commit: "
|
|
11767
|
-
npm: "@mutmutco/cli@4.
|
|
11765
|
+
version: "4.2.0",
|
|
11766
|
+
tag: "v4.2.0",
|
|
11767
|
+
commit: "d4802646ffca",
|
|
11768
|
+
npm: "@mutmutco/cli@4.2.0"
|
|
11768
11769
|
},
|
|
11769
11770
|
exitCriterion: "fleet-n-of-n",
|
|
11770
11771
|
hubOnlyShortcut: "forbidden",
|
|
@@ -11781,14 +11782,14 @@ var rollout_plan_default = {
|
|
|
11781
11782
|
repo: "mutmutco/mmi-hub",
|
|
11782
11783
|
role: "canary",
|
|
11783
11784
|
schedule: "train",
|
|
11784
|
-
v3Target: "v4.
|
|
11785
|
+
v3Target: "v4.2.0"
|
|
11785
11786
|
}
|
|
11786
11787
|
],
|
|
11787
11788
|
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.",
|
|
11788
11789
|
rollback: {
|
|
11789
11790
|
independent: true,
|
|
11790
|
-
mechanism: "npm dist-tag latest -> 4.
|
|
11791
|
-
v3Target: "v4.
|
|
11791
|
+
mechanism: "npm dist-tag latest -> 4.2.0 and redeploy the Hub Lambda from tag v4.2.0 (d4802646ffca); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
11792
|
+
v3Target: "v4.2.0 (@mutmutco/cli@4.2.0, tag commit d4802646ffca \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
11792
11793
|
}
|
|
11793
11794
|
},
|
|
11794
11795
|
{
|
|
@@ -15824,7 +15825,7 @@ function assertNpmMajorPreflight(opts) {
|
|
|
15824
15825
|
if (localMajor === void 0 || expectedMajor === void 0) return;
|
|
15825
15826
|
if (localMajor === expectedMajor) return;
|
|
15826
15827
|
throw new Error(
|
|
15827
|
-
`${opts.repo}: Step 0a npm-major preflight failed \u2014 local npm ${localNpm} (major ${localMajor}) \u2260 CI npm ${expectedNpm} (major ${expectedMajor}).
|
|
15828
|
+
`${opts.repo}: Step 0a npm-major preflight failed \u2014 local npm ${localNpm} (major ${localMajor}) \u2260 CI npm ${expectedNpm} (major ${expectedMajor}). Supported repair (preserves Node): \`npm install -g npm@${expectedNpm}\`; then verify with \`node -v && npm -v\` and rerun the same release command. This is an npm-major mismatch (#5666/#5616/#4578), not a lockfile or publish-surface problem. When gate.yml has no bundled npm pin, read the publish workflow npm pin; otherwise compare \`npm -v\` with the green gate log's toolchain-preflight line (#3446).`
|
|
15828
15829
|
);
|
|
15829
15830
|
}
|
|
15830
15831
|
async function assertNpmMajorPreflightFromWorkflows(deps, repo) {
|
|
@@ -16108,15 +16109,9 @@ async function runRcandResume(deps, options = {}) {
|
|
|
16108
16109
|
note: `reused immutable ${tag}; pushed its exact SHA to rc and dispatched the rc deploy`
|
|
16109
16110
|
};
|
|
16110
16111
|
}
|
|
16111
|
-
async function
|
|
16112
|
+
async function runReleaseResumePipeline(deps, options, prepared) {
|
|
16112
16113
|
const watch = options.watch ?? false;
|
|
16113
|
-
const ctx =
|
|
16114
|
-
await requireCleanTree(deps);
|
|
16115
|
-
await runGitRemoteRead(deps, ["fetch", "origin", "--tags"]);
|
|
16116
|
-
const meta = requireProjectMetaForTrain(await loadProjectMeta(deps, ctx), ctx.repo);
|
|
16117
|
-
const branchHints = await loadReleaseTrackBranchHints(deps);
|
|
16118
|
-
const directTrack = isHubControlRepo(ctx.repo) || resolveReleaseTrack(meta, branchHints) === "direct";
|
|
16119
|
-
const deployModel = await preflight(deps, ctx, "main", meta);
|
|
16114
|
+
const { ctx, meta, branchHints, directTrack, deployModel } = prepared;
|
|
16120
16115
|
const tags = clean2(await deps.run("git", ["tag", "--list", "v[0-9]*.[0-9]*.[0-9]*", "--sort=-v:refname"])).split("\n").map((t) => t.trim()).filter((t) => /^v\d+\.\d+\.\d+$/.test(t));
|
|
16121
16116
|
const tag = tags[0];
|
|
16122
16117
|
if (!tag) throw new Error("release --resume: no final v* release tag found \u2014 there is no release to resume");
|
|
@@ -16224,6 +16219,36 @@ async function runReleaseResume(deps, options = {}) {
|
|
|
16224
16219
|
note: followUp === "failed" ? `resumed ${tag} (version preserved, not re-cut) \u2014 but a watched deploy/publish run FAILED; the Release stands, re-run the failed job (do NOT re-tag)` : followUp === "pending" ? `resumed ${tag} (version preserved, not re-cut) \u2014 a deploy/publish or alignment follow-up is unresolved; watch it to completion` : `resumed and completed ${tag} \u2014 the original version was preserved, not re-cut`
|
|
16225
16220
|
};
|
|
16226
16221
|
}
|
|
16222
|
+
async function withCheckoutRestoredAfterReleaseResume(deps, startBranch, operation) {
|
|
16223
|
+
let result;
|
|
16224
|
+
try {
|
|
16225
|
+
result = await operation();
|
|
16226
|
+
} catch (error) {
|
|
16227
|
+
let checkoutNote;
|
|
16228
|
+
try {
|
|
16229
|
+
checkoutNote = (await restoreCheckoutAfterRelease(deps, startBranch)).note;
|
|
16230
|
+
} catch (restoreError) {
|
|
16231
|
+
checkoutNote = `checkout restoration failed while returning to ${startBranch}: ${restoreError instanceof Error ? restoreError.message : String(restoreError)}`;
|
|
16232
|
+
}
|
|
16233
|
+
throw new Error(`${error instanceof Error ? error.message : String(error)}; ${checkoutNote}`);
|
|
16234
|
+
}
|
|
16235
|
+
return withCheckoutRestoredAfterRelease(deps, result, startBranch);
|
|
16236
|
+
}
|
|
16237
|
+
async function runReleaseResume(deps, options = {}) {
|
|
16238
|
+
const ctx = await buildTrainApplyContext(deps);
|
|
16239
|
+
await requireCleanTree(deps);
|
|
16240
|
+
await runGitRemoteRead(deps, ["fetch", "origin", "--tags"]);
|
|
16241
|
+
const meta = requireProjectMetaForTrain(await loadProjectMeta(deps, ctx), ctx.repo);
|
|
16242
|
+
const branchHints = await loadReleaseTrackBranchHints(deps);
|
|
16243
|
+
const directTrack = isHubControlRepo(ctx.repo) || resolveReleaseTrack(meta, branchHints) === "direct";
|
|
16244
|
+
const deployModel = await preflight(deps, ctx, "main", meta);
|
|
16245
|
+
const startBranch = directTrack ? "development" : "rc";
|
|
16246
|
+
return withCheckoutRestoredAfterReleaseResume(
|
|
16247
|
+
deps,
|
|
16248
|
+
startBranch,
|
|
16249
|
+
() => runReleaseResumePipeline(deps, options, { ctx, meta, branchHints, directTrack, deployModel })
|
|
16250
|
+
);
|
|
16251
|
+
}
|
|
16227
16252
|
async function runReleaseAbort(deps, options = {}) {
|
|
16228
16253
|
if (!options.approved) {
|
|
16229
16254
|
throw new Error("release --abort requires --apply after explicit approval; nothing was written");
|
|
@@ -17130,7 +17155,7 @@ async function auditRepoCi(repo, deps) {
|
|
|
17130
17155
|
ok: info.allow_auto_merge === true,
|
|
17131
17156
|
label: "allow_auto_merge enabled",
|
|
17132
17157
|
detail: info.allow_auto_merge === true ? void 0 : "false or unavailable",
|
|
17133
|
-
remediation: `gh api -X PATCH repos/${repo} -f allow_auto_merge=true -f allow_squash_merge=true -
|
|
17158
|
+
remediation: `gh api -X PATCH repos/${repo} -f allow_auto_merge=true -f allow_squash_merge=true -F delete_branch_on_merge=${MANAGED_DELETE_BRANCH_ON_MERGE}`
|
|
17134
17159
|
});
|
|
17135
17160
|
checks.push({
|
|
17136
17161
|
ok: info.allow_squash_merge === true,
|
|
@@ -17138,9 +17163,9 @@ async function auditRepoCi(repo, deps) {
|
|
|
17138
17163
|
detail: info.allow_squash_merge === true ? void 0 : "false or unavailable"
|
|
17139
17164
|
});
|
|
17140
17165
|
checks.push({
|
|
17141
|
-
ok: info.delete_branch_on_merge ===
|
|
17142
|
-
label: "delete_branch_on_merge
|
|
17143
|
-
detail: info.delete_branch_on_merge ===
|
|
17166
|
+
ok: info.delete_branch_on_merge === MANAGED_DELETE_BRANCH_ON_MERGE,
|
|
17167
|
+
label: "delete_branch_on_merge disabled",
|
|
17168
|
+
detail: info.delete_branch_on_merge === MANAGED_DELETE_BRANCH_ON_MERGE ? void 0 : "merged branches must survive until delayed cleanup proves no open PR uses them as a base (#5789)"
|
|
17144
17169
|
});
|
|
17145
17170
|
checks.push({
|
|
17146
17171
|
ok: info.has_wiki === false,
|
|
@@ -17447,11 +17472,11 @@ async function applyCiReconcileMergeSettingsFromReport(repo, deps, report) {
|
|
|
17447
17472
|
body: {
|
|
17448
17473
|
allow_auto_merge: true,
|
|
17449
17474
|
allow_squash_merge: true,
|
|
17450
|
-
delete_branch_on_merge:
|
|
17475
|
+
delete_branch_on_merge: MANAGED_DELETE_BRANCH_ON_MERGE,
|
|
17451
17476
|
has_wiki: false
|
|
17452
17477
|
}
|
|
17453
17478
|
});
|
|
17454
|
-
applied.push("allow_auto_merge, allow_squash_merge,
|
|
17479
|
+
applied.push("allow_auto_merge, allow_squash_merge, preserve merged remote branches, has_wiki=false");
|
|
17455
17480
|
} catch (e) {
|
|
17456
17481
|
errors.push(e.message);
|
|
17457
17482
|
}
|
|
@@ -20892,6 +20917,11 @@ async function readBoard(options, deps = {}) {
|
|
|
20892
20917
|
report[scope].claimable = filtered.claimable;
|
|
20893
20918
|
report.warnings.push(...filtered.warnings);
|
|
20894
20919
|
}
|
|
20920
|
+
for (const scope of ["primary", "secondary"]) {
|
|
20921
|
+
const demoted = await demoteLiveClaimedClaimables(report[scope], client);
|
|
20922
|
+
report.warnings.push(...demoted.warnings);
|
|
20923
|
+
report.partial = report.partial || demoted.partial;
|
|
20924
|
+
}
|
|
20895
20925
|
return report;
|
|
20896
20926
|
}
|
|
20897
20927
|
function findBoardItem(items, selector, board) {
|
|
@@ -21753,6 +21783,66 @@ function boardItemClaim(item) {
|
|
|
21753
21783
|
currentlyClaimed: item.assignees.length > 0 && item.status === "In Progress"
|
|
21754
21784
|
};
|
|
21755
21785
|
}
|
|
21786
|
+
function classifyClaimableMarker(comments, actor, now = Date.now()) {
|
|
21787
|
+
const marker = comments ? latestClaimMarker(comments) : void 0;
|
|
21788
|
+
if (!marker) return { verdict: "claimable" };
|
|
21789
|
+
const ownership = laneOwnership(marker, actor);
|
|
21790
|
+
if (ownership === "mine") return { verdict: "owned", holder: describeClaimMarker(marker) };
|
|
21791
|
+
const age = now - Date.parse(marker.ts);
|
|
21792
|
+
if (Number.isFinite(age) && age >= 0 && age < CLAIM_MARKER_LIVE_MS) {
|
|
21793
|
+
return { verdict: "taken", holder: describeClaimMarker(marker) };
|
|
21794
|
+
}
|
|
21795
|
+
return { verdict: "claimable" };
|
|
21796
|
+
}
|
|
21797
|
+
async function demoteLiveClaimedClaimables(scope, client, actor = describeSessionIdentity(), deps = {}) {
|
|
21798
|
+
const warnings = [];
|
|
21799
|
+
let partial = false;
|
|
21800
|
+
const fetchComments = deps.fetchComments ?? (async (item) => {
|
|
21801
|
+
if (item.details) return item.details.comments;
|
|
21802
|
+
const comments = await client.restPaginate(`repos/${item.repository}/issues/${item.number}/comments`);
|
|
21803
|
+
return comments.map((comment) => ({ body: comment.body ?? "" }));
|
|
21804
|
+
});
|
|
21805
|
+
const stillClaimable = [];
|
|
21806
|
+
const movedTaken = [];
|
|
21807
|
+
const movedOwned = [];
|
|
21808
|
+
for (const item of scope.claimable) {
|
|
21809
|
+
if (item.contentType !== "Issue") {
|
|
21810
|
+
stillClaimable.push(item);
|
|
21811
|
+
continue;
|
|
21812
|
+
}
|
|
21813
|
+
let comments;
|
|
21814
|
+
try {
|
|
21815
|
+
comments = await fetchComments(item);
|
|
21816
|
+
} catch {
|
|
21817
|
+
comments = void 0;
|
|
21818
|
+
}
|
|
21819
|
+
if (comments === void 0) {
|
|
21820
|
+
partial = true;
|
|
21821
|
+
warnings.push(`${item.ref} left claimable: claim-marker evidence could not be read`);
|
|
21822
|
+
continue;
|
|
21823
|
+
}
|
|
21824
|
+
const verdict = classifyClaimableMarker(comments, actor, deps.now);
|
|
21825
|
+
if (verdict.verdict === "claimable") {
|
|
21826
|
+
stillClaimable.push(item);
|
|
21827
|
+
} else if (verdict.verdict === "owned") {
|
|
21828
|
+
movedOwned.push(item);
|
|
21829
|
+
warnings.push(`${item.ref} is held by your own lane (${verdict.holder}) \u2014 moved to user-owned`);
|
|
21830
|
+
} else {
|
|
21831
|
+
movedTaken.push(item);
|
|
21832
|
+
warnings.push(`${item.ref} is held by a live claim marker (lane ${verdict.holder}) \u2014 no longer claimable`);
|
|
21833
|
+
}
|
|
21834
|
+
}
|
|
21835
|
+
scope.claimable = stillClaimable;
|
|
21836
|
+
if (movedTaken.length) {
|
|
21837
|
+
scope.taken.push(...movedTaken);
|
|
21838
|
+
scope.taken.sort(compareItems);
|
|
21839
|
+
}
|
|
21840
|
+
if (movedOwned.length) {
|
|
21841
|
+
scope.userOwned.push(...movedOwned);
|
|
21842
|
+
scope.userOwned.sort(compareItems);
|
|
21843
|
+
}
|
|
21844
|
+
return { warnings, partial };
|
|
21845
|
+
}
|
|
21756
21846
|
async function postClaimMarkerComment(client, item, actor = describeSessionIdentity()) {
|
|
21757
21847
|
try {
|
|
21758
21848
|
const marker = {
|
|
@@ -23374,11 +23464,69 @@ function renderPluginCachePlan(plan, applied) {
|
|
|
23374
23464
|
return lines.join("\n");
|
|
23375
23465
|
}
|
|
23376
23466
|
|
|
23377
|
-
// src/
|
|
23467
|
+
// src/train-self-converge.ts
|
|
23378
23468
|
var import_node_child_process10 = require("node:child_process");
|
|
23469
|
+
var SELF_CONVERGE_INSTALL_TIMEOUT_MS = 12e4;
|
|
23470
|
+
var SELF_CONVERGE_VERSION_TIMEOUT_MS = 3e4;
|
|
23471
|
+
var isWin2 = process.platform === "win32";
|
|
23472
|
+
var defaultDeps = {
|
|
23473
|
+
// Pin the exact released version (never `@latest` — a cached packument resolves the tag to the
|
|
23474
|
+
// PREVIOUS version in exactly this post-publish window, #3422). execFileP rejects on nonzero exit.
|
|
23475
|
+
update: async (target) => {
|
|
23476
|
+
const args = ["install", "-g", `@mutmutco/cli@${target}`];
|
|
23477
|
+
if (isWin2) {
|
|
23478
|
+
await execFileP2("cmd.exe", ["/c", "npm", ...args], { timeout: SELF_CONVERGE_INSTALL_TIMEOUT_MS });
|
|
23479
|
+
} else {
|
|
23480
|
+
await execFileP2("npm", args, { timeout: SELF_CONVERGE_INSTALL_TIMEOUT_MS });
|
|
23481
|
+
}
|
|
23482
|
+
},
|
|
23483
|
+
// Read the version the freshly installed `mmi-cli` on PATH actually resolves to. Unparseable or
|
|
23484
|
+
// failed read answers undefined so the flow reports not-taken-effect instead of guessing green.
|
|
23485
|
+
readInstalledVersion: async () => {
|
|
23486
|
+
try {
|
|
23487
|
+
const { stdout } = isWin2 ? await execFileP2("cmd.exe", ["/c", "mmi-cli", "--version"], { timeout: SELF_CONVERGE_VERSION_TIMEOUT_MS }) : await execFileP2("mmi-cli", ["--version"], { timeout: SELF_CONVERGE_VERSION_TIMEOUT_MS });
|
|
23488
|
+
return parseNpmVersion(stdout);
|
|
23489
|
+
} catch {
|
|
23490
|
+
return void 0;
|
|
23491
|
+
}
|
|
23492
|
+
},
|
|
23493
|
+
// Re-exec the SAME command line in the fresh CLI with inherited stdio (spawn, not execFile, so
|
|
23494
|
+
// output streams live) and hand its exit code back — the stale parent exits with it (#5762).
|
|
23495
|
+
reexec: () => new Promise((resolve6) => {
|
|
23496
|
+
const args = process.argv.slice(2);
|
|
23497
|
+
const child2 = isWin2 ? (0, import_node_child_process10.spawn)("cmd.exe", ["/c", "mmi-cli", ...args], { stdio: "inherit", windowsHide: true }) : (0, import_node_child_process10.spawn)("mmi-cli", args, { stdio: "inherit", windowsHide: true });
|
|
23498
|
+
child2.on("exit", (code) => resolve6(code ?? 1));
|
|
23499
|
+
child2.on("error", () => resolve6(1));
|
|
23500
|
+
}),
|
|
23501
|
+
// stderr only — stdout may be carrying --json output.
|
|
23502
|
+
warn: (line) => console.error(line)
|
|
23503
|
+
};
|
|
23504
|
+
async function selfConvergeTrainCli(input) {
|
|
23505
|
+
const deps = { ...defaultDeps, ...input.deps };
|
|
23506
|
+
try {
|
|
23507
|
+
deps.warn(
|
|
23508
|
+
`train: running mmi-cli ${input.currentVersion} is stale against released ${input.releasedVersion} \u2014 self-converging via npm install -g @mutmutco/cli@${input.releasedVersion} before resuming ${input.commandName}`
|
|
23509
|
+
);
|
|
23510
|
+
await deps.update(input.releasedVersion);
|
|
23511
|
+
const installed = await deps.readInstalledVersion();
|
|
23512
|
+
if (!installed || compareVersions(installed, input.releasedVersion) < 0) {
|
|
23513
|
+
return {
|
|
23514
|
+
kind: "failed",
|
|
23515
|
+
error: `self-converge did not take effect: mmi-cli on PATH still resolves to ${installed ?? "unknown"}, expected >= ${input.releasedVersion}`
|
|
23516
|
+
};
|
|
23517
|
+
}
|
|
23518
|
+
const reexecCode = await deps.reexec();
|
|
23519
|
+
return { kind: "converged", reexecCode };
|
|
23520
|
+
} catch (e) {
|
|
23521
|
+
return { kind: "failed", error: e instanceof Error ? e.message : String(e) };
|
|
23522
|
+
}
|
|
23523
|
+
}
|
|
23524
|
+
|
|
23525
|
+
// src/statusline-invalidate.ts
|
|
23526
|
+
var import_node_child_process11 = require("node:child_process");
|
|
23379
23527
|
function invalidateStatuslineBoardCache() {
|
|
23380
23528
|
try {
|
|
23381
|
-
const child2 = (0,
|
|
23529
|
+
const child2 = (0, import_node_child_process11.spawn)("jerv-cli", ["lane", "ops", "invalidate-board"], {
|
|
23382
23530
|
detached: true,
|
|
23383
23531
|
stdio: "ignore",
|
|
23384
23532
|
windowsHide: true
|
|
@@ -23651,12 +23799,12 @@ async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
|
23651
23799
|
}
|
|
23652
23800
|
|
|
23653
23801
|
// src/hotfix-coverage.ts
|
|
23654
|
-
var
|
|
23802
|
+
var import_node_child_process12 = require("node:child_process");
|
|
23655
23803
|
var CHERRY_TRAILER = /\(cherry picked from commit ([0-9a-f]{7,40})\)/g;
|
|
23656
23804
|
function checkHotfixCoverage(options = {}) {
|
|
23657
23805
|
const { cwd = process.cwd(), mainRef = "origin/main", rcRef = "origin/rc", manifestPaths = [] } = options;
|
|
23658
23806
|
const ack = (options.ack ?? []).filter(Boolean);
|
|
23659
|
-
const git2 = options.git ?? ((args, opts) => (0,
|
|
23807
|
+
const git2 = options.git ?? ((args, opts) => (0, import_node_child_process12.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
|
|
23660
23808
|
const revList = (range) => {
|
|
23661
23809
|
const out = git2(["rev-list", "--no-merges", range]).trim();
|
|
23662
23810
|
return out ? out.split("\n") : [];
|
|
@@ -23724,7 +23872,7 @@ function checkHotfixCoverage(options = {}) {
|
|
|
23724
23872
|
}
|
|
23725
23873
|
function checkHotfixCarries(options) {
|
|
23726
23874
|
const { cwd = process.cwd(), branch, baseRef, targets } = options;
|
|
23727
|
-
const git2 = options.git ?? ((args, opts) => (0,
|
|
23875
|
+
const git2 = options.git ?? ((args, opts) => (0, import_node_child_process12.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
|
|
23728
23876
|
const isAncestor = (sha, ref) => {
|
|
23729
23877
|
try {
|
|
23730
23878
|
git2(["merge-base", "--is-ancestor", sha, ref]);
|
|
@@ -24821,7 +24969,7 @@ function renderDeployPortDoctor(report) {
|
|
|
24821
24969
|
}
|
|
24822
24970
|
|
|
24823
24971
|
// src/spawn-policy-core.ts
|
|
24824
|
-
var
|
|
24972
|
+
var import_node_child_process13 = require("node:child_process");
|
|
24825
24973
|
var import_node_fs24 = require("node:fs");
|
|
24826
24974
|
var import_node_path21 = require("node:path");
|
|
24827
24975
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
@@ -24892,7 +25040,7 @@ function findViolationsInSource(raw) {
|
|
|
24892
25040
|
return found;
|
|
24893
25041
|
}
|
|
24894
25042
|
function policedFiles(root) {
|
|
24895
|
-
const r = (0,
|
|
25043
|
+
const r = (0, import_node_child_process13.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
24896
25044
|
cwd: root,
|
|
24897
25045
|
encoding: "utf8",
|
|
24898
25046
|
windowsHide: true,
|
|
@@ -24926,7 +25074,7 @@ function runSpawnPolicy(root) {
|
|
|
24926
25074
|
}
|
|
24927
25075
|
|
|
24928
25076
|
// src/test-policy-core.ts
|
|
24929
|
-
var
|
|
25077
|
+
var import_node_child_process14 = require("node:child_process");
|
|
24930
25078
|
var import_node_fs25 = require("node:fs");
|
|
24931
25079
|
var import_node_path22 = require("node:path");
|
|
24932
25080
|
|
|
@@ -25374,14 +25522,14 @@ function evaluate(changed, policy, present = () => false) {
|
|
|
25374
25522
|
return findings;
|
|
25375
25523
|
}
|
|
25376
25524
|
function git(args, cwd) {
|
|
25377
|
-
return (0,
|
|
25525
|
+
return (0, import_node_child_process14.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
|
|
25378
25526
|
}
|
|
25379
25527
|
var COAUTHOR_KEY = "Co-authored-by";
|
|
25380
25528
|
var GH_MESSAGE_SEPARATOR = /^-{5,}$/;
|
|
25381
25529
|
var LIFTED_KEYS = new RegExp(`^(?:${TRAILER_KEY}|${COAUTHOR_KEY}):`, "i");
|
|
25382
25530
|
function parseTrailers(message, cwd) {
|
|
25383
25531
|
try {
|
|
25384
|
-
return (0,
|
|
25532
|
+
return (0, import_node_child_process14.execFileSync)("git", ["interpret-trailers", "--parse", "--unfold"], {
|
|
25385
25533
|
windowsHide: true,
|
|
25386
25534
|
cwd,
|
|
25387
25535
|
input: message,
|
|
@@ -25594,7 +25742,7 @@ function runTestPolicy(root, deps = {}) {
|
|
|
25594
25742
|
}
|
|
25595
25743
|
|
|
25596
25744
|
// src/dist-drift.ts
|
|
25597
|
-
var
|
|
25745
|
+
var import_node_child_process15 = require("node:child_process");
|
|
25598
25746
|
var import_node_crypto7 = require("node:crypto");
|
|
25599
25747
|
var import_node_fs27 = require("node:fs");
|
|
25600
25748
|
var import_node_os13 = require("node:os");
|
|
@@ -25739,7 +25887,7 @@ function bomPathFor(root) {
|
|
|
25739
25887
|
}
|
|
25740
25888
|
}
|
|
25741
25889
|
function rebuildTo(packageRoot, outDir) {
|
|
25742
|
-
(0,
|
|
25890
|
+
(0, import_node_child_process15.execFileSync)(process.execPath, ["build.mjs"], {
|
|
25743
25891
|
cwd: packageRoot,
|
|
25744
25892
|
env: { ...process.env, MMI_DIST_OUTDIR: outDir },
|
|
25745
25893
|
windowsHide: true,
|
|
@@ -27456,9 +27604,9 @@ ${SSH_RECIPE_AGENT_NOTE}`);
|
|
|
27456
27604
|
|
|
27457
27605
|
// src/schedules-commands.ts
|
|
27458
27606
|
var import_promises4 = require("node:fs/promises");
|
|
27459
|
-
var
|
|
27607
|
+
var import_node_child_process16 = require("node:child_process");
|
|
27460
27608
|
var import_node_util7 = require("node:util");
|
|
27461
|
-
var execFileP5 = (0, import_node_util7.promisify)(
|
|
27609
|
+
var execFileP5 = (0, import_node_util7.promisify)(import_node_child_process16.execFile);
|
|
27462
27610
|
var AWS_REGION = "eu-central-1";
|
|
27463
27611
|
var AWS_TIMEOUT_MS = 3e4;
|
|
27464
27612
|
var AWS_RETRY_DELAY_MS = 1500;
|
|
@@ -29551,7 +29699,7 @@ function registerBootstrapCommands(program3) {
|
|
|
29551
29699
|
}
|
|
29552
29700
|
let seedPrUrl;
|
|
29553
29701
|
if (o.execute && seedPlan.mode === "pr" && seedPlan.branch && seededToBranch > 0) {
|
|
29554
|
-
await gh(["api", "-X", "PATCH", `repos/${repo}`, "-f", "allow_auto_merge=true", "-f", "allow_squash_merge=true", "-
|
|
29702
|
+
await gh(["api", "-X", "PATCH", `repos/${repo}`, "-f", "allow_auto_merge=true", "-f", "allow_squash_merge=true", "-F", `delete_branch_on_merge=${MANAGED_DELETE_BRANCH_ON_MERGE}`]).catch(() => {
|
|
29555
29703
|
});
|
|
29556
29704
|
const openPrs = await gh(["pr", "list", "--repo", repo, "--head", seedPlan.branch, "--base", baseBranch, "--state", "open", "--json", "number,url"]);
|
|
29557
29705
|
const prDecision = decideSeedPrAction(JSON.parse(openPrs.stdout || "[]"));
|
|
@@ -29600,8 +29748,8 @@ ${onlyManagedBlock ? `Only the marker-bounded Hub-managed block inside repo-owne
|
|
|
29600
29748
|
}
|
|
29601
29749
|
if (o.execute && !onlyTarget && o.class === "deployable") {
|
|
29602
29750
|
try {
|
|
29603
|
-
await gh(["api", "-X", "PATCH", `repos/${repo}`, "-f", "allow_auto_merge=true", "-f", "allow_squash_merge=true", "-
|
|
29604
|
-
applied.push("merge settings: allow_auto_merge, squash,
|
|
29751
|
+
await gh(["api", "-X", "PATCH", `repos/${repo}`, "-f", "allow_auto_merge=true", "-f", "allow_squash_merge=true", "-F", `delete_branch_on_merge=${MANAGED_DELETE_BRANCH_ON_MERGE}`]);
|
|
29752
|
+
applied.push("merge settings: allow_auto_merge, squash, preserve merged remote branches");
|
|
29605
29753
|
} catch (e) {
|
|
29606
29754
|
applied.push(`merge settings (failed: ${e.message})`);
|
|
29607
29755
|
}
|
|
@@ -30794,6 +30942,12 @@ function ghPrMergeLocalBranchDeleteWarning(message) {
|
|
|
30794
30942
|
function mergeAutoRejectedPrAlreadyClean(message) {
|
|
30795
30943
|
return /clean status \(enablePullRequestAutoMerge\)|is in clean status/i.test(message);
|
|
30796
30944
|
}
|
|
30945
|
+
function assertRemoteBranchPreservedOnMerge(repo, deleteBranchOnMerge) {
|
|
30946
|
+
if (deleteBranchOnMerge === false) return;
|
|
30947
|
+
throw new Error(
|
|
30948
|
+
`pr merge: ${repo} does not prove delete_branch_on_merge=false. Automatic deletion can irreversibly close stacked PRs. Run \`mmi-cli devops ci reconcile --repo ${repo} --apply\`, then retry; remote branch cleanup is delayed.`
|
|
30949
|
+
);
|
|
30950
|
+
}
|
|
30797
30951
|
function mergeMethodFromParentCount(parentCount) {
|
|
30798
30952
|
if (parentCount >= 2) return "merge";
|
|
30799
30953
|
return void 0;
|
|
@@ -31043,6 +31197,7 @@ async function remoteBranchExists2(branch, options = {}) {
|
|
|
31043
31197
|
|
|
31044
31198
|
// src/worktree-merge-cleanup.ts
|
|
31045
31199
|
var import_node_fs36 = require("node:fs");
|
|
31200
|
+
var import_node_path34 = require("node:path");
|
|
31046
31201
|
|
|
31047
31202
|
// src/worktree-evidence-archive.ts
|
|
31048
31203
|
var import_node_fs35 = require("node:fs");
|
|
@@ -31221,6 +31376,18 @@ function formatArtifactsArchiveReceipt(archive) {
|
|
|
31221
31376
|
function normPath2(p) {
|
|
31222
31377
|
return p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
31223
31378
|
}
|
|
31379
|
+
function removeResidueDirectory(wtPath) {
|
|
31380
|
+
try {
|
|
31381
|
+
try {
|
|
31382
|
+
if ((0, import_node_fs36.lstatSync)((0, import_node_path34.join)(wtPath, "node_modules")).isSymbolicLink()) (0, import_node_fs36.rmdirSync)((0, import_node_path34.join)(wtPath, "node_modules"));
|
|
31383
|
+
} catch {
|
|
31384
|
+
}
|
|
31385
|
+
(0, import_node_fs36.rmSync)(wtPath, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
|
|
31386
|
+
return { ok: true };
|
|
31387
|
+
} catch (e) {
|
|
31388
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
31389
|
+
}
|
|
31390
|
+
}
|
|
31224
31391
|
function samePath(a, b) {
|
|
31225
31392
|
const na = normPath2(a);
|
|
31226
31393
|
const nb = normPath2(b);
|
|
@@ -31328,6 +31495,16 @@ async function removeWorktreeWithReconcile(wtPath, git2, listWorktrees, pathExis
|
|
|
31328
31495
|
};
|
|
31329
31496
|
}
|
|
31330
31497
|
if (!isWorktreePathRegistered(worktrees, wtPath)) return pruneAndVerify("reconciled-unregistered");
|
|
31498
|
+
await git2(["worktree", "prune"]).catch(() => "");
|
|
31499
|
+
try {
|
|
31500
|
+
worktrees = await listWorktrees();
|
|
31501
|
+
} catch (e) {
|
|
31502
|
+
return {
|
|
31503
|
+
status: "failed",
|
|
31504
|
+
error: `could not verify worktree registration after prune: ${formatGitCommandError(e)}; path=${wtPath}`
|
|
31505
|
+
};
|
|
31506
|
+
}
|
|
31507
|
+
if (!isWorktreePathRegistered(worktrees, wtPath)) return pruneAndVerify("reconciled-pruned");
|
|
31331
31508
|
const retryError = await attemptRemove();
|
|
31332
31509
|
if (!retryError) return pruneAndVerify("reconciled-after-retry");
|
|
31333
31510
|
if (isNotAWorkingTreeMessage(retryError) || isNotAWorkingTreeMessage(firstError)) {
|
|
@@ -31493,6 +31670,15 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
31493
31670
|
stageTeardown,
|
|
31494
31671
|
tmpEvidenceCount: tmpEvidence.length
|
|
31495
31672
|
};
|
|
31673
|
+
if (pathExists(wtPath)) {
|
|
31674
|
+
const residue = options.removeResidueDir?.(wtPath) ?? removeResidueDirectory(wtPath);
|
|
31675
|
+
if (residue.ok) {
|
|
31676
|
+
report.worktree.residue = "swept";
|
|
31677
|
+
} else {
|
|
31678
|
+
report.worktree.residue = "left";
|
|
31679
|
+
report.worktree.residueError = residue.error;
|
|
31680
|
+
}
|
|
31681
|
+
}
|
|
31496
31682
|
try {
|
|
31497
31683
|
await git2(["branch", "-D", branch]);
|
|
31498
31684
|
report.localBranch = { name: branch, status: "deleted" };
|
|
@@ -31520,6 +31706,9 @@ function renderPrMergeCleanupLines(cleanup) {
|
|
|
31520
31706
|
} else if (wt.status === "preserved") {
|
|
31521
31707
|
lines.push(`pr merge: preserved worktree ${wt.path} (--preserve-worktree)`);
|
|
31522
31708
|
}
|
|
31709
|
+
if (wt.residue === "left") {
|
|
31710
|
+
lines.push(`pr merge: worktree ${wt.path} registration is gone but residue remains \u2014 delete by hand: ${wt.residueError ?? wt.path}`);
|
|
31711
|
+
}
|
|
31523
31712
|
if (wt.artifactsArchive?.status === "archived" && wt.artifactsArchive.path) {
|
|
31524
31713
|
lines.push(`pr merge: archived worktree evidence to ${wt.artifactsArchive.path}`);
|
|
31525
31714
|
} else if (wt.artifactsArchive?.status === "blocked" && wt.artifactsArchive.error) {
|
|
@@ -33107,7 +33296,7 @@ ${lines}`, {
|
|
|
33107
33296
|
|
|
33108
33297
|
// src/train-commands.ts
|
|
33109
33298
|
var import_node_fs38 = require("node:fs");
|
|
33110
|
-
var
|
|
33299
|
+
var import_node_path35 = require("node:path");
|
|
33111
33300
|
var RELEASE_BUMP_INTENTS = ["major", "minor", "patch"];
|
|
33112
33301
|
function resolveReleaseBumpIntent(raw) {
|
|
33113
33302
|
const intent = typeof raw === "string" ? raw.trim() : "";
|
|
@@ -33118,7 +33307,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
33118
33307
|
}
|
|
33119
33308
|
function readRepoVersion() {
|
|
33120
33309
|
try {
|
|
33121
|
-
return JSON.parse((0, import_node_fs38.readFileSync)((0,
|
|
33310
|
+
return JSON.parse((0, import_node_fs38.readFileSync)((0, import_node_path35.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
33122
33311
|
} catch {
|
|
33123
33312
|
return void 0;
|
|
33124
33313
|
}
|
|
@@ -33277,7 +33466,7 @@ function registerDeployCommands(program3) {
|
|
|
33277
33466
|
// src/discovery-commands.ts
|
|
33278
33467
|
var import_node_fs39 = require("node:fs");
|
|
33279
33468
|
var import_node_os17 = require("node:os");
|
|
33280
|
-
var
|
|
33469
|
+
var import_node_path36 = require("node:path");
|
|
33281
33470
|
var GC_GH_TIMEOUT_MS2 = 2e4;
|
|
33282
33471
|
async function collectStatus() {
|
|
33283
33472
|
const repo = await resolveRepo();
|
|
@@ -33467,8 +33656,8 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
33467
33656
|
}
|
|
33468
33657
|
const home = (0, import_node_os17.homedir)();
|
|
33469
33658
|
const plugin = onboardPluginGate({
|
|
33470
|
-
readKnown: () => readFileSyncSafe((0,
|
|
33471
|
-
readSettings: () => readFileSyncSafe((0,
|
|
33659
|
+
readKnown: () => readFileSyncSafe((0, import_node_path36.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs39.readFileSync),
|
|
33660
|
+
readSettings: () => readFileSyncSafe((0, import_node_path36.join)(home, ".claude", "settings.json"), import_node_fs39.readFileSync)
|
|
33472
33661
|
});
|
|
33473
33662
|
return { track, board, registry: registry2, secrets, plugin, doors: opts.doors ?? [], nextCommand };
|
|
33474
33663
|
}
|
|
@@ -33777,7 +33966,10 @@ function findCommandInManifest(manifest, commandPath3) {
|
|
|
33777
33966
|
return house ? houseRootAsParent(manifest, house) : void 0;
|
|
33778
33967
|
}
|
|
33779
33968
|
const visit = (command) => {
|
|
33780
|
-
|
|
33969
|
+
const aliasMatches = command.aliases?.some(
|
|
33970
|
+
(alias) => [command.path, command.flat_path].some((path2) => `${path2.slice(0, path2.lastIndexOf(" ") + 1)}${alias}` === commandPath3)
|
|
33971
|
+
);
|
|
33972
|
+
if (command.path === commandPath3 || command.flat_path === commandPath3 || aliasMatches) return command;
|
|
33781
33973
|
for (const child2 of command.subcommands) {
|
|
33782
33974
|
const found = visit(child2);
|
|
33783
33975
|
if (found) return found;
|
|
@@ -34229,7 +34421,7 @@ function registerPrLifecycleCommands(program3) {
|
|
|
34229
34421
|
|
|
34230
34422
|
// src/post-merge-recon.ts
|
|
34231
34423
|
var import_node_fs40 = require("node:fs");
|
|
34232
|
-
var
|
|
34424
|
+
var import_node_path37 = require("node:path");
|
|
34233
34425
|
|
|
34234
34426
|
// src/cross-repo-filing-issue.ts
|
|
34235
34427
|
function crossRepoFilingRetryCommand(prRepo, prNumber) {
|
|
@@ -34395,7 +34587,7 @@ function buildPostMergeReconRecovery(input) {
|
|
|
34395
34587
|
}
|
|
34396
34588
|
function writePostMergeReconRecovery(cwd, recovery) {
|
|
34397
34589
|
const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
|
|
34398
|
-
(0, import_node_fs40.mkdirSync)((0,
|
|
34590
|
+
(0, import_node_fs40.mkdirSync)((0, import_node_path37.dirname)(path2), { recursive: true });
|
|
34399
34591
|
(0, import_node_fs40.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
|
|
34400
34592
|
`, "utf8");
|
|
34401
34593
|
return path2;
|
|
@@ -37461,17 +37653,17 @@ function parseOriginRepo(remoteUrl) {
|
|
|
37461
37653
|
}
|
|
37462
37654
|
function ghHostsConfigPath(env, platform2) {
|
|
37463
37655
|
const sep3 = platform2 === "win32" ? "\\" : "/";
|
|
37464
|
-
const
|
|
37656
|
+
const join33 = (...parts) => parts.join(sep3);
|
|
37465
37657
|
const explicit = env.GH_CONFIG_DIR?.trim();
|
|
37466
|
-
if (explicit) return
|
|
37658
|
+
if (explicit) return join33(explicit, "hosts.yml");
|
|
37467
37659
|
if (platform2 === "win32") {
|
|
37468
37660
|
const appData = (env.AppData ?? env.APPDATA)?.trim();
|
|
37469
|
-
return appData ?
|
|
37661
|
+
return appData ? join33(appData, "GitHub CLI", "hosts.yml") : void 0;
|
|
37470
37662
|
}
|
|
37471
37663
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
37472
|
-
if (xdg) return
|
|
37664
|
+
if (xdg) return join33(xdg, "gh", "hosts.yml");
|
|
37473
37665
|
const home = env.HOME?.trim();
|
|
37474
|
-
return home ?
|
|
37666
|
+
return home ? join33(home, ".config", "gh", "hosts.yml") : void 0;
|
|
37475
37667
|
}
|
|
37476
37668
|
function parseGhHostsAccounts(yaml, host = "github.com") {
|
|
37477
37669
|
let hostIndent = null;
|
|
@@ -37523,8 +37715,8 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
37523
37715
|
// src/doctor-io.ts
|
|
37524
37716
|
var import_node_fs41 = require("node:fs");
|
|
37525
37717
|
var import_node_os18 = require("node:os");
|
|
37526
|
-
var
|
|
37527
|
-
var
|
|
37718
|
+
var import_node_path38 = require("node:path");
|
|
37719
|
+
var import_node_child_process17 = require("node:child_process");
|
|
37528
37720
|
var import_node_util8 = require("node:util");
|
|
37529
37721
|
|
|
37530
37722
|
// src/discard-sink.ts
|
|
@@ -37533,13 +37725,13 @@ function nodeDiscardSinkPath(platform2 = process.platform) {
|
|
|
37533
37725
|
}
|
|
37534
37726
|
|
|
37535
37727
|
// src/doctor-io.ts
|
|
37536
|
-
var execFileP6 = (0, import_node_util8.promisify)(
|
|
37728
|
+
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process17.execFile);
|
|
37537
37729
|
function execFileCapture(file, args, opts = {}) {
|
|
37538
37730
|
const sink = nodeDiscardSinkPath();
|
|
37539
37731
|
const inFd = (0, import_node_fs41.openSync)(sink, "r");
|
|
37540
37732
|
const errFd = (0, import_node_fs41.openSync)(sink, "w");
|
|
37541
37733
|
try {
|
|
37542
|
-
return (0,
|
|
37734
|
+
return (0, import_node_child_process17.execFileSync)(file, args, {
|
|
37543
37735
|
...opts,
|
|
37544
37736
|
encoding: "utf8",
|
|
37545
37737
|
stdio: [inFd, "pipe", errFd]
|
|
@@ -37553,7 +37745,7 @@ var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
|
37553
37745
|
function installedClaudePluginVersion() {
|
|
37554
37746
|
try {
|
|
37555
37747
|
const file = JSON.parse(
|
|
37556
|
-
(0, import_node_fs41.readFileSync)((0,
|
|
37748
|
+
(0, import_node_fs41.readFileSync)((0, import_node_path38.join)((0, import_node_os18.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
37557
37749
|
);
|
|
37558
37750
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
37559
37751
|
if (versions.length === 0) return void 0;
|
|
@@ -37574,7 +37766,7 @@ function readHermesPluginEvidence(env = process.env) {
|
|
|
37574
37766
|
const host = hermesConfigRoot(env);
|
|
37575
37767
|
const root = hermesPluginRoot(env);
|
|
37576
37768
|
const installRecordPresent = (0, import_node_fs41.existsSync)(root);
|
|
37577
|
-
const manifestPath = (0,
|
|
37769
|
+
const manifestPath = (0, import_node_path38.join)(root, "plugin.yaml");
|
|
37578
37770
|
let installedVersion;
|
|
37579
37771
|
let manifest = "missing";
|
|
37580
37772
|
try {
|
|
@@ -37598,14 +37790,14 @@ function readHermesPluginEvidence(env = process.env) {
|
|
|
37598
37790
|
}
|
|
37599
37791
|
let skills = false;
|
|
37600
37792
|
try {
|
|
37601
|
-
skills = (0, import_node_fs41.existsSync)((0,
|
|
37793
|
+
skills = (0, import_node_fs41.existsSync)((0, import_node_path38.join)(root, "skills")) && (0, import_node_fs41.statSync)((0, import_node_path38.join)(root, "skills")).isDirectory();
|
|
37602
37794
|
} catch {
|
|
37603
37795
|
}
|
|
37604
37796
|
return {
|
|
37605
37797
|
hostPresent: (0, import_node_fs41.existsSync)(host),
|
|
37606
37798
|
installRecordPresent,
|
|
37607
37799
|
manifest,
|
|
37608
|
-
payloadPresent: (0, import_node_fs41.existsSync)((0,
|
|
37800
|
+
payloadPresent: (0, import_node_fs41.existsSync)((0, import_node_path38.join)(root, "__init__.py")) && skills && manifest === "valid",
|
|
37609
37801
|
...installedVersion ? { installedVersion } : {}
|
|
37610
37802
|
};
|
|
37611
37803
|
}
|
|
@@ -37613,7 +37805,7 @@ function installedSurfacePluginVersion(surface) {
|
|
|
37613
37805
|
const token = surfaceToken(surface);
|
|
37614
37806
|
if (token === "kilo") {
|
|
37615
37807
|
try {
|
|
37616
|
-
const stamp = (0, import_node_fs41.readFileSync)((0,
|
|
37808
|
+
const stamp = (0, import_node_fs41.readFileSync)((0, import_node_path38.join)((0, import_node_os18.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
|
|
37617
37809
|
return stamp || void 0;
|
|
37618
37810
|
} catch {
|
|
37619
37811
|
return void 0;
|
|
@@ -37621,13 +37813,13 @@ function installedSurfacePluginVersion(surface) {
|
|
|
37621
37813
|
}
|
|
37622
37814
|
if (token === "hermes") return readHermesPluginEvidence().installedVersion;
|
|
37623
37815
|
if (token === "cursor") {
|
|
37624
|
-
return manifestVersion((0,
|
|
37816
|
+
return manifestVersion((0, import_node_path38.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
|
|
37625
37817
|
}
|
|
37626
37818
|
if (token === "jervcode") {
|
|
37627
37819
|
return installedJervCodePackageVersion();
|
|
37628
37820
|
}
|
|
37629
37821
|
if (token === "kimi") {
|
|
37630
|
-
return manifestVersion((0,
|
|
37822
|
+
return manifestVersion((0, import_node_path38.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
|
|
37631
37823
|
}
|
|
37632
37824
|
if (token === "claude") return installedClaudePluginVersion();
|
|
37633
37825
|
if (token !== "codex") return void 0;
|
|
@@ -37661,7 +37853,7 @@ function worktreeRootSync() {
|
|
|
37661
37853
|
}
|
|
37662
37854
|
var gitignorePath = () => {
|
|
37663
37855
|
const root = worktreeRootSync();
|
|
37664
|
-
return root === null ? null : (0,
|
|
37856
|
+
return root === null ? null : (0, import_node_path38.join)(root, ".gitignore");
|
|
37665
37857
|
};
|
|
37666
37858
|
function readGitignore() {
|
|
37667
37859
|
const path2 = gitignorePath();
|
|
@@ -37683,7 +37875,7 @@ function writeGitignore(content) {
|
|
|
37683
37875
|
}
|
|
37684
37876
|
}
|
|
37685
37877
|
function lineEndingState(root) {
|
|
37686
|
-
const attributesPresent = (0, import_node_fs41.existsSync)((0,
|
|
37878
|
+
const attributesPresent = (0, import_node_fs41.existsSync)((0, import_node_path38.join)(root, ".gitattributes"));
|
|
37687
37879
|
try {
|
|
37688
37880
|
const output = execFileCapture("git", ["-C", root, "ls-files", "--eol", "--", ":(glob)**/*.sh"], {
|
|
37689
37881
|
windowsHide: true
|
|
@@ -37759,7 +37951,7 @@ function ghMultiAccountCaveat(announcedLogin) {
|
|
|
37759
37951
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
37760
37952
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
37761
37953
|
function envHealLockPath(home) {
|
|
37762
|
-
return (0,
|
|
37954
|
+
return (0, import_node_path39.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
37763
37955
|
}
|
|
37764
37956
|
async function withEnvHealLock(what, run) {
|
|
37765
37957
|
try {
|
|
@@ -37934,7 +38126,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
37934
38126
|
// adopted the generated routing index (MMG-Unlive: `docs/Archive/**` only, no index, no gate.yml) would
|
|
37935
38127
|
// get a permanent — demanding an artifact it never asked for.
|
|
37936
38128
|
docsIndexState: (root) => {
|
|
37937
|
-
if (!(0, import_node_fs42.existsSync)((0,
|
|
38129
|
+
if (!(0, import_node_fs42.existsSync)((0, import_node_path39.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
37938
38130
|
const real = createDocsIndexDeps(root);
|
|
37939
38131
|
let docs2;
|
|
37940
38132
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -37943,7 +38135,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
37943
38135
|
},
|
|
37944
38136
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
37945
38137
|
healDocsIndex: (root) => {
|
|
37946
|
-
if (!(0, import_node_fs42.existsSync)((0,
|
|
38138
|
+
if (!(0, import_node_fs42.existsSync)((0, import_node_path39.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
37947
38139
|
const real = createDocsIndexDeps(root);
|
|
37948
38140
|
let docs2;
|
|
37949
38141
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -38040,7 +38232,18 @@ async function requireFreshTrainCli(commandName) {
|
|
|
38040
38232
|
releasedVersion
|
|
38041
38233
|
});
|
|
38042
38234
|
if (report.ok) return;
|
|
38043
|
-
|
|
38235
|
+
if (process.env.MMI_TRAIN_NO_SELF_UPDATE === "1") {
|
|
38236
|
+
throw new Error(staleTrainCliMessage(report, commandName));
|
|
38237
|
+
}
|
|
38238
|
+
const converge = await selfConvergeTrainCli({
|
|
38239
|
+
currentVersion: resolveClientVersion(),
|
|
38240
|
+
releasedVersion: report.releasedVersion,
|
|
38241
|
+
commandName
|
|
38242
|
+
});
|
|
38243
|
+
if (converge.kind === "failed") {
|
|
38244
|
+
throw new Error(`${commandName}: ${converge.error}. ${staleTrainCliMessage(report, commandName)}`);
|
|
38245
|
+
}
|
|
38246
|
+
process.exit(converge.reexecCode);
|
|
38044
38247
|
}
|
|
38045
38248
|
var cachedCommandPaths;
|
|
38046
38249
|
function allCommandPaths() {
|
|
@@ -38253,7 +38456,7 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
38253
38456
|
});
|
|
38254
38457
|
var rules = program2.command("rules").description("org-managed .gitignore delivery");
|
|
38255
38458
|
rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").option("--json", "machine-readable output").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
|
|
38256
|
-
const path2 = (0,
|
|
38459
|
+
const path2 = (0, import_node_path39.join)(process.cwd(), ".gitignore");
|
|
38257
38460
|
const current = (0, import_node_fs42.existsSync)(path2) ? (0, import_node_fs42.readFileSync)(path2, "utf8") : null;
|
|
38258
38461
|
const plan = planManagedGitignore(current);
|
|
38259
38462
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
@@ -38451,7 +38654,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
38451
38654
|
try {
|
|
38452
38655
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
|
|
38453
38656
|
if (o.repo) args.push("--repo", o.repo);
|
|
38454
|
-
spawnDetachedSelf(args, { spawn:
|
|
38657
|
+
spawnDetachedSelf(args, { spawn: import_node_child_process18.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
|
|
38455
38658
|
} catch {
|
|
38456
38659
|
}
|
|
38457
38660
|
}
|
|
@@ -39666,11 +39869,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
|
|
|
39666
39869
|
}
|
|
39667
39870
|
});
|
|
39668
39871
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
39669
|
-
const wfDir = (0,
|
|
39872
|
+
const wfDir = (0, import_node_path39.join)(cwd, ".github", "workflows");
|
|
39670
39873
|
if (!(0, import_node_fs42.existsSync)(wfDir)) return [];
|
|
39671
39874
|
return (0, import_node_fs42.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
39672
39875
|
try {
|
|
39673
|
-
return workflowReportsPrChecks((0, import_node_fs42.readFileSync)((0,
|
|
39876
|
+
return workflowReportsPrChecks((0, import_node_fs42.readFileSync)((0, import_node_path39.join)(wfDir, name), "utf8"));
|
|
39674
39877
|
} catch {
|
|
39675
39878
|
return true;
|
|
39676
39879
|
}
|
|
@@ -39736,16 +39939,16 @@ function ciAuditDeps() {
|
|
|
39736
39939
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
39737
39940
|
readSeedFile: (path2) => {
|
|
39738
39941
|
if (!root) return null;
|
|
39739
|
-
const fullPath = (0,
|
|
39942
|
+
const fullPath = (0, import_node_path39.join)(root, path2);
|
|
39740
39943
|
return (0, import_node_fs42.existsSync)(fullPath) ? (0, import_node_fs42.readFileSync)(fullPath, "utf8") : null;
|
|
39741
39944
|
}
|
|
39742
39945
|
};
|
|
39743
39946
|
}
|
|
39744
39947
|
function hubRoot() {
|
|
39745
|
-
const fromPkg = (0,
|
|
39948
|
+
const fromPkg = (0, import_node_path39.join)(__dirname, "..", "..");
|
|
39746
39949
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
39747
|
-
if ((0, import_node_fs42.existsSync)((0,
|
|
39748
|
-
if ((0, import_node_fs42.existsSync)((0,
|
|
39950
|
+
if ((0, import_node_fs42.existsSync)((0, import_node_path39.join)(fromPkg, marker))) return fromPkg;
|
|
39951
|
+
if ((0, import_node_fs42.existsSync)((0, import_node_path39.join)(process.cwd(), marker))) return process.cwd();
|
|
39749
39952
|
return null;
|
|
39750
39953
|
}
|
|
39751
39954
|
async function waitLoopCorePool(label) {
|
|
@@ -40021,11 +40224,11 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
40021
40224
|
const method = o.rebase ? "--rebase" : o.merge ? "--merge" : "--squash";
|
|
40022
40225
|
const repoArgs = o.repo ? ["--repo", o.repo] : [];
|
|
40023
40226
|
const repoForPostCleanup = await resolveRepo(o.repo) ?? o.repo;
|
|
40024
|
-
const prMeta = await execFileP2("gh", ["pr", "view", number, ...repoArgs, "--json", "headRefName,baseRefName,headRefOid", "--jq", "{head: .headRefName, base: .baseRefName, oid: .headRefOid}"], { timeout: GC_GH_TIMEOUT_MS }).then((r) => JSON.parse(r.stdout)).catch(async (e) => {
|
|
40227
|
+
const prMeta = await execFileP2("gh", ["pr", "view", number, ...repoArgs, "--json", "headRefName,baseRefName,headRefOid,state", "--jq", "{head: .headRefName, base: .baseRefName, oid: .headRefOid, state: .state}"], { timeout: GC_GH_TIMEOUT_MS }).then((r) => JSON.parse(r.stdout)).catch(async (e) => {
|
|
40025
40228
|
if (!isGitHubRateLimitError(e) || !repoForPostCleanup) throw e;
|
|
40026
40229
|
console.warn(`pr merge: gh GraphQL rate-limited \u2014 reading PR #${number} via REST instead (#4588).`);
|
|
40027
40230
|
const snapshot = await fetchRestPrSnapshot(number, repoForPostCleanup);
|
|
40028
|
-
return { head: snapshot.headRef, base: snapshot.baseRef, oid: snapshot.headSha };
|
|
40231
|
+
return { head: snapshot.headRef, base: snapshot.baseRef, oid: snapshot.headSha, state: snapshot.merged ? "MERGED" : snapshot.state.toUpperCase() };
|
|
40029
40232
|
});
|
|
40030
40233
|
const headRef = prMeta.head;
|
|
40031
40234
|
const baseRef = prMeta.base;
|
|
@@ -40060,6 +40263,11 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
40060
40263
|
return;
|
|
40061
40264
|
}
|
|
40062
40265
|
if (closingGuardVerdict.message) console.warn(closingGuardVerdict.message);
|
|
40266
|
+
if (prMeta.state !== "MERGED") {
|
|
40267
|
+
if (!repoForPostCleanup) throw new Error("pr merge: repository is unreadable; cannot prove remote branch preservation");
|
|
40268
|
+
const repoSettings = await defaultGitHubClient().rest("GET", `repos/${repoForPostCleanup}`);
|
|
40269
|
+
assertRemoteBranchPreservedOnMerge(repoForPostCleanup, repoSettings.delete_branch_on_merge);
|
|
40270
|
+
}
|
|
40063
40271
|
const startingPath = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
40064
40272
|
const housekeeping = assertPrMergeHousekeepingClean(startingPath || process.cwd(), "pr merge", { force: o.force });
|
|
40065
40273
|
const beforeWorktreesRead = await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => ({ state: "ok", stdout: r.stdout })).catch((e) => ({ state: "failed", error: e.message || "git worktree list failed" }));
|
|
@@ -40107,7 +40315,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
40107
40315
|
}
|
|
40108
40316
|
const remoteBefore = await remoteBranchExists2(headRef);
|
|
40109
40317
|
let upgradedToAuto = false;
|
|
40110
|
-
let remoteNotAttemptedReason = "
|
|
40318
|
+
let remoteNotAttemptedReason = "preserved-delayed-cleanup";
|
|
40111
40319
|
const overrideBody = mergeSquashBody ? { ...writeSquashBodyFile(mergeSquashBody), text: mergeSquashBody } : await composeOverrideBodyFile(
|
|
40112
40320
|
number,
|
|
40113
40321
|
repoArgs,
|
|
@@ -40326,7 +40534,7 @@ function trainApplyDeps() {
|
|
|
40326
40534
|
run: async (file, args) => {
|
|
40327
40535
|
const timeout = file === "node" && args[1] === "prepare" ? NODE_PREPARE_TIMEOUT_MS : file === "node" && args[1] === "verify" ? NODE_VERIFY_TIMEOUT_MS : file === "npm" ? NPM_TRAIN_TIMEOUT_MS : file === "bash" && args[0] === "scripts/jerv-gateway-release-deploy.sh" ? JERV_GATEWAY_DEPLOY_TIMEOUT_MS : file !== "gh" ? GIT_TIMEOUT_MS : args[0] === "run" && args[1] === "watch" ? GH_RUN_WATCH_TIMEOUT_MS : GH_TRAIN_TIMEOUT_MS;
|
|
40328
40536
|
try {
|
|
40329
|
-
return
|
|
40537
|
+
return isWin3 && file === "npm" ? (await execFileP2("cmd.exe", ["/c", "npm", ...args], { timeout })).stdout : (await execFileP2(file, args, { timeout })).stdout;
|
|
40330
40538
|
} catch (e) {
|
|
40331
40539
|
throw surfaceTrainSubprocessFailure(e, file, args);
|
|
40332
40540
|
}
|
|
@@ -40422,6 +40630,7 @@ function renderReleaseResume(r) {
|
|
|
40422
40630
|
if (r.dispatch) lines.push(` deploy: ${r.dispatch.note}`);
|
|
40423
40631
|
if (r.devRollForward) lines.push(` development: ${r.devRollForward.note}`);
|
|
40424
40632
|
if (r.rcAlignment) lines.push(` rc: ${r.rcAlignment.note}`);
|
|
40633
|
+
if (r.checkout) lines.push(` checkout: ${r.checkout.note}`);
|
|
40425
40634
|
return lines.join("\n");
|
|
40426
40635
|
}
|
|
40427
40636
|
function renderReleaseAbort(r) {
|
|
@@ -40762,7 +40971,7 @@ function hotfixStatusDeps() {
|
|
|
40762
40971
|
run: async (file, args) => {
|
|
40763
40972
|
const timeout = file === "npm" ? STATUS_NPM_MS : file === "gh" ? STATUS_GH_MS : STATUS_GIT_MS;
|
|
40764
40973
|
try {
|
|
40765
|
-
return
|
|
40974
|
+
return isWin3 && file === "npm" ? (await execFileP2("cmd.exe", ["/c", "npm", ...args], { timeout })).stdout : (await execFileP2(file, args, { timeout })).stdout;
|
|
40766
40975
|
} catch (e) {
|
|
40767
40976
|
throw surfaceTrainSubprocessFailure(e, file, args);
|
|
40768
40977
|
}
|
|
@@ -40866,7 +41075,7 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
40866
41075
|
if (!report.ok) process.exitCode = 1;
|
|
40867
41076
|
});
|
|
40868
41077
|
access.command("capabilities").description("enumerate your effective vault reach \u2014 every credential NAME + tier + scope you can read/use across project + org/master tiers (names only, no values) (#1615)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json", "machine-readable output").action((o) => withSecrets((d) => secretsCapabilities(d, o)));
|
|
40869
|
-
var
|
|
41078
|
+
var isWin3 = process.platform === "win32";
|
|
40870
41079
|
program2.command("doctor").description("safely heal active plugin wiring and report hygiene; repository cleanup requires explicit --apply (#5023), while version convergence belongs to `mmi-hub update`").option("--banner", "one-line resume summary; silent when all gates pass").option("--fast", "offline-safe fast lane; read-only, skips slow checks and network probes").option("--preflight", "read-only fast gate for automation: measures and reports with the shared exit code, zero writes").option("--verbose", "print the evidence behind every check \u2014 probes, resolved paths, versions compared, and the names behind each count (#2977)").option("--guide", "print the canonical MMI Agentic Onboarding URL").option("--json", "machine-readable output (an output format \u2014 repairs still run by lane)").option("--apply", "advanced explicit lane: also apply guarded repository maintenance (gitignore, docs index, board mechanics, merged branches, aged scratch); the host owns workspace cleanup").option("--no-repo-writes", "compatibility spelling for the safe default: env/plugin repairs only, never mutate the repository").option("--self", "verify CLI/plugin version parity and gh auth reach; suggests plugin-heal on a hard gap (reads the published version, so not offline-safe) (#2689)").addHelpText("after", "\nExit codes:\n 0 no hard gaps remain; advisory gaps may exist\n 1 one or more included hard checks failed\n\nA plain run may heal safe machine-global plugin wiring but never mutates the repository. Run\n`mmi-cli doctor --apply` only when you explicitly want the guarded full repository cleanup lane.\nCLI and host version convergence belongs to `mmi-hub update`; doctor reports lag and last-run evidence.\n--banner/--fast/--self/--preflight are read-only lanes.\n").action(async (opts) => {
|
|
40871
41080
|
if (opts.guide) {
|
|
40872
41081
|
consoleIo.log(`MMI Agentic Onboarding: ${CANONICAL_ONBOARDING_URL}`);
|
|
@@ -40897,7 +41106,7 @@ function directoryBytes(path2) {
|
|
|
40897
41106
|
return 0;
|
|
40898
41107
|
}
|
|
40899
41108
|
for (const entry of entries) {
|
|
40900
|
-
const child2 = (0,
|
|
41109
|
+
const child2 = (0, import_node_path39.join)(path2, entry.name);
|
|
40901
41110
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
40902
41111
|
else {
|
|
40903
41112
|
try {
|
|
@@ -40927,7 +41136,7 @@ function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
|
40927
41136
|
dirBytes,
|
|
40928
41137
|
listStagingDirs: (root) => (0, import_node_fs42.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
40929
41138
|
try {
|
|
40930
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
41139
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path39.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs42.statSync)(p).mtimeMs) };
|
|
40931
41140
|
} catch {
|
|
40932
41141
|
return { name: d.name, mtimeMs: Date.now() };
|
|
40933
41142
|
}
|
|
@@ -40941,7 +41150,7 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
40941
41150
|
return {
|
|
40942
41151
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
40943
41152
|
mtimeMs: (name) => {
|
|
40944
|
-
const p = (0,
|
|
41153
|
+
const p = (0, import_node_path39.join)(stagingRoot, name);
|
|
40945
41154
|
if (!(0, import_node_fs42.existsSync)(p)) return null;
|
|
40946
41155
|
try {
|
|
40947
41156
|
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs42.statSync)(q).mtimeMs);
|
package/package.json
CHANGED