@mutmutco/claude-plugin 4.5.195 → 4.5.197
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/.claude-plugin/plugin.json +1 -1
- package/cli/dist/main.cjs +53 -25
- package/cli/package.json +1 -1
- package/package.json +1 -1
package/cli/dist/main.cjs
CHANGED
|
@@ -6837,11 +6837,11 @@ var import_node_fs15 = require("node:fs");
|
|
|
6837
6837
|
var import_node_path12 = require("node:path");
|
|
6838
6838
|
var PLAN_ADVISORY_AGE_MS = 30 * 24 * 36e5;
|
|
6839
6839
|
var ROOT_SCRATCH_STALE_MS = 24 * 36e5;
|
|
6840
|
-
function executeScratchGc(repoRoot2, opts, now = Date.now()) {
|
|
6841
|
-
const snap = collectScratchSnapshot(repoRoot2);
|
|
6840
|
+
function executeScratchGc(repoRoot2, opts, now = Date.now(), deps = {}) {
|
|
6841
|
+
const snap = collectScratchSnapshot(repoRoot2, deps);
|
|
6842
6842
|
const plan = planScratchGc(snap, now);
|
|
6843
6843
|
if (!opts.apply) return { plan };
|
|
6844
|
-
return { plan, applied: applyScratchGc(plan, repoRoot2, now) };
|
|
6844
|
+
return { plan, applied: applyScratchGc(plan, repoRoot2, now, deps) };
|
|
6845
6845
|
}
|
|
6846
6846
|
function buildPrMergeScratchHousekeeping(repoRoot2, deps = {}) {
|
|
6847
6847
|
const runScratchGc = deps.runScratchGc ?? executeScratchGc;
|
|
@@ -6954,7 +6954,8 @@ function treeOlderThan(root, now, floor) {
|
|
|
6954
6954
|
}
|
|
6955
6955
|
return true;
|
|
6956
6956
|
}
|
|
6957
|
-
function applyScratchGc(plan, repoRoot2, now = Date.now()) {
|
|
6957
|
+
function applyScratchGc(plan, repoRoot2, now = Date.now(), deps = {}) {
|
|
6958
|
+
const trackedStatus = deps.trackedStatus ?? trackedPathStatus;
|
|
6958
6959
|
const result = { pruned: [], skipped: 0, bytes: 0 };
|
|
6959
6960
|
let repoAnchor;
|
|
6960
6961
|
try {
|
|
@@ -6969,7 +6970,7 @@ function applyScratchGc(plan, repoRoot2, now = Date.now()) {
|
|
|
6969
6970
|
result.skipped += 1;
|
|
6970
6971
|
continue;
|
|
6971
6972
|
}
|
|
6972
|
-
if (
|
|
6973
|
+
if (trackedStatus(c, real, repoAnchor) !== false) {
|
|
6973
6974
|
result.skipped += 1;
|
|
6974
6975
|
continue;
|
|
6975
6976
|
}
|
|
@@ -7017,14 +7018,27 @@ function trackedPathStatus(c, realPath, repoAnchor) {
|
|
|
7017
7018
|
const rel = (0, import_node_path12.relative)(repoAnchor, realPath).replace(/\\/g, "/");
|
|
7018
7019
|
if (!rel || rel.startsWith("../") || rel === "..") return null;
|
|
7019
7020
|
const gitPath = c.kind === "dir" ? `${rel.replace(/\/+$/, "")}/` : rel;
|
|
7021
|
+
let hasDotGit = false;
|
|
7022
|
+
try {
|
|
7023
|
+
(0, import_node_fs15.lstatSync)((0, import_node_path12.join)(repoAnchor, ".git"));
|
|
7024
|
+
hasDotGit = true;
|
|
7025
|
+
} catch {
|
|
7026
|
+
hasDotGit = false;
|
|
7027
|
+
}
|
|
7020
7028
|
try {
|
|
7021
7029
|
(0, import_node_child_process4.execFileSync)("git", ["-C", repoAnchor, "rev-parse", "--is-inside-work-tree"], {
|
|
7022
7030
|
windowsHide: true,
|
|
7023
|
-
|
|
7031
|
+
encoding: "utf8",
|
|
7032
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
7024
7033
|
timeout: 1e4
|
|
7025
7034
|
});
|
|
7026
|
-
} catch {
|
|
7027
|
-
return
|
|
7035
|
+
} catch (e) {
|
|
7036
|
+
if (hasDotGit) return null;
|
|
7037
|
+
const err = e;
|
|
7038
|
+
if (err.killed || err.signal || err.code === "ETIMEDOUT" || err.code === "ENOENT") return null;
|
|
7039
|
+
const stderr = typeof err.stderr === "string" ? err.stderr : Buffer.isBuffer(err.stderr) ? err.stderr.toString("utf8") : "";
|
|
7040
|
+
if (/not a git repository|cannot change to/i.test(stderr)) return false;
|
|
7041
|
+
return null;
|
|
7028
7042
|
}
|
|
7029
7043
|
try {
|
|
7030
7044
|
const out = (0, import_node_child_process4.execFileSync)("git", ["-C", repoAnchor, "ls-files", "-z", "--", gitPath], {
|
|
@@ -7065,9 +7079,19 @@ function rootScratchDirSnapshot(root, readdir2, stat4) {
|
|
|
7065
7079
|
return null;
|
|
7066
7080
|
}
|
|
7067
7081
|
}
|
|
7082
|
+
function resolveRealPathOrRaw(p, realpath) {
|
|
7083
|
+
try {
|
|
7084
|
+
return realpath(p);
|
|
7085
|
+
} catch {
|
|
7086
|
+
return p;
|
|
7087
|
+
}
|
|
7088
|
+
}
|
|
7068
7089
|
function collectScratchSnapshot(repoRoot2, deps = {}) {
|
|
7069
7090
|
const readdir2 = deps.readdir ?? import_node_fs15.readdirSync;
|
|
7070
7091
|
const stat4 = deps.stat ?? import_node_fs15.statSync;
|
|
7092
|
+
const realpath = deps.realpath ?? import_node_fs15.realpathSync;
|
|
7093
|
+
const trackedStatus = deps.trackedStatus ?? trackedPathStatus;
|
|
7094
|
+
const repoAnchor = resolveRealPathOrRaw(repoRoot2, realpath).replace(/\\/g, "/").replace(/\/+$/, "");
|
|
7071
7095
|
const plansRoot = (0, import_node_path12.join)(repoRoot2, "plans");
|
|
7072
7096
|
const rootScratchFiles = [];
|
|
7073
7097
|
try {
|
|
@@ -7078,7 +7102,12 @@ function collectScratchSnapshot(repoRoot2, deps = {}) {
|
|
|
7078
7102
|
continue;
|
|
7079
7103
|
}
|
|
7080
7104
|
const full = (0, import_node_path12.join)(repoRoot2, ent.name);
|
|
7105
|
+
const kind = isDir ? "dir" : "file";
|
|
7081
7106
|
try {
|
|
7107
|
+
const realFull = resolveRealPathOrRaw(full, realpath).replace(/\\/g, "/");
|
|
7108
|
+
if (trackedStatus({ kind, path: full }, realFull, repoAnchor) === true) {
|
|
7109
|
+
continue;
|
|
7110
|
+
}
|
|
7082
7111
|
if (isDir) {
|
|
7083
7112
|
const snap = rootScratchDirSnapshot(full, readdir2, stat4);
|
|
7084
7113
|
if (snap) rootScratchFiles.push({ path: full, dir: repoRoot2, name: ent.name, mtimeMs: snap.mtimeMs, bytes: snap.bytes, kind: "dir" });
|
|
@@ -19250,10 +19279,10 @@ var rollout_plan_default = {
|
|
|
19250
19279
|
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)."
|
|
19251
19280
|
},
|
|
19252
19281
|
baseline: {
|
|
19253
|
-
version: "4.5.
|
|
19254
|
-
tag: "v4.5.
|
|
19255
|
-
commit: "
|
|
19256
|
-
npm: "@mutmutco/cli@4.5.
|
|
19282
|
+
version: "4.5.197",
|
|
19283
|
+
tag: "v4.5.197",
|
|
19284
|
+
commit: "52a7574dc96e",
|
|
19285
|
+
npm: "@mutmutco/cli@4.5.197"
|
|
19257
19286
|
},
|
|
19258
19287
|
exitCriterion: "fleet-n-of-n",
|
|
19259
19288
|
hubOnlyShortcut: "forbidden",
|
|
@@ -19270,14 +19299,14 @@ var rollout_plan_default = {
|
|
|
19270
19299
|
repo: "mutmutco/mmi-hub",
|
|
19271
19300
|
role: "canary",
|
|
19272
19301
|
schedule: "train",
|
|
19273
|
-
v3Target: "v4.5.
|
|
19302
|
+
v3Target: "v4.5.197"
|
|
19274
19303
|
}
|
|
19275
19304
|
],
|
|
19276
19305
|
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.",
|
|
19277
19306
|
rollback: {
|
|
19278
19307
|
independent: true,
|
|
19279
|
-
mechanism: "npm dist-tag latest -> 4.5.
|
|
19280
|
-
v3Target: "v4.5.
|
|
19308
|
+
mechanism: "npm dist-tag latest -> 4.5.197 and redeploy the Hub Lambda from tag v4.5.197 (52a7574dc96e); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
19309
|
+
v3Target: "v4.5.197 (@mutmutco/cli@4.5.197, tag commit 52a7574dc96e \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
19281
19310
|
}
|
|
19282
19311
|
},
|
|
19283
19312
|
{
|
|
@@ -42884,19 +42913,18 @@ function evaluatePrMergeHousekeeping(report, context, force = false) {
|
|
|
42884
42913
|
const failed2 = scratch?.status === "failed";
|
|
42885
42914
|
const prunable = scratch?.safeAuto ?? 0;
|
|
42886
42915
|
const unprunable = scratch?.skipped ?? 0;
|
|
42887
|
-
if (!(failed2 || prunable > 0 || unprunable > 0)) return {
|
|
42888
|
-
if (force) return { blocked: false };
|
|
42916
|
+
if (!(failed2 || prunable > 0 || unprunable > 0) || force) return {};
|
|
42889
42917
|
const detail = failed2 ? `scratch housekeeping failed${scratch?.error ? `: ${scratch.error}` : ""}` : `${prunable} prunable + ${unprunable} un-prunable scratch item(s) remain after cleanup`;
|
|
42890
42918
|
return {
|
|
42891
|
-
|
|
42892
|
-
reason: `${context} housekeeping blocked merge: ${detail}. Run host-owned scratch cleanup, then retry \u2014 or re-run \`mmi-cli ${context} --force\` to acknowledge and land anyway. (Kept advisory plans/ scratch never blocks: it is gitignored and can never reach origin.)`
|
|
42919
|
+
warning: `${context} housekeeping warning (not blocking): ${detail}. Run \`mmi-cli doctor --apply\` to prune aged repo-root scratch (or \`mmi-cli doctor --verbose\` to inspect what could not be removed). Local scratch never reaches origin, so the merge proceeds.`
|
|
42893
42920
|
};
|
|
42894
42921
|
}
|
|
42895
|
-
function
|
|
42922
|
+
function reportPrMergeHousekeeping(startingPath, context, options = {}) {
|
|
42896
42923
|
const build = options.buildHousekeeping ?? ((repoRoot2) => buildPrMergeScratchHousekeeping(repoRoot2));
|
|
42897
42924
|
const housekeeping = build(startingPath || process.cwd());
|
|
42898
42925
|
const verdict = evaluatePrMergeHousekeeping(housekeeping, context, options.force);
|
|
42899
|
-
if (verdict.
|
|
42926
|
+
if (verdict.warning) process.stderr.write(`${verdict.warning}
|
|
42927
|
+
`);
|
|
42900
42928
|
return housekeeping;
|
|
42901
42929
|
}
|
|
42902
42930
|
async function pollGhPrChecks(prNumber, repoArgs) {
|
|
@@ -48667,7 +48695,7 @@ ${list}`);
|
|
|
48667
48695
|
}
|
|
48668
48696
|
class PrHeadBehindBaseError extends Error {
|
|
48669
48697
|
}
|
|
48670
|
-
pr.command("land <numbers...>").description("agent merge path (#1440): train probe \u2014 checks-wait \u2014 merge --auto \u2014 poll enqueued \u2014 development PRs only. Several PR numbers land together (#8322): all are enqueued, waited on together, a queue-ejected PR is updated and re-enqueued once, a queue merge is followed by worktree/branch cleanup and board Done, and one table (or --json) reports each outcome").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the PR repo)").option("--enqueue-only", "return once queue membership is confirmed; without a merge queue, land normally").option("--no-require-train", "skip train-authority preflight (not recommended for autonomous agents)").option("--force", "
|
|
48698
|
+
pr.command("land <numbers...>").description("agent merge path (#1440): train probe \u2014 checks-wait \u2014 merge --auto \u2014 poll enqueued \u2014 development PRs only. Several PR numbers land together (#8322): all are enqueued, waited on together, a queue-ejected PR is updated and re-enqueued once, a queue merge is followed by worktree/branch cleanup and board Done, and one table (or --json) reports each outcome").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the PR repo)").option("--enqueue-only", "return once queue membership is confirmed; without a merge queue, land normally").option("--no-require-train", "skip train-authority preflight (not recommended for autonomous agents)").option("--force", "silence the leftover-scratch warning (scratch never blocks, #8785) and acknowledge a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal").option("--without-review <reason>", "compatibility option; shared merge helpers do not require a review verdict").option("--timeout <minutes>", `merge-queue wait per PR (default ${PR_LAND_QUEUE_TIMEOUT_MS / 6e4} minutes, one window per ${PR_LAND_QUEUE_BATCH_SIZE} PRs \u2014 the queue's own check timeout is 60)`).option("--preserve-worktree", "after a queue merge keep the local worktree and branch (skips the post-merge cleanup)").option("--expect-head <sha>", "pin the PR head to this full 40-hex SHA (#8456): checked before landing, after checks and by GitHub at enqueue (--match-head-commit); never updates a BEHIND head and never retries an ejection \u2014 it stops with pinnedHead.refused instead. One PR only").action(async (numbers, opts) => {
|
|
48671
48699
|
const timeoutMinutes = opts.timeout === void 0 ? void 0 : Number(opts.timeout);
|
|
48672
48700
|
if (timeoutMinutes !== void 0 && !(timeoutMinutes > 0)) return fail("pr land: --timeout must be a positive number of minutes");
|
|
48673
48701
|
if (opts.expectHead !== void 0) {
|
|
@@ -48692,7 +48720,7 @@ ${list}`);
|
|
|
48692
48720
|
}
|
|
48693
48721
|
const repoArgs = o.repo ? ["--repo", o.repo] : [];
|
|
48694
48722
|
const startingPath = (await execFileP("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
48695
|
-
|
|
48723
|
+
reportPrMergeHousekeeping(startingPath || process.cwd(), "pr land", { force: o.force });
|
|
48696
48724
|
const landRepoForGuard = await resolveRepo(o.repo).catch(() => void 0) ?? o.repo;
|
|
48697
48725
|
const landClosingGuardInput = await withAlreadyClosedCommitTargets(
|
|
48698
48726
|
await readClosingGuardInput(number, repoArgs, landRepoForGuard, "pr land"),
|
|
@@ -48921,7 +48949,7 @@ ${list}`);
|
|
|
48921
48949
|
}
|
|
48922
48950
|
if (entries.some((e) => e.outcome !== "merged" && e.outcome !== "queued")) process.exitCode = 1;
|
|
48923
48951
|
});
|
|
48924
|
-
jsonParity(pr.command("merge <number>").description("merge a PR (squash by default); archives gitignored tmp/** before worktree teardown; on no-ci repos run pr ci-policy / checks-wait first (#1432, #5679); on a merge-queue base use `pr land <n> [<n>...]` instead (#8322) \u2014 this verb is refused there except for train PRs").option("--squash", "squash merge (default)").option("--merge", "create a merge commit").option("--rebase", "rebase merge").option("--repo <owner/repo>", "target repo (defaults to the current repo); from a foreign checkout the remote probe/delete address this repo and local cleanup runs only in the verified sibling checkout ../<repo>, else localBranch reports skipped-foreign-cwd and the receipt carries foreignCwd: true (#6148)").option("--auto", "enable auto-merge \u2014 merge once the base-branch policy is satisfied (use for policy-gated repos)").addOption(new Option("--disable-auto", "disable a queued auto-merge without merging").conflicts(["auto", "wait", "squash", "merge", "rebase", "preserveWorktree", "gc", "squashBodyFile", "force"])).option("--wait", `wait for checks to reach a terminal passing verdict before merging (default budget ${PR_CHECKS_TIMEOUT_MS / 6e4}m) \u2014 run as a background/monitor task or under a shell timeout above that budget; a short foreground timeout (e.g. 120s) kills it after checks pass and leaves the PR open (#6027)`).option("--preserve-worktree", "after merge, keep the local PR worktree/branch for an active batch (#1888); also the merge settings-proof bypass when delete_branch_on_merge cannot be proven (#6210)").option("--gc", "acknowledge deleting unarchived gitignored tmp/** evidence newer than the branch base (#5679)").option("--squash-body-file <path>", "squash commit body (overrides GitHub COMMIT_MESSAGES); use when a pushed commit mentions close/fix/resolve + #N that must not close (#5723)").option("--force", "
|
|
48952
|
+
jsonParity(pr.command("merge <number>").description("merge a PR (squash by default); archives gitignored tmp/** before worktree teardown; on no-ci repos run pr ci-policy / checks-wait first (#1432, #5679); on a merge-queue base use `pr land <n> [<n>...]` instead (#8322) \u2014 this verb is refused there except for train PRs").option("--squash", "squash merge (default)").option("--merge", "create a merge commit").option("--rebase", "rebase merge").option("--repo <owner/repo>", "target repo (defaults to the current repo); from a foreign checkout the remote probe/delete address this repo and local cleanup runs only in the verified sibling checkout ../<repo>, else localBranch reports skipped-foreign-cwd and the receipt carries foreignCwd: true (#6148)").option("--auto", "enable auto-merge \u2014 merge once the base-branch policy is satisfied (use for policy-gated repos)").addOption(new Option("--disable-auto", "disable a queued auto-merge without merging").conflicts(["auto", "wait", "squash", "merge", "rebase", "preserveWorktree", "gc", "squashBodyFile", "force"])).option("--wait", `wait for checks to reach a terminal passing verdict before merging (default budget ${PR_CHECKS_TIMEOUT_MS / 6e4}m) \u2014 run as a background/monitor task or under a shell timeout above that budget; a short foreground timeout (e.g. 120s) kills it after checks pass and leaves the PR open (#6027)`).option("--preserve-worktree", "after merge, keep the local PR worktree/branch for an active batch (#1888); also the merge settings-proof bypass when delete_branch_on_merge cannot be proven (#6210)").option("--gc", "acknowledge deleting unarchived gitignored tmp/** evidence newer than the branch base (#5679)").option("--squash-body-file <path>", "squash commit body (overrides GitHub COMMIT_MESSAGES); use when a pushed commit mentions close/fix/resolve + #N that must not close (#5723)").option("--force", "silence the leftover-scratch warning (scratch never blocks, #8785) and acknowledge a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal").option("--without-review <reason>", "compatibility option; shared merge helpers do not require a review verdict")).action(async (number, o) => {
|
|
48925
48953
|
const method = o.rebase ? "--rebase" : o.merge ? "--merge" : "--squash";
|
|
48926
48954
|
const repoArgs = o.repo ? ["--repo", o.repo] : [];
|
|
48927
48955
|
if (o.disableAuto) {
|
|
@@ -48981,7 +49009,7 @@ ${list}`);
|
|
|
48981
49009
|
assertRemoteBranchPreservedOnMerge(repoForPostCleanup, repoSettings.deleteBranchOnMerge);
|
|
48982
49010
|
}
|
|
48983
49011
|
const startingPath = (await execFileP("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
48984
|
-
const housekeeping =
|
|
49012
|
+
const housekeeping = reportPrMergeHousekeeping(startingPath || process.cwd(), "pr merge", { force: o.force });
|
|
48985
49013
|
const beforeWorktreesRead = await execFileP("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" }));
|
|
48986
49014
|
let beforeWorktrees = beforeWorktreesRead.state === "ok" ? parseGitWorktreePorcelain(beforeWorktreesRead.stdout) : [];
|
|
48987
49015
|
const cwdRepo = repoFromRemoteUrl(await gitOut(["remote", "get-url", "origin"]).catch(() => ""));
|
package/cli/package.json
CHANGED