@mutmutco/cli 3.127.0 → 3.129.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 +749 -468
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -6744,7 +6744,7 @@ function localTrainSyncBannerLine(result) {
|
|
|
6744
6744
|
// src/gc.ts
|
|
6745
6745
|
var import_node_fs11 = require("node:fs");
|
|
6746
6746
|
var import_promises = require("node:fs/promises");
|
|
6747
|
-
var
|
|
6747
|
+
var import_node_path10 = require("node:path");
|
|
6748
6748
|
|
|
6749
6749
|
// src/active-workspace-root.ts
|
|
6750
6750
|
var import_node_fs9 = require("node:fs");
|
|
@@ -6762,6 +6762,12 @@ function isPathUnderDirectory(childPath, parentPath, platform2 = process.platfor
|
|
|
6762
6762
|
if (child2 === parent) return true;
|
|
6763
6763
|
return child2.startsWith(`${parent}/`);
|
|
6764
6764
|
}
|
|
6765
|
+
function isCursorAgentHost(env = process.env) {
|
|
6766
|
+
return env.CURSOR_AGENT === "1" || Boolean(env.CURSOR_EXTENSION_HOST_ROLE?.trim()) || Boolean(env.AGENT_TRANSCRIPTS?.trim());
|
|
6767
|
+
}
|
|
6768
|
+
function unresolvedWorkspaceRefusalMessage() {
|
|
6769
|
+
return "refusing to remove a worktree: Cursor agent host cannot resolve the active workspace root. Open the primary checkout in Cursor first (keep the workspace root on the primary; edit the worktree without move_agent_to_root), or set MMI_ACTIVE_WORKSPACE_ROOT to the primary path, then retry `mmi-cli worktree gc sweep-deferred` / `worktree gc --apply` / `worktree land --apply`.";
|
|
6770
|
+
}
|
|
6765
6771
|
function normalizeActiveWorkspaceRoot(value, opts = {}) {
|
|
6766
6772
|
const raw = value?.trim();
|
|
6767
6773
|
if (!raw) return void 0;
|
|
@@ -6779,8 +6785,13 @@ function removalTouchesActiveWorkspace(targetPath, activeWorkspaceRoot, platform
|
|
|
6779
6785
|
function activeWorkspaceRefusalMessage(targetPath, activeWorkspaceRoot) {
|
|
6780
6786
|
return `refusing to remove active Cursor workspace ${activeWorkspaceRoot} (target ${targetPath}). Open the primary checkout in Cursor first, then retry \`mmi-cli worktree gc sweep-deferred\` / \`worktree gc --apply\`.`;
|
|
6781
6787
|
}
|
|
6782
|
-
function decideActiveWorkspaceGuard(targetPath, activeWorkspaceRoot, platform2 = process.platform) {
|
|
6783
|
-
if (!activeWorkspaceRoot)
|
|
6788
|
+
function decideActiveWorkspaceGuard(targetPath, activeWorkspaceRoot, platform2 = process.platform, opts = {}) {
|
|
6789
|
+
if (!activeWorkspaceRoot) {
|
|
6790
|
+
if (opts.cursorAgentHost) {
|
|
6791
|
+
return { action: "refuse", reason: "unresolved-workspace", message: unresolvedWorkspaceRefusalMessage() };
|
|
6792
|
+
}
|
|
6793
|
+
return { action: "proceed" };
|
|
6794
|
+
}
|
|
6784
6795
|
if (!removalTouchesActiveWorkspace(targetPath, activeWorkspaceRoot, platform2)) {
|
|
6785
6796
|
return { action: "proceed" };
|
|
6786
6797
|
}
|
|
@@ -6882,10 +6893,137 @@ function resolveActiveWorkspaceRoot(deps = {}) {
|
|
|
6882
6893
|
return resolveCursorAgentWorkspaceRoot(deps);
|
|
6883
6894
|
}
|
|
6884
6895
|
|
|
6896
|
+
// src/estate-hygiene.ts
|
|
6897
|
+
var import_node_path8 = require("node:path");
|
|
6898
|
+
function win32LongPath(p, platform2 = process.platform) {
|
|
6899
|
+
if (platform2 !== "win32") return p;
|
|
6900
|
+
if (p.startsWith("\\\\?\\")) return p;
|
|
6901
|
+
if (p.startsWith("\\\\")) return `\\\\?\\UNC\\${p.slice(2)}`;
|
|
6902
|
+
return `\\\\?\\${p}`;
|
|
6903
|
+
}
|
|
6904
|
+
function normalizeGithubRemote(url) {
|
|
6905
|
+
const raw = url.trim();
|
|
6906
|
+
if (!raw) return void 0;
|
|
6907
|
+
const ssh = raw.match(/^git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/i);
|
|
6908
|
+
if (ssh) return `${ssh[1]}/${ssh[2]}`.toLowerCase();
|
|
6909
|
+
const https = raw.match(/^https?:\/\/github\.com\/([^/]+)\/(.+?)(?:\.git)?$/i);
|
|
6910
|
+
if (https) return `${https[1]}/${https[2]}`.toLowerCase();
|
|
6911
|
+
return void 0;
|
|
6912
|
+
}
|
|
6913
|
+
function remotesOverlap(a, b) {
|
|
6914
|
+
const left = new Set(a.map(normalizeGithubRemote).filter((x) => Boolean(x)));
|
|
6915
|
+
if (!left.size) return false;
|
|
6916
|
+
return b.some((url) => {
|
|
6917
|
+
const key = normalizeGithubRemote(url);
|
|
6918
|
+
return Boolean(key && left.has(key));
|
|
6919
|
+
});
|
|
6920
|
+
}
|
|
6921
|
+
function worktreesRootOf(primaryCheckout) {
|
|
6922
|
+
return (0, import_node_path8.join)((0, import_node_path8.dirname)(primaryCheckout), "mmi-worktrees", (0, import_node_path8.basename)(primaryCheckout));
|
|
6923
|
+
}
|
|
6924
|
+
function helperWorktreeRoots(primaryCheckout) {
|
|
6925
|
+
return [
|
|
6926
|
+
(0, import_node_path8.join)(primaryCheckout, ".claude", "worktrees"),
|
|
6927
|
+
(0, import_node_path8.join)(primaryCheckout, ".worktrees")
|
|
6928
|
+
];
|
|
6929
|
+
}
|
|
6930
|
+
function pathProvesRepoContainerOwnership(dir, repoContainer, platform2 = process.platform) {
|
|
6931
|
+
const norm = (p) => {
|
|
6932
|
+
const unified = p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
6933
|
+
return platform2 === "win32" || platform2 === "darwin" ? unified.toLowerCase() : unified;
|
|
6934
|
+
};
|
|
6935
|
+
return norm((0, import_node_path8.dirname)(dir)) === norm(repoContainer);
|
|
6936
|
+
}
|
|
6937
|
+
function worktreesRootFromLeaseRef(ref) {
|
|
6938
|
+
const unified = ref.replace(/\\/g, "/");
|
|
6939
|
+
const marker = "/mmi-worktrees/";
|
|
6940
|
+
const idx = unified.toLowerCase().lastIndexOf(marker);
|
|
6941
|
+
if (idx < 0) return void 0;
|
|
6942
|
+
const after = unified.slice(idx + marker.length);
|
|
6943
|
+
const repo = after.split("/").filter(Boolean)[0];
|
|
6944
|
+
if (!repo) return void 0;
|
|
6945
|
+
return unified.slice(0, idx + marker.length + repo.length);
|
|
6946
|
+
}
|
|
6947
|
+
function classifyEstateAudit(input) {
|
|
6948
|
+
const otherPrimaries = input.otherCheckouts.filter((c) => remotesOverlap(input.remoteUrls, c.remoteUrls)).map((c) => c.path);
|
|
6949
|
+
const thisRoot = input.thisWorktreesRoot.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
6950
|
+
const foreignLeaseRoots = [...new Set(
|
|
6951
|
+
(input.leaseRefs ?? []).map(worktreesRootFromLeaseRef).filter((root) => Boolean(root)).filter((root) => root.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase() !== thisRoot)
|
|
6952
|
+
)];
|
|
6953
|
+
const reasons = [];
|
|
6954
|
+
if (otherPrimaries.length) {
|
|
6955
|
+
reasons.push(
|
|
6956
|
+
`another primary checkout of the same remotes exists: ${otherPrimaries.join(", ")}`
|
|
6957
|
+
);
|
|
6958
|
+
}
|
|
6959
|
+
if (foreignLeaseRoots.length) {
|
|
6960
|
+
reasons.push(
|
|
6961
|
+
`jerv lease ledger points at another worktrees root: ${foreignLeaseRoots.join(", ")}`
|
|
6962
|
+
);
|
|
6963
|
+
}
|
|
6964
|
+
return {
|
|
6965
|
+
auditedClone: input.auditedClone,
|
|
6966
|
+
otherPrimaries,
|
|
6967
|
+
foreignLeaseRoots,
|
|
6968
|
+
blocksGreen: reasons.length > 0,
|
|
6969
|
+
reasons
|
|
6970
|
+
};
|
|
6971
|
+
}
|
|
6972
|
+
function formatEstateAuditLines(audit) {
|
|
6973
|
+
const lines = [`audited clone: ${audit.auditedClone}`];
|
|
6974
|
+
if (!audit.blocksGreen) return lines;
|
|
6975
|
+
lines.push('refusing a clean "no leaks" report \u2014 this clone is not the whole estate:');
|
|
6976
|
+
for (const reason of audit.reasons) lines.push(` ${reason}`);
|
|
6977
|
+
lines.push(" fix: run doctor / worktree list --stale / worktree gc from the canonical primary (E:\\AI Projects\\Mutatis Mutandis\\\u2026), not a second clone");
|
|
6978
|
+
return lines;
|
|
6979
|
+
}
|
|
6980
|
+
function classifyOriginLeftovers(input) {
|
|
6981
|
+
const local = new Set(input.localBranches ?? []);
|
|
6982
|
+
const leftovers = [];
|
|
6983
|
+
for (const branch of input.remoteBranches) {
|
|
6984
|
+
const name = branch.replace(/^origin\//, "").trim();
|
|
6985
|
+
if (!name || name === "HEAD" || input.protectedBranches.has(name)) continue;
|
|
6986
|
+
if (input.openPrBranches.has(name)) {
|
|
6987
|
+
leftovers.push({
|
|
6988
|
+
branch: name,
|
|
6989
|
+
kind: "open-pr",
|
|
6990
|
+
autoReap: false,
|
|
6991
|
+
detail: `origin/${name} has an open PR \u2014 gc will not delete it`
|
|
6992
|
+
});
|
|
6993
|
+
continue;
|
|
6994
|
+
}
|
|
6995
|
+
if (input.mergedPrBranches.has(name)) {
|
|
6996
|
+
leftovers.push({
|
|
6997
|
+
branch: name,
|
|
6998
|
+
kind: "merged-missed",
|
|
6999
|
+
autoReap: true,
|
|
7000
|
+
detail: local.has(name) ? `origin/${name} is merged; this clone still has the head` : `origin/${name} is merged; this clone has no local branch (missed reap)`
|
|
7001
|
+
});
|
|
7002
|
+
continue;
|
|
7003
|
+
}
|
|
7004
|
+
if (input.closedUnmergedBranches.has(name)) {
|
|
7005
|
+
leftovers.push({
|
|
7006
|
+
branch: name,
|
|
7007
|
+
kind: "closed-not-merged",
|
|
7008
|
+
autoReap: Boolean(input.trainOnly),
|
|
7009
|
+
detail: `origin/${name} is closed-not-merged \u2014 gc will not auto-delete it${input.trainOnly ? " unless --train-only --apply" : ""}`
|
|
7010
|
+
});
|
|
7011
|
+
continue;
|
|
7012
|
+
}
|
|
7013
|
+
leftovers.push({
|
|
7014
|
+
branch: name,
|
|
7015
|
+
kind: local.has(name) ? "no-pr" : "other-clone",
|
|
7016
|
+
autoReap: Boolean(input.trainOnly),
|
|
7017
|
+
detail: local.has(name) ? `origin/${name} has no PR \u2014 gc will not auto-delete it` : `origin/${name} is an other-clone leftover with no PR this clone can see`
|
|
7018
|
+
});
|
|
7019
|
+
}
|
|
7020
|
+
return leftovers;
|
|
7021
|
+
}
|
|
7022
|
+
|
|
6885
7023
|
// src/worktree-ownership.ts
|
|
6886
7024
|
var import_node_fs10 = require("node:fs");
|
|
6887
7025
|
var import_node_os4 = require("node:os");
|
|
6888
|
-
var
|
|
7026
|
+
var import_node_path9 = require("node:path");
|
|
6889
7027
|
var OWNERS_FILE = "worktree-owners.json";
|
|
6890
7028
|
var EVENTS_FILE = "worktree-events.jsonl";
|
|
6891
7029
|
var WORKTREE_ACTIVITY_WINDOW_MS = 30 * 6e4;
|
|
@@ -6990,7 +7128,7 @@ function decideWorktreeRemoval(input) {
|
|
|
6990
7128
|
}
|
|
6991
7129
|
function readFreshWorktreeLease(path2, now) {
|
|
6992
7130
|
try {
|
|
6993
|
-
const candidate = JSON.parse((0, import_node_fs10.readFileSync)((0,
|
|
7131
|
+
const candidate = JSON.parse((0, import_node_fs10.readFileSync)((0, import_node_path9.join)(path2, WORKTREE_LEASE_MARKER), "utf8"));
|
|
6994
7132
|
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return void 0;
|
|
6995
7133
|
const lease = candidate;
|
|
6996
7134
|
if (lease.kind !== "worktree" || lease.state !== "active" || typeof lease.agent !== "string" || !lease.agent.trim() || typeof lease.ref !== "string" || !sameWorktreePath(lease.ref, path2) || typeof lease.createdAt !== "string" || typeof lease.ttlHours !== "number" || !Number.isFinite(lease.ttlHours) || lease.ttlHours <= 0) return void 0;
|
|
@@ -7017,7 +7155,7 @@ function readOwners(primaryRoot) {
|
|
|
7017
7155
|
function writeOwners(primaryRoot, entries) {
|
|
7018
7156
|
try {
|
|
7019
7157
|
const path2 = worktreeOwnersPath(primaryRoot);
|
|
7020
|
-
(0, import_node_fs10.mkdirSync)((0,
|
|
7158
|
+
(0, import_node_fs10.mkdirSync)((0, import_node_path9.dirname)(path2), { recursive: true });
|
|
7021
7159
|
(0, import_node_fs10.writeFileSync)(path2, serializeWorktreeOwners(entries), "utf8");
|
|
7022
7160
|
} catch {
|
|
7023
7161
|
}
|
|
@@ -7053,7 +7191,7 @@ function dropWorktreeOwner(primaryRoot, path2, expectedCreatedAt) {
|
|
|
7053
7191
|
function appendWorktreeEvent(primaryRoot, event) {
|
|
7054
7192
|
try {
|
|
7055
7193
|
const path2 = worktreeEventsPath(primaryRoot);
|
|
7056
|
-
(0, import_node_fs10.mkdirSync)((0,
|
|
7194
|
+
(0, import_node_fs10.mkdirSync)((0, import_node_path9.dirname)(path2), { recursive: true });
|
|
7057
7195
|
(0, import_node_fs10.appendFileSync)(path2, `${JSON.stringify({ at: event.at ?? (/* @__PURE__ */ new Date()).toISOString(), ...event })}
|
|
7058
7196
|
`, "utf8");
|
|
7059
7197
|
} catch {
|
|
@@ -7132,7 +7270,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
|
7132
7270
|
const resolveRoot = deps.resolveArchiveRoot ?? repoRuntimeStatePath;
|
|
7133
7271
|
if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
|
|
7134
7272
|
if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
|
|
7135
|
-
const source = (0,
|
|
7273
|
+
const source = (0, import_node_path10.join)(args.worktreePath, ".jerv");
|
|
7136
7274
|
if (!exists(source)) return { status: "absent" };
|
|
7137
7275
|
if (!isDirectory(source)) return { status: "skipped", reason: "jerv-not-a-directory" };
|
|
7138
7276
|
const runScope = resolveJervArtifactRunScope(env);
|
|
@@ -7140,7 +7278,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
|
7140
7278
|
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
7141
7279
|
const dest = resolveRoot(args.primaryRoot, "jerv-artifacts", runScope, branchSlug, stamp, ".jerv");
|
|
7142
7280
|
try {
|
|
7143
|
-
mkdirp((0,
|
|
7281
|
+
mkdirp((0, import_node_path10.dirname)(dest));
|
|
7144
7282
|
copyDir(source, dest);
|
|
7145
7283
|
if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
|
|
7146
7284
|
return { status: "archived", path: dest, runScope };
|
|
@@ -7294,7 +7432,9 @@ async function sweepDeferredWorktrees(store, deps, removalContext) {
|
|
|
7294
7432
|
const owner = removalContext ? lookupWorktreeOwner(removalContext.primaryRoot, entry.path) : void 0;
|
|
7295
7433
|
if (removalContext) {
|
|
7296
7434
|
const activeRoot = removalContext.activeWorkspaceRoot ?? resolveActiveWorkspaceRoot();
|
|
7297
|
-
const activeGuard = decideActiveWorkspaceGuard(entry.path, activeRoot
|
|
7435
|
+
const activeGuard = decideActiveWorkspaceGuard(entry.path, activeRoot, process.platform, {
|
|
7436
|
+
cursorAgentHost: removalContext.cursorAgentHost ?? false
|
|
7437
|
+
});
|
|
7298
7438
|
if (activeGuard.action === "refuse") {
|
|
7299
7439
|
stillDeferred.push({ ...entry, reason: "active-workspace" });
|
|
7300
7440
|
recordWorktreeRemoval(removalContext.primaryRoot, {
|
|
@@ -7639,23 +7779,23 @@ function resolveSafeSiblingWorktreeCleanupTarget(worktreePath, siblingRoots, dep
|
|
|
7639
7779
|
return { ok: false, reason: reasons.join("; ") || "no worktrees root to check against" };
|
|
7640
7780
|
}
|
|
7641
7781
|
function siblingMmiWorktreesRoot(repoRoot2) {
|
|
7642
|
-
const parent = (0,
|
|
7643
|
-
if ((0,
|
|
7644
|
-
const grandparent = (0,
|
|
7645
|
-
if ((0,
|
|
7646
|
-
return (0,
|
|
7782
|
+
const parent = (0, import_node_path10.dirname)(repoRoot2);
|
|
7783
|
+
if ((0, import_node_path10.basename)(parent).toLowerCase() === "mmi-worktrees") return parent;
|
|
7784
|
+
const grandparent = (0, import_node_path10.dirname)(parent);
|
|
7785
|
+
if ((0, import_node_path10.basename)(grandparent).toLowerCase() === "mmi-worktrees") return grandparent;
|
|
7786
|
+
return (0, import_node_path10.join)(parent, "mmi-worktrees");
|
|
7647
7787
|
}
|
|
7648
7788
|
function agentWorktreesRoot(repoRoot2) {
|
|
7649
|
-
return (0,
|
|
7789
|
+
return (0, import_node_path10.join)(repoRoot2, ".claude", "worktrees");
|
|
7650
7790
|
}
|
|
7651
7791
|
function worktreeScanDirs(root, repoRoot2, listDirs, isRepoCheckout) {
|
|
7652
|
-
const projectsDir = (0,
|
|
7653
|
-
const ownName = (0,
|
|
7792
|
+
const projectsDir = (0, import_node_path10.dirname)(root);
|
|
7793
|
+
const ownName = (0, import_node_path10.basename)(repoRoot2).toLowerCase();
|
|
7654
7794
|
const flat = [];
|
|
7655
7795
|
let ownContainer = null;
|
|
7656
7796
|
for (const dir of listDirs(root)) {
|
|
7657
|
-
const name = (0,
|
|
7658
|
-
if (isRepoCheckout((0,
|
|
7797
|
+
const name = (0, import_node_path10.basename)(dir);
|
|
7798
|
+
if (isRepoCheckout((0, import_node_path10.join)(projectsDir, name))) {
|
|
7659
7799
|
if (name.toLowerCase() === ownName) ownContainer = dir;
|
|
7660
7800
|
continue;
|
|
7661
7801
|
}
|
|
@@ -7664,9 +7804,9 @@ function worktreeScanDirs(root, repoRoot2, listDirs, isRepoCheckout) {
|
|
|
7664
7804
|
return ownContainer ? [...flat, ...listDirs(ownContainer)] : flat;
|
|
7665
7805
|
}
|
|
7666
7806
|
function explicitRepoWorktreesRoot(root, repoRoot2, rootDirs) {
|
|
7667
|
-
const repoName2 = (0,
|
|
7807
|
+
const repoName2 = (0, import_node_path10.basename)(repoRoot2);
|
|
7668
7808
|
const repoDir = rootDirs.find((name) => name.toLowerCase() === repoName2.toLowerCase());
|
|
7669
|
-
return repoDir ? (0,
|
|
7809
|
+
return repoDir ? (0, import_node_path10.join)(root, repoDir) : root;
|
|
7670
7810
|
}
|
|
7671
7811
|
function classifySiblingWorktreeDir(entry) {
|
|
7672
7812
|
if (!entry.ownedByCurrentRepo) {
|
|
@@ -7914,7 +8054,33 @@ function buildGcPlan(inputs) {
|
|
|
7914
8054
|
const state = closedState(prSet);
|
|
7915
8055
|
return state ? { ref, branch, prState: state.state, prNumbers: state.numbers } : null;
|
|
7916
8056
|
}).filter((r) => Boolean(r));
|
|
7917
|
-
|
|
8057
|
+
const originLeftovers = classifyOriginLeftovers({
|
|
8058
|
+
remoteBranches: inputs.originBranches ?? [],
|
|
8059
|
+
localBranches: inputs.localBranches,
|
|
8060
|
+
protectedBranches,
|
|
8061
|
+
openPrBranches: new Set(
|
|
8062
|
+
[...prs.entries()].filter(([, set]) => set.some((pr2) => pr2.state === "OPEN")).map(([b]) => b)
|
|
8063
|
+
),
|
|
8064
|
+
mergedPrBranches: new Set(
|
|
8065
|
+
[...prs.entries()].filter(([, set]) => set.some((pr2) => pr2.state === "MERGED")).map(([b]) => b)
|
|
8066
|
+
),
|
|
8067
|
+
closedUnmergedBranches: new Set(
|
|
8068
|
+
[...prs.entries()].filter(([, set]) => set.some((pr2) => pr2.state === "CLOSED") && !set.some((pr2) => pr2.state === "MERGED")).map(([b]) => b)
|
|
8069
|
+
),
|
|
8070
|
+
trainOnly: inputs.trainOnly
|
|
8071
|
+
});
|
|
8072
|
+
const plannedBranches = new Set(branches.map((b) => b.branch));
|
|
8073
|
+
const reapOriginHeads = originLeftovers.filter((l) => l.autoReap && !plannedBranches.has(l.branch)).map((l) => {
|
|
8074
|
+
const prSet = prs.get(l.branch);
|
|
8075
|
+
const state = closedState(prSet);
|
|
8076
|
+
return {
|
|
8077
|
+
branch: l.branch,
|
|
8078
|
+
prState: state?.state ?? "MERGED",
|
|
8079
|
+
prNumbers: state?.numbers ?? [],
|
|
8080
|
+
...state?.headOids.length ? { reviewedHeadOids: state.headOids } : {}
|
|
8081
|
+
};
|
|
8082
|
+
});
|
|
8083
|
+
return { branches, trackingRefs, worktreeDirs, skippedWorktreeDirs, skipped, originLeftovers, reapOriginHeads };
|
|
7918
8084
|
}
|
|
7919
8085
|
function parseRemotePruneDryRun(stdout) {
|
|
7920
8086
|
const refs = [];
|
|
@@ -8229,7 +8395,9 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
8229
8395
|
report.worktree = { path: wtPath, status: "not-attempted", reason: "main-worktree" };
|
|
8230
8396
|
} else if (wtPath) {
|
|
8231
8397
|
const activeRoot = options.removalContext?.activeWorkspaceRoot ?? resolveActiveWorkspaceRoot();
|
|
8232
|
-
const activeGuard = decideActiveWorkspaceGuard(wtPath, activeRoot
|
|
8398
|
+
const activeGuard = decideActiveWorkspaceGuard(wtPath, activeRoot, process.platform, {
|
|
8399
|
+
cursorAgentHost: options.removalContext?.cursorAgentHost ?? false
|
|
8400
|
+
});
|
|
8233
8401
|
if (activeGuard.action === "refuse") {
|
|
8234
8402
|
if (options.deferredStore) {
|
|
8235
8403
|
try {
|
|
@@ -8463,9 +8631,13 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
8463
8631
|
if (wtPath) await git2(["worktree", "prune"]).catch(() => "");
|
|
8464
8632
|
return report;
|
|
8465
8633
|
}
|
|
8466
|
-
function formatGcPlan(plan, apply) {
|
|
8634
|
+
function formatGcPlan(plan, apply, auditClone) {
|
|
8467
8635
|
const lines = [`mmi-cli worktree gc: ${apply ? "apply" : "dry-run"}`];
|
|
8468
|
-
if (
|
|
8636
|
+
if (auditClone) lines.push(`audited clone: ${auditClone}`);
|
|
8637
|
+
const hasOriginNames = Boolean(plan.originLeftovers?.length);
|
|
8638
|
+
if (!plan.branches.length && !plan.trackingRefs.length && !plan.worktreeDirs.length && !plan.reapOriginHeads?.length) {
|
|
8639
|
+
lines.push(hasOriginNames ? "nothing this clone will auto-delete" : "nothing to clean");
|
|
8640
|
+
}
|
|
8469
8641
|
if (plan.branches.length) {
|
|
8470
8642
|
lines.push("local branches:");
|
|
8471
8643
|
for (const b of plan.branches) {
|
|
@@ -8507,6 +8679,12 @@ function formatGcPlan(plan, apply) {
|
|
|
8507
8679
|
lines.push(` - ${s.path}: ${s.reason}${s.detail ? ` (${s.detail})` : ""}`);
|
|
8508
8680
|
}
|
|
8509
8681
|
}
|
|
8682
|
+
if (plan.originLeftovers?.length) {
|
|
8683
|
+
lines.push("origin leftovers gc will not silently delete (use --train-only for closed-not-merged / no-PR):");
|
|
8684
|
+
for (const leftover of plan.originLeftovers) {
|
|
8685
|
+
lines.push(` - ${leftover.branch}: ${leftover.kind}${leftover.autoReap ? " (will reap)" : ""} \u2014 ${leftover.detail}`);
|
|
8686
|
+
}
|
|
8687
|
+
}
|
|
8510
8688
|
if (!apply && (plan.branches.length || plan.trackingRefs.length || plan.worktreeDirs.length)) lines.push("rerun with --apply to delete only the listed local, remote, tracking, and directory items");
|
|
8511
8689
|
return lines.join("\n");
|
|
8512
8690
|
}
|
|
@@ -8592,7 +8770,7 @@ async function gatherStaleWorktreeWarning(gitRun = defaultGitRun) {
|
|
|
8592
8770
|
|
|
8593
8771
|
// src/released-version-cache.ts
|
|
8594
8772
|
var import_node_fs12 = require("node:fs");
|
|
8595
|
-
var
|
|
8773
|
+
var import_node_path11 = require("node:path");
|
|
8596
8774
|
|
|
8597
8775
|
// src/version-lag.ts
|
|
8598
8776
|
var VERSION_LABEL = "installed plugin/adapter cache freshness";
|
|
@@ -8688,7 +8866,7 @@ function versionAutoUpdateAction(report, releasedSource) {
|
|
|
8688
8866
|
// src/released-version-cache.ts
|
|
8689
8867
|
var RELEASED_VERSION_CACHE_MS = 24 * 36e5;
|
|
8690
8868
|
function releasedVersionCachePath(runtimeRoot) {
|
|
8691
|
-
return (0,
|
|
8869
|
+
return (0, import_node_path11.join)(runtimeRoot, "head-ts", ".released-version");
|
|
8692
8870
|
}
|
|
8693
8871
|
function readReleasedVersionCache(cachePath, now = Date.now(), read = import_node_fs12.readFileSync, currentVersion = resolveClientVersion()) {
|
|
8694
8872
|
let parsed;
|
|
@@ -8708,7 +8886,7 @@ function readReleasedVersionCache(cachePath, now = Date.now(), read = import_nod
|
|
|
8708
8886
|
}
|
|
8709
8887
|
function writeReleasedVersionCache(cachePath, version, now = Date.now()) {
|
|
8710
8888
|
try {
|
|
8711
|
-
(0, import_node_fs12.mkdirSync)((0,
|
|
8889
|
+
(0, import_node_fs12.mkdirSync)((0, import_node_path11.dirname)(cachePath), { recursive: true });
|
|
8712
8890
|
(0, import_node_fs12.writeFileSync)(cachePath, JSON.stringify({ version, at: now }), "utf8");
|
|
8713
8891
|
} catch {
|
|
8714
8892
|
}
|
|
@@ -8723,10 +8901,10 @@ function cachedReadNote(cachedAt, now = Date.now()) {
|
|
|
8723
8901
|
|
|
8724
8902
|
// src/schedules-drift-cache.ts
|
|
8725
8903
|
var import_node_fs13 = require("node:fs");
|
|
8726
|
-
var
|
|
8904
|
+
var import_node_path12 = require("node:path");
|
|
8727
8905
|
var SCHEDULES_DRIFT_CACHE_MS = 6 * 36e5;
|
|
8728
8906
|
function schedulesDriftCachePath(runtimeRoot) {
|
|
8729
|
-
return (0,
|
|
8907
|
+
return (0, import_node_path12.join)(runtimeRoot, "head-ts", ".schedules-drift");
|
|
8730
8908
|
}
|
|
8731
8909
|
function readSchedulesDriftCache(cachePath, now = Date.now(), read = import_node_fs13.readFileSync) {
|
|
8732
8910
|
let parsed;
|
|
@@ -8745,7 +8923,7 @@ function readSchedulesDriftCache(cachePath, now = Date.now(), read = import_node
|
|
|
8745
8923
|
}
|
|
8746
8924
|
function writeSchedulesDriftCache(cachePath, driftLines, now = Date.now()) {
|
|
8747
8925
|
try {
|
|
8748
|
-
(0, import_node_fs13.mkdirSync)((0,
|
|
8926
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path12.dirname)(cachePath), { recursive: true });
|
|
8749
8927
|
(0, import_node_fs13.writeFileSync)(cachePath, JSON.stringify({ driftLines, at: now }), "utf8");
|
|
8750
8928
|
} catch {
|
|
8751
8929
|
}
|
|
@@ -8901,7 +9079,7 @@ function resolveCatalogRef(probe) {
|
|
|
8901
9079
|
// src/plugin-guard-io.ts
|
|
8902
9080
|
var import_node_fs14 = require("node:fs");
|
|
8903
9081
|
var import_node_child_process6 = require("node:child_process");
|
|
8904
|
-
var
|
|
9082
|
+
var import_node_path13 = require("node:path");
|
|
8905
9083
|
var import_node_os5 = require("node:os");
|
|
8906
9084
|
var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
|
|
8907
9085
|
|
|
@@ -9204,17 +9382,17 @@ function runHostBin(bin, args, opts) {
|
|
|
9204
9382
|
return step ? execFileHard(file, argv, { ...shared, step }) : execFileP2(file, argv, shared);
|
|
9205
9383
|
}
|
|
9206
9384
|
function surfaceConfigRoot(surface, env = process.env, home = (0, import_node_os5.homedir)()) {
|
|
9207
|
-
if (surface === "codex") return env.CODEX_HOME?.trim() || (0,
|
|
9208
|
-
if (surface === "kimi") return env.KIMI_CODE_HOME?.trim() || (0,
|
|
9209
|
-
if (surface === "kilo") return env.KILO_CONFIG_DIR?.trim() || (0,
|
|
9210
|
-
if (surface === "cursor") return (0,
|
|
9385
|
+
if (surface === "codex") return env.CODEX_HOME?.trim() || (0, import_node_path13.join)(home, ".codex");
|
|
9386
|
+
if (surface === "kimi") return env.KIMI_CODE_HOME?.trim() || (0, import_node_path13.join)(home, ".kimi-code");
|
|
9387
|
+
if (surface === "kilo") return env.KILO_CONFIG_DIR?.trim() || (0, import_node_path13.join)(home, ".config", "kilo");
|
|
9388
|
+
if (surface === "cursor") return (0, import_node_path13.join)(home, ".cursor");
|
|
9211
9389
|
if (surface === "jervcode") {
|
|
9212
|
-
return env.JERVCODE_CODING_AGENT_DIR?.trim() || env.PI_CODING_AGENT_DIR?.trim() || (0,
|
|
9390
|
+
return env.JERVCODE_CODING_AGENT_DIR?.trim() || env.PI_CODING_AGENT_DIR?.trim() || (0, import_node_path13.join)(home, ".jerv", "agent");
|
|
9213
9391
|
}
|
|
9214
|
-
return (0,
|
|
9392
|
+
return (0, import_node_path13.join)(home, ".claude");
|
|
9215
9393
|
}
|
|
9216
9394
|
var installedPluginsPath = (surface = detectSurface(process.env)) => {
|
|
9217
|
-
return (0,
|
|
9395
|
+
return (0, import_node_path13.join)(surfaceConfigRoot(surface), "plugins", "installed_plugins.json");
|
|
9218
9396
|
};
|
|
9219
9397
|
function readInstalledPlugins(surface = detectSurface(process.env)) {
|
|
9220
9398
|
try {
|
|
@@ -9227,15 +9405,15 @@ function marketplaceCloneCandidates(surface, home, env = process.env) {
|
|
|
9227
9405
|
if (surface === "codex") {
|
|
9228
9406
|
const root = surfaceConfigRoot(surface, env, home);
|
|
9229
9407
|
return [
|
|
9230
|
-
(0,
|
|
9231
|
-
(0,
|
|
9408
|
+
(0, import_node_path13.join)(root, ".tmp", "marketplaces", CODEX_MARKETPLACE),
|
|
9409
|
+
(0, import_node_path13.join)(root, "plugins", "marketplaces", CODEX_MARKETPLACE)
|
|
9232
9410
|
];
|
|
9233
9411
|
}
|
|
9234
9412
|
if (surface === "kimi") return [];
|
|
9235
9413
|
if (surface === "kilo") return [];
|
|
9236
9414
|
if (surface === "cursor") return [];
|
|
9237
9415
|
if (surface === "jervcode") return [];
|
|
9238
|
-
return [(0,
|
|
9416
|
+
return [(0, import_node_path13.join)(home, ".claude", "plugins", "marketplaces", "mutmutco")];
|
|
9239
9417
|
}
|
|
9240
9418
|
function marketplaceClonePresent(surface, home, exists = import_node_fs14.existsSync, env = process.env) {
|
|
9241
9419
|
return marketplaceCloneCandidates(surface, home, env).some(exists);
|
|
@@ -9286,11 +9464,11 @@ function codexHookTrustState(status = codexPluginStatus()) {
|
|
|
9286
9464
|
return { applicable: false, trusted: false, trustedCount: 0, requiredCount: 0 };
|
|
9287
9465
|
}
|
|
9288
9466
|
const root = surfaceConfigRoot("codex");
|
|
9289
|
-
const hooksPath = (0,
|
|
9467
|
+
const hooksPath = (0, import_node_path13.join)(root, "plugins", "cache", CODEX_MARKETPLACE, "mmi", status.version, "hooks", "codex-hooks.json");
|
|
9290
9468
|
const requiredCount = countCodexHookCommands(hooksPath);
|
|
9291
9469
|
let config = "";
|
|
9292
9470
|
try {
|
|
9293
|
-
config = (0, import_node_fs14.readFileSync)((0,
|
|
9471
|
+
config = (0, import_node_fs14.readFileSync)((0, import_node_path13.join)(root, "config.toml"), "utf8");
|
|
9294
9472
|
} catch {
|
|
9295
9473
|
return { applicable: true, trusted: false, trustedCount: 0, requiredCount };
|
|
9296
9474
|
}
|
|
@@ -9317,7 +9495,7 @@ var NPM_INSTALL_TIMEOUT_MS = 12e4;
|
|
|
9317
9495
|
var CLI_VERSION_PROBE_TIMEOUT_MS = 3e4;
|
|
9318
9496
|
function resolveNpmSpawn(args) {
|
|
9319
9497
|
if (!isWin) return { file: "npm", args };
|
|
9320
|
-
const cli = (0,
|
|
9498
|
+
const cli = (0, import_node_path13.join)((0, import_node_path13.dirname)(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
|
|
9321
9499
|
if ((0, import_node_fs14.existsSync)(cli)) return { file: process.execPath, args: [cli, ...args] };
|
|
9322
9500
|
return { file: "cmd.exe", args: ["/c", "npm", ...args] };
|
|
9323
9501
|
}
|
|
@@ -9346,9 +9524,9 @@ async function npmSelfUpdateCli(target, onStep, deps = {}) {
|
|
|
9346
9524
|
}
|
|
9347
9525
|
function kiloConfigListsPlugin(configRoot, home = (0, import_node_os5.homedir)(), read = (p) => (0, import_node_fs14.readFileSync)(p, "utf8"), exists = import_node_fs14.existsSync) {
|
|
9348
9526
|
const candidates = ["kilo.json", "kilo.jsonc", "opencode.json", "opencode.jsonc", "config.json"];
|
|
9349
|
-
for (const dir of [configRoot, (0,
|
|
9527
|
+
for (const dir of [configRoot, (0, import_node_path13.join)(home, ".kilo")]) {
|
|
9350
9528
|
for (const file of candidates) {
|
|
9351
|
-
const path2 = (0,
|
|
9529
|
+
const path2 = (0, import_node_path13.join)(dir, file);
|
|
9352
9530
|
if (!exists(path2)) continue;
|
|
9353
9531
|
try {
|
|
9354
9532
|
const stripped = read(path2).replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "");
|
|
@@ -9365,7 +9543,7 @@ function kiloConfigListsPlugin(configRoot, home = (0, import_node_os5.homedir)()
|
|
|
9365
9543
|
return false;
|
|
9366
9544
|
}
|
|
9367
9545
|
function cursorLocalPluginRoot(env = process.env, home = (0, import_node_os5.homedir)()) {
|
|
9368
|
-
return (0,
|
|
9546
|
+
return (0, import_node_path13.join)(surfaceConfigRoot("cursor", env, home), "plugins", "local", "mmi");
|
|
9369
9547
|
}
|
|
9370
9548
|
function cursorPluginTreeHealthy(root, exists = import_node_fs14.existsSync) {
|
|
9371
9549
|
return [
|
|
@@ -9374,14 +9552,14 @@ function cursorPluginTreeHealthy(root, exists = import_node_fs14.existsSync) {
|
|
|
9374
9552
|
"hooks/cursor-hooks.json",
|
|
9375
9553
|
"scripts/hook-run.mjs",
|
|
9376
9554
|
"scripts/hook-policy.mjs"
|
|
9377
|
-
].every((path2) => exists((0,
|
|
9555
|
+
].every((path2) => exists((0, import_node_path13.join)(root, ...path2.split("/"))));
|
|
9378
9556
|
}
|
|
9379
9557
|
function kimiPluginTreeHealthy(root, exists = import_node_fs14.existsSync) {
|
|
9380
9558
|
return [
|
|
9381
9559
|
".kimi-plugin/plugin.json",
|
|
9382
9560
|
"skills/mmi/SKILL.md",
|
|
9383
9561
|
"scripts/hook-run.mjs"
|
|
9384
|
-
].every((path2) => exists((0,
|
|
9562
|
+
].every((path2) => exists((0, import_node_path13.join)(root, ...path2.split("/"))));
|
|
9385
9563
|
}
|
|
9386
9564
|
var JERVCODE_WRAPPER_DIR = ".pi-plugin";
|
|
9387
9565
|
function normalizePiEntry(value) {
|
|
@@ -9404,7 +9582,7 @@ function jervcodePackageFamily(entry) {
|
|
|
9404
9582
|
function isMmiOwnedPiEntry(entry) {
|
|
9405
9583
|
if (typeof entry !== "string") return false;
|
|
9406
9584
|
try {
|
|
9407
|
-
const pkg = JSON.parse((0, import_node_fs14.readFileSync)((0,
|
|
9585
|
+
const pkg = JSON.parse((0, import_node_fs14.readFileSync)((0, import_node_path13.join)(piEntryFsPath(entry), "package.json"), "utf8"));
|
|
9408
9586
|
return pkg.name === "mmi";
|
|
9409
9587
|
} catch {
|
|
9410
9588
|
return false;
|
|
@@ -9436,8 +9614,8 @@ function readPiSettings(path2) {
|
|
|
9436
9614
|
}
|
|
9437
9615
|
}
|
|
9438
9616
|
function jervcodeSettingsCandidates(env = process.env, home = (0, import_node_os5.homedir)()) {
|
|
9439
|
-
const primary = (0,
|
|
9440
|
-
const legacy = (0,
|
|
9617
|
+
const primary = (0, import_node_path13.join)(surfaceConfigRoot("jervcode", env, home), "settings.json");
|
|
9618
|
+
const legacy = (0, import_node_path13.join)(home, ".pi", "agent", "settings.json");
|
|
9441
9619
|
return legacy === primary ? [primary] : [primary, legacy];
|
|
9442
9620
|
}
|
|
9443
9621
|
function mmiPiWrapperEntry(env = process.env, home = (0, import_node_os5.homedir)()) {
|
|
@@ -9456,17 +9634,17 @@ function mmiPiWrapperEntry(env = process.env, home = (0, import_node_os5.homedir
|
|
|
9456
9634
|
function mmiPiWrapperHealthy(entry) {
|
|
9457
9635
|
if (!entry) return false;
|
|
9458
9636
|
const wrapper = piEntryFsPath(entry);
|
|
9459
|
-
return isMmiOwnedPiEntry(entry) && (0, import_node_fs14.existsSync)((0,
|
|
9637
|
+
return isMmiOwnedPiEntry(entry) && (0, import_node_fs14.existsSync)((0, import_node_path13.join)((0, import_node_path13.dirname)(wrapper), "skills", "mmi", "SKILL.md"));
|
|
9460
9638
|
}
|
|
9461
9639
|
function findMmiPiSourceClone(home = (0, import_node_os5.homedir)()) {
|
|
9462
|
-
const cacheRoot = (0,
|
|
9640
|
+
const cacheRoot = (0, import_node_path13.join)(home, ".claude", "plugins", "cache", "mutmutco", "mmi");
|
|
9463
9641
|
let best = null;
|
|
9464
9642
|
try {
|
|
9465
9643
|
for (const entry of (0, import_node_fs14.readdirSync)(cacheRoot, { withFileTypes: true })) {
|
|
9466
9644
|
if (!entry.isDirectory() || !/^\d+\.\d+\.\d+$/.test(entry.name)) continue;
|
|
9467
|
-
if (!(0, import_node_fs14.existsSync)((0,
|
|
9645
|
+
if (!(0, import_node_fs14.existsSync)((0, import_node_path13.join)(cacheRoot, entry.name, JERVCODE_WRAPPER_DIR, "package.json"))) continue;
|
|
9468
9646
|
if (!best || compareVersions(entry.name, best.version) > 0) {
|
|
9469
|
-
best = { path: (0,
|
|
9647
|
+
best = { path: (0, import_node_path13.join)(cacheRoot, entry.name), version: entry.name };
|
|
9470
9648
|
}
|
|
9471
9649
|
}
|
|
9472
9650
|
} catch {
|
|
@@ -9520,7 +9698,7 @@ function healOneJervCodeSettingsFile(settingsPath2, packagePath, version, nextLa
|
|
|
9520
9698
|
}
|
|
9521
9699
|
current.packages = merged.next;
|
|
9522
9700
|
try {
|
|
9523
|
-
(0, import_node_fs14.mkdirSync)((0,
|
|
9701
|
+
(0, import_node_fs14.mkdirSync)((0, import_node_path13.dirname)(settingsPath2), { recursive: true });
|
|
9524
9702
|
const tmp = `${settingsPath2}.tmp-${process.pid}`;
|
|
9525
9703
|
(0, import_node_fs14.writeFileSync)(tmp, `${JSON.stringify(current, null, 2)}
|
|
9526
9704
|
`, "utf8");
|
|
@@ -9551,7 +9729,7 @@ function healJervCodePackageRegistration(opts = {}) {
|
|
|
9551
9729
|
const nextLaunch = inSeat ? " \u2014 takes effect on the next seat launch" : "";
|
|
9552
9730
|
const home = opts.home ?? (0, import_node_os5.homedir)();
|
|
9553
9731
|
const agentDir = surfaceConfigRoot("jervcode", env, home);
|
|
9554
|
-
const legacyPi = (0,
|
|
9732
|
+
const legacyPi = (0, import_node_path13.join)(home, ".pi", "agent");
|
|
9555
9733
|
if (!(0, import_node_fs14.existsSync)(agentDir) && !(0, import_node_fs14.existsSync)(legacyPi)) {
|
|
9556
9734
|
return { available: false, ok: true, changed: false, version: null, detail: "skipped \u2014 no Pi/JervCode install (no agent config dir)" };
|
|
9557
9735
|
}
|
|
@@ -9560,8 +9738,8 @@ function healJervCodePackageRegistration(opts = {}) {
|
|
|
9560
9738
|
return { available: true, ok: true, changed: false, version: null, detail: "skipped \u2014 no installed MMI clone carries the pi wrapper (install the Claude plugin first)" };
|
|
9561
9739
|
}
|
|
9562
9740
|
const packagePath = `${clone.path.replace(/\\/g, "/").replace(/\/+$/, "")}/${JERVCODE_WRAPPER_DIR}`;
|
|
9563
|
-
const targets = [(0,
|
|
9564
|
-
const legacySettings = (0,
|
|
9741
|
+
const targets = [(0, import_node_path13.join)(agentDir, "settings.json")];
|
|
9742
|
+
const legacySettings = (0, import_node_path13.join)(legacyPi, "settings.json");
|
|
9565
9743
|
if ((0, import_node_fs14.existsSync)(legacyPi) && legacySettings !== targets[0]) targets.push(legacySettings);
|
|
9566
9744
|
const details = [];
|
|
9567
9745
|
let anyChanged = false;
|
|
@@ -9590,7 +9768,7 @@ function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRep
|
|
|
9590
9768
|
return {
|
|
9591
9769
|
isOrgRepo,
|
|
9592
9770
|
installRecordPresent: hasUserInstallRecord(installed, MMI_PLUGIN_ID) || hasProjectInstallRecord(installed, MMI_PLUGIN_ID, process.cwd()) || // Kimi's managed plugin directory is its native install record; it has no Claude-style ledger.
|
|
9593
|
-
surface === "kimi" && (0, import_node_fs14.existsSync)((0,
|
|
9771
|
+
surface === "kimi" && (0, import_node_fs14.existsSync)((0, import_node_path13.join)(root, "plugins", "managed", "mmi")) || // kilo-p1: the install record is the config file itself.
|
|
9594
9772
|
surface === "kilo" && kiloConfigListsPlugin(root) || // #4188: jervcode's install record is the settings-file packages[] entry itself.
|
|
9595
9773
|
surface === "jervcode" && piEntry !== null || surface === "cursor" && (0, import_node_fs14.existsSync)(cursorLocalPluginRoot()),
|
|
9596
9774
|
// Kilo has no marketplace to clone — the config file IS the install record, so this dimension of
|
|
@@ -9599,9 +9777,9 @@ function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRep
|
|
|
9599
9777
|
// Kimi keeps no plugin cache dir — installs are copied to plugins/managed/<id> and run from there.
|
|
9600
9778
|
// Kilo (kilo-p1) keeps no cache dir either: the plugin's server() provisions ~/.kilo behind the
|
|
9601
9779
|
// version stamp, so the stamp's presence is the cache signal.
|
|
9602
|
-
pluginCachePresent: surface === "jervcode" ? mmiPiWrapperHealthy(piEntry) : surface === "kilo" ? (0, import_node_fs14.existsSync)((0,
|
|
9603
|
-
codexStatus?.installed && codexStatus.enabled && codexStatus.version && (0, import_node_fs14.existsSync)((0,
|
|
9604
|
-
) : (0, import_node_fs14.existsSync)((0,
|
|
9780
|
+
pluginCachePresent: surface === "jervcode" ? mmiPiWrapperHealthy(piEntry) : surface === "kilo" ? (0, import_node_fs14.existsSync)((0, import_node_path13.join)((0, import_node_os5.homedir)(), ".kilo", ".mmi-kilo-version")) : surface === "kimi" ? kimiPluginTreeHealthy((0, import_node_path13.join)(root, "plugins", "managed", "mmi")) : surface === "cursor" ? cursorPluginTreeHealthy(cursorLocalPluginRoot()) : surface === "codex" ? Boolean(
|
|
9781
|
+
codexStatus?.installed && codexStatus.enabled && codexStatus.version && (0, import_node_fs14.existsSync)((0, import_node_path13.join)(root, "plugins", "cache", CODEX_MARKETPLACE, "mmi", codexStatus.version))
|
|
9782
|
+
) : (0, import_node_fs14.existsSync)((0, import_node_path13.join)(root, "plugins", "cache", "mutmutco", "mmi"))
|
|
9605
9783
|
};
|
|
9606
9784
|
}
|
|
9607
9785
|
async function runHostBinLogged(bin, args, opts) {
|
|
@@ -9621,9 +9799,9 @@ async function runPluginCli(bin, args, log) {
|
|
|
9621
9799
|
function captureCodexHookLauncher() {
|
|
9622
9800
|
const status = codexPluginStatus();
|
|
9623
9801
|
if (!status.installed || !status.enabled || !status.version) return void 0;
|
|
9624
|
-
const root = (0,
|
|
9802
|
+
const root = (0, import_node_path13.join)(surfaceConfigRoot("codex"), "plugins", "cache", CODEX_MARKETPLACE, "mmi", status.version);
|
|
9625
9803
|
const files = ["mmi-hook", "mmi-hook.exe"].flatMap((name) => {
|
|
9626
|
-
const path2 = (0,
|
|
9804
|
+
const path2 = (0, import_node_path13.join)(root, "bin", name);
|
|
9627
9805
|
try {
|
|
9628
9806
|
return [{ name, content: (0, import_node_fs14.readFileSync)(path2) }];
|
|
9629
9807
|
} catch {
|
|
@@ -9633,11 +9811,11 @@ function captureCodexHookLauncher() {
|
|
|
9633
9811
|
return files.length === 2 ? { root, files } : void 0;
|
|
9634
9812
|
}
|
|
9635
9813
|
function restoreCodexHookLauncher(snapshot) {
|
|
9636
|
-
if (!snapshot || (0, import_node_fs14.existsSync)((0,
|
|
9637
|
-
const bin = (0,
|
|
9814
|
+
if (!snapshot || (0, import_node_fs14.existsSync)((0, import_node_path13.join)(snapshot.root, "scripts", "hook-run.mjs"))) return false;
|
|
9815
|
+
const bin = (0, import_node_path13.join)(snapshot.root, "bin");
|
|
9638
9816
|
(0, import_node_fs14.mkdirSync)(bin, { recursive: true });
|
|
9639
9817
|
for (const file of snapshot.files) {
|
|
9640
|
-
const path2 = (0,
|
|
9818
|
+
const path2 = (0, import_node_path13.join)(bin, file.name);
|
|
9641
9819
|
(0, import_node_fs14.writeFileSync)(path2, file.content);
|
|
9642
9820
|
if (file.name === "mmi-hook") (0, import_node_fs14.chmodSync)(path2, 493);
|
|
9643
9821
|
}
|
|
@@ -9648,8 +9826,8 @@ function canonicalCursorRemote(remote) {
|
|
|
9648
9826
|
}
|
|
9649
9827
|
async function installCursorPluginCheckout(env = process.env) {
|
|
9650
9828
|
const configRoot = surfaceConfigRoot("cursor", env);
|
|
9651
|
-
const pluginsRoot = (0,
|
|
9652
|
-
const target = (0,
|
|
9829
|
+
const pluginsRoot = (0, import_node_path13.join)(configRoot, "plugins");
|
|
9830
|
+
const target = (0, import_node_path13.join)(pluginsRoot, "local", "mmi");
|
|
9653
9831
|
const source = env.MMI_CURSOR_PLUGIN_SOURCE?.trim();
|
|
9654
9832
|
if ((0, import_node_fs14.existsSync)(target) && !source) {
|
|
9655
9833
|
try {
|
|
@@ -9661,12 +9839,12 @@ async function installCursorPluginCheckout(env = process.env) {
|
|
|
9661
9839
|
return { ok: false, detail: `refused to replace unmanaged Cursor plugin directory at ${target}` };
|
|
9662
9840
|
}
|
|
9663
9841
|
}
|
|
9664
|
-
(0, import_node_fs14.mkdirSync)((0,
|
|
9665
|
-
(0, import_node_fs14.mkdirSync)((0,
|
|
9666
|
-
(0, import_node_fs14.mkdirSync)((0,
|
|
9842
|
+
(0, import_node_fs14.mkdirSync)((0, import_node_path13.join)(pluginsRoot, "local"), { recursive: true });
|
|
9843
|
+
(0, import_node_fs14.mkdirSync)((0, import_node_path13.join)(pluginsRoot, "staging"), { recursive: true });
|
|
9844
|
+
(0, import_node_fs14.mkdirSync)((0, import_node_path13.join)(pluginsRoot, "quarantine"), { recursive: true });
|
|
9667
9845
|
const suffix = `${Date.now()}-${process.pid}`;
|
|
9668
|
-
const staged = (0,
|
|
9669
|
-
const quarantined = (0,
|
|
9846
|
+
const staged = (0, import_node_path13.join)(pluginsRoot, "staging", `mmi-${suffix}`);
|
|
9847
|
+
const quarantined = (0, import_node_path13.join)(pluginsRoot, "quarantine", `mmi-${suffix}`);
|
|
9670
9848
|
try {
|
|
9671
9849
|
if (source) {
|
|
9672
9850
|
(0, import_node_fs14.cpSync)(source, staged, {
|
|
@@ -9737,7 +9915,7 @@ async function runHealSteps(host, tableSteps, deps) {
|
|
|
9737
9915
|
const refSupported = needsRefProbe ? await marketplaceAddRefSupported(host) : true;
|
|
9738
9916
|
const { steps } = adaptHealStepsForRefSupport(tableSteps, refSupported);
|
|
9739
9917
|
if (deps.banner) log(deps.banner(refSupported));
|
|
9740
|
-
const pinsPath = (0,
|
|
9918
|
+
const pinsPath = (0, import_node_path13.join)((0, import_node_os5.homedir)(), ...KNOWN_MARKETPLACES_RELATIVE);
|
|
9741
9919
|
const pins = host === "claude" ? captureMarketplacePins(readKnownMarketplacesFile(pinsPath), [MMI_MARKETPLACE_NAME, JERV_MARKETPLACE_NAME]) : /* @__PURE__ */ new Map();
|
|
9742
9920
|
let failure;
|
|
9743
9921
|
try {
|
|
@@ -10048,7 +10226,7 @@ function describeUpdatePlan(hasBinary) {
|
|
|
10048
10226
|
|
|
10049
10227
|
// src/hook-activity.ts
|
|
10050
10228
|
var import_node_fs15 = require("node:fs");
|
|
10051
|
-
var
|
|
10229
|
+
var import_node_path14 = require("node:path");
|
|
10052
10230
|
var DEFAULT_SURFACE = "claude";
|
|
10053
10231
|
function activityLogPath(cwd) {
|
|
10054
10232
|
return repoRuntimeStatePath(cwd, "hooks", "activity.jsonl");
|
|
@@ -10061,7 +10239,7 @@ function appendHookActivity(cwd, entry) {
|
|
|
10061
10239
|
surface: DEFAULT_SURFACE,
|
|
10062
10240
|
...entry
|
|
10063
10241
|
};
|
|
10064
|
-
(0, import_node_fs15.mkdirSync)((0,
|
|
10242
|
+
(0, import_node_fs15.mkdirSync)((0, import_node_path14.dirname)(path2), { recursive: true });
|
|
10065
10243
|
(0, import_node_fs15.appendFileSync)(path2, `${JSON.stringify(line)}
|
|
10066
10244
|
`, "utf8");
|
|
10067
10245
|
} catch {
|
|
@@ -10070,11 +10248,11 @@ function appendHookActivity(cwd, entry) {
|
|
|
10070
10248
|
|
|
10071
10249
|
// src/worktree.ts
|
|
10072
10250
|
var import_node_fs16 = require("node:fs");
|
|
10073
|
-
var
|
|
10251
|
+
var import_node_path16 = require("node:path");
|
|
10074
10252
|
|
|
10075
10253
|
// src/file-lock.ts
|
|
10076
10254
|
var import_promises2 = require("node:fs/promises");
|
|
10077
|
-
var
|
|
10255
|
+
var import_node_path15 = require("node:path");
|
|
10078
10256
|
var sleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
10079
10257
|
var IMMEDIATE_RETRY_BUDGET = 3;
|
|
10080
10258
|
var FileLockBusyError = class extends Error {
|
|
@@ -10159,7 +10337,7 @@ async function releaseFileLock(lockPath, guard) {
|
|
|
10159
10337
|
}
|
|
10160
10338
|
async function withFileLock(lockPath, opts, fn) {
|
|
10161
10339
|
const resolved = resolveFileLockOpts(opts);
|
|
10162
|
-
await (0, import_promises2.mkdir)((0,
|
|
10340
|
+
await (0, import_promises2.mkdir)((0, import_node_path15.dirname)(lockPath), { recursive: true }).catch(() => void 0);
|
|
10163
10341
|
const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
|
|
10164
10342
|
try {
|
|
10165
10343
|
return await fn();
|
|
@@ -10214,7 +10392,7 @@ var realFsProbe = {
|
|
|
10214
10392
|
}
|
|
10215
10393
|
};
|
|
10216
10394
|
function declaredProvision(fs2, abs) {
|
|
10217
|
-
const raw = fs2.readFile?.((0,
|
|
10395
|
+
const raw = fs2.readFile?.((0, import_node_path16.join)(abs, PKG));
|
|
10218
10396
|
if (raw === void 0) return void 0;
|
|
10219
10397
|
try {
|
|
10220
10398
|
const scripts = JSON.parse(raw).scripts;
|
|
@@ -10226,13 +10404,13 @@ function declaredProvision(fs2, abs) {
|
|
|
10226
10404
|
}
|
|
10227
10405
|
function scanInstallDirs(root, fs2 = realFsProbe) {
|
|
10228
10406
|
const factsFor = (dir) => {
|
|
10229
|
-
const abs = dir ? (0,
|
|
10230
|
-
const match = LOCKFILE_INSTALLS.find((c) => fs2.isFile((0,
|
|
10231
|
-
const hasPackageJson = fs2.isFile((0,
|
|
10407
|
+
const abs = dir ? (0, import_node_path16.join)(root, dir) : root;
|
|
10408
|
+
const match = LOCKFILE_INSTALLS.find((c) => fs2.isFile((0, import_node_path16.join)(abs, c.lockfile)));
|
|
10409
|
+
const hasPackageJson = fs2.isFile((0, import_node_path16.join)(abs, PKG));
|
|
10232
10410
|
return {
|
|
10233
10411
|
dir,
|
|
10234
10412
|
hasPackageJson,
|
|
10235
|
-
hasNodeModules: fs2.isDir((0,
|
|
10413
|
+
hasNodeModules: fs2.isDir((0, import_node_path16.join)(abs, NODE_MODULES)),
|
|
10236
10414
|
install: match?.command,
|
|
10237
10415
|
provision: hasPackageJson ? declaredProvision(fs2, abs) : void 0
|
|
10238
10416
|
};
|
|
@@ -10248,7 +10426,7 @@ function npmInstallTargets(dirs) {
|
|
|
10248
10426
|
}));
|
|
10249
10427
|
}
|
|
10250
10428
|
function isLinkedWorktree(root, fs2 = realFsProbe) {
|
|
10251
|
-
return fs2.isFile((0,
|
|
10429
|
+
return fs2.isFile((0, import_node_path16.join)(root, ".git"));
|
|
10252
10430
|
}
|
|
10253
10431
|
function worktreeAutoProvisionBanner(root, fs2 = realFsProbe) {
|
|
10254
10432
|
if (!isLinkedWorktree(root, fs2)) return null;
|
|
@@ -10258,7 +10436,7 @@ function worktreeAutoProvisionBanner(root, fs2 = realFsProbe) {
|
|
|
10258
10436
|
return `[worktree] provisioning tooling in the background (deps in ${where} + local config) \u2014 \`mmi-cli worktree setup\` to redo`;
|
|
10259
10437
|
}
|
|
10260
10438
|
function defaultCopyFile(from, to) {
|
|
10261
|
-
(0, import_node_fs16.mkdirSync)((0,
|
|
10439
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path16.dirname)(to), { recursive: true });
|
|
10262
10440
|
(0, import_node_fs16.copyFileSync)(from, to);
|
|
10263
10441
|
}
|
|
10264
10442
|
async function runDeclaredProvision(target, cwd, runInstall) {
|
|
@@ -10289,7 +10467,7 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
10289
10467
|
const targets = npmInstallTargets(allDirs);
|
|
10290
10468
|
if (deps.validateInstall) {
|
|
10291
10469
|
for (const dir of allDirs.filter((d) => d.hasPackageJson && (d.provision ?? d.install) && d.hasNodeModules)) {
|
|
10292
|
-
const cwd = dir.dir ? (0,
|
|
10470
|
+
const cwd = dir.dir ? (0, import_node_path16.join)(worktreeRoot, dir.dir) : worktreeRoot;
|
|
10293
10471
|
if (!await deps.validateInstall(cwd)) {
|
|
10294
10472
|
targets.push({
|
|
10295
10473
|
dir: dir.dir,
|
|
@@ -10303,7 +10481,7 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
10303
10481
|
const skippedInstall = allDirs.filter((d) => d.hasPackageJson && d.hasNodeModules && !targetDirs.has(d.dir)).map((d) => d.dir);
|
|
10304
10482
|
const installed = [];
|
|
10305
10483
|
for (const target of targets) {
|
|
10306
|
-
const cwd = target.dir ? (0,
|
|
10484
|
+
const cwd = target.dir ? (0, import_node_path16.join)(worktreeRoot, target.dir) : worktreeRoot;
|
|
10307
10485
|
log(`installing deps: ${target.command} in ${target.dir || "."}`);
|
|
10308
10486
|
if (target.declared) await runDeclaredProvision(target, cwd, deps.runInstall);
|
|
10309
10487
|
else await deps.runInstall(target.command, cwd);
|
|
@@ -10313,7 +10491,7 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
10313
10491
|
const copySkipped = [];
|
|
10314
10492
|
const primary = await deps.primaryCheckout();
|
|
10315
10493
|
for (const rel of LOCAL_ONLY_FILES) {
|
|
10316
|
-
const dest = (0,
|
|
10494
|
+
const dest = (0, import_node_path16.join)(worktreeRoot, rel);
|
|
10317
10495
|
if (fs2.isFile(dest)) {
|
|
10318
10496
|
copySkipped.push({ file: rel, reason: "already-present" });
|
|
10319
10497
|
continue;
|
|
@@ -10322,11 +10500,11 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
10322
10500
|
copySkipped.push({ file: rel, reason: "no-primary" });
|
|
10323
10501
|
continue;
|
|
10324
10502
|
}
|
|
10325
|
-
if (!fs2.isFile((0,
|
|
10503
|
+
if (!fs2.isFile((0, import_node_path16.join)(primary, rel))) {
|
|
10326
10504
|
copySkipped.push({ file: rel, reason: "absent-in-primary" });
|
|
10327
10505
|
continue;
|
|
10328
10506
|
}
|
|
10329
|
-
copyFile((0,
|
|
10507
|
+
copyFile((0, import_node_path16.join)(primary, rel), dest);
|
|
10330
10508
|
copied.push(rel);
|
|
10331
10509
|
log(`copied local config: ${rel}`);
|
|
10332
10510
|
}
|
|
@@ -10340,12 +10518,12 @@ function capWorktreeDirName(name, max = 40) {
|
|
|
10340
10518
|
}
|
|
10341
10519
|
function defaultWorktreePath(repoRoot2, branch) {
|
|
10342
10520
|
const safe = capWorktreeDirName(branch.replace(/[/\\]+/g, "-"));
|
|
10343
|
-
return (0,
|
|
10521
|
+
return (0, import_node_path16.join)((0, import_node_path16.dirname)(repoRoot2), "mmi-worktrees", (0, import_node_path16.basename)(repoRoot2), safe);
|
|
10344
10522
|
}
|
|
10345
10523
|
async function primaryCheckoutRootOf(git2) {
|
|
10346
10524
|
try {
|
|
10347
10525
|
const out = (await git2(["rev-parse", "--path-format=absolute", "--git-common-dir"])).trim();
|
|
10348
|
-
return out ? (0,
|
|
10526
|
+
return out ? (0, import_node_path16.dirname)(out) : void 0;
|
|
10349
10527
|
} catch {
|
|
10350
10528
|
return void 0;
|
|
10351
10529
|
}
|
|
@@ -10488,7 +10666,7 @@ function commandLadderHint() {
|
|
|
10488
10666
|
}
|
|
10489
10667
|
|
|
10490
10668
|
// src/index.ts
|
|
10491
|
-
var
|
|
10669
|
+
var import_node_path46 = require("node:path");
|
|
10492
10670
|
|
|
10493
10671
|
// src/merge-ci-policy.ts
|
|
10494
10672
|
function resolveMergeCiPolicy(input) {
|
|
@@ -11162,12 +11340,12 @@ function planManagedGitignore(current) {
|
|
|
11162
11340
|
|
|
11163
11341
|
// src/docs-index-command.ts
|
|
11164
11342
|
var import_node_fs18 = require("node:fs");
|
|
11165
|
-
var
|
|
11343
|
+
var import_node_path18 = require("node:path");
|
|
11166
11344
|
|
|
11167
11345
|
// src/doc-refs-core.ts
|
|
11168
11346
|
var import_node_child_process7 = require("node:child_process");
|
|
11169
11347
|
var import_node_fs17 = require("node:fs");
|
|
11170
|
-
var
|
|
11348
|
+
var import_node_path17 = require("node:path");
|
|
11171
11349
|
var PIN_RE = /<!--\s*pinned by\s+([^:]+?)\s*:\s*"([^"]+)"[^"]*-->/;
|
|
11172
11350
|
var PIN_MENTION_RE = /<!--\s*pinned by\b/;
|
|
11173
11351
|
var FWD_RE = /<!--\s*forward-ref:\s*(\S+?)\s*-->/;
|
|
@@ -11226,7 +11404,7 @@ function checkPins(root, readFile9, docs2) {
|
|
|
11226
11404
|
findings.push({ kind: "malformed-pin", doc, line: pin.line, detail: pin.text });
|
|
11227
11405
|
continue;
|
|
11228
11406
|
}
|
|
11229
|
-
const source = readFile9((0,
|
|
11407
|
+
const source = readFile9((0, import_node_path17.join)(root, pin.file));
|
|
11230
11408
|
if (source == null) {
|
|
11231
11409
|
findings.push({ kind: "missing-test", doc, line: pin.line, detail: pin.file });
|
|
11232
11410
|
continue;
|
|
@@ -11288,11 +11466,11 @@ function checkRefs(root, deps, docs2) {
|
|
|
11288
11466
|
for (const { ref } of extractRefs(markdown)) allFirstSegments.add(refFirstSegment(ref));
|
|
11289
11467
|
}
|
|
11290
11468
|
const tracked = allFirstSegments.size ? deps.trackedFirstSegments?.([...allFirstSegments]) ?? null : null;
|
|
11291
|
-
const firstVerifiable = (first) => tracked ? tracked.has(first) : exists((0,
|
|
11469
|
+
const firstVerifiable = (first) => tracked ? tracked.has(first) : exists((0, import_node_path17.join)(root, first));
|
|
11292
11470
|
const candidates = [];
|
|
11293
11471
|
const direct = [];
|
|
11294
11472
|
for (const [doc, markdown] of Object.entries(docs2)) {
|
|
11295
|
-
const docDir =
|
|
11473
|
+
const docDir = import_node_path17.posix.dirname(doc);
|
|
11296
11474
|
const base = docDir === "." ? "" : docDir;
|
|
11297
11475
|
const covered = /* @__PURE__ */ new Set();
|
|
11298
11476
|
const markers = [];
|
|
@@ -11301,21 +11479,21 @@ function checkRefs(root, deps, docs2) {
|
|
|
11301
11479
|
direct.push({ kind: "malformed-forward-ref", doc, line: fwd.line, detail: fwd.text });
|
|
11302
11480
|
continue;
|
|
11303
11481
|
}
|
|
11304
|
-
const docRel =
|
|
11305
|
-
const rootRel =
|
|
11482
|
+
const docRel = import_node_path17.posix.normalize(import_node_path17.posix.join(base, fwd.target));
|
|
11483
|
+
const rootRel = import_node_path17.posix.normalize(fwd.target.replace(/^\/+/, ""));
|
|
11306
11484
|
markers.push({ target: fwd.target, line: fwd.line, docRel, rootRel });
|
|
11307
11485
|
covered.add(docRel);
|
|
11308
11486
|
covered.add(rootRel);
|
|
11309
11487
|
}
|
|
11310
11488
|
const links = extractLinks(markdown).map(({ target, line }) => {
|
|
11311
|
-
const resolved =
|
|
11312
|
-
return { target, line, resolved, missing: !exists((0,
|
|
11489
|
+
const resolved = import_node_path17.posix.normalize(import_node_path17.posix.join(base, target));
|
|
11490
|
+
return { target, line, resolved, missing: !exists((0, import_node_path17.join)(root, resolved)) };
|
|
11313
11491
|
});
|
|
11314
11492
|
for (const marker of markers) {
|
|
11315
11493
|
const coversMissing = links.some(
|
|
11316
11494
|
(l) => l.missing && (l.resolved === marker.docRel || l.resolved === marker.rootRel)
|
|
11317
11495
|
);
|
|
11318
|
-
if (!coversMissing && (exists((0,
|
|
11496
|
+
if (!coversMissing && (exists((0, import_node_path17.join)(root, marker.docRel)) || exists((0, import_node_path17.join)(root, marker.rootRel)))) {
|
|
11319
11497
|
direct.push({
|
|
11320
11498
|
kind: "stale-forward-ref",
|
|
11321
11499
|
doc,
|
|
@@ -11326,7 +11504,7 @@ function checkRefs(root, deps, docs2) {
|
|
|
11326
11504
|
}
|
|
11327
11505
|
for (const { ref, line } of extractRefs(markdown)) {
|
|
11328
11506
|
if (!firstVerifiable(refFirstSegment(ref))) continue;
|
|
11329
|
-
if (!exists((0,
|
|
11507
|
+
if (!exists((0, import_node_path17.join)(root, ref))) candidates.push({ kind: "missing-path", doc, line, detail: ref });
|
|
11330
11508
|
}
|
|
11331
11509
|
for (const { target, line, resolved, missing } of links) {
|
|
11332
11510
|
if (resolved.startsWith("..")) {
|
|
@@ -11378,18 +11556,18 @@ function readFileOrNull(path2) {
|
|
|
11378
11556
|
}
|
|
11379
11557
|
function walk(dir, root, out) {
|
|
11380
11558
|
for (const entry of (0, import_node_fs17.readdirSync)(dir)) {
|
|
11381
|
-
const full = (0,
|
|
11559
|
+
const full = (0, import_node_path17.join)(dir, entry);
|
|
11382
11560
|
if ((0, import_node_fs17.statSync)(full).isDirectory()) walk(full, root, out);
|
|
11383
11561
|
else if (entry.endsWith(".md")) out.push(full.slice(root.length + 1).replaceAll("\\", "/"));
|
|
11384
11562
|
}
|
|
11385
11563
|
return out;
|
|
11386
11564
|
}
|
|
11387
11565
|
function defaultListDocs(root) {
|
|
11388
|
-
const docsDir = (0,
|
|
11566
|
+
const docsDir = (0, import_node_path17.join)(root, "docs");
|
|
11389
11567
|
const docs2 = ((0, import_node_fs17.existsSync)(docsDir) ? walk(docsDir, root, []) : []).filter(
|
|
11390
11568
|
(rel) => !SKIP_WALK.some((skip) => rel.startsWith(skip))
|
|
11391
11569
|
);
|
|
11392
|
-
return [...ROOT_DOCS.filter((rel) => (0, import_node_fs17.existsSync)((0,
|
|
11570
|
+
return [...ROOT_DOCS.filter((rel) => (0, import_node_fs17.existsSync)((0, import_node_path17.join)(root, rel))), ...docs2];
|
|
11393
11571
|
}
|
|
11394
11572
|
var CHECK_IGNORE_MAX_BUFFER = 32 * 1024 * 1024;
|
|
11395
11573
|
function defaultIsIgnored(root, relPaths, exec = import_node_child_process7.execFileSync) {
|
|
@@ -11449,7 +11627,7 @@ function runDocRefs(root, deps = {}) {
|
|
|
11449
11627
|
const walked = listDocs(root);
|
|
11450
11628
|
const ignoredDocs = walked.length ? isIgnored(walked) : /* @__PURE__ */ new Set();
|
|
11451
11629
|
const docs2 = Object.fromEntries(
|
|
11452
|
-
walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile9((0,
|
|
11630
|
+
walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile9((0, import_node_path17.join)(root, rel))]).filter(([, body]) => body != null)
|
|
11453
11631
|
);
|
|
11454
11632
|
const refResult = checkRefs(root, { exists, isIgnored, trackedFirstSegments }, docs2);
|
|
11455
11633
|
const findings = [
|
|
@@ -11557,19 +11735,19 @@ function walkMarkdown(dir) {
|
|
|
11557
11735
|
while (stack.length) {
|
|
11558
11736
|
const current = stack.pop();
|
|
11559
11737
|
for (const entry of (0, import_node_fs18.readdirSync)(current, { withFileTypes: true })) {
|
|
11560
|
-
const full = (0,
|
|
11738
|
+
const full = (0, import_node_path18.join)(current, entry.name);
|
|
11561
11739
|
if (entry.isDirectory()) {
|
|
11562
11740
|
stack.push(full);
|
|
11563
11741
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
11564
|
-
out.push((0,
|
|
11742
|
+
out.push((0, import_node_path18.relative)(dir, full).split(import_node_path18.sep).join("/"));
|
|
11565
11743
|
}
|
|
11566
11744
|
}
|
|
11567
11745
|
}
|
|
11568
11746
|
return out;
|
|
11569
11747
|
}
|
|
11570
11748
|
function createDocsIndexDeps(repoRoot2) {
|
|
11571
|
-
const docsDir = (0,
|
|
11572
|
-
const indexPath = (0,
|
|
11749
|
+
const docsDir = (0, import_node_path18.join)(repoRoot2, "docs");
|
|
11750
|
+
const indexPath = (0, import_node_path18.join)(repoRoot2, DOCS_INDEX_PATH);
|
|
11573
11751
|
return {
|
|
11574
11752
|
listDocs: () => {
|
|
11575
11753
|
if (!(0, import_node_fs18.existsSync)(docsDir)) return [];
|
|
@@ -11578,7 +11756,7 @@ function createDocsIndexDeps(repoRoot2) {
|
|
|
11578
11756
|
const ignored = defaultIsIgnored(repoRoot2, walked.map((rel) => `docs/${rel}`));
|
|
11579
11757
|
return walked.filter((rel) => !ignored.has(`docs/${rel}`)).sort();
|
|
11580
11758
|
},
|
|
11581
|
-
readDoc: (relPath) => (0, import_node_fs18.readFileSync)((0,
|
|
11759
|
+
readDoc: (relPath) => (0, import_node_fs18.readFileSync)((0, import_node_path18.join)(docsDir, relPath), "utf8"),
|
|
11582
11760
|
readIndex: () => (0, import_node_fs18.existsSync)(indexPath) ? (0, import_node_fs18.readFileSync)(indexPath, "utf8") : null,
|
|
11583
11761
|
writeIndex: (content) => (0, import_node_fs18.writeFileSync)(indexPath, content, "utf8")
|
|
11584
11762
|
};
|
|
@@ -12373,7 +12551,7 @@ function parseVerifyBroker(stdout) {
|
|
|
12373
12551
|
// src/train-apply.ts
|
|
12374
12552
|
var import_node_fs21 = require("node:fs");
|
|
12375
12553
|
var import_promises4 = require("node:fs/promises");
|
|
12376
|
-
var
|
|
12554
|
+
var import_node_path21 = require("node:path");
|
|
12377
12555
|
|
|
12378
12556
|
// src/bootstrap-org-ruleset.ts
|
|
12379
12557
|
var ORG_NO_AGENT_FILES_RULESET_NAME = "mmi-no-agent-files-org";
|
|
@@ -12846,7 +13024,7 @@ function renderAccessReport(report) {
|
|
|
12846
13024
|
|
|
12847
13025
|
// src/cli-doctor-shared.ts
|
|
12848
13026
|
var import_node_fs19 = require("node:fs");
|
|
12849
|
-
var
|
|
13027
|
+
var import_node_path20 = require("node:path");
|
|
12850
13028
|
var import_node_fs20 = require("node:fs");
|
|
12851
13029
|
|
|
12852
13030
|
// ../infra/registry-endpoints.mjs
|
|
@@ -12976,9 +13154,9 @@ function extractWorkflowCrons(yamlText) {
|
|
|
12976
13154
|
function workflowEntry(repo, workflowPath, yamlText) {
|
|
12977
13155
|
const crons = extractWorkflowCrons(yamlText);
|
|
12978
13156
|
if (!crons.length) return null;
|
|
12979
|
-
const
|
|
13157
|
+
const basename8 = workflowPath.split("/").pop() ?? workflowPath;
|
|
12980
13158
|
return {
|
|
12981
|
-
name: `${repo}/${
|
|
13159
|
+
name: `${repo}/${basename8.replace(/\.ya?ml$/, "")}`,
|
|
12982
13160
|
cadence: crons.join(" + "),
|
|
12983
13161
|
executor: "github-actions",
|
|
12984
13162
|
llm: llmFromHeader(yamlText),
|
|
@@ -13388,8 +13566,8 @@ function scheduleRecordFromWorkflow(repo, workflowPath, yamlText) {
|
|
|
13388
13566
|
const crons = extractWorkflowCrons(yamlText);
|
|
13389
13567
|
const header = parseScheduleHeader(yamlText);
|
|
13390
13568
|
if (!crons.length && !header.schedule) return null;
|
|
13391
|
-
const
|
|
13392
|
-
const expectedId = `${repo}/${
|
|
13569
|
+
const basename8 = workflowPath.split("/").pop() ?? workflowPath;
|
|
13570
|
+
const expectedId = `${repo}/${basename8.replace(/\.ya?ml$/, "")}`;
|
|
13393
13571
|
if (isHarbourUnmanaged(yamlText)) return null;
|
|
13394
13572
|
const missing = SCHEDULE_HEADER_FIELDS.filter((f) => !header[f]);
|
|
13395
13573
|
if (missing.length) {
|
|
@@ -13764,7 +13942,7 @@ var import_node_os7 = require("node:os");
|
|
|
13764
13942
|
// src/gh-create.ts
|
|
13765
13943
|
var import_promises3 = require("node:fs/promises");
|
|
13766
13944
|
var import_node_os6 = require("node:os");
|
|
13767
|
-
var
|
|
13945
|
+
var import_node_path19 = require("node:path");
|
|
13768
13946
|
var import_node_crypto3 = require("node:crypto");
|
|
13769
13947
|
|
|
13770
13948
|
// src/board-priority.ts
|
|
@@ -13847,8 +14025,8 @@ async function bodyArgsViaFile(args, deps = {}) {
|
|
|
13847
14025
|
const remove2 = deps.remove ?? import_promises3.unlink;
|
|
13848
14026
|
const ensureDir = deps.ensureDir ?? import_promises3.mkdir;
|
|
13849
14027
|
const dir = deps.dir ?? (0, import_node_os6.tmpdir)();
|
|
13850
|
-
const file = (0,
|
|
13851
|
-
await ensureDir((0,
|
|
14028
|
+
const file = (0, import_node_path19.join)(dir, `mmi-gh-body-${process.pid}-${(0, import_node_crypto3.randomBytes)(4).toString("hex")}.md`);
|
|
14029
|
+
await ensureDir((0, import_node_path19.dirname)(file), { recursive: true }).catch(() => {
|
|
13852
14030
|
});
|
|
13853
14031
|
await write(file, args[i + 1], "utf8");
|
|
13854
14032
|
return {
|
|
@@ -15391,7 +15569,7 @@ async function localBranchHeads() {
|
|
|
15391
15569
|
}
|
|
15392
15570
|
async function currentRepoWorktreeGitRoot(repoRoot2) {
|
|
15393
15571
|
const gitCommonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
15394
|
-
return gitCommonDir ? (0,
|
|
15572
|
+
return gitCommonDir ? (0, import_node_path20.resolve)(repoRoot2, gitCommonDir, "worktrees") : "";
|
|
15395
15573
|
}
|
|
15396
15574
|
async function worktreeBranches() {
|
|
15397
15575
|
const { stdout } = await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS });
|
|
@@ -15411,7 +15589,7 @@ function resolveGitdirForWorktreeFile(worktreePath, content) {
|
|
|
15411
15589
|
const match = /^gitdir:\s*(.+)\s*$/im.exec(content);
|
|
15412
15590
|
if (!match?.[1]) return void 0;
|
|
15413
15591
|
const raw = match[1].trim();
|
|
15414
|
-
return (0,
|
|
15592
|
+
return (0, import_node_path20.isAbsolute)(raw) ? raw : (0, import_node_path20.resolve)(worktreePath, raw);
|
|
15415
15593
|
}
|
|
15416
15594
|
function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
15417
15595
|
if (!worktreeGitRoot) return false;
|
|
@@ -15420,9 +15598,9 @@ function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
|
15420
15598
|
for (const ent of entries) {
|
|
15421
15599
|
if (!ent.isDirectory()) continue;
|
|
15422
15600
|
try {
|
|
15423
|
-
const gitdirPath = (0, import_node_fs19.readFileSync)((0,
|
|
15424
|
-
const resolvedGitdir = (0,
|
|
15425
|
-
if (sameWorktreeMetadataPath((0,
|
|
15601
|
+
const gitdirPath = (0, import_node_fs19.readFileSync)((0, import_node_path20.join)(worktreeGitRoot, ent.name, "gitdir"), "utf8").trim();
|
|
15602
|
+
const resolvedGitdir = (0, import_node_path20.isAbsolute)(gitdirPath) ? gitdirPath : (0, import_node_path20.resolve)(worktreeGitRoot, ent.name, gitdirPath);
|
|
15603
|
+
if (sameWorktreeMetadataPath((0, import_node_path20.dirname)(resolvedGitdir), worktreePath)) return true;
|
|
15426
15604
|
} catch {
|
|
15427
15605
|
}
|
|
15428
15606
|
}
|
|
@@ -15441,7 +15619,7 @@ function pathExistsKnown(path2) {
|
|
|
15441
15619
|
}
|
|
15442
15620
|
}
|
|
15443
15621
|
function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
15444
|
-
const gitPath = (0,
|
|
15622
|
+
const gitPath = (0, import_node_path20.join)(path2, ".git");
|
|
15445
15623
|
let st;
|
|
15446
15624
|
try {
|
|
15447
15625
|
st = (0, import_node_fs20.lstatSync)(gitPath);
|
|
@@ -15495,24 +15673,25 @@ async function preservedBranches() {
|
|
|
15495
15673
|
async function siblingWorktreeDirs(explicitRoot) {
|
|
15496
15674
|
const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
15497
15675
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
15498
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
15676
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path20.dirname)((0, import_node_path20.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
15499
15677
|
try {
|
|
15500
15678
|
const dirs = explicitRoot ? listDirsIn(resolveExplicitScanRoot(explicitRoot, primaryRepoRoot)) : worktreeScanDirs(siblingMmiWorktreesRoot(primaryRepoRoot), primaryRepoRoot, listDirsIn, isRepoCheckoutDir);
|
|
15501
15679
|
const agentDirs2 = listDirsIn(agentWorktreesRoot(primaryRepoRoot));
|
|
15502
|
-
|
|
15680
|
+
const repoLocalWorktrees = listDirsIn((0, import_node_path20.join)(primaryRepoRoot, ".worktrees"));
|
|
15681
|
+
return [...dirs, ...agentDirs2, ...repoLocalWorktrees].map((dir) => inspectSiblingWorktreeDir(dir, worktreeGitRoot)).filter((entry) => Boolean(entry));
|
|
15503
15682
|
} catch {
|
|
15504
15683
|
return [];
|
|
15505
15684
|
}
|
|
15506
15685
|
}
|
|
15507
15686
|
function listDirsIn(dir) {
|
|
15508
15687
|
try {
|
|
15509
|
-
return (0, import_node_fs20.readdirSync)(dir, { withFileTypes: true }).filter((ent) => ent.isDirectory()).map((ent) => (0,
|
|
15688
|
+
return (0, import_node_fs20.readdirSync)(dir, { withFileTypes: true }).filter((ent) => ent.isDirectory()).map((ent) => (0, import_node_path20.join)(dir, ent.name));
|
|
15510
15689
|
} catch {
|
|
15511
15690
|
return [];
|
|
15512
15691
|
}
|
|
15513
15692
|
}
|
|
15514
15693
|
function isRepoCheckoutDir(dir) {
|
|
15515
|
-
return (0, import_node_fs20.existsSync)((0,
|
|
15694
|
+
return (0, import_node_fs20.existsSync)((0, import_node_path20.join)(dir, ".git"));
|
|
15516
15695
|
}
|
|
15517
15696
|
function resolveExplicitScanRoot(explicitRoot, repoRoot2) {
|
|
15518
15697
|
let rootDirs;
|
|
@@ -15524,7 +15703,7 @@ function resolveExplicitScanRoot(explicitRoot, repoRoot2) {
|
|
|
15524
15703
|
return explicitRepoWorktreesRoot(explicitRoot, repoRoot2, rootDirs);
|
|
15525
15704
|
}
|
|
15526
15705
|
async function gcPlan(remote, limit, opts = {}) {
|
|
15527
|
-
const [branches, heads, current, stale, worktrees, siblingDirs, preserved, mergedIntoBase] = await Promise.all([
|
|
15706
|
+
const [branches, heads, current, stale, worktrees, siblingDirs, preserved, mergedIntoBase, originListed] = await Promise.all([
|
|
15528
15707
|
gitOut(["branch", "--format=%(refname:short)"]),
|
|
15529
15708
|
localBranchHeads(),
|
|
15530
15709
|
gitOut(["rev-parse", "--abbrev-ref", "HEAD"]),
|
|
@@ -15534,11 +15713,18 @@ async function gcPlan(remote, limit, opts = {}) {
|
|
|
15534
15713
|
worktreeBranches(),
|
|
15535
15714
|
siblingWorktreeDirs(opts.root),
|
|
15536
15715
|
preservedBranches(),
|
|
15537
|
-
branchesMergedIntoBase(remote)
|
|
15716
|
+
branchesMergedIntoBase(remote),
|
|
15717
|
+
gitOut(["branch", "-r", "--format=%(refname:short)"]).catch(() => "")
|
|
15538
15718
|
]);
|
|
15539
15719
|
const localBranches = branches.split(/\r?\n/).map((b) => b.trim()).filter(Boolean);
|
|
15720
|
+
const originBranches = originListed.split(/\r?\n/).map((b) => b.trim()).filter((b) => b.startsWith(`${remote}/`) && b !== `${remote}/HEAD`);
|
|
15721
|
+
const originNames = originBranches.map((b) => b.slice(remote.length + 1));
|
|
15540
15722
|
const { prs, failures } = await resolveBranchPrs(
|
|
15541
|
-
[
|
|
15723
|
+
[.../* @__PURE__ */ new Set([
|
|
15724
|
+
...localBranches,
|
|
15725
|
+
...stale.map((ref) => branchForTrackingRef(ref, remote)).filter((b) => Boolean(b)),
|
|
15726
|
+
...originNames
|
|
15727
|
+
])].filter((b) => !isProtectedBranch(b)),
|
|
15542
15728
|
limit
|
|
15543
15729
|
);
|
|
15544
15730
|
return buildGcPlan({
|
|
@@ -15552,7 +15738,9 @@ async function gcPlan(remote, limit, opts = {}) {
|
|
|
15552
15738
|
worktrees,
|
|
15553
15739
|
remote,
|
|
15554
15740
|
preservedBranches: preserved,
|
|
15555
|
-
mergedIntoBase
|
|
15741
|
+
mergedIntoBase,
|
|
15742
|
+
originBranches,
|
|
15743
|
+
trainOnly: opts.trainOnly
|
|
15556
15744
|
});
|
|
15557
15745
|
}
|
|
15558
15746
|
async function branchesMergedIntoBase(remote) {
|
|
@@ -15849,10 +16037,10 @@ var rollout_plan_default = {
|
|
|
15849
16037
|
note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
|
|
15850
16038
|
},
|
|
15851
16039
|
baseline: {
|
|
15852
|
-
version: "3.
|
|
15853
|
-
tag: "v3.
|
|
15854
|
-
commit: "
|
|
15855
|
-
npm: "@mutmutco/cli@3.
|
|
16040
|
+
version: "3.129.0",
|
|
16041
|
+
tag: "v3.129.0",
|
|
16042
|
+
commit: "7067f7164642",
|
|
16043
|
+
npm: "@mutmutco/cli@3.129.0"
|
|
15856
16044
|
},
|
|
15857
16045
|
exitCriterion: "fleet-n-of-n",
|
|
15858
16046
|
hubOnlyShortcut: "forbidden",
|
|
@@ -15869,14 +16057,14 @@ var rollout_plan_default = {
|
|
|
15869
16057
|
repo: "mutmutco/mmi-hub",
|
|
15870
16058
|
role: "canary",
|
|
15871
16059
|
schedule: "train",
|
|
15872
|
-
v3Target: "v3.
|
|
16060
|
+
v3Target: "v3.129.0"
|
|
15873
16061
|
}
|
|
15874
16062
|
],
|
|
15875
16063
|
rollbackTrigger: "Any red inside the post-cut soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a v3 client refused while the compat window must still admit it (SUPPORTED_MINOR_WINDOW=2, MIN_CLIENT_VERSION 0.0.0 \u2014 D6a), or npm consumer install/doctor failure on the v4 dist.",
|
|
15876
16064
|
rollback: {
|
|
15877
16065
|
independent: true,
|
|
15878
|
-
mechanism: "npm dist-tag latest -> 3.
|
|
15879
|
-
v3Target: "v3.
|
|
16066
|
+
mechanism: "npm dist-tag latest -> 3.129.0 and redeploy the Hub Lambda from tag v3.129.0 (7067f7164642); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
16067
|
+
v3Target: "v3.129.0 (@mutmutco/cli@3.129.0, tag commit 7067f7164642 \u2014 the preserved latest-v3 distribution, D6b)"
|
|
15880
16068
|
}
|
|
15881
16069
|
},
|
|
15882
16070
|
{
|
|
@@ -18125,7 +18313,7 @@ function publishVisibilityFor(surfaceId) {
|
|
|
18125
18313
|
}
|
|
18126
18314
|
function npmPackArtifactName(packagePath) {
|
|
18127
18315
|
try {
|
|
18128
|
-
const pkg = JSON.parse((0, import_node_fs21.readFileSync)((0,
|
|
18316
|
+
const pkg = JSON.parse((0, import_node_fs21.readFileSync)((0, import_node_path21.join)(packagePath, "package.json"), "utf8"));
|
|
18129
18317
|
return pkg.name || void 0;
|
|
18130
18318
|
} catch {
|
|
18131
18319
|
return void 0;
|
|
@@ -19296,7 +19484,7 @@ async function dispatchDeploy(deps, ctx, stage, ref, model, watch, autoRunSince,
|
|
|
19296
19484
|
return { note: `no manual dispatch: ${model} repo deploys via its own push-triggered workflow`, deployStatus: "pending" };
|
|
19297
19485
|
}
|
|
19298
19486
|
function readLocalGateWorkflows() {
|
|
19299
|
-
const dir = (0,
|
|
19487
|
+
const dir = (0, import_node_path21.join)(".github", "workflows");
|
|
19300
19488
|
let names;
|
|
19301
19489
|
try {
|
|
19302
19490
|
names = (0, import_node_fs21.readdirSync)(dir);
|
|
@@ -19306,7 +19494,7 @@ function readLocalGateWorkflows() {
|
|
|
19306
19494
|
const files = [];
|
|
19307
19495
|
for (const name of names.filter(isGateWorkflowPath)) {
|
|
19308
19496
|
try {
|
|
19309
|
-
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs21.readFileSync)((0,
|
|
19497
|
+
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs21.readFileSync)((0, import_node_path21.join)(dir, name), "utf8") });
|
|
19310
19498
|
} catch {
|
|
19311
19499
|
}
|
|
19312
19500
|
}
|
|
@@ -21543,7 +21731,7 @@ var import_node_fs23 = require("node:fs");
|
|
|
21543
21731
|
// src/stage-runner.ts
|
|
21544
21732
|
var import_node_child_process9 = require("node:child_process");
|
|
21545
21733
|
var import_node_fs22 = require("node:fs");
|
|
21546
|
-
var
|
|
21734
|
+
var import_node_path22 = require("node:path");
|
|
21547
21735
|
var import_node_net = require("node:net");
|
|
21548
21736
|
var import_node_util5 = require("node:util");
|
|
21549
21737
|
|
|
@@ -21682,11 +21870,11 @@ function appendForceRecreate(up) {
|
|
|
21682
21870
|
return `${up.trimEnd()} --force-recreate`;
|
|
21683
21871
|
}
|
|
21684
21872
|
function stageStatePath(cwd = process.cwd()) {
|
|
21685
|
-
return (0,
|
|
21873
|
+
return (0, import_node_path22.join)(cwd, "tmp", "stage", "state.json");
|
|
21686
21874
|
}
|
|
21687
21875
|
function stageGlobalStatePath(cwd = process.cwd(), gitCommonDir = ".git") {
|
|
21688
|
-
const dir = (0,
|
|
21689
|
-
return (0,
|
|
21876
|
+
const dir = (0, import_node_path22.isAbsolute)(gitCommonDir) ? gitCommonDir : (0, import_node_path22.resolve)(cwd, gitCommonDir);
|
|
21877
|
+
return (0, import_node_path22.join)(dir, "mmi", "stage", "state.json");
|
|
21690
21878
|
}
|
|
21691
21879
|
function normPath3(path2) {
|
|
21692
21880
|
return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
@@ -21910,8 +22098,8 @@ function stageProcessEnv(stagePort, extraEnv) {
|
|
|
21910
22098
|
}
|
|
21911
22099
|
async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
21912
22100
|
if (!config.ensureEnv) return;
|
|
21913
|
-
const target = (0,
|
|
21914
|
-
const example = (0,
|
|
22101
|
+
const target = (0, import_node_path22.join)(cwd, config.ensureEnv.target);
|
|
22102
|
+
const example = (0, import_node_path22.join)(cwd, config.ensureEnv.example);
|
|
21915
22103
|
if (!(0, import_node_fs22.existsSync)(target) && (0, import_node_fs22.existsSync)(example)) {
|
|
21916
22104
|
(0, import_node_fs22.copyFileSync)(example, target);
|
|
21917
22105
|
} else if ((0, import_node_fs22.existsSync)(target) && (0, import_node_fs22.existsSync)(example)) {
|
|
@@ -22276,13 +22464,13 @@ async function executeWaveLand(plan, deps, opts = { preserveWorktree: true }) {
|
|
|
22276
22464
|
}
|
|
22277
22465
|
|
|
22278
22466
|
// src/index.ts
|
|
22279
|
-
var
|
|
22467
|
+
var import_node_os20 = require("node:os");
|
|
22280
22468
|
|
|
22281
22469
|
// src/board.ts
|
|
22282
22470
|
var import_node_child_process10 = require("node:child_process");
|
|
22283
22471
|
var import_node_fs24 = require("node:fs");
|
|
22284
22472
|
var import_node_os8 = require("node:os");
|
|
22285
|
-
var
|
|
22473
|
+
var import_node_path23 = require("node:path");
|
|
22286
22474
|
var import_node_util6 = require("node:util");
|
|
22287
22475
|
|
|
22288
22476
|
// src/board-dependency.ts
|
|
@@ -22629,9 +22817,6 @@ var BOARD_GIT_TIMEOUT_MS = 1e4;
|
|
|
22629
22817
|
var WRITE_PROBE_CONCURRENCY = 8;
|
|
22630
22818
|
var CLAIM_CONCURRENCY = 5;
|
|
22631
22819
|
var BOARD_ACTIVE_READ_MAX_PAGES = 100;
|
|
22632
|
-
var BOARD_PROJECTION_STALE_MS = 24 * 60 * 6e4;
|
|
22633
|
-
var BOARD_PROJECTION_RECONCILE_STALE_MS = 3 * 60 * 6e4;
|
|
22634
|
-
var BOARD_PROJECTION_TIMEOUT_MS = 8e3;
|
|
22635
22820
|
var execFileP4 = (file, args, options = {}) => (
|
|
22636
22821
|
// encoding 'utf8' guarantees string output at runtime; the cast pins the type (promisify's
|
|
22637
22822
|
// overloads widen to string|Buffer when options is spread in).
|
|
@@ -22899,7 +23084,7 @@ function renderBoardItem(item) {
|
|
|
22899
23084
|
return lines.join("\n");
|
|
22900
23085
|
}
|
|
22901
23086
|
function renderBoardReport(report) {
|
|
22902
|
-
const lines = [`Board \xB7 ${report.project.title} \xB7 @${report.viewer}`, renderBoardSource(
|
|
23087
|
+
const lines = [`Board \xB7 ${report.project.title} \xB7 @${report.viewer}`, renderBoardSource()];
|
|
22903
23088
|
renderScope(lines, "PRIMARY", report.repo, report.primary, report.viewer);
|
|
22904
23089
|
renderScope(lines, "SECONDARY", "Other repos on this project", report.secondary, report.viewer);
|
|
22905
23090
|
if (report.warnings.length) {
|
|
@@ -23003,132 +23188,13 @@ async function resolveWritableReposForClaimables(items, client) {
|
|
|
23003
23188
|
function writableOrUnknown(writable) {
|
|
23004
23189
|
return /* @__PURE__ */ new Set([...writable.repos, ...writable.unknown]);
|
|
23005
23190
|
}
|
|
23006
|
-
|
|
23007
|
-
|
|
23008
|
-
const token = await hubAuthToken({ baseUrl, githubToken });
|
|
23009
|
-
if (!token) throw new Error("no Hub session token");
|
|
23010
|
-
const url = new URL("/board-projection", `${baseUrl.replace(/\/$/, "")}/`);
|
|
23011
|
-
url.searchParams.set("owner", owner);
|
|
23012
|
-
url.searchParams.set("number", String(number));
|
|
23013
|
-
const res = await fetch(url, {
|
|
23014
|
-
method: "GET",
|
|
23015
|
-
headers: { ...clientVersionHeaders(), Authorization: `Bearer ${token}` },
|
|
23016
|
-
signal: AbortSignal.timeout(BOARD_PROJECTION_TIMEOUT_MS)
|
|
23017
|
-
});
|
|
23018
|
-
if (res.status === 404) return null;
|
|
23019
|
-
if (res.status === 426) throw new Error(upgradeRequiredError(res, await res.json().catch(() => null)));
|
|
23020
|
-
if (!res.ok) throw new Error(`board projection HTTP ${res.status}`);
|
|
23021
|
-
return await res.json();
|
|
23022
|
-
}
|
|
23023
|
-
function projectionItems(row) {
|
|
23024
|
-
if (!row.items || typeof row.items !== "object" || Array.isArray(row.items)) {
|
|
23025
|
-
throw new Error("malformed projection items");
|
|
23026
|
-
}
|
|
23027
|
-
const items = [];
|
|
23028
|
-
for (const raw of Object.values(row.items)) {
|
|
23029
|
-
if (!raw || typeof raw !== "object") throw new Error("malformed projection item");
|
|
23030
|
-
if (raw.tombstone === true) continue;
|
|
23031
|
-
const item = raw;
|
|
23032
|
-
if (typeof item.itemId !== "string" || typeof item.contentId !== "string" || item.contentType !== "Issue" && item.contentType !== "PullRequest" || typeof item.repository !== "string" || typeof item.number !== "number" || !Number.isFinite(item.number) || typeof item.ref !== "string" || typeof item.url !== "string" || typeof item.title !== "string" || typeof item.state !== "string" || !BOARD_STATUSES.includes(item.status) || !Array.isArray(item.assignees) || item.assignees.some((value) => typeof value !== "string") || !Array.isArray(item.labels) || item.labels.some((value) => typeof value !== "string") || typeof item.sourceTimestamp !== "string") {
|
|
23033
|
-
throw new Error("malformed projection item");
|
|
23034
|
-
}
|
|
23035
|
-
if (item.state.toUpperCase() === "CLOSED" || item.status === "Done") continue;
|
|
23036
|
-
items.push(withLoopState({
|
|
23037
|
-
itemId: item.itemId,
|
|
23038
|
-
contentId: item.contentId,
|
|
23039
|
-
contentType: item.contentType,
|
|
23040
|
-
repository: item.repository,
|
|
23041
|
-
number: item.number,
|
|
23042
|
-
ref: item.ref,
|
|
23043
|
-
url: item.url,
|
|
23044
|
-
title: item.title,
|
|
23045
|
-
state: item.state,
|
|
23046
|
-
// #4441: the reconciler stamps each item's content updatedAt as sourceTimestamp — the same
|
|
23047
|
-
// idle-age evidence the live read carries, so projection reads derive staleness too.
|
|
23048
|
-
updatedAt: item.sourceTimestamp,
|
|
23049
|
-
status: item.status,
|
|
23050
|
-
statusOptionId: item.statusOptionId,
|
|
23051
|
-
priority: item.priority,
|
|
23052
|
-
priorityOptionId: item.priorityOptionId,
|
|
23053
|
-
assignees: [...item.assignees],
|
|
23054
|
-
labels: [...item.labels],
|
|
23055
|
-
type: item.type
|
|
23056
|
-
}));
|
|
23057
|
-
}
|
|
23058
|
-
return items;
|
|
23059
|
-
}
|
|
23060
|
-
async function readViewerLogin(client) {
|
|
23061
|
-
const user = await client.rest("GET", "user");
|
|
23062
|
-
if (!user?.login) throw new Error("viewer login unavailable");
|
|
23063
|
-
return user.login;
|
|
23064
|
-
}
|
|
23065
|
-
function projectionFreshness(row, now) {
|
|
23066
|
-
const projectedAtMs = Date.parse(row.watermark?.projectedAt);
|
|
23067
|
-
const sourceTimestampMs = Date.parse(row.watermark?.sourceTimestamp);
|
|
23068
|
-
if (!Number.isFinite(projectedAtMs) || !Number.isFinite(sourceTimestampMs)) {
|
|
23069
|
-
throw new Error("malformed projection watermark");
|
|
23070
|
-
}
|
|
23071
|
-
const projectedAgeMs = Math.max(0, now - projectedAtMs);
|
|
23072
|
-
const sourceAgeMs = Math.max(0, now - sourceTimestampMs);
|
|
23073
|
-
return {
|
|
23074
|
-
projectedAt: row.watermark.projectedAt,
|
|
23075
|
-
projectedAgeMs,
|
|
23076
|
-
sourceTimestamp: row.watermark.sourceTimestamp,
|
|
23077
|
-
sourceAgeMs,
|
|
23078
|
-
// #4774: health must agree with BOTH ages a caller can see on the source line — a projection whose
|
|
23079
|
-
// rebuild (projectedAt) went stale is exactly as unreliable as one whose source events did, even
|
|
23080
|
-
// while recent webhook activity keeps sourceAgeMs looking fresh.
|
|
23081
|
-
health: row.watermark.health === "stale" || sourceAgeMs > BOARD_PROJECTION_STALE_MS || projectedAgeMs > BOARD_PROJECTION_RECONCILE_STALE_MS ? "stale" : "fresh"
|
|
23082
|
-
};
|
|
23083
|
-
}
|
|
23084
|
-
function renderBoardSource(report) {
|
|
23085
|
-
if (report.source === "projection" && report.freshness) {
|
|
23086
|
-
return `source: projection \xB7 projected ${formatClaimAge(report.freshness.projectedAgeMs)} ago \xB7 source events through ${report.freshness.sourceTimestamp} \xB7 health ${report.freshness.health}`;
|
|
23087
|
-
}
|
|
23088
|
-
return `source: live${report.sourceReason ? ` (${report.sourceReason})` : ""}`;
|
|
23191
|
+
function renderBoardSource() {
|
|
23192
|
+
return "source: live";
|
|
23089
23193
|
}
|
|
23090
23194
|
async function readBoard(options, deps = {}) {
|
|
23091
23195
|
const cfg = resolveBoardConfig(options.config);
|
|
23092
23196
|
const client = deps.client ?? defaultGitHubClient();
|
|
23093
|
-
|
|
23094
|
-
let source = "live";
|
|
23095
|
-
let freshness = null;
|
|
23096
|
-
let sourceReason;
|
|
23097
|
-
if (!options.live) {
|
|
23098
|
-
try {
|
|
23099
|
-
const baseUrl = options.config.sagaApiUrl;
|
|
23100
|
-
const row = await (deps.fetchProjection ?? defaultFetchBoardProjection)(cfg.projectOwner, cfg.projectNumber, baseUrl);
|
|
23101
|
-
if (!row) {
|
|
23102
|
-
sourceReason = "projection absent";
|
|
23103
|
-
} else if (row.schemaVersion !== 1) {
|
|
23104
|
-
sourceReason = `unsupported projection schema ${String(row.schemaVersion)}`;
|
|
23105
|
-
} else if (row.watermark?.health === "oversize") {
|
|
23106
|
-
sourceReason = "projection oversize";
|
|
23107
|
-
} else {
|
|
23108
|
-
const [viewer, repo] = await Promise.all([
|
|
23109
|
-
readViewerLogin(client),
|
|
23110
|
-
resolveCurrentRepo(options, deps)
|
|
23111
|
-
]);
|
|
23112
|
-
const now = (deps.now ?? Date.now)();
|
|
23113
|
-
collected = {
|
|
23114
|
-
items: projectionItems(row),
|
|
23115
|
-
viewer,
|
|
23116
|
-
repo,
|
|
23117
|
-
projectId: cfg.projectId,
|
|
23118
|
-
projectTitle: String(cfg.projectNumber),
|
|
23119
|
-
warnings: [],
|
|
23120
|
-
partial: false
|
|
23121
|
-
};
|
|
23122
|
-
freshness = projectionFreshness(row, now);
|
|
23123
|
-
source = "projection";
|
|
23124
|
-
}
|
|
23125
|
-
} catch {
|
|
23126
|
-
sourceReason = "projection read error";
|
|
23127
|
-
}
|
|
23128
|
-
}
|
|
23129
|
-
if (!collected) {
|
|
23130
|
-
collected = await collectBoardItems(cfg, { repo: options.repo, allowPartial: options.allowPartial, activeOnly: true }, deps);
|
|
23131
|
-
}
|
|
23197
|
+
const collected = await collectBoardItems(cfg, { repo: options.repo, allowPartial: options.allowPartial, activeOnly: true }, deps);
|
|
23132
23198
|
const writable = await resolveWritableReposForClaimables(collected.items, client);
|
|
23133
23199
|
collected.warnings.push(...writable.warnings);
|
|
23134
23200
|
collected.partial = collected.partial || writable.partial;
|
|
@@ -23140,9 +23206,7 @@ async function readBoard(options, deps = {}) {
|
|
|
23140
23206
|
...groups,
|
|
23141
23207
|
warnings: collected.warnings,
|
|
23142
23208
|
partial: collected.partial,
|
|
23143
|
-
source
|
|
23144
|
-
freshness,
|
|
23145
|
-
...sourceReason ? { sourceReason } : {}
|
|
23209
|
+
source: "live"
|
|
23146
23210
|
};
|
|
23147
23211
|
if (options.includeBundleDetails || options.includeAllBodies) {
|
|
23148
23212
|
await attachBundleDetails(report, client, options.allowPartial ?? false, { all: options.includeAllBodies });
|
|
@@ -24025,14 +24089,14 @@ function probeLocalClaimSession(marker, now = Date.now()) {
|
|
|
24025
24089
|
claimSessionProbeCache.set(cacheKey, { checkedAt: now, state });
|
|
24026
24090
|
return state;
|
|
24027
24091
|
};
|
|
24028
|
-
const root = (0,
|
|
24092
|
+
const root = (0, import_node_path23.join)((0, import_node_os8.homedir)(), ".claude", "projects");
|
|
24029
24093
|
try {
|
|
24030
24094
|
const wanted = `${marker.session}.jsonl`.toLowerCase();
|
|
24031
24095
|
const pending = [root];
|
|
24032
24096
|
while (pending.length) {
|
|
24033
24097
|
const dir = pending.pop();
|
|
24034
24098
|
for (const entry of (0, import_node_fs24.readdirSync)(dir, { withFileTypes: true })) {
|
|
24035
|
-
const path2 = (0,
|
|
24099
|
+
const path2 = (0, import_node_path23.join)(dir, entry.name);
|
|
24036
24100
|
if (entry.isDirectory()) pending.push(path2);
|
|
24037
24101
|
else if (entry.isFile() && entry.name.toLowerCase() === wanted) {
|
|
24038
24102
|
return remember(now - (0, import_node_fs24.statSync)(path2).mtimeMs <= CLAIM_SESSION_ACTIVITY_MS ? "live" : "dead");
|
|
@@ -24938,7 +25002,7 @@ function consolidateCommandNamespaces(program3) {
|
|
|
24938
25002
|
|
|
24939
25003
|
// src/pi-plugin-registration.ts
|
|
24940
25004
|
var import_node_fs25 = require("node:fs");
|
|
24941
|
-
var
|
|
25005
|
+
var import_node_path24 = require("node:path");
|
|
24942
25006
|
var import_proper_lockfile2 = __toESM(require_proper_lockfile(), 1);
|
|
24943
25007
|
|
|
24944
25008
|
// src/plugin-cache-prune.ts
|
|
@@ -25201,17 +25265,17 @@ function newestExistingPiPlugin(home) {
|
|
|
25201
25265
|
return void 0;
|
|
25202
25266
|
}
|
|
25203
25267
|
for (const version of names.filter(isVersionDirName).sort((a, b) => compareVersions(b, a))) {
|
|
25204
|
-
const candidate = (0,
|
|
25268
|
+
const candidate = (0, import_node_path24.join)(cacheRoot, version, ".pi-plugin");
|
|
25205
25269
|
if ((0, import_node_fs25.existsSync)(candidate)) return candidate;
|
|
25206
25270
|
}
|
|
25207
25271
|
return void 0;
|
|
25208
25272
|
}
|
|
25209
25273
|
function expectedPiPluginPath(home, env, installedVersion) {
|
|
25210
25274
|
const root = env.CLAUDE_PLUGIN_ROOT?.trim();
|
|
25211
|
-
if (root && /[\\/]mutmutco[\\/]mmi[\\/]/.test(root)) return (0,
|
|
25275
|
+
if (root && /[\\/]mutmutco[\\/]mmi[\\/]/.test(root)) return (0, import_node_path24.join)(root, ".pi-plugin");
|
|
25212
25276
|
const version = installedVersion ?? runningPluginVersion(env);
|
|
25213
25277
|
if (version) {
|
|
25214
|
-
const pinned = (0,
|
|
25278
|
+
const pinned = (0, import_node_path24.join)(home, ".claude", "plugins", "cache", "mutmutco", "mmi", version, ".pi-plugin");
|
|
25215
25279
|
if ((0, import_node_fs25.existsSync)(pinned)) return pinned;
|
|
25216
25280
|
}
|
|
25217
25281
|
return newestExistingPiPlugin(home);
|
|
@@ -25219,15 +25283,15 @@ function expectedPiPluginPath(home, env, installedVersion) {
|
|
|
25219
25283
|
function agentDirs(home, env = process.env) {
|
|
25220
25284
|
const override = env.JERVCODE_CODING_AGENT_DIR?.trim() || env.PI_CODING_AGENT_DIR?.trim();
|
|
25221
25285
|
if (override) return [override];
|
|
25222
|
-
const jerv = (0,
|
|
25223
|
-
const pi = (0,
|
|
25286
|
+
const jerv = (0, import_node_path24.join)(home, ".jerv", "agent");
|
|
25287
|
+
const pi = (0, import_node_path24.join)(home, ".pi", "agent");
|
|
25224
25288
|
if (!(0, import_node_fs25.existsSync)(jerv) && !(0, import_node_fs25.existsSync)(pi)) return [];
|
|
25225
25289
|
const dirs = [jerv];
|
|
25226
25290
|
if ((0, import_node_fs25.existsSync)(pi) && pi !== jerv) dirs.push(pi);
|
|
25227
25291
|
return dirs;
|
|
25228
25292
|
}
|
|
25229
25293
|
function settingsPath(agentDir) {
|
|
25230
|
-
return (0,
|
|
25294
|
+
return (0, import_node_path24.join)(agentDir, "settings.json");
|
|
25231
25295
|
}
|
|
25232
25296
|
function readPiPluginState(home, env, installedVersion) {
|
|
25233
25297
|
const dirs = agentDirs(home, env);
|
|
@@ -25261,7 +25325,7 @@ function acquirePiSettingsLock2(settingsFile) {
|
|
|
25261
25325
|
return void 0;
|
|
25262
25326
|
}
|
|
25263
25327
|
function atomicWriteSettings(file, body) {
|
|
25264
|
-
(0, import_node_fs25.mkdirSync)((0,
|
|
25328
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path24.dirname)(file), { recursive: true });
|
|
25265
25329
|
const tmp = `${file}.tmp-${process.pid}`;
|
|
25266
25330
|
(0, import_node_fs25.writeFileSync)(tmp, body, "utf8");
|
|
25267
25331
|
try {
|
|
@@ -25339,18 +25403,18 @@ function healPiPluginRegistration(home, env, installedVersion) {
|
|
|
25339
25403
|
// src/claude-binary-doctor.ts
|
|
25340
25404
|
var import_node_fs27 = require("node:fs");
|
|
25341
25405
|
var import_node_os11 = require("node:os");
|
|
25342
|
-
var
|
|
25406
|
+
var import_node_path26 = require("node:path");
|
|
25343
25407
|
|
|
25344
25408
|
// src/jerv-cli-spawn.ts
|
|
25345
25409
|
var import_node_fs26 = require("node:fs");
|
|
25346
25410
|
var import_node_os10 = require("node:os");
|
|
25347
|
-
var
|
|
25411
|
+
var import_node_path25 = require("node:path");
|
|
25348
25412
|
var WIN_NAMES = ["jerv-cli.cmd", "jerv-cli.exe", "jerv-cli"];
|
|
25349
25413
|
var POSIX_NAMES = ["jerv-cli"];
|
|
25350
|
-
var JERV_CLI_ENTRY = (0,
|
|
25414
|
+
var JERV_CLI_ENTRY = (0, import_node_path25.join)("node_modules", "@jervaise", "jerv-cli", "dist", "index.cjs");
|
|
25351
25415
|
function pathEnvEntries(pathEnv, platform2 = process.platform) {
|
|
25352
25416
|
if (platform2 !== "win32") {
|
|
25353
|
-
return pathEnv.split(
|
|
25417
|
+
return pathEnv.split(import_node_path25.delimiter).map((e) => e.trim()).filter(Boolean);
|
|
25354
25418
|
}
|
|
25355
25419
|
if (pathEnv.includes(";")) {
|
|
25356
25420
|
return pathEnv.split(";").map((e) => e.trim()).filter(Boolean);
|
|
@@ -25383,10 +25447,10 @@ function jervCliCandidateDirs(env = process.env, home = (0, import_node_os10.hom
|
|
|
25383
25447
|
push(normalizeSpawnPathEntry(entry, platform2));
|
|
25384
25448
|
}
|
|
25385
25449
|
if (platform2 === "win32") {
|
|
25386
|
-
if (env.APPDATA) push((0,
|
|
25387
|
-
if (env.LOCALAPPDATA) push((0,
|
|
25450
|
+
if (env.APPDATA) push((0, import_node_path25.join)(env.APPDATA, "npm"));
|
|
25451
|
+
if (env.LOCALAPPDATA) push((0, import_node_path25.join)(env.LOCALAPPDATA, "npm"));
|
|
25388
25452
|
} else {
|
|
25389
|
-
push((0,
|
|
25453
|
+
push((0, import_node_path25.join)(home, ".local", "bin"));
|
|
25390
25454
|
}
|
|
25391
25455
|
return out;
|
|
25392
25456
|
}
|
|
@@ -25394,7 +25458,7 @@ function jervCliCandidatePaths(env = process.env, home = (0, import_node_os10.ho
|
|
|
25394
25458
|
const names = platform2 === "win32" ? WIN_NAMES : POSIX_NAMES;
|
|
25395
25459
|
const out = [];
|
|
25396
25460
|
for (const dir of jervCliCandidateDirs(env, home, platform2)) {
|
|
25397
|
-
for (const name of names) out.push((0,
|
|
25461
|
+
for (const name of names) out.push((0, import_node_path25.join)(dir, name));
|
|
25398
25462
|
}
|
|
25399
25463
|
return out;
|
|
25400
25464
|
}
|
|
@@ -25405,7 +25469,7 @@ function resolveJervCliPath(env = process.env, home = (0, import_node_os10.homed
|
|
|
25405
25469
|
return void 0;
|
|
25406
25470
|
}
|
|
25407
25471
|
function resolveJervCliNodeEntry(shimPath, exists = import_node_fs26.existsSync) {
|
|
25408
|
-
const entry = (0,
|
|
25472
|
+
const entry = (0, import_node_path25.join)((0, import_node_path25.dirname)(shimPath), JERV_CLI_ENTRY);
|
|
25409
25473
|
return exists(entry) ? entry : void 0;
|
|
25410
25474
|
}
|
|
25411
25475
|
function jervCliExecFileArgs(args, opts = {}) {
|
|
@@ -25500,10 +25564,10 @@ function globalNodeModulesRoots(host) {
|
|
|
25500
25564
|
out.push(dir);
|
|
25501
25565
|
};
|
|
25502
25566
|
const prefix = env.npm_config_prefix?.trim();
|
|
25503
|
-
if (prefix) push(platform2 === "win32" ? (0,
|
|
25567
|
+
if (prefix) push(platform2 === "win32" ? (0, import_node_path26.join)(prefix, "node_modules") : (0, import_node_path26.join)(prefix, "lib", "node_modules"));
|
|
25504
25568
|
for (const dir of jervCliCandidateDirs(env, host.home ?? (0, import_node_os11.homedir)(), platform2)) {
|
|
25505
|
-
push((0,
|
|
25506
|
-
push((0,
|
|
25569
|
+
push((0, import_node_path26.join)(dir, "node_modules"));
|
|
25570
|
+
push((0, import_node_path26.join)((0, import_node_path26.dirname)(dir), "lib", "node_modules"));
|
|
25507
25571
|
}
|
|
25508
25572
|
return out;
|
|
25509
25573
|
}
|
|
@@ -25541,17 +25605,17 @@ function readClaudeBinaryState(host = {}) {
|
|
|
25541
25605
|
const arch = host.arch ?? process.arch;
|
|
25542
25606
|
const magic = EXECUTABLE_MAGIC[platform2];
|
|
25543
25607
|
if (!magic) return void 0;
|
|
25544
|
-
const packageRoot = globalNodeModulesRoots(host).map((root) => (0,
|
|
25608
|
+
const packageRoot = globalNodeModulesRoots(host).map((root) => (0, import_node_path26.join)(root, ...PACKAGE.split("/"))).find((dir) => (0, import_node_fs27.existsSync)((0, import_node_path26.join)(dir, "package.json")));
|
|
25545
25609
|
if (!packageRoot) return void 0;
|
|
25546
25610
|
const keys = platformPackageKeys(platform2, arch);
|
|
25547
25611
|
const fallbackPackage = `${PACKAGE}-${keys[0]}`;
|
|
25548
25612
|
let manifest;
|
|
25549
25613
|
try {
|
|
25550
|
-
manifest = JSON.parse((0, import_node_fs27.readFileSync)((0,
|
|
25614
|
+
manifest = JSON.parse((0, import_node_fs27.readFileSync)((0, import_node_path26.join)(packageRoot, "package.json"), "utf8"));
|
|
25551
25615
|
} catch (e) {
|
|
25552
25616
|
return {
|
|
25553
25617
|
state: "unreadable",
|
|
25554
|
-
binPath: (0,
|
|
25618
|
+
binPath: (0, import_node_path26.join)(packageRoot, "package.json"),
|
|
25555
25619
|
expectedMagic: magic.name,
|
|
25556
25620
|
platformPackage: fallbackPackage,
|
|
25557
25621
|
error: `package.json could not be read \u2014 ${e.message}`
|
|
@@ -25568,15 +25632,15 @@ function readClaudeBinaryState(host = {}) {
|
|
|
25568
25632
|
error: "package.json declares no `bin` entry \u2014 the executed file cannot be resolved"
|
|
25569
25633
|
};
|
|
25570
25634
|
}
|
|
25571
|
-
const binPath = (0,
|
|
25572
|
-
const binName = (0,
|
|
25635
|
+
const binPath = (0, import_node_path26.join)(packageRoot, binRelative);
|
|
25636
|
+
const binName = (0, import_node_path26.basename)(binRelative);
|
|
25573
25637
|
const optional = manifest.optionalDependencies;
|
|
25574
25638
|
const publishes = (name) => typeof optional === "object" && optional !== null && Object.prototype.hasOwnProperty.call(optional, name);
|
|
25575
25639
|
const published = keys.map((key) => `${PACKAGE}-${key}`).filter(publishes);
|
|
25576
25640
|
if (published.length === 0) return void 0;
|
|
25577
25641
|
const binIn = (name) => [
|
|
25578
|
-
(0,
|
|
25579
|
-
(0,
|
|
25642
|
+
(0, import_node_path26.join)(packageRoot, "node_modules", ...name.split("/"), binName),
|
|
25643
|
+
(0, import_node_path26.join)((0, import_node_path26.dirname)((0, import_node_path26.dirname)(packageRoot)), ...name.split("/"), binName)
|
|
25580
25644
|
];
|
|
25581
25645
|
const found = published.map((name) => ({ name, path: binIn(name).find((file) => (0, import_node_fs27.existsSync)(file)) })).find((c) => c.path);
|
|
25582
25646
|
const platformPackage = found?.name ?? published[0];
|
|
@@ -25901,7 +25965,7 @@ function renderVerifyBroker(input) {
|
|
|
25901
25965
|
var import_node_crypto4 = require("node:crypto");
|
|
25902
25966
|
var import_node_fs28 = require("node:fs");
|
|
25903
25967
|
var import_promises5 = require("node:fs/promises");
|
|
25904
|
-
var
|
|
25968
|
+
var import_node_path27 = require("node:path");
|
|
25905
25969
|
var ARTIFACT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
25906
25970
|
var MAX_BYTES = 5 * 1024 * 1024 * 1024;
|
|
25907
25971
|
async function sha256File(path2) {
|
|
@@ -25911,7 +25975,7 @@ async function sha256File(path2) {
|
|
|
25911
25975
|
}
|
|
25912
25976
|
async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
25913
25977
|
if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
|
|
25914
|
-
const path2 = (0,
|
|
25978
|
+
const path2 = (0, import_node_path27.resolve)(inputPath);
|
|
25915
25979
|
const info = await (0, import_promises5.stat)(path2);
|
|
25916
25980
|
if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
|
|
25917
25981
|
if (!Number.isSafeInteger(info.size) || info.size < 1 || info.size > MAX_BYTES) throw new Error(`tenant artifact put: file must be 1..${MAX_BYTES} bytes`);
|
|
@@ -27048,7 +27112,7 @@ async function announceRelease(deps, args) {
|
|
|
27048
27112
|
var import_node_crypto5 = require("node:crypto");
|
|
27049
27113
|
var import_node_child_process13 = require("node:child_process");
|
|
27050
27114
|
var import_node_fs29 = require("node:fs");
|
|
27051
|
-
var
|
|
27115
|
+
var import_node_path28 = require("node:path");
|
|
27052
27116
|
var REPO_INDEX_SCHEMA = 1;
|
|
27053
27117
|
var HARD_DENY = [
|
|
27054
27118
|
/(^|\/)\.env(\.|$)/i,
|
|
@@ -27173,7 +27237,7 @@ function loadReadmeHints(cwd, candidatePaths) {
|
|
|
27173
27237
|
}
|
|
27174
27238
|
for (const rel of readmes) {
|
|
27175
27239
|
if (isHardDeniedPath(rel)) continue;
|
|
27176
|
-
const abs = (0,
|
|
27240
|
+
const abs = (0, import_node_path28.join)(cwd, ...rel.split("/"));
|
|
27177
27241
|
if (!(0, import_node_fs29.existsSync)(abs)) continue;
|
|
27178
27242
|
let text;
|
|
27179
27243
|
try {
|
|
@@ -27190,7 +27254,7 @@ function loadReadmeHints(cwd, candidatePaths) {
|
|
|
27190
27254
|
return hints;
|
|
27191
27255
|
}
|
|
27192
27256
|
function toPosix(p) {
|
|
27193
|
-
return p.split(
|
|
27257
|
+
return p.split(import_node_path28.sep).join("/");
|
|
27194
27258
|
}
|
|
27195
27259
|
function listCandidatePaths(cwd, exec = import_node_child_process13.execFileSync) {
|
|
27196
27260
|
try {
|
|
@@ -27212,7 +27276,7 @@ function rebuildRepoIndex(cwd, repoSlug2) {
|
|
|
27212
27276
|
for (const rel of candidates) {
|
|
27213
27277
|
if (ignored.has(rel)) continue;
|
|
27214
27278
|
if (isHardDeniedPath(rel)) continue;
|
|
27215
|
-
const abs = (0,
|
|
27279
|
+
const abs = (0, import_node_path28.join)(cwd, ...rel.split("/"));
|
|
27216
27280
|
if (!(0, import_node_fs29.existsSync)(abs)) continue;
|
|
27217
27281
|
let text;
|
|
27218
27282
|
try {
|
|
@@ -27241,7 +27305,7 @@ function rebuildRepoIndex(cwd, repoSlug2) {
|
|
|
27241
27305
|
entries
|
|
27242
27306
|
};
|
|
27243
27307
|
const store = repoIndexStorePath(cwd);
|
|
27244
|
-
(0, import_node_fs29.mkdirSync)((0,
|
|
27308
|
+
(0, import_node_fs29.mkdirSync)((0, import_node_path28.dirname)(store), { recursive: true });
|
|
27245
27309
|
(0, import_node_fs29.writeFileSync)(store, `${JSON.stringify(projection, null, 2)}
|
|
27246
27310
|
`, "utf8");
|
|
27247
27311
|
return projection;
|
|
@@ -27322,7 +27386,7 @@ function inferRepoSlug(cwd, exec = import_node_child_process13.execFileSync) {
|
|
|
27322
27386
|
if (m?.[1]) return m[1].toLowerCase();
|
|
27323
27387
|
} catch {
|
|
27324
27388
|
}
|
|
27325
|
-
return ((0,
|
|
27389
|
+
return ((0, import_node_path28.basename)(cwd) || "local").toLowerCase();
|
|
27326
27390
|
}
|
|
27327
27391
|
|
|
27328
27392
|
// src/repo-index-cloud-client.ts
|
|
@@ -27464,7 +27528,7 @@ async function gcRepoIndexCloud(deps) {
|
|
|
27464
27528
|
// src/repo-index-sync.ts
|
|
27465
27529
|
var import_node_fs30 = require("node:fs");
|
|
27466
27530
|
var import_node_os12 = require("node:os");
|
|
27467
|
-
var
|
|
27531
|
+
var import_node_path29 = require("node:path");
|
|
27468
27532
|
var import_node_child_process14 = require("node:child_process");
|
|
27469
27533
|
var MAX_EMBED_BACKFILL_ROUNDS = 40;
|
|
27470
27534
|
function normalizeRepo(raw) {
|
|
@@ -27508,7 +27572,7 @@ async function syncEstateRepoIndex(opts) {
|
|
|
27508
27572
|
const failed = [];
|
|
27509
27573
|
const skipped = [];
|
|
27510
27574
|
for (const repo of repos) {
|
|
27511
|
-
const dir = (0, import_node_fs30.mkdtempSync)((0,
|
|
27575
|
+
const dir = (0, import_node_fs30.mkdtempSync)((0, import_node_path29.join)((0, import_node_os12.tmpdir)(), "mmi-repo-index-"));
|
|
27512
27576
|
try {
|
|
27513
27577
|
shallowClone(repo, dir, opts.githubToken);
|
|
27514
27578
|
const built = rebuildRepoIndex(dir, repo);
|
|
@@ -27764,7 +27828,7 @@ async function runRepoIndexHealth(opts) {
|
|
|
27764
27828
|
// src/spawn-policy-core.ts
|
|
27765
27829
|
var import_node_child_process15 = require("node:child_process");
|
|
27766
27830
|
var import_node_fs32 = require("node:fs");
|
|
27767
|
-
var
|
|
27831
|
+
var import_node_path30 = require("node:path");
|
|
27768
27832
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
27769
27833
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
27770
27834
|
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
@@ -27850,7 +27914,7 @@ function runSpawnPolicy(root) {
|
|
|
27850
27914
|
for (const file of files) {
|
|
27851
27915
|
let raw;
|
|
27852
27916
|
try {
|
|
27853
|
-
raw = (0, import_node_fs32.readFileSync)((0,
|
|
27917
|
+
raw = (0, import_node_fs32.readFileSync)((0, import_node_path30.join)(root, file), "utf8");
|
|
27854
27918
|
} catch {
|
|
27855
27919
|
continue;
|
|
27856
27920
|
}
|
|
@@ -27869,7 +27933,7 @@ function runSpawnPolicy(root) {
|
|
|
27869
27933
|
// src/test-policy-core.ts
|
|
27870
27934
|
var import_node_child_process16 = require("node:child_process");
|
|
27871
27935
|
var import_node_fs33 = require("node:fs");
|
|
27872
|
-
var
|
|
27936
|
+
var import_node_path31 = require("node:path");
|
|
27873
27937
|
var POLICY_FILE = "test-policy.json";
|
|
27874
27938
|
var TEST_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
|
27875
27939
|
var PY_TEST_RE = /(?:^|\/)test_[^/]*\.py$|_test\.py$/;
|
|
@@ -27922,7 +27986,7 @@ function isTestPath(path2) {
|
|
|
27922
27986
|
return TEST_RE.test(path2) || PY_TEST_RE.test(path2);
|
|
27923
27987
|
}
|
|
27924
27988
|
function loadPolicy(root, readFile9 = readFileOrNull2) {
|
|
27925
|
-
const raw = readFile9((0,
|
|
27989
|
+
const raw = readFile9((0, import_node_path31.join)(root, POLICY_FILE));
|
|
27926
27990
|
if (raw == null) return { mandatory: [], declared: false };
|
|
27927
27991
|
try {
|
|
27928
27992
|
return { ...JSON.parse(raw), declared: true };
|
|
@@ -27960,11 +28024,11 @@ function classify(changed, policy, present = () => false) {
|
|
|
27960
28024
|
return { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected };
|
|
27961
28025
|
}
|
|
27962
28026
|
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs33.existsSync)(path2)) {
|
|
27963
|
-
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0,
|
|
28027
|
+
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path31.join)(root, p)));
|
|
27964
28028
|
}
|
|
27965
28029
|
function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs33.existsSync)(path2)) {
|
|
27966
28030
|
const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
|
|
27967
|
-
return [...new Set(declared)].filter((p) => !exists((0,
|
|
28031
|
+
return [...new Set(declared)].filter((p) => !exists((0, import_node_path31.join)(root, p)));
|
|
27968
28032
|
}
|
|
27969
28033
|
function evaluate(changed, policy, present = () => false) {
|
|
27970
28034
|
const { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected } = classify(changed, policy, present);
|
|
@@ -28152,7 +28216,7 @@ function runTestPolicy(root, deps = {}) {
|
|
|
28152
28216
|
const refusal = deps.changed ? null : untrustworthyRange(root, base);
|
|
28153
28217
|
const changed = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
|
|
28154
28218
|
const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride(base, root) : { override: null, refusals: [] };
|
|
28155
|
-
const present = (path2) => exists((0,
|
|
28219
|
+
const present = (path2) => exists((0, import_node_path31.join)(root, path2));
|
|
28156
28220
|
const removedByThisDiff = removedPaths(changed);
|
|
28157
28221
|
const staleFindings = [];
|
|
28158
28222
|
const unresolved = unresolvedProtectedEntries(policy, root, exists).filter((p) => !removedByThisDiff.has(p));
|
|
@@ -28191,7 +28255,7 @@ function runTestPolicy(root, deps = {}) {
|
|
|
28191
28255
|
|
|
28192
28256
|
// src/project-info-sync.ts
|
|
28193
28257
|
var import_node_fs34 = require("node:fs");
|
|
28194
|
-
var
|
|
28258
|
+
var import_node_path32 = require("node:path");
|
|
28195
28259
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
28196
28260
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
28197
28261
|
projectV2 { id }
|
|
@@ -28236,7 +28300,7 @@ function sharedName(entries, fallback) {
|
|
|
28236
28300
|
}
|
|
28237
28301
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
28238
28302
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
28239
|
-
const readmePath = (0,
|
|
28303
|
+
const readmePath = (0, import_node_path32.join)(repoRoot2, "README.md");
|
|
28240
28304
|
if (!(0, import_node_fs34.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
28241
28305
|
const entries = entriesFor(project2, projects);
|
|
28242
28306
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
@@ -28262,8 +28326,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
28262
28326
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
28263
28327
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
28264
28328
|
const orgDocs = [
|
|
28265
|
-
(0, import_node_fs34.existsSync)((0,
|
|
28266
|
-
(0, import_node_fs34.existsSync)((0,
|
|
28329
|
+
(0, import_node_fs34.existsSync)((0, import_node_path32.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
|
|
28330
|
+
(0, import_node_fs34.existsSync)((0, import_node_path32.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
|
|
28267
28331
|
].filter(Boolean);
|
|
28268
28332
|
if (orgDocs.length) lines.push("", "## Organisation docs", "", ...orgDocs);
|
|
28269
28333
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines.join("\n")}
|
|
@@ -29090,7 +29154,7 @@ function writeError(res) {
|
|
|
29090
29154
|
|
|
29091
29155
|
// src/secrets-commands.ts
|
|
29092
29156
|
var import_node_fs35 = require("node:fs");
|
|
29093
|
-
var
|
|
29157
|
+
var import_node_path33 = require("node:path");
|
|
29094
29158
|
var import_node_os13 = require("node:os");
|
|
29095
29159
|
|
|
29096
29160
|
// src/secrets-diff.ts
|
|
@@ -29193,11 +29257,11 @@ function collectMap(value, previous = []) {
|
|
|
29193
29257
|
return [...previous, value];
|
|
29194
29258
|
}
|
|
29195
29259
|
async function decryptRailsCredentials(input) {
|
|
29196
|
-
const appDir = (0,
|
|
29260
|
+
const appDir = (0, import_node_path33.resolve)(input.appDir ?? process.cwd());
|
|
29197
29261
|
const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
29198
29262
|
const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
29199
|
-
const credentialsPath = (0,
|
|
29200
|
-
const masterKeyPath = (0,
|
|
29263
|
+
const credentialsPath = (0, import_node_path33.resolve)(appDir, credentialsFile);
|
|
29264
|
+
const masterKeyPath = (0, import_node_path33.resolve)(appDir, masterKeyFile);
|
|
29201
29265
|
const env = {
|
|
29202
29266
|
...process.env,
|
|
29203
29267
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
@@ -29214,8 +29278,8 @@ async function decryptRailsCredentials(input) {
|
|
|
29214
29278
|
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
29215
29279
|
"puts JSON.generate(config.config)"
|
|
29216
29280
|
].join("\n");
|
|
29217
|
-
const scriptDir = (0, import_node_fs35.mkdtempSync)((0,
|
|
29218
|
-
const scriptPath = (0,
|
|
29281
|
+
const scriptDir = (0, import_node_fs35.mkdtempSync)((0, import_node_path33.join)((0, import_node_os13.tmpdir)(), "mmi-rails-decrypt-"));
|
|
29282
|
+
const scriptPath = (0, import_node_path33.join)(scriptDir, "decrypt.rb");
|
|
29219
29283
|
(0, import_node_fs35.writeFileSync)(scriptPath, script, "utf8");
|
|
29220
29284
|
try {
|
|
29221
29285
|
const args = ["exec", "ruby", scriptPath];
|
|
@@ -29318,7 +29382,7 @@ function registerSecretsCommands(program3) {
|
|
|
29318
29382
|
let body;
|
|
29319
29383
|
if (o.file) {
|
|
29320
29384
|
try {
|
|
29321
|
-
body = (0, import_node_fs35.readFileSync)((0,
|
|
29385
|
+
body = (0, import_node_fs35.readFileSync)((0, import_node_path33.resolve)(o.file), "utf8");
|
|
29322
29386
|
} catch (e) {
|
|
29323
29387
|
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
29324
29388
|
}
|
|
@@ -29423,7 +29487,7 @@ function registerSecretsCommands(program3) {
|
|
|
29423
29487
|
{
|
|
29424
29488
|
...d,
|
|
29425
29489
|
decryptRailsCredentials,
|
|
29426
|
-
removeFile: (path2) => (0, import_node_fs35.unlinkSync)((0,
|
|
29490
|
+
removeFile: (path2) => (0, import_node_fs35.unlinkSync)((0, import_node_path33.resolve)(o.appDir ?? process.cwd(), path2))
|
|
29427
29491
|
},
|
|
29428
29492
|
{
|
|
29429
29493
|
repo: o.repo,
|
|
@@ -29877,8 +29941,8 @@ async function repoWorkflowEntries(client, repo) {
|
|
|
29877
29941
|
for (const wf of workflowsList) {
|
|
29878
29942
|
if (typeof wf?.path !== "string" || !wf.path) continue;
|
|
29879
29943
|
if (!wf.path.startsWith(".github/workflows/")) continue;
|
|
29880
|
-
const
|
|
29881
|
-
const name = `${repo}/${
|
|
29944
|
+
const basename8 = wf.path.split("/").pop() ?? wf.path;
|
|
29945
|
+
const name = `${repo}/${basename8.replace(/\.ya?ml$/, "")}`;
|
|
29882
29946
|
if (wf.state !== "active") {
|
|
29883
29947
|
disabled.push(name);
|
|
29884
29948
|
continue;
|
|
@@ -30150,7 +30214,7 @@ function registerSchedulesCommands(program3) {
|
|
|
30150
30214
|
|
|
30151
30215
|
// src/schedules-lift-command.ts
|
|
30152
30216
|
var import_promises7 = require("node:fs/promises");
|
|
30153
|
-
var
|
|
30217
|
+
var import_node_path34 = require("node:path");
|
|
30154
30218
|
var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
|
|
30155
30219
|
var SCHEDULE_REPO_RE = /^[A-Za-z0-9_.-]+$/;
|
|
30156
30220
|
var SchedulesLiftUsageError = class extends Error {
|
|
@@ -30177,7 +30241,7 @@ async function readWorkflowFiles(dir) {
|
|
|
30177
30241
|
const files = [];
|
|
30178
30242
|
for (const name of names.sort()) {
|
|
30179
30243
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
30180
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises7.readFile)((0,
|
|
30244
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises7.readFile)((0, import_node_path34.join)(dir, name), "utf8") });
|
|
30181
30245
|
}
|
|
30182
30246
|
return files;
|
|
30183
30247
|
}
|
|
@@ -30327,7 +30391,7 @@ function registerEdgeCommands(program3) {
|
|
|
30327
30391
|
// src/bootstrap-commands.ts
|
|
30328
30392
|
var import_node_fs37 = require("node:fs");
|
|
30329
30393
|
var import_node_os14 = require("node:os");
|
|
30330
|
-
var
|
|
30394
|
+
var import_node_path35 = require("node:path");
|
|
30331
30395
|
|
|
30332
30396
|
// src/bootstrap-drift.ts
|
|
30333
30397
|
var import_node_crypto7 = require("node:crypto");
|
|
@@ -31369,7 +31433,7 @@ function registerBootstrapCommands(program3) {
|
|
|
31369
31433
|
const readFile9 = (p) => (0, import_node_fs37.existsSync)(p) ? (0, import_node_fs37.readFileSync)(p, "utf8") : null;
|
|
31370
31434
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
31371
31435
|
const putSeed = async (target, content, ref, sha) => {
|
|
31372
|
-
const tmp = (0,
|
|
31436
|
+
const tmp = (0, import_node_path35.join)((0, import_node_os14.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
31373
31437
|
(0, import_node_fs37.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
31374
31438
|
try {
|
|
31375
31439
|
await gh(contentPutInputArgs(repo, target, tmp));
|
|
@@ -31780,7 +31844,7 @@ LIVE apply to ${repo}:
|
|
|
31780
31844
|
} catch {
|
|
31781
31845
|
existingSha = void 0;
|
|
31782
31846
|
}
|
|
31783
|
-
const tmp = (0,
|
|
31847
|
+
const tmp = (0, import_node_path35.join)((0, import_node_os14.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
31784
31848
|
(0, import_node_fs37.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, hubContent, branch, existingSha)), "utf8");
|
|
31785
31849
|
try {
|
|
31786
31850
|
await gh(contentPutInputArgs(rec.repo, seed.target, tmp));
|
|
@@ -31936,7 +32000,7 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
31936
32000
|
} catch {
|
|
31937
32001
|
existingSha = void 0;
|
|
31938
32002
|
}
|
|
31939
|
-
const tmp = (0,
|
|
32003
|
+
const tmp = (0, import_node_path35.join)((0, import_node_os14.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
31940
32004
|
(0, import_node_fs37.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
|
|
31941
32005
|
try {
|
|
31942
32006
|
await gh(contentPutInputArgs(repo, seed.target, tmp));
|
|
@@ -31965,11 +32029,11 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
31965
32029
|
|
|
31966
32030
|
// src/stage-commands.ts
|
|
31967
32031
|
var import_node_fs39 = require("node:fs");
|
|
31968
|
-
var
|
|
32032
|
+
var import_node_path37 = require("node:path");
|
|
31969
32033
|
|
|
31970
32034
|
// src/port-registry.ts
|
|
31971
32035
|
var import_node_fs38 = require("node:fs");
|
|
31972
|
-
var
|
|
32036
|
+
var import_node_path36 = require("node:path");
|
|
31973
32037
|
|
|
31974
32038
|
// ../infra/port-geometry.mjs
|
|
31975
32039
|
var PORT_BLOCK = 100;
|
|
@@ -32022,22 +32086,22 @@ function existingPortRange(repo, registry2) {
|
|
|
32022
32086
|
return registry2[repo] ?? null;
|
|
32023
32087
|
}
|
|
32024
32088
|
function portRangeInfraAt(root, source) {
|
|
32025
|
-
const registryPath = (0,
|
|
32026
|
-
const ddbScriptPath = (0,
|
|
32089
|
+
const registryPath = (0, import_node_path36.join)(root, "infra", "port-ranges.json");
|
|
32090
|
+
const ddbScriptPath = (0, import_node_path36.join)(root, "infra", "port-ddb.mjs");
|
|
32027
32091
|
if (!(0, import_node_fs38.existsSync)(registryPath) || !(0, import_node_fs38.existsSync)(ddbScriptPath)) return null;
|
|
32028
32092
|
return { root, source, registryPath, ddbScriptPath };
|
|
32029
32093
|
}
|
|
32030
32094
|
function resolvePortRangeInfra(cwd, packageDir) {
|
|
32031
32095
|
const direct = portRangeInfraAt(cwd, "cwd");
|
|
32032
32096
|
if (direct) return direct;
|
|
32033
|
-
for (let dir = cwd; ; dir = (0,
|
|
32034
|
-
const sibling = portRangeInfraAt((0,
|
|
32097
|
+
for (let dir = cwd; ; dir = (0, import_node_path36.dirname)(dir)) {
|
|
32098
|
+
const sibling = portRangeInfraAt((0, import_node_path36.join)(dir, "MMI-Hub"), "sibling-hub");
|
|
32035
32099
|
if (sibling) return sibling;
|
|
32036
|
-
const parent = (0,
|
|
32100
|
+
const parent = (0, import_node_path36.dirname)(dir);
|
|
32037
32101
|
if (parent === dir) break;
|
|
32038
32102
|
}
|
|
32039
32103
|
if (packageDir) {
|
|
32040
|
-
const pkgRoot = (0,
|
|
32104
|
+
const pkgRoot = (0, import_node_path36.join)(packageDir, "..", "..");
|
|
32041
32105
|
const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
|
|
32042
32106
|
if (pkgFrom) return pkgFrom;
|
|
32043
32107
|
}
|
|
@@ -32231,8 +32295,8 @@ function registerStageCommands(program3) {
|
|
|
32231
32295
|
const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
|
|
32232
32296
|
return decideStage({
|
|
32233
32297
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
32234
|
-
hasCompose: (0, import_node_fs39.existsSync)((0,
|
|
32235
|
-
hasEnvExample: (0, import_node_fs39.existsSync)((0,
|
|
32298
|
+
hasCompose: (0, import_node_fs39.existsSync)((0, import_node_path37.join)(process.cwd(), "docker-compose.yml")),
|
|
32299
|
+
hasEnvExample: (0, import_node_fs39.existsSync)((0, import_node_path37.join)(process.cwd(), ".env.example"))
|
|
32236
32300
|
});
|
|
32237
32301
|
}
|
|
32238
32302
|
async function fetchStageVaultEnvMerge() {
|
|
@@ -32503,8 +32567,7 @@ function registerBoardCommands(program3) {
|
|
|
32503
32567
|
repo: o.repo,
|
|
32504
32568
|
includeBundleDetails: o.bundleDetails,
|
|
32505
32569
|
includeAllBodies: o.bodies,
|
|
32506
|
-
allowPartial: o.allowPartial
|
|
32507
|
-
live: o.live
|
|
32570
|
+
allowPartial: o.allowPartial
|
|
32508
32571
|
});
|
|
32509
32572
|
console.log(o.json ? JSON.stringify(report) : renderBoardReport(report));
|
|
32510
32573
|
} catch (e) {
|
|
@@ -32515,7 +32578,7 @@ function registerBoardCommands(program3) {
|
|
|
32515
32578
|
return alreadyClaimed ? `Check ${ref}: claimed and In Progress, no live contest - claim would renew the lease (nothing written)` : `Check ${ref}: free - claim would proceed (nothing written)`;
|
|
32516
32579
|
}
|
|
32517
32580
|
const board = program3.command("board").description("read, claim, show, and move Project v2 work items for the current repo");
|
|
32518
|
-
board.command("read", { isDefault: true }).description("read the board and print user-owned, claimable, and taken items").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo (defaults to git origin)").option("--bundle-details", "fetch body/comments only for user-owned and claimable issues").option("--bodies", "fetch body/comments for EVERY scoped row, including taken and unowned in-flight ones \u2014 for consumers that scope by Status rather than ownership (#4861); implies --bundle-details and costs one extra read per row").option("--allow-partial", "return partial board results when later page/detail reads fail").
|
|
32581
|
+
board.command("read", { isDefault: true }).description("read the board and print user-owned, claimable, and taken items").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo (defaults to git origin)").option("--bundle-details", "fetch body/comments only for user-owned and claimable issues").option("--bodies", "fetch body/comments for EVERY scoped row, including taken and unowned in-flight ones \u2014 for consumers that scope by Status rather than ownership (#4861); implies --bundle-details and costs one extra read per row").option("--allow-partial", "return partial board results when later page/detail reads fail").addHelpText("after", "\nread is always the authoritative live GitHub Project v2 board (#4926).\n--allow-partial applies to the paginated path and detail reads.\n").action((o) => runBoardRead(o));
|
|
32519
32582
|
withExamples(mutating(
|
|
32520
32583
|
board.command("claim <issues...>").description("claim issues: assign them and move their Project v2 Status to In Progress \u2014 idempotent, so an item already yours and In Progress succeeds unchanged (one or more refs)").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo for local issue numbers (defaults to git origin)").option("--for <login>", "assign to this login instead of @me \u2014 agent claims on behalf of the master").option("--force", "take an item already claimed by another lane that shows live evidence of active work (#3727)").option("--check", "read-only: run every claim gate and report the verdict, writing nothing \u2014 exits 1 with the same refusal a real claim would raise (#4511)").option("--allow-partial", "return success JSON if assignment succeeds but the status move fails"),
|
|
32521
32584
|
(_opts, args) => ({ command: "board claim", issues: args[0] ?? [] })
|
|
@@ -32726,7 +32789,7 @@ function registerBoardCommands(program3) {
|
|
|
32726
32789
|
// src/merge-cleanup.ts
|
|
32727
32790
|
var import_node_fs40 = require("node:fs");
|
|
32728
32791
|
var import_promises9 = require("node:fs/promises");
|
|
32729
|
-
var
|
|
32792
|
+
var import_node_path39 = require("node:path");
|
|
32730
32793
|
var import_node_os15 = require("node:os");
|
|
32731
32794
|
var import_node_child_process18 = require("node:child_process");
|
|
32732
32795
|
|
|
@@ -32814,7 +32877,7 @@ function boardAdvanceFailureMessage(result) {
|
|
|
32814
32877
|
|
|
32815
32878
|
// src/deferred-registry-store.ts
|
|
32816
32879
|
var import_promises8 = require("node:fs/promises");
|
|
32817
|
-
var
|
|
32880
|
+
var import_node_path38 = require("node:path");
|
|
32818
32881
|
var sleep2 = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
32819
32882
|
async function atomicWrite(target, contents) {
|
|
32820
32883
|
const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
@@ -32865,12 +32928,12 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
|
|
|
32865
32928
|
},
|
|
32866
32929
|
// Standalone atomic write — THROWS on failure (no best-effort swallow, #2846).
|
|
32867
32930
|
write: async (entries) => {
|
|
32868
|
-
await (0, import_promises8.mkdir)((0,
|
|
32931
|
+
await (0, import_promises8.mkdir)((0, import_node_path38.dirname)(registryPath), { recursive: true });
|
|
32869
32932
|
await atomicWrite(registryPath, serializeDeferredWorktrees(entries));
|
|
32870
32933
|
},
|
|
32871
32934
|
// Serialized read-modify-write under the repo-wide lock (#2846).
|
|
32872
32935
|
update: async (mutate) => {
|
|
32873
|
-
await (0, import_promises8.mkdir)((0,
|
|
32936
|
+
await (0, import_promises8.mkdir)((0, import_node_path38.dirname)(registryPath), { recursive: true });
|
|
32874
32937
|
const deadline = Date.now() + opts.maxWaitMs;
|
|
32875
32938
|
for (; ; ) {
|
|
32876
32939
|
const guard = await acquireLock(lockPath, opts, deadline);
|
|
@@ -33034,6 +33097,23 @@ ${err.stderr ?? ""}`;
|
|
|
33034
33097
|
return { step, status: `failed: ${msg}` };
|
|
33035
33098
|
}
|
|
33036
33099
|
}
|
|
33100
|
+
async function bestEffortCloseMissingWorktreeLeases(leaseDir = (0, import_node_path39.join)((0, import_node_os15.homedir)(), ".jerv", "leases"), exists = import_node_fs40.existsSync) {
|
|
33101
|
+
let names = [];
|
|
33102
|
+
try {
|
|
33103
|
+
names = (0, import_node_fs40.readdirSync)(leaseDir);
|
|
33104
|
+
} catch {
|
|
33105
|
+
return;
|
|
33106
|
+
}
|
|
33107
|
+
for (const name of names) {
|
|
33108
|
+
if (!name.endsWith(".json")) continue;
|
|
33109
|
+
try {
|
|
33110
|
+
const rec = JSON.parse((0, import_node_fs40.readFileSync)((0, import_node_path39.join)(leaseDir, name), "utf8"));
|
|
33111
|
+
if (rec.kind !== "worktree" || rec.state === "closed" || typeof rec.ref !== "string" || !rec.ref.trim()) continue;
|
|
33112
|
+
if (!exists(rec.ref)) await bestEffortLeaseClose(rec.ref);
|
|
33113
|
+
} catch {
|
|
33114
|
+
}
|
|
33115
|
+
}
|
|
33116
|
+
}
|
|
33037
33117
|
async function applyGcPlan(plan, remote, opts = {}) {
|
|
33038
33118
|
const result = { removedBranches: [], removedRemoteBranches: [], removedTrackingRefs: [], removedWorktreeDirs: [], refused: [], failed: [], pruned: false };
|
|
33039
33119
|
const beforeWorktrees = parseWorktreePorcelain(
|
|
@@ -33041,7 +33121,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
33041
33121
|
);
|
|
33042
33122
|
const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
33043
33123
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
33044
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
33124
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path39.dirname)((0, import_node_path39.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
33045
33125
|
const gcActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
|
|
33046
33126
|
const owners = readWorktreeOwners(primaryRepoRoot);
|
|
33047
33127
|
const removalNow = Date.now();
|
|
@@ -33050,7 +33130,9 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
33050
33130
|
const activeWorkspaceDeferred = [];
|
|
33051
33131
|
const refusesRemoval = (path2, branch) => {
|
|
33052
33132
|
if (!path2) return false;
|
|
33053
|
-
const activeGuard = decideActiveWorkspaceGuard(path2, activeWorkspaceRoot
|
|
33133
|
+
const activeGuard = decideActiveWorkspaceGuard(path2, activeWorkspaceRoot, process.platform, {
|
|
33134
|
+
cursorAgentHost: isCursorAgentHost()
|
|
33135
|
+
});
|
|
33054
33136
|
if (activeGuard.action === "refuse") {
|
|
33055
33137
|
result.refused.push(activeGuard.message);
|
|
33056
33138
|
const owner2 = findWorktreeOwner(owners, path2);
|
|
@@ -33129,7 +33211,8 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
33129
33211
|
const removeDeps = worktreeRemoveDeps(async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
33130
33212
|
const cleanupRoots = [
|
|
33131
33213
|
opts.root ? resolveExplicitScanRoot(opts.root, primaryRepoRoot) : siblingMmiWorktreesRoot(primaryRepoRoot),
|
|
33132
|
-
agentWorktreesRoot(primaryRepoRoot)
|
|
33214
|
+
agentWorktreesRoot(primaryRepoRoot),
|
|
33215
|
+
...helperWorktreeRoots(primaryRepoRoot)
|
|
33133
33216
|
];
|
|
33134
33217
|
for (const wt of worktreeDirsToRemove) {
|
|
33135
33218
|
const owner = findWorktreeOwner(owners, wt.path);
|
|
@@ -33181,12 +33264,27 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
33181
33264
|
}
|
|
33182
33265
|
}
|
|
33183
33266
|
}
|
|
33267
|
+
for (const head of plan.reapOriginHeads ?? []) {
|
|
33268
|
+
try {
|
|
33269
|
+
await execFileP2("git", ["push", remote, "--delete", head.branch], { timeout: GIT_TIMEOUT_MS });
|
|
33270
|
+
result.removedRemoteBranches.push(head.branch);
|
|
33271
|
+
} catch (e) {
|
|
33272
|
+
const detail = `${e.message}
|
|
33273
|
+
${e.stderr ?? ""}`;
|
|
33274
|
+
if (/not found|does not exist|unable to delete|remote ref does not exist/i.test(detail)) {
|
|
33275
|
+
result.removedRemoteBranches.push(head.branch);
|
|
33276
|
+
} else {
|
|
33277
|
+
result.failed.push(`${head.branch}: origin delete failed (${e.message.split("\n")[0]})`);
|
|
33278
|
+
}
|
|
33279
|
+
}
|
|
33280
|
+
}
|
|
33184
33281
|
try {
|
|
33185
33282
|
await execFileP2("git", ["worktree", "prune"], { timeout: GIT_TIMEOUT_MS });
|
|
33186
33283
|
result.pruned = true;
|
|
33187
33284
|
} catch (e) {
|
|
33188
33285
|
result.failed.push(`worktree prune: ${e.message.split("\n")[0]}`);
|
|
33189
33286
|
}
|
|
33287
|
+
await bestEffortCloseMissingWorktreeLeases();
|
|
33190
33288
|
return result;
|
|
33191
33289
|
}
|
|
33192
33290
|
async function pollGhPrChecks(prNumber, repoArgs) {
|
|
@@ -33223,8 +33321,8 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
|
|
|
33223
33321
|
const commits = JSON.parse(raw).commits ?? [];
|
|
33224
33322
|
const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
|
|
33225
33323
|
if (!body) return void 0;
|
|
33226
|
-
const dir = (0, import_node_fs40.mkdtempSync)((0,
|
|
33227
|
-
const path2 = (0,
|
|
33324
|
+
const dir = (0, import_node_fs40.mkdtempSync)((0, import_node_path39.join)((0, import_node_os15.tmpdir)(), "mmi-squash-body-"));
|
|
33325
|
+
const path2 = (0, import_node_path39.join)(dir, "body.txt");
|
|
33228
33326
|
(0, import_node_fs40.writeFileSync)(path2, `${body}
|
|
33229
33327
|
`, "utf8");
|
|
33230
33328
|
return { path: path2, cleanup: () => {
|
|
@@ -33349,15 +33447,16 @@ async function createDeferredWorktreeStore() {
|
|
|
33349
33447
|
}
|
|
33350
33448
|
var realWorktreeDirRemover = {
|
|
33351
33449
|
probe: (p) => {
|
|
33450
|
+
const target = win32LongPath(p);
|
|
33352
33451
|
let st;
|
|
33353
33452
|
try {
|
|
33354
|
-
st = (0, import_node_fs40.lstatSync)(
|
|
33453
|
+
st = (0, import_node_fs40.lstatSync)(target);
|
|
33355
33454
|
} catch {
|
|
33356
33455
|
return null;
|
|
33357
33456
|
}
|
|
33358
33457
|
if (st.isSymbolicLink()) return "link";
|
|
33359
33458
|
try {
|
|
33360
|
-
(0, import_node_fs40.readlinkSync)(
|
|
33459
|
+
(0, import_node_fs40.readlinkSync)(target);
|
|
33361
33460
|
return "link";
|
|
33362
33461
|
} catch {
|
|
33363
33462
|
}
|
|
@@ -33365,7 +33464,7 @@ var realWorktreeDirRemover = {
|
|
|
33365
33464
|
},
|
|
33366
33465
|
readdir: (p) => {
|
|
33367
33466
|
try {
|
|
33368
|
-
return (0, import_node_fs40.readdirSync)(p);
|
|
33467
|
+
return (0, import_node_fs40.readdirSync)(win32LongPath(p));
|
|
33369
33468
|
} catch {
|
|
33370
33469
|
return [];
|
|
33371
33470
|
}
|
|
@@ -33373,13 +33472,16 @@ var realWorktreeDirRemover = {
|
|
|
33373
33472
|
// A directory reparse point (junction / dir-symlink) is detached with rmdir (unlinks the mount point,
|
|
33374
33473
|
// leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
|
|
33375
33474
|
detachLink: (p) => {
|
|
33475
|
+
const target = win32LongPath(p);
|
|
33376
33476
|
try {
|
|
33377
|
-
(0, import_node_fs40.rmdirSync)(
|
|
33477
|
+
(0, import_node_fs40.rmdirSync)(target);
|
|
33378
33478
|
} catch {
|
|
33379
|
-
(0, import_node_fs40.unlinkSync)(
|
|
33479
|
+
(0, import_node_fs40.unlinkSync)(target);
|
|
33380
33480
|
}
|
|
33381
33481
|
},
|
|
33382
|
-
|
|
33482
|
+
// #4904: Windows MAX_PATH aborts git worktree remove; the fallback must use \\?\ so the dir
|
|
33483
|
+
// (and its lease) do not survive as an unregistered leftover.
|
|
33484
|
+
removeTree: (p) => (0, import_promises9.rm)(win32LongPath(p), { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
|
|
33383
33485
|
};
|
|
33384
33486
|
async function resolvePrimaryCheckout(execGit) {
|
|
33385
33487
|
try {
|
|
@@ -33850,12 +33952,13 @@ async function checkDocsIndexAtHead(opts, deps) {
|
|
|
33850
33952
|
// src/worktree-lifecycle-commands.ts
|
|
33851
33953
|
var import_node_fs42 = require("node:fs");
|
|
33852
33954
|
var import_promises10 = require("node:fs/promises");
|
|
33853
|
-
var
|
|
33955
|
+
var import_node_os16 = require("node:os");
|
|
33956
|
+
var import_node_path41 = require("node:path");
|
|
33854
33957
|
|
|
33855
33958
|
// src/worktree-install-cache.ts
|
|
33856
33959
|
var import_node_crypto8 = require("node:crypto");
|
|
33857
33960
|
var import_node_fs41 = require("node:fs");
|
|
33858
|
-
var
|
|
33961
|
+
var import_node_path40 = require("node:path");
|
|
33859
33962
|
var CACHE_DIR = "worktree-install-cache";
|
|
33860
33963
|
var MANIFEST = "manifest.json";
|
|
33861
33964
|
var NODE_MODULES2 = "node_modules";
|
|
@@ -33885,7 +33988,7 @@ function hashLockfileBytes(contents) {
|
|
|
33885
33988
|
}
|
|
33886
33989
|
function resolveWorktreeInstallLockfile(packageDir, fs2 = realWorktreeInstallCacheFs) {
|
|
33887
33990
|
for (const name of LOCKFILE_NAMES) {
|
|
33888
|
-
const path2 = (0,
|
|
33991
|
+
const path2 = (0, import_node_path40.join)(packageDir, name);
|
|
33889
33992
|
if (!fs2.exists(path2)) continue;
|
|
33890
33993
|
try {
|
|
33891
33994
|
const hash = hashLockfileBytes(fs2.readFile(path2));
|
|
@@ -33900,8 +34003,8 @@ function worktreeInstallCacheEntry(primaryRoot, lockfileHash) {
|
|
|
33900
34003
|
const root = repoRuntimeStatePath(primaryRoot, CACHE_DIR, lockfileHash);
|
|
33901
34004
|
return {
|
|
33902
34005
|
root,
|
|
33903
|
-
manifestPath: (0,
|
|
33904
|
-
nodeModulesPath: (0,
|
|
34006
|
+
manifestPath: (0, import_node_path40.join)(root, MANIFEST),
|
|
34007
|
+
nodeModulesPath: (0, import_node_path40.join)(root, NODE_MODULES2)
|
|
33905
34008
|
};
|
|
33906
34009
|
}
|
|
33907
34010
|
function readWorktreeInstallCacheManifest(manifestPath, fs2 = realWorktreeInstallCacheFs) {
|
|
@@ -33941,7 +34044,7 @@ function invalidateWorktreeInstallCacheEntry(entry, fs2 = realWorktreeInstallCac
|
|
|
33941
34044
|
}
|
|
33942
34045
|
}
|
|
33943
34046
|
function removeMaterializedTree(packageDir, fs2) {
|
|
33944
|
-
const dest = (0,
|
|
34047
|
+
const dest = (0, import_node_path40.join)(packageDir, NODE_MODULES2);
|
|
33945
34048
|
if (!fs2.exists(dest)) return;
|
|
33946
34049
|
fs2.rm(dest);
|
|
33947
34050
|
if (fs2.exists(dest)) {
|
|
@@ -33949,7 +34052,7 @@ function removeMaterializedTree(packageDir, fs2) {
|
|
|
33949
34052
|
}
|
|
33950
34053
|
}
|
|
33951
34054
|
function materializeCachedNodeModules(entry, destPackageDir, fs2 = realWorktreeInstallCacheFs) {
|
|
33952
|
-
const dest = (0,
|
|
34055
|
+
const dest = (0, import_node_path40.join)(destPackageDir, NODE_MODULES2);
|
|
33953
34056
|
try {
|
|
33954
34057
|
if (fs2.exists(dest)) fs2.rm(dest);
|
|
33955
34058
|
fs2.mkdirp(destPackageDir);
|
|
@@ -33964,7 +34067,7 @@ function materializeCachedNodeModules(entry, destPackageDir, fs2 = realWorktreeI
|
|
|
33964
34067
|
}
|
|
33965
34068
|
}
|
|
33966
34069
|
async function storeWorktreeInstallCacheEntry(primaryRoot, lockfile3, command, sourcePackageDir, fs2 = realWorktreeInstallCacheFs, now = Date.now()) {
|
|
33967
|
-
const source = (0,
|
|
34070
|
+
const source = (0, import_node_path40.join)(sourcePackageDir, NODE_MODULES2);
|
|
33968
34071
|
if (!isMaterializableNodeModulesDir(source, fs2)) return;
|
|
33969
34072
|
const entry = worktreeInstallCacheEntry(primaryRoot, lockfile3.hash);
|
|
33970
34073
|
const manifest = {
|
|
@@ -34219,12 +34322,36 @@ function classifyStaleLeaks(input) {
|
|
|
34219
34322
|
remediation: "mmi-cli worktree gc --apply"
|
|
34220
34323
|
});
|
|
34221
34324
|
}
|
|
34325
|
+
for (const leftover of input.originLeftovers ?? []) {
|
|
34326
|
+
leaks.push({
|
|
34327
|
+
kind: "origin-leftover",
|
|
34328
|
+
ref: leftover.branch,
|
|
34329
|
+
detail: leftover.detail,
|
|
34330
|
+
remediation: leftover.autoReap ? "mmi-cli worktree gc --apply (conservative merged-head reap)" : leftover.kind === "open-pr" ? "leave open \u2014 gc will not delete an open PR head" : "mmi-cli worktree gc --apply --train-only (explicit; will not silently delete every non-train branch)"
|
|
34331
|
+
});
|
|
34332
|
+
}
|
|
34333
|
+
for (const helper of input.helperWorktrees ?? []) {
|
|
34334
|
+
leaks.push({
|
|
34335
|
+
kind: "helper-worktree",
|
|
34336
|
+
ref: helper.path,
|
|
34337
|
+
detail: helper.detail,
|
|
34338
|
+
remediation: helper.reapable ? "mmi-cli worktree gc --apply" : "inspect / git -C <primary> worktree remove --force after the helper is abandoned"
|
|
34339
|
+
});
|
|
34340
|
+
}
|
|
34341
|
+
for (const ref of input.missingLeaseRefs ?? []) {
|
|
34342
|
+
leaks.push({
|
|
34343
|
+
kind: "missing-lease",
|
|
34344
|
+
ref,
|
|
34345
|
+
detail: `jerv worktree lease still active but path is gone`,
|
|
34346
|
+
remediation: "mmi-cli worktree gc --apply (closes the lease; JPT #5548 also reaps on sweep)"
|
|
34347
|
+
});
|
|
34348
|
+
}
|
|
34222
34349
|
return leaks;
|
|
34223
34350
|
}
|
|
34224
34351
|
var defaultOrphanDirScanDeps = {
|
|
34225
34352
|
listDirs: (root) => {
|
|
34226
34353
|
try {
|
|
34227
|
-
return (0, import_node_fs42.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0,
|
|
34354
|
+
return (0, import_node_fs42.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path41.join)(root, e.name));
|
|
34228
34355
|
} catch {
|
|
34229
34356
|
return [];
|
|
34230
34357
|
}
|
|
@@ -34239,8 +34366,12 @@ function scanOrphanDirs(worktreesRoot, worktreeGitRoot, deps = defaultOrphanDirS
|
|
|
34239
34366
|
}
|
|
34240
34367
|
return candidates;
|
|
34241
34368
|
}
|
|
34242
|
-
function formatStaleLeaks(leaks, prLookupFailures = []) {
|
|
34243
|
-
const
|
|
34369
|
+
function formatStaleLeaks(leaks, prLookupFailures = [], audit) {
|
|
34370
|
+
const blocked = Boolean(audit?.blocksGreen);
|
|
34371
|
+
const lines = leaks.length ? [`worktree list --stale: ${leaks.length} leak(s)`] : blocked ? ["worktree list --stale: incomplete \u2014 refusing a clean report"] : ["worktree list --stale: no leaks found"];
|
|
34372
|
+
if (audit) {
|
|
34373
|
+
for (const line of formatEstateAuditLines(audit)) lines.push(` ${line}`);
|
|
34374
|
+
}
|
|
34244
34375
|
for (const leak of leaks) {
|
|
34245
34376
|
lines.push(` [${leak.kind}] ${leak.ref} \u2014 ${leak.detail}`);
|
|
34246
34377
|
lines.push(` fix: ${leak.remediation}`);
|
|
@@ -34390,13 +34521,13 @@ function registerWorktreeCommands(program3) {
|
|
|
34390
34521
|
const detached = headBorn && !symbolicBranch;
|
|
34391
34522
|
const branch = symbolicBranch || (detached ? "HEAD" : "");
|
|
34392
34523
|
if (!wtPath || !branch) return fail("worktree land: not inside a git worktree");
|
|
34393
|
-
const gitFile = (0,
|
|
34524
|
+
const gitFile = (0, import_node_path41.join)(wtPath, ".git");
|
|
34394
34525
|
const isLinked = (0, import_node_fs42.existsSync)(gitFile) && (0, import_node_fs42.statSync)(gitFile).isFile();
|
|
34395
34526
|
if (apply && !isLinked) {
|
|
34396
34527
|
return fail("worktree land: run from inside the linked worktree you want to land (this is the primary checkout)");
|
|
34397
34528
|
}
|
|
34398
34529
|
const commonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
34399
|
-
const primaryCheckout = commonDir ? (0,
|
|
34530
|
+
const primaryCheckout = commonDir ? (0, import_node_path41.dirname)(commonDir) : wtPath;
|
|
34400
34531
|
const localBranchNames = await execFileP2("git", ["-C", primaryCheckout, "for-each-ref", "--format=%(refname:short)", "refs/heads"], { timeout: GIT_TIMEOUT_MS }).then(({ stdout }) => new Set((stdout || "").split("\n").map((l) => l.trim()).filter(Boolean))).catch(() => void 0);
|
|
34401
34532
|
const orphan = classifyOrphanedWorktree({
|
|
34402
34533
|
branch,
|
|
@@ -34460,7 +34591,9 @@ function registerWorktreeCommands(program3) {
|
|
|
34460
34591
|
const reportedMergeState = treeOnly ? "not-checked" : mergeVerdict.state;
|
|
34461
34592
|
const landOwner = lookupWorktreeOwner(primaryCheckout, wtPath);
|
|
34462
34593
|
const activeWorkspaceRoot = resolveActiveWorkspaceRoot();
|
|
34463
|
-
const activeGuard = decideActiveWorkspaceGuard(wtPath, activeWorkspaceRoot
|
|
34594
|
+
const activeGuard = decideActiveWorkspaceGuard(wtPath, activeWorkspaceRoot, process.platform, {
|
|
34595
|
+
cursorAgentHost: isCursorAgentHost()
|
|
34596
|
+
});
|
|
34464
34597
|
if (activeGuard.action === "refuse") {
|
|
34465
34598
|
const deferredStore = await createDeferredWorktreeStore();
|
|
34466
34599
|
if (deferredStore) {
|
|
@@ -34536,6 +34669,9 @@ function registerWorktreeCommands(program3) {
|
|
|
34536
34669
|
step: "remove worktree",
|
|
34537
34670
|
status: removeOutcome.status === "removed" ? removeOutcome.recovery ? `done (${removeOutcome.recovery})` : "done" : removeOutcome.remainsOnDisk ? `failed: ${removeOutcome.error ?? "lock held"} \u2014 the directory "${wtPath}" is still on disk; cd every shell out of it (a cwd inside holds it open on Windows), then delete that directory` : `failed: ${removeOutcome.error ?? "lock held"} \u2014 run: git -C "${primaryCheckout}" worktree remove --force "${wtPath}"`
|
|
34538
34671
|
});
|
|
34672
|
+
if (!removeOutcome.remainsOnDisk) {
|
|
34673
|
+
report.push(await bestEffortLeaseClose(wtPath));
|
|
34674
|
+
}
|
|
34539
34675
|
if (!shouldContinueLandCleanup(removeOutcome.status)) {
|
|
34540
34676
|
const result2 = {
|
|
34541
34677
|
dryRun: false,
|
|
@@ -34620,8 +34756,17 @@ function registerWorktreeCommands(program3) {
|
|
|
34620
34756
|
if (o.stale) {
|
|
34621
34757
|
const leaks = classifyStaleLeaks(ctx);
|
|
34622
34758
|
const failures = ctx.prLookupFailures ?? [];
|
|
34623
|
-
|
|
34624
|
-
|
|
34759
|
+
const complete = failures.length === 0 && !ctx.audit?.blocksGreen;
|
|
34760
|
+
if (o.json) {
|
|
34761
|
+
return console.log(JSON.stringify({
|
|
34762
|
+
stale: leaks,
|
|
34763
|
+
count: leaks.length,
|
|
34764
|
+
prLookupFailures: failures,
|
|
34765
|
+
complete,
|
|
34766
|
+
audit: ctx.audit
|
|
34767
|
+
}, null, 2));
|
|
34768
|
+
}
|
|
34769
|
+
return console.log(formatStaleLeaks(leaks, failures, ctx.audit));
|
|
34625
34770
|
}
|
|
34626
34771
|
if (o.json) return console.log(JSON.stringify({ worktrees: ctx.worktrees }, null, 2));
|
|
34627
34772
|
if (!ctx.worktrees.length) return console.log("worktree list: no worktrees");
|
|
@@ -34645,10 +34790,18 @@ async function gatherWorktreeContext() {
|
|
|
34645
34790
|
const branchOut = (await execFileP2("git", ["branch", "--format=%(refname:short)"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
|
|
34646
34791
|
const localBranches = branchOut.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
34647
34792
|
const currentBranch2 = (await execFileP2("git", ["rev-parse", "--abbrev-ref", "HEAD"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || void 0;
|
|
34648
|
-
const
|
|
34793
|
+
const remoteBranchOut = (await execFileP2("git", ["branch", "-r", "--format=%(refname:short)"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
|
|
34794
|
+
const remoteBranches = remoteBranchOut.split(/\r?\n/).map((l) => l.trim()).filter((b) => b.startsWith("origin/") && b !== "origin/HEAD");
|
|
34795
|
+
const remoteNames = remoteBranches.map((b) => b.replace(/^origin\//, ""));
|
|
34796
|
+
const prTargets = [.../* @__PURE__ */ new Set([
|
|
34797
|
+
...localBranches.filter((b) => !PROTECTED_BRANCHES2.has(b)),
|
|
34798
|
+
...remoteNames.filter((b) => !PROTECTED_BRANCHES2.has(b))
|
|
34799
|
+
])];
|
|
34800
|
+
const { prs, failures: prLookupFailures } = await resolveBranchPrs(prTargets, STALE_PR_LOOKUP_LIMIT);
|
|
34649
34801
|
const openPrBranches = /* @__PURE__ */ new Set();
|
|
34650
34802
|
const closedBranches = /* @__PURE__ */ new Set();
|
|
34651
34803
|
const closedUnmergedBranches = /* @__PURE__ */ new Set();
|
|
34804
|
+
const mergedPrBranches = /* @__PURE__ */ new Set();
|
|
34652
34805
|
const byBranch = /* @__PURE__ */ new Map();
|
|
34653
34806
|
for (const pr2 of prs) {
|
|
34654
34807
|
const arr = byBranch.get(pr2.headRefName) ?? [];
|
|
@@ -34659,6 +34812,7 @@ async function gatherWorktreeContext() {
|
|
|
34659
34812
|
if (states.some((s) => s === "OPEN")) openPrBranches.add(br);
|
|
34660
34813
|
else if (states.some((s) => s === "MERGED" || s === "CLOSED")) {
|
|
34661
34814
|
closedBranches.add(br);
|
|
34815
|
+
if (states.some((s) => s === "MERGED")) mergedPrBranches.add(br);
|
|
34662
34816
|
if (!states.some((s) => s === "MERGED") && states.some((s) => s === "CLOSED")) {
|
|
34663
34817
|
closedUnmergedBranches.add(br);
|
|
34664
34818
|
}
|
|
@@ -34670,7 +34824,7 @@ async function gatherWorktreeContext() {
|
|
|
34670
34824
|
if (s) stages.push({ path: wt.path, port: s.port });
|
|
34671
34825
|
}
|
|
34672
34826
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
34673
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
34827
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path41.dirname)((0, import_node_path41.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
34674
34828
|
const wtRoot = siblingMmiWorktreesRoot(primaryRepoRoot);
|
|
34675
34829
|
let orphanDirs = [];
|
|
34676
34830
|
if ((0, import_node_fs42.existsSync)(wtRoot)) {
|
|
@@ -34679,6 +34833,44 @@ async function gatherWorktreeContext() {
|
|
|
34679
34833
|
listDirs: (root) => worktreeScanDirs(root, primaryRepoRoot, defaultOrphanDirScanDeps.listDirs, isRepoCheckoutDir)
|
|
34680
34834
|
});
|
|
34681
34835
|
}
|
|
34836
|
+
const repoContainer = worktreesRootOf(primaryRepoRoot);
|
|
34837
|
+
if ((0, import_node_fs42.existsSync)(repoContainer)) {
|
|
34838
|
+
for (const dir of defaultOrphanDirScanDeps.listDirs(repoContainer)) {
|
|
34839
|
+
if (orphanDirs.some((o) => o.path === dir)) continue;
|
|
34840
|
+
const inspected = inspectSiblingWorktreeDir(dir, worktreeGitRoot);
|
|
34841
|
+
const classified = classifySiblingWorktreeDir(inspected);
|
|
34842
|
+
if (classified.cleanup) {
|
|
34843
|
+
orphanDirs.push({ path: dir, reason: classified.cleanup.reason });
|
|
34844
|
+
continue;
|
|
34845
|
+
}
|
|
34846
|
+
if (inspected.gitType === "missing" && pathProvesRepoContainerOwnership(dir, repoContainer)) {
|
|
34847
|
+
orphanDirs.push({ path: dir, reason: "orphaned-folder" });
|
|
34848
|
+
}
|
|
34849
|
+
}
|
|
34850
|
+
}
|
|
34851
|
+
const originLeftovers = classifyOriginLeftovers({
|
|
34852
|
+
remoteBranches,
|
|
34853
|
+
localBranches,
|
|
34854
|
+
protectedBranches: PROTECTED_BRANCHES2,
|
|
34855
|
+
openPrBranches,
|
|
34856
|
+
mergedPrBranches,
|
|
34857
|
+
closedUnmergedBranches
|
|
34858
|
+
});
|
|
34859
|
+
const helperWorktrees = scanHelperWorktrees(primaryRepoRoot, worktreeGitRoot);
|
|
34860
|
+
const leaseRefs = readJervWorktreeLeaseRefs();
|
|
34861
|
+
const missingLeaseRefs = leaseRefs.filter((ref) => !(0, import_node_fs42.existsSync)(ref));
|
|
34862
|
+
const remoteUrls = await gitRemoteUrls();
|
|
34863
|
+
const otherCheckouts = discoverOtherPrimaries(primaryRepoRoot).map((path2) => ({
|
|
34864
|
+
path: path2,
|
|
34865
|
+
remoteUrls: gitConfigRemoteUrls(path2)
|
|
34866
|
+
}));
|
|
34867
|
+
const audit = classifyEstateAudit({
|
|
34868
|
+
auditedClone: primaryRepoRoot,
|
|
34869
|
+
remoteUrls,
|
|
34870
|
+
otherCheckouts,
|
|
34871
|
+
leaseRefs,
|
|
34872
|
+
thisWorktreesRoot: repoContainer
|
|
34873
|
+
});
|
|
34682
34874
|
return {
|
|
34683
34875
|
worktrees,
|
|
34684
34876
|
localBranches,
|
|
@@ -34688,9 +34880,96 @@ async function gatherWorktreeContext() {
|
|
|
34688
34880
|
closedUnmergedBranches,
|
|
34689
34881
|
stages,
|
|
34690
34882
|
orphanDirs,
|
|
34691
|
-
prLookupFailures
|
|
34883
|
+
prLookupFailures,
|
|
34884
|
+
originLeftovers,
|
|
34885
|
+
helperWorktrees,
|
|
34886
|
+
missingLeaseRefs,
|
|
34887
|
+
audit
|
|
34692
34888
|
};
|
|
34693
34889
|
}
|
|
34890
|
+
function gitConfigRemoteUrls(checkout) {
|
|
34891
|
+
const gitPath = (0, import_node_path41.join)(checkout, ".git");
|
|
34892
|
+
let configPath = (0, import_node_path41.join)(checkout, ".git", "config");
|
|
34893
|
+
try {
|
|
34894
|
+
const st = (0, import_node_fs42.statSync)(gitPath);
|
|
34895
|
+
if (st.isFile()) return [];
|
|
34896
|
+
} catch {
|
|
34897
|
+
return [];
|
|
34898
|
+
}
|
|
34899
|
+
try {
|
|
34900
|
+
const text = (0, import_node_fs42.readFileSync)(configPath, "utf8");
|
|
34901
|
+
return [...text.matchAll(/^\s*url\s*=\s*(.+)$/gm)].map((m) => m[1].trim());
|
|
34902
|
+
} catch {
|
|
34903
|
+
return [];
|
|
34904
|
+
}
|
|
34905
|
+
}
|
|
34906
|
+
async function gitRemoteUrls() {
|
|
34907
|
+
const out = (await execFileP2("git", ["remote", "-v"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
|
|
34908
|
+
return [...new Set(
|
|
34909
|
+
out.split(/\r?\n/).map((line) => line.trim().split(/\s+/)[1]).filter((url) => Boolean(url))
|
|
34910
|
+
)];
|
|
34911
|
+
}
|
|
34912
|
+
function discoverOtherPrimaries(primaryRepoRoot) {
|
|
34913
|
+
const found = /* @__PURE__ */ new Set();
|
|
34914
|
+
const parent = (0, import_node_path41.dirname)(primaryRepoRoot);
|
|
34915
|
+
try {
|
|
34916
|
+
for (const name of (0, import_node_fs42.readdirSync)(parent)) {
|
|
34917
|
+
const path2 = (0, import_node_path41.join)(parent, name);
|
|
34918
|
+
if (path2 === primaryRepoRoot) continue;
|
|
34919
|
+
if ((0, import_node_fs42.existsSync)((0, import_node_path41.join)(path2, ".git"))) found.add(path2);
|
|
34920
|
+
}
|
|
34921
|
+
} catch {
|
|
34922
|
+
}
|
|
34923
|
+
const mirror = (0, import_node_path41.join)((0, import_node_os16.homedir)(), "Projects", (0, import_node_path41.basename)(primaryRepoRoot));
|
|
34924
|
+
if (mirror !== primaryRepoRoot && (0, import_node_fs42.existsSync)((0, import_node_path41.join)(mirror, ".git"))) found.add(mirror);
|
|
34925
|
+
return [...found];
|
|
34926
|
+
}
|
|
34927
|
+
function readJervWorktreeLeaseRefs(leaseDir = (0, import_node_path41.join)((0, import_node_os16.homedir)(), ".jerv", "leases")) {
|
|
34928
|
+
try {
|
|
34929
|
+
const refs = [];
|
|
34930
|
+
for (const name of (0, import_node_fs42.readdirSync)(leaseDir)) {
|
|
34931
|
+
if (!name.endsWith(".json")) continue;
|
|
34932
|
+
try {
|
|
34933
|
+
const rec = JSON.parse((0, import_node_fs42.readFileSync)((0, import_node_path41.join)(leaseDir, name), "utf8"));
|
|
34934
|
+
if (rec.kind === "worktree" && rec.state !== "closed" && typeof rec.ref === "string" && rec.ref.trim()) {
|
|
34935
|
+
refs.push(rec.ref);
|
|
34936
|
+
}
|
|
34937
|
+
} catch {
|
|
34938
|
+
}
|
|
34939
|
+
}
|
|
34940
|
+
return refs;
|
|
34941
|
+
} catch {
|
|
34942
|
+
return [];
|
|
34943
|
+
}
|
|
34944
|
+
}
|
|
34945
|
+
function scanHelperWorktrees(thisPrimary, thisWorktreeGitRoot) {
|
|
34946
|
+
const primaries = [thisPrimary, ...discoverOtherPrimaries(thisPrimary)];
|
|
34947
|
+
const out = [];
|
|
34948
|
+
for (const primary of primaries) {
|
|
34949
|
+
const gitRoot = primary === thisPrimary ? thisWorktreeGitRoot : (0, import_node_path41.join)(primary, ".git", "worktrees");
|
|
34950
|
+
for (const root of helperWorktreeRoots(primary)) {
|
|
34951
|
+
if (!(0, import_node_fs42.existsSync)(root)) continue;
|
|
34952
|
+
for (const dir of defaultOrphanDirScanDeps.listDirs(root)) {
|
|
34953
|
+
const inspected = inspectSiblingWorktreeDir(dir, gitRoot);
|
|
34954
|
+
const classified = classifySiblingWorktreeDir(inspected);
|
|
34955
|
+
if (classified.cleanup) {
|
|
34956
|
+
out.push({
|
|
34957
|
+
path: dir,
|
|
34958
|
+
detail: `${classified.cleanup.reason} helper worktree under ${root}`,
|
|
34959
|
+
reapable: true
|
|
34960
|
+
});
|
|
34961
|
+
} else {
|
|
34962
|
+
out.push({
|
|
34963
|
+
path: dir,
|
|
34964
|
+
detail: `helper worktree outside ../mmi-worktrees (${classified.skip?.reason ?? inspected.gitType}) at ${dir}`,
|
|
34965
|
+
reapable: false
|
|
34966
|
+
});
|
|
34967
|
+
}
|
|
34968
|
+
}
|
|
34969
|
+
}
|
|
34970
|
+
}
|
|
34971
|
+
return out;
|
|
34972
|
+
}
|
|
34694
34973
|
async function bestEffortGit(args, cwd, step, timeoutMs = GIT_TIMEOUT_MS) {
|
|
34695
34974
|
try {
|
|
34696
34975
|
const fullArgs = cwd ? ["-C", cwd, ...args] : args;
|
|
@@ -35408,7 +35687,7 @@ ${lines}`, {
|
|
|
35408
35687
|
|
|
35409
35688
|
// src/train-commands.ts
|
|
35410
35689
|
var import_node_fs44 = require("node:fs");
|
|
35411
|
-
var
|
|
35690
|
+
var import_node_path42 = require("node:path");
|
|
35412
35691
|
var RELEASE_BUMP_INTENTS = ["major", "minor", "patch"];
|
|
35413
35692
|
function resolveReleaseBumpIntent(raw) {
|
|
35414
35693
|
const intent = typeof raw === "string" ? raw.trim() : "";
|
|
@@ -35419,7 +35698,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
35419
35698
|
}
|
|
35420
35699
|
function readRepoVersion() {
|
|
35421
35700
|
try {
|
|
35422
|
-
return JSON.parse((0, import_node_fs44.readFileSync)((0,
|
|
35701
|
+
return JSON.parse((0, import_node_fs44.readFileSync)((0, import_node_path42.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
35423
35702
|
} catch {
|
|
35424
35703
|
return void 0;
|
|
35425
35704
|
}
|
|
@@ -35577,8 +35856,8 @@ function registerDeployCommands(program3) {
|
|
|
35577
35856
|
|
|
35578
35857
|
// src/discovery-commands.ts
|
|
35579
35858
|
var import_node_fs45 = require("node:fs");
|
|
35580
|
-
var
|
|
35581
|
-
var
|
|
35859
|
+
var import_node_os17 = require("node:os");
|
|
35860
|
+
var import_node_path43 = require("node:path");
|
|
35582
35861
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
35583
35862
|
async function collectStatus() {
|
|
35584
35863
|
const repo = await resolveRepo();
|
|
@@ -35766,10 +36045,10 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
35766
36045
|
else if (top) nextCommand = `mmi-cli oracle board claim ${top.number} # ${top.title}`;
|
|
35767
36046
|
else nextCommand = "mmi-cli oracle board read \u2014 no claimable items found";
|
|
35768
36047
|
}
|
|
35769
|
-
const home = (0,
|
|
36048
|
+
const home = (0, import_node_os17.homedir)();
|
|
35770
36049
|
const plugin = onboardPluginGate({
|
|
35771
|
-
readKnown: () => readFileSyncSafe((0,
|
|
35772
|
-
readSettings: () => readFileSyncSafe((0,
|
|
36050
|
+
readKnown: () => readFileSyncSafe((0, import_node_path43.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs45.readFileSync),
|
|
36051
|
+
readSettings: () => readFileSyncSafe((0, import_node_path43.join)(home, ".claude", "settings.json"), import_node_fs45.readFileSync)
|
|
35773
36052
|
});
|
|
35774
36053
|
return { track, board, registry: registry2, secrets, plugin, estateCli, doors: opts.doors ?? [], nextCommand };
|
|
35775
36054
|
}
|
|
@@ -36905,18 +37184,18 @@ function registerOrgHealthQuery(program3, deps = defaultOrgHealthQueryDeps()) {
|
|
|
36905
37184
|
|
|
36906
37185
|
// src/plugin-release-catchup.ts
|
|
36907
37186
|
var import_node_fs46 = require("node:fs");
|
|
36908
|
-
var
|
|
36909
|
-
var
|
|
37187
|
+
var import_node_path44 = require("node:path");
|
|
37188
|
+
var import_node_os18 = require("node:os");
|
|
36910
37189
|
var RELEASE_CATCHUP_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
36911
37190
|
var RELEASE_CATCHUP_DISABLE_ENV = "MMI_NO_RELEASE_CATCHUP";
|
|
36912
37191
|
function releaseCatchupStatePath(env = process.env) {
|
|
36913
37192
|
if (env.MMI_RELEASE_CATCHUP_STATE) return env.MMI_RELEASE_CATCHUP_STATE;
|
|
36914
37193
|
if (process.platform === "win32") {
|
|
36915
|
-
const base2 = env.LOCALAPPDATA || (0,
|
|
36916
|
-
return (0,
|
|
37194
|
+
const base2 = env.LOCALAPPDATA || (0, import_node_path44.join)((0, import_node_os18.homedir)(), "AppData", "Local");
|
|
37195
|
+
return (0, import_node_path44.join)(base2, "MMI Future", "mmi-cli", "release-catchup.json");
|
|
36917
37196
|
}
|
|
36918
|
-
const base = env.XDG_STATE_HOME || (0,
|
|
36919
|
-
return (0,
|
|
37197
|
+
const base = env.XDG_STATE_HOME || (0, import_node_path44.join)((0, import_node_os18.homedir)(), ".local", "state");
|
|
37198
|
+
return (0, import_node_path44.join)(base, "mmi-cli", "release-catchup.json");
|
|
36920
37199
|
}
|
|
36921
37200
|
function releaseCatchupDue(state, now, force = false) {
|
|
36922
37201
|
if (force) return true;
|
|
@@ -36934,15 +37213,15 @@ function newestCachedPluginVersion(home) {
|
|
|
36934
37213
|
}
|
|
36935
37214
|
function marketplaceClonePath(home) {
|
|
36936
37215
|
try {
|
|
36937
|
-
const parsed = JSON.parse((0, import_node_fs46.readFileSync)((0,
|
|
37216
|
+
const parsed = JSON.parse((0, import_node_fs46.readFileSync)((0, import_node_path44.join)(home, ".claude", "plugins", "known_marketplaces.json"), "utf8"));
|
|
36938
37217
|
if (parsed.mutmutco?.installLocation) return parsed.mutmutco.installLocation;
|
|
36939
37218
|
} catch {
|
|
36940
37219
|
}
|
|
36941
|
-
return (0,
|
|
37220
|
+
return (0, import_node_path44.join)(home, ".claude", "plugins", "marketplaces", "mutmutco");
|
|
36942
37221
|
}
|
|
36943
37222
|
function readCatalogVersion(home) {
|
|
36944
37223
|
try {
|
|
36945
|
-
const parsed = JSON.parse((0, import_node_fs46.readFileSync)((0,
|
|
37224
|
+
const parsed = JSON.parse((0, import_node_fs46.readFileSync)((0, import_node_path44.join)(marketplaceClonePath(home), ".claude-plugin", "marketplace.json"), "utf8"));
|
|
36946
37225
|
return parsed.plugins?.find((p) => p.name === "mmi")?.version;
|
|
36947
37226
|
} catch {
|
|
36948
37227
|
return void 0;
|
|
@@ -36950,7 +37229,7 @@ function readCatalogVersion(home) {
|
|
|
36950
37229
|
}
|
|
36951
37230
|
function readMmiInstallRecord(home) {
|
|
36952
37231
|
try {
|
|
36953
|
-
const parsed = JSON.parse((0, import_node_fs46.readFileSync)((0,
|
|
37232
|
+
const parsed = JSON.parse((0, import_node_fs46.readFileSync)((0, import_node_path44.join)(home, ".claude", "plugins", "installed_plugins.json"), "utf8"));
|
|
36954
37233
|
const record = parsed.plugins?.["mmi@mutmutco"]?.[0];
|
|
36955
37234
|
return record?.version ? { version: record.version, gitCommitSha: record.gitCommitSha } : void 0;
|
|
36956
37235
|
} catch {
|
|
@@ -37012,7 +37291,7 @@ async function runReleaseCatchup(home, env, deps, opts = {}) {
|
|
|
37012
37291
|
return { ok: false, detail: `${cliUpdateDetail}; plugin install record could not be cleared (still ${prior.version})` };
|
|
37013
37292
|
}
|
|
37014
37293
|
const installed = await deps.runClaude(["plugin", "install", "mmi@mutmutco"], "claude plugin install mmi@mutmutco");
|
|
37015
|
-
const payload = (0,
|
|
37294
|
+
const payload = (0, import_node_path44.join)(pluginCacheRoot(home), latest, ".pi-plugin");
|
|
37016
37295
|
if (!installed || !(0, import_node_fs46.existsSync)(payload)) {
|
|
37017
37296
|
const why = installed ? `install of ${latest} did not produce a verifiable .pi-plugin payload` : `install of ${latest} failed`;
|
|
37018
37297
|
if (!prior) return { ok: false, detail: `${cliUpdateDetail}; ${why}; no prior record to restore` };
|
|
@@ -38048,10 +38327,10 @@ function checkRepoWorktrees(probe) {
|
|
|
38048
38327
|
ok: !(probe.isOrgRepo && probe.hasRepoLocalWorktrees),
|
|
38049
38328
|
id: "repo-worktrees",
|
|
38050
38329
|
label: "repo worktrees",
|
|
38051
|
-
fix: "repo-local `.worktrees/`
|
|
38330
|
+
fix: "repo-local `.worktrees/` / `.claude/worktrees` are not the canonical path \u2014 use `mmi-cli worktree create <branch>` (sibling `../mmi-worktrees/<Repo>/`), then `mmi-cli worktree gc --apply` for abandoned helper trees",
|
|
38052
38331
|
verbose: [
|
|
38053
38332
|
`org repo: ${probe.isOrgRepo ? "yes" : "no"}`,
|
|
38054
|
-
`repo-local .worktrees/: ${probe.hasRepoLocalWorktrees ? "present" : "absent"}`
|
|
38333
|
+
`repo-local .worktrees/ or .claude/worktrees/: ${probe.hasRepoLocalWorktrees ? "present" : "absent"}`
|
|
38055
38334
|
]
|
|
38056
38335
|
};
|
|
38057
38336
|
}
|
|
@@ -39238,17 +39517,17 @@ function parseOriginRepo(remoteUrl) {
|
|
|
39238
39517
|
}
|
|
39239
39518
|
function ghHostsConfigPath(env, platform2) {
|
|
39240
39519
|
const sep3 = platform2 === "win32" ? "\\" : "/";
|
|
39241
|
-
const
|
|
39520
|
+
const join41 = (...parts) => parts.join(sep3);
|
|
39242
39521
|
const explicit = env.GH_CONFIG_DIR?.trim();
|
|
39243
|
-
if (explicit) return
|
|
39522
|
+
if (explicit) return join41(explicit, "hosts.yml");
|
|
39244
39523
|
if (platform2 === "win32") {
|
|
39245
39524
|
const appData = (env.AppData ?? env.APPDATA)?.trim();
|
|
39246
|
-
return appData ?
|
|
39525
|
+
return appData ? join41(appData, "GitHub CLI", "hosts.yml") : void 0;
|
|
39247
39526
|
}
|
|
39248
39527
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
39249
|
-
if (xdg) return
|
|
39528
|
+
if (xdg) return join41(xdg, "gh", "hosts.yml");
|
|
39250
39529
|
const home = env.HOME?.trim();
|
|
39251
|
-
return home ?
|
|
39530
|
+
return home ? join41(home, ".config", "gh", "hosts.yml") : void 0;
|
|
39252
39531
|
}
|
|
39253
39532
|
function parseGhHostsAccounts(yaml, host = "github.com") {
|
|
39254
39533
|
let hostIndent = null;
|
|
@@ -39299,8 +39578,8 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
39299
39578
|
|
|
39300
39579
|
// src/doctor-io.ts
|
|
39301
39580
|
var import_node_fs47 = require("node:fs");
|
|
39302
|
-
var
|
|
39303
|
-
var
|
|
39581
|
+
var import_node_os19 = require("node:os");
|
|
39582
|
+
var import_node_path45 = require("node:path");
|
|
39304
39583
|
var import_node_child_process19 = require("node:child_process");
|
|
39305
39584
|
var import_node_util8 = require("node:util");
|
|
39306
39585
|
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process19.execFile);
|
|
@@ -39308,7 +39587,7 @@ var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
|
39308
39587
|
function installedClaudePluginVersion() {
|
|
39309
39588
|
try {
|
|
39310
39589
|
const file = JSON.parse(
|
|
39311
|
-
(0, import_node_fs47.readFileSync)((0,
|
|
39590
|
+
(0, import_node_fs47.readFileSync)((0, import_node_path45.join)((0, import_node_os19.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
39312
39591
|
);
|
|
39313
39592
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
39314
39593
|
if (versions.length === 0) return void 0;
|
|
@@ -39329,22 +39608,22 @@ function installedSurfacePluginVersion(surface) {
|
|
|
39329
39608
|
const token = surfaceToken(surface);
|
|
39330
39609
|
if (token === "kilo") {
|
|
39331
39610
|
try {
|
|
39332
|
-
const stamp = (0, import_node_fs47.readFileSync)((0,
|
|
39611
|
+
const stamp = (0, import_node_fs47.readFileSync)((0, import_node_path45.join)((0, import_node_os19.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
|
|
39333
39612
|
return stamp || void 0;
|
|
39334
39613
|
} catch {
|
|
39335
39614
|
return void 0;
|
|
39336
39615
|
}
|
|
39337
39616
|
}
|
|
39338
39617
|
if (token === "cursor") {
|
|
39339
|
-
return manifestVersion((0,
|
|
39618
|
+
return manifestVersion((0, import_node_path45.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
|
|
39340
39619
|
}
|
|
39341
39620
|
if (token === "jervcode") {
|
|
39342
39621
|
const entry = mmiPiWrapperEntry();
|
|
39343
39622
|
if (!entry) return void 0;
|
|
39344
|
-
return manifestVersion((0,
|
|
39623
|
+
return manifestVersion((0, import_node_path45.join)(decodeURIComponent(entry.replace(/^file:\/\/\/?/, "")), "package.json"));
|
|
39345
39624
|
}
|
|
39346
39625
|
if (token === "kimi") {
|
|
39347
|
-
return manifestVersion((0,
|
|
39626
|
+
return manifestVersion((0, import_node_path45.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
|
|
39348
39627
|
}
|
|
39349
39628
|
if (token === "claude") return installedClaudePluginVersion();
|
|
39350
39629
|
if (token !== "codex") return void 0;
|
|
@@ -39382,7 +39661,7 @@ function worktreeRootSync() {
|
|
|
39382
39661
|
}
|
|
39383
39662
|
var gitignorePath = () => {
|
|
39384
39663
|
const root = worktreeRootSync();
|
|
39385
|
-
return root === null ? null : (0,
|
|
39664
|
+
return root === null ? null : (0, import_node_path45.join)(root, ".gitignore");
|
|
39386
39665
|
};
|
|
39387
39666
|
function readGitignore() {
|
|
39388
39667
|
const path2 = gitignorePath();
|
|
@@ -39416,7 +39695,7 @@ async function repoRoot() {
|
|
|
39416
39695
|
}
|
|
39417
39696
|
function hasRepoLocalWorktrees() {
|
|
39418
39697
|
const root = worktreeRootSync();
|
|
39419
|
-
return root !== null && (0, import_node_fs47.existsSync)((0,
|
|
39698
|
+
return root !== null && ((0, import_node_fs47.existsSync)((0, import_node_path45.join)(root, ".worktrees")) || (0, import_node_fs47.existsSync)((0, import_node_path45.join)(root, ".claude", "worktrees")));
|
|
39420
39699
|
}
|
|
39421
39700
|
|
|
39422
39701
|
// src/cross-repo-filing-issue.ts
|
|
@@ -39512,7 +39791,7 @@ function binaryOnPath(bin) {
|
|
|
39512
39791
|
for (const dir of pathEnvEntries(process.env.PATH ?? "")) {
|
|
39513
39792
|
for (const ext of exts) {
|
|
39514
39793
|
try {
|
|
39515
|
-
if ((0, import_node_fs48.existsSync)((0,
|
|
39794
|
+
if ((0, import_node_fs48.existsSync)((0, import_node_path46.join)(dir, `${bin}${ext}`))) return true;
|
|
39516
39795
|
} catch {
|
|
39517
39796
|
}
|
|
39518
39797
|
}
|
|
@@ -39543,12 +39822,12 @@ function ghMultiAccountCaveat(announcedLogin) {
|
|
|
39543
39822
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
39544
39823
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
39545
39824
|
function envHealLockPath(home) {
|
|
39546
|
-
return (0,
|
|
39825
|
+
return (0, import_node_path46.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
39547
39826
|
}
|
|
39548
39827
|
async function withEnvHealLock(what, run) {
|
|
39549
39828
|
try {
|
|
39550
39829
|
return await withFileLock(
|
|
39551
|
-
envHealLockPath((0,
|
|
39830
|
+
envHealLockPath((0, import_node_os20.homedir)()),
|
|
39552
39831
|
{ staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
|
|
39553
39832
|
run
|
|
39554
39833
|
);
|
|
@@ -39645,7 +39924,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39645
39924
|
const configRoot = surfaceConfigRoot(surface);
|
|
39646
39925
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
39647
39926
|
const plan = buildPluginCachePlan(
|
|
39648
|
-
(0,
|
|
39927
|
+
(0, import_node_os20.homedir)(),
|
|
39649
39928
|
running,
|
|
39650
39929
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
39651
39930
|
{ configRoot, includeStaging: surface !== "codex" }
|
|
@@ -39669,7 +39948,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39669
39948
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
39670
39949
|
const installed = installedActivePluginVersion(surface);
|
|
39671
39950
|
const plan = buildPluginCachePlan(
|
|
39672
|
-
(0,
|
|
39951
|
+
(0, import_node_os20.homedir)(),
|
|
39673
39952
|
running,
|
|
39674
39953
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
39675
39954
|
{ configRoot, includeStaging: surface !== "codex", installedVersion: installed }
|
|
@@ -39704,12 +39983,12 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39704
39983
|
piPluginState: () => {
|
|
39705
39984
|
const env = { ...process.env };
|
|
39706
39985
|
delete env.CLAUDE_PLUGIN_ROOT;
|
|
39707
|
-
return readPiPluginState((0,
|
|
39986
|
+
return readPiPluginState((0, import_node_os20.homedir)(), env);
|
|
39708
39987
|
},
|
|
39709
39988
|
healPiPlugin: () => {
|
|
39710
39989
|
const env = { ...process.env };
|
|
39711
39990
|
delete env.CLAUDE_PLUGIN_ROOT;
|
|
39712
|
-
return healPiPluginRegistration((0,
|
|
39991
|
+
return healPiPluginRegistration((0, import_node_os20.homedir)(), env);
|
|
39713
39992
|
},
|
|
39714
39993
|
// #4743: the global `claude` binary left as a ~500-byte placeholder by a self-update that died
|
|
39715
39994
|
// EBUSY mid-install. A local read (package.json + the first bytes of two files) — no npm spawn:
|
|
@@ -39748,7 +40027,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39748
40027
|
disableAutoUpdate: () => {
|
|
39749
40028
|
if (detectSurface(process.env) === "codex") return void 0;
|
|
39750
40029
|
return disableOrgMarketplaceBackgroundUpdates(
|
|
39751
|
-
(0,
|
|
40030
|
+
(0, import_node_path46.join)((0, import_node_os20.homedir)(), ...KNOWN_MARKETPLACES_RELATIVE),
|
|
39752
40031
|
[MMI_MARKETPLACE_NAME]
|
|
39753
40032
|
);
|
|
39754
40033
|
}
|
|
@@ -39762,7 +40041,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39762
40041
|
// adopted the generated routing index (MMG-Unlive: `docs/Archive/**` only, no index, no gate.yml) would
|
|
39763
40042
|
// get a permanent — demanding an artifact it never asked for.
|
|
39764
40043
|
docsIndexState: (root) => {
|
|
39765
|
-
if (!(0, import_node_fs48.existsSync)((0,
|
|
40044
|
+
if (!(0, import_node_fs48.existsSync)((0, import_node_path46.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
39766
40045
|
const real = createDocsIndexDeps(root);
|
|
39767
40046
|
let docs2;
|
|
39768
40047
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -39771,7 +40050,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39771
40050
|
},
|
|
39772
40051
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
39773
40052
|
healDocsIndex: (root) => {
|
|
39774
|
-
if (!(0, import_node_fs48.existsSync)((0,
|
|
40053
|
+
if (!(0, import_node_fs48.existsSync)((0, import_node_path46.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
39775
40054
|
const real = createDocsIndexDeps(root);
|
|
39776
40055
|
let docs2;
|
|
39777
40056
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -40137,7 +40416,7 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
40137
40416
|
});
|
|
40138
40417
|
var rules = program2.command("rules").description("org-managed .gitignore delivery");
|
|
40139
40418
|
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) => {
|
|
40140
|
-
const path2 = (0,
|
|
40419
|
+
const path2 = (0, import_node_path46.join)(process.cwd(), ".gitignore");
|
|
40141
40420
|
const current = (0, import_node_fs48.existsSync)(path2) ? (0, import_node_fs48.readFileSync)(path2, "utf8") : null;
|
|
40142
40421
|
const plan = planManagedGitignore(current);
|
|
40143
40422
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
@@ -40291,7 +40570,7 @@ gcCmd.command("sweep-deferred").description("retry IDE-locked deferred worktree
|
|
|
40291
40570
|
process.exit(process.exitCode ?? 0);
|
|
40292
40571
|
});
|
|
40293
40572
|
});
|
|
40294
|
-
gcCmd.option("--dry-run", "show what would be deleted (default)").option("--apply", "delete only the listed clean merged/closed PR local+remote branches, linked worktrees, and stale tracking refs").option("--json", "machine-readable output").option("--scratch", "prune safe local scratch (#1864) instead of git branches/refs; local plans are surfaced advisory-only, never auto-pruned").option("--remote <name>", "remote name", "origin").option("--limit <n>", "PRs to read PER BRANCH \u2014 an effort bound, not a correctness one: merge state is resolved per branch, so no branch is ever skipped for being old (#4227)", "200").option("--force", "remove worktrees even when the ownership registry shows another session created or worked in them recently (#3580), or when a dead worktree directory still holds leftover files (#4779)").option("--root <path>", "sweep an explicitly named worktrees root instead of the authoritative ../mmi-worktrees (#3471) \u2014 descends into this repo container when present; ownership, dead-dir, and content guards still apply").action(async (o) => {
|
|
40573
|
+
gcCmd.option("--dry-run", "show what would be deleted (default)").option("--apply", "delete only the listed clean merged/closed PR local+remote branches, linked worktrees, and stale tracking refs").option("--json", "machine-readable output").option("--scratch", "prune safe local scratch (#1864) instead of git branches/refs; local plans are surfaced advisory-only, never auto-pruned").option("--remote <name>", "remote name", "origin").option("--limit <n>", "PRs to read PER BRANCH \u2014 an effort bound, not a correctness one: merge state is resolved per branch, so no branch is ever skipped for being old (#4227)", "200").option("--force", "remove worktrees even when the ownership registry shows another session created or worked in them recently (#3580), or when a dead worktree directory still holds leftover files (#4779)").option("--root <path>", "sweep an explicitly named worktrees root instead of the authoritative ../mmi-worktrees (#3471) \u2014 descends into this repo container when present; ownership, dead-dir, and content guards still apply").option("--train-only", "explicit keep-only-train path: also reap closed-not-merged / no-PR origin leftovers; never deletes open PR heads (#4903)").action(async (o) => {
|
|
40295
40574
|
if (o.apply && o.dryRun) return fail("worktree gc: choose either --dry-run or --apply");
|
|
40296
40575
|
if (o.scratch) {
|
|
40297
40576
|
try {
|
|
@@ -40308,7 +40587,7 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
|
|
|
40308
40587
|
if (!Number.isFinite(limit) || limit < 1) return fail("worktree gc: --limit must be a positive integer");
|
|
40309
40588
|
let root;
|
|
40310
40589
|
if (o.root !== void 0) {
|
|
40311
|
-
root = (0,
|
|
40590
|
+
root = (0, import_node_path46.resolve)(o.root);
|
|
40312
40591
|
if (!(0, import_node_fs48.existsSync)(root) || !(0, import_node_fs48.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
|
|
40313
40592
|
const gcRepoRoot = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
40314
40593
|
if (isPathUnderDirectory2(gcRepoRoot, root)) {
|
|
@@ -40316,10 +40595,11 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
|
|
|
40316
40595
|
}
|
|
40317
40596
|
}
|
|
40318
40597
|
try {
|
|
40319
|
-
const plan = await gcPlan(o.remote, limit, { root });
|
|
40598
|
+
const plan = await gcPlan(o.remote, limit, { root, trainOnly: o.trainOnly });
|
|
40599
|
+
const auditedClone = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
40320
40600
|
if (root && !o.json) console.log(`worktree gc: scanning the explicitly named root ${root} for worktrees proven to belong to this repo
|
|
40321
40601
|
`);
|
|
40322
|
-
if (o.apply && !o.json) console.log(formatGcPlan(plan, true));
|
|
40602
|
+
if (o.apply && !o.json) console.log(formatGcPlan(plan, true, auditedClone));
|
|
40323
40603
|
let applyResult;
|
|
40324
40604
|
if (o.apply) {
|
|
40325
40605
|
const deferredStore = await createDeferredWorktreeStore();
|
|
@@ -40338,7 +40618,7 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
|
|
|
40338
40618
|
if (o.json) {
|
|
40339
40619
|
console.log(JSON.stringify({ dryRun: !o.apply, remote: o.remote, ...root ? { root } : {}, plan, applyResult }, null, 2));
|
|
40340
40620
|
} else if (!o.apply) {
|
|
40341
|
-
console.log(formatGcPlan(plan, false));
|
|
40621
|
+
console.log(formatGcPlan(plan, false, auditedClone));
|
|
40342
40622
|
} else {
|
|
40343
40623
|
if (applyResult) console.log(`
|
|
40344
40624
|
${renderGcApplyResult(applyResult, plan.skipped)}`);
|
|
@@ -40402,7 +40682,8 @@ async function currentWorktreeRemovalContext(command, force) {
|
|
|
40402
40682
|
actor: describeActor({ env: process.env, surface: detectSurface(process.env), cwd }),
|
|
40403
40683
|
command,
|
|
40404
40684
|
...force ? { force: true } : {},
|
|
40405
|
-
...activeWorkspaceRoot ? { activeWorkspaceRoot } : {}
|
|
40685
|
+
...activeWorkspaceRoot ? { activeWorkspaceRoot } : {},
|
|
40686
|
+
cursorAgentHost: isCursorAgentHost()
|
|
40406
40687
|
};
|
|
40407
40688
|
}
|
|
40408
40689
|
async function unprovenWorktreeReason(wtPath, repoRoot2) {
|
|
@@ -40450,7 +40731,7 @@ function acquireWorktreeSetupLock(worktreeRoot) {
|
|
|
40450
40731
|
};
|
|
40451
40732
|
};
|
|
40452
40733
|
try {
|
|
40453
|
-
(0, import_node_fs48.mkdirSync)((0,
|
|
40734
|
+
(0, import_node_fs48.mkdirSync)((0, import_node_path46.dirname)(lockPath), { recursive: true });
|
|
40454
40735
|
return take();
|
|
40455
40736
|
} catch {
|
|
40456
40737
|
try {
|
|
@@ -42136,11 +42417,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
|
|
|
42136
42417
|
}
|
|
42137
42418
|
});
|
|
42138
42419
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
42139
|
-
const wfDir = (0,
|
|
42420
|
+
const wfDir = (0, import_node_path46.join)(cwd, ".github", "workflows");
|
|
42140
42421
|
if (!(0, import_node_fs48.existsSync)(wfDir)) return [];
|
|
42141
42422
|
return (0, import_node_fs48.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
42142
42423
|
try {
|
|
42143
|
-
return workflowReportsPrChecks((0, import_node_fs48.readFileSync)((0,
|
|
42424
|
+
return workflowReportsPrChecks((0, import_node_fs48.readFileSync)((0, import_node_path46.join)(wfDir, name), "utf8"));
|
|
42144
42425
|
} catch {
|
|
42145
42426
|
return true;
|
|
42146
42427
|
}
|
|
@@ -42192,16 +42473,16 @@ function ciAuditDeps() {
|
|
|
42192
42473
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
42193
42474
|
readSeedFile: (path2) => {
|
|
42194
42475
|
if (!root) return null;
|
|
42195
|
-
const fullPath = (0,
|
|
42476
|
+
const fullPath = (0, import_node_path46.join)(root, path2);
|
|
42196
42477
|
return (0, import_node_fs48.existsSync)(fullPath) ? (0, import_node_fs48.readFileSync)(fullPath, "utf8") : null;
|
|
42197
42478
|
}
|
|
42198
42479
|
};
|
|
42199
42480
|
}
|
|
42200
42481
|
function hubRoot() {
|
|
42201
|
-
const fromPkg = (0,
|
|
42482
|
+
const fromPkg = (0, import_node_path46.join)(__dirname, "..", "..");
|
|
42202
42483
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
42203
|
-
if ((0, import_node_fs48.existsSync)((0,
|
|
42204
|
-
if ((0, import_node_fs48.existsSync)((0,
|
|
42484
|
+
if ((0, import_node_fs48.existsSync)((0, import_node_path46.join)(fromPkg, marker))) return fromPkg;
|
|
42485
|
+
if ((0, import_node_fs48.existsSync)((0, import_node_path46.join)(process.cwd(), marker))) return process.cwd();
|
|
42205
42486
|
return null;
|
|
42206
42487
|
}
|
|
42207
42488
|
async function waitLoopCorePool(label) {
|
|
@@ -43257,7 +43538,7 @@ function directoryBytes(path2) {
|
|
|
43257
43538
|
return 0;
|
|
43258
43539
|
}
|
|
43259
43540
|
for (const entry of entries) {
|
|
43260
|
-
const child2 = (0,
|
|
43541
|
+
const child2 = (0, import_node_path46.join)(path2, entry.name);
|
|
43261
43542
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
43262
43543
|
else {
|
|
43263
43544
|
try {
|
|
@@ -43287,7 +43568,7 @@ function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
|
43287
43568
|
dirBytes,
|
|
43288
43569
|
listStagingDirs: (root) => (0, import_node_fs48.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
43289
43570
|
try {
|
|
43290
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
43571
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path46.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs48.statSync)(p).mtimeMs) };
|
|
43291
43572
|
} catch {
|
|
43292
43573
|
return { name: d.name, mtimeMs: Date.now() };
|
|
43293
43574
|
}
|
|
@@ -43301,7 +43582,7 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
43301
43582
|
return {
|
|
43302
43583
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
43303
43584
|
mtimeMs: (name) => {
|
|
43304
|
-
const p = (0,
|
|
43585
|
+
const p = (0, import_node_path46.join)(stagingRoot, name);
|
|
43305
43586
|
if (!(0, import_node_fs48.existsSync)(p)) return null;
|
|
43306
43587
|
try {
|
|
43307
43588
|
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs48.statSync)(q).mtimeMs);
|
|
@@ -43324,7 +43605,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
43324
43605
|
return;
|
|
43325
43606
|
}
|
|
43326
43607
|
const plan = buildPluginCachePlan(
|
|
43327
|
-
(0,
|
|
43608
|
+
(0, import_node_os20.homedir)(),
|
|
43328
43609
|
running,
|
|
43329
43610
|
pluginCacheFsDeps(configRoot, directoryBytes),
|
|
43330
43611
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
@@ -43346,7 +43627,7 @@ function readReleaseCatchupState(path2) {
|
|
|
43346
43627
|
}
|
|
43347
43628
|
function writeReleaseCatchupState(path2, state) {
|
|
43348
43629
|
try {
|
|
43349
|
-
(0, import_node_fs48.mkdirSync)((0,
|
|
43630
|
+
(0, import_node_fs48.mkdirSync)((0, import_node_path46.dirname)(path2), { recursive: true });
|
|
43350
43631
|
(0, import_node_fs48.writeFileSync)(path2, `${JSON.stringify(state)}
|
|
43351
43632
|
`);
|
|
43352
43633
|
} catch {
|
|
@@ -43355,7 +43636,7 @@ function writeReleaseCatchupState(path2, state) {
|
|
|
43355
43636
|
program2.command("plugin-release-catchup").description("install a newer released MMI plugin clone non-destructively and re-point the Pi registration (#4297); TTL-gated no-op when current").option("--force", "skip the 24h TTL (acceptance proof / manual run)").option("--quiet", "print only failures (the detached session-start lane)").option("--json", "machine-readable output").action(async (o) => {
|
|
43356
43637
|
const outcome = await withEnvHealLock(
|
|
43357
43638
|
"plugin release catch-up",
|
|
43358
|
-
() => runReleaseCatchup((0,
|
|
43639
|
+
() => runReleaseCatchup((0, import_node_os20.homedir)(), process.env, {
|
|
43359
43640
|
fetchReleased: fetchNpmReleasedVersion,
|
|
43360
43641
|
runClaude: (args, step) => runPluginCli("claude", args, (msg) => {
|
|
43361
43642
|
if (!o.quiet && !o.json) console.log(msg);
|
|
@@ -43371,7 +43652,7 @@ program2.command("plugin-release-catchup").description("install a newer released
|
|
|
43371
43652
|
},
|
|
43372
43653
|
readState: readReleaseCatchupState,
|
|
43373
43654
|
writeState: writeReleaseCatchupState,
|
|
43374
|
-
healRegistration: defaultRegistrationHeal((0,
|
|
43655
|
+
healRegistration: defaultRegistrationHeal((0, import_node_os20.homedir)(), process.env),
|
|
43375
43656
|
now: () => Date.now()
|
|
43376
43657
|
}, { force: o.force })
|
|
43377
43658
|
);
|
|
@@ -43439,7 +43720,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
|
|
|
43439
43720
|
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process20.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
43440
43721
|
bannerIo.log(worktreeBanner);
|
|
43441
43722
|
}
|
|
43442
|
-
if (shouldSpawnReleaseCatchup((0,
|
|
43723
|
+
if (shouldSpawnReleaseCatchup((0, import_node_os20.homedir)(), process.env, readReleaseCatchupState)) {
|
|
43443
43724
|
spawnDetachedSelf(["plugin", "release-catchup", "--quiet"], { spawn: import_node_child_process20.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
43444
43725
|
}
|
|
43445
43726
|
if (isLinkedWorktree(process.cwd())) {
|