@mutmutco/cli 3.105.10 → 3.105.12
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 +1037 -558
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -5000,7 +5000,7 @@ var program = new Command();
|
|
|
5000
5000
|
|
|
5001
5001
|
// src/index.ts
|
|
5002
5002
|
var import_promises11 = require("node:fs/promises");
|
|
5003
|
-
var
|
|
5003
|
+
var import_node_fs43 = require("node:fs");
|
|
5004
5004
|
var import_node_child_process19 = require("node:child_process");
|
|
5005
5005
|
|
|
5006
5006
|
// src/cli-shared.ts
|
|
@@ -6517,12 +6517,148 @@ function localTrainSyncBannerLine(result) {
|
|
|
6517
6517
|
|
|
6518
6518
|
// src/gc.ts
|
|
6519
6519
|
var import_promises = require("node:fs/promises");
|
|
6520
|
-
var
|
|
6520
|
+
var import_node_path9 = require("node:path");
|
|
6521
6521
|
|
|
6522
|
-
// src/
|
|
6522
|
+
// src/active-workspace-root.ts
|
|
6523
6523
|
var import_node_fs9 = require("node:fs");
|
|
6524
6524
|
var import_node_os3 = require("node:os");
|
|
6525
6525
|
var import_node_path7 = require("node:path");
|
|
6526
|
+
var ACTIVE_WORKSPACE_ROOT_ENV = "MMI_ACTIVE_WORKSPACE_ROOT";
|
|
6527
|
+
function normPath(p, platform2 = process.platform) {
|
|
6528
|
+
const unified = p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
6529
|
+
return platform2 === "win32" || platform2 === "darwin" ? unified.toLowerCase() : unified;
|
|
6530
|
+
}
|
|
6531
|
+
function isPathUnderDirectory(childPath, parentPath, platform2 = process.platform) {
|
|
6532
|
+
const child2 = normPath(childPath, platform2);
|
|
6533
|
+
const parent = normPath(parentPath, platform2);
|
|
6534
|
+
if (!child2 || !parent) return false;
|
|
6535
|
+
if (child2 === parent) return true;
|
|
6536
|
+
return child2.startsWith(`${parent}/`);
|
|
6537
|
+
}
|
|
6538
|
+
function normalizeActiveWorkspaceRoot(value, opts = {}) {
|
|
6539
|
+
const raw = value?.trim();
|
|
6540
|
+
if (!raw) return void 0;
|
|
6541
|
+
if (!(0, import_node_path7.isAbsolute)(raw)) return void 0;
|
|
6542
|
+
const exists = opts.exists ?? import_node_fs9.existsSync;
|
|
6543
|
+
if (!exists(raw)) return void 0;
|
|
6544
|
+
return normPath(raw, opts.platform);
|
|
6545
|
+
}
|
|
6546
|
+
function removalTouchesActiveWorkspace(targetPath, activeWorkspaceRoot, platform2 = process.platform) {
|
|
6547
|
+
const target = normPath(targetPath, platform2);
|
|
6548
|
+
const root = normPath(activeWorkspaceRoot, platform2);
|
|
6549
|
+
if (!target || !root) return false;
|
|
6550
|
+
return isPathUnderDirectory(root, target, platform2);
|
|
6551
|
+
}
|
|
6552
|
+
function activeWorkspaceRefusalMessage(targetPath, activeWorkspaceRoot) {
|
|
6553
|
+
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\`.`;
|
|
6554
|
+
}
|
|
6555
|
+
function decideActiveWorkspaceGuard(targetPath, activeWorkspaceRoot, platform2 = process.platform) {
|
|
6556
|
+
if (!activeWorkspaceRoot) return { action: "proceed" };
|
|
6557
|
+
if (!removalTouchesActiveWorkspace(targetPath, activeWorkspaceRoot, platform2)) {
|
|
6558
|
+
return { action: "proceed" };
|
|
6559
|
+
}
|
|
6560
|
+
return {
|
|
6561
|
+
action: "refuse",
|
|
6562
|
+
reason: "active-workspace",
|
|
6563
|
+
activeWorkspaceRoot,
|
|
6564
|
+
message: activeWorkspaceRefusalMessage(targetPath, activeWorkspaceRoot)
|
|
6565
|
+
};
|
|
6566
|
+
}
|
|
6567
|
+
function cursorProjectSlugFromAgentTranscripts(agentTranscripts) {
|
|
6568
|
+
const raw = agentTranscripts?.trim();
|
|
6569
|
+
if (!raw) return void 0;
|
|
6570
|
+
const unified = raw.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
6571
|
+
const parts = unified.split("/");
|
|
6572
|
+
const idx = parts.lastIndexOf("agent-transcripts");
|
|
6573
|
+
if (idx <= 0) return void 0;
|
|
6574
|
+
const slug = parts[idx - 1]?.trim();
|
|
6575
|
+
return slug || void 0;
|
|
6576
|
+
}
|
|
6577
|
+
function cursorProjectSlugFromFolderPath(folderPath, platform2 = process.platform) {
|
|
6578
|
+
let unified = folderPath.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
6579
|
+
if (platform2 === "win32") {
|
|
6580
|
+
unified = unified.replace(/^\/+/, "");
|
|
6581
|
+
} else if (unified.startsWith("/")) {
|
|
6582
|
+
unified = unified.slice(1);
|
|
6583
|
+
}
|
|
6584
|
+
return unified.replace(/\//g, "-");
|
|
6585
|
+
}
|
|
6586
|
+
function cursorWorkspaceStorageRoot(platform2 = process.platform, home = (0, import_node_os3.homedir)()) {
|
|
6587
|
+
if (platform2 === "darwin") {
|
|
6588
|
+
return (0, import_node_path7.join)(home, "Library", "Application Support", "Cursor", "User", "workspaceStorage");
|
|
6589
|
+
}
|
|
6590
|
+
if (platform2 === "win32") {
|
|
6591
|
+
return (0, import_node_path7.join)(home, "AppData", "Roaming", "Cursor", "User", "workspaceStorage");
|
|
6592
|
+
}
|
|
6593
|
+
return (0, import_node_path7.join)(home, ".config", "Cursor", "User", "workspaceStorage");
|
|
6594
|
+
}
|
|
6595
|
+
function folderUriToPath(uri) {
|
|
6596
|
+
const trimmed = uri.trim();
|
|
6597
|
+
if (!trimmed.startsWith("file://")) return void 0;
|
|
6598
|
+
let rest = trimmed.slice("file://".length);
|
|
6599
|
+
if (/^\/[A-Za-z]:\//.test(rest)) rest = rest.slice(1);
|
|
6600
|
+
try {
|
|
6601
|
+
return decodeURIComponent(rest);
|
|
6602
|
+
} catch {
|
|
6603
|
+
return rest;
|
|
6604
|
+
}
|
|
6605
|
+
}
|
|
6606
|
+
function resolveCursorAgentWorkspaceRoot(deps = {}) {
|
|
6607
|
+
const env = deps.env ?? process.env;
|
|
6608
|
+
const platform2 = deps.platform ?? process.platform;
|
|
6609
|
+
const exists = deps.exists ?? import_node_fs9.existsSync;
|
|
6610
|
+
const readTextFile = deps.readTextFile ?? ((path2) => {
|
|
6611
|
+
try {
|
|
6612
|
+
return (0, import_node_fs9.readFileSync)(path2, "utf8");
|
|
6613
|
+
} catch {
|
|
6614
|
+
return void 0;
|
|
6615
|
+
}
|
|
6616
|
+
});
|
|
6617
|
+
const listDir = deps.listDir ?? ((path2) => {
|
|
6618
|
+
try {
|
|
6619
|
+
return (0, import_node_fs9.readdirSync)(path2);
|
|
6620
|
+
} catch {
|
|
6621
|
+
return [];
|
|
6622
|
+
}
|
|
6623
|
+
});
|
|
6624
|
+
const slug = cursorProjectSlugFromAgentTranscripts(env.AGENT_TRANSCRIPTS);
|
|
6625
|
+
if (!slug) return void 0;
|
|
6626
|
+
const storageRoot = cursorWorkspaceStorageRoot(platform2, (deps.homedir ?? import_node_os3.homedir)());
|
|
6627
|
+
if (!exists(storageRoot)) return void 0;
|
|
6628
|
+
const matches = /* @__PURE__ */ new Set();
|
|
6629
|
+
for (const entry of listDir(storageRoot)) {
|
|
6630
|
+
const text = readTextFile((0, import_node_path7.join)(storageRoot, entry, "workspace.json"));
|
|
6631
|
+
if (!text) continue;
|
|
6632
|
+
let parsed;
|
|
6633
|
+
try {
|
|
6634
|
+
parsed = JSON.parse(text.replace(/^\uFEFF/, ""));
|
|
6635
|
+
} catch {
|
|
6636
|
+
continue;
|
|
6637
|
+
}
|
|
6638
|
+
if (typeof parsed.folder !== "string") continue;
|
|
6639
|
+
const folderPath = folderUriToPath(parsed.folder);
|
|
6640
|
+
if (!folderPath || !exists(folderPath)) continue;
|
|
6641
|
+
if (cursorProjectSlugFromFolderPath(folderPath, platform2) !== slug) continue;
|
|
6642
|
+
matches.add(normPath(folderPath, platform2));
|
|
6643
|
+
}
|
|
6644
|
+
if (matches.size !== 1) return void 0;
|
|
6645
|
+
return [...matches][0];
|
|
6646
|
+
}
|
|
6647
|
+
function resolveActiveWorkspaceRoot(deps = {}) {
|
|
6648
|
+
const env = deps.env ?? process.env;
|
|
6649
|
+
const platform2 = deps.platform ?? process.platform;
|
|
6650
|
+
const exists = deps.exists ?? import_node_fs9.existsSync;
|
|
6651
|
+
const fromEnv = normalizeActiveWorkspaceRoot(env[ACTIVE_WORKSPACE_ROOT_ENV], { exists, platform: platform2 });
|
|
6652
|
+
if (fromEnv) return fromEnv;
|
|
6653
|
+
const cursorAgent = env.CURSOR_AGENT === "1" || Boolean(env.CURSOR_EXTENSION_HOST_ROLE?.trim());
|
|
6654
|
+
if (!cursorAgent && !env.AGENT_TRANSCRIPTS?.trim()) return void 0;
|
|
6655
|
+
return resolveCursorAgentWorkspaceRoot(deps);
|
|
6656
|
+
}
|
|
6657
|
+
|
|
6658
|
+
// src/worktree-ownership.ts
|
|
6659
|
+
var import_node_fs10 = require("node:fs");
|
|
6660
|
+
var import_node_os4 = require("node:os");
|
|
6661
|
+
var import_node_path8 = require("node:path");
|
|
6526
6662
|
var OWNERS_FILE = "worktree-owners.json";
|
|
6527
6663
|
var EVENTS_FILE = "worktree-events.jsonl";
|
|
6528
6664
|
var WORKTREE_ACTIVITY_WINDOW_MS = 30 * 6e4;
|
|
@@ -6546,7 +6682,7 @@ function describeActor(input) {
|
|
|
6546
6682
|
const session = readSessionId(input.env);
|
|
6547
6683
|
return {
|
|
6548
6684
|
surface: input.surface,
|
|
6549
|
-
host: input.host ?? (0,
|
|
6685
|
+
host: input.host ?? (0, import_node_os4.hostname)(),
|
|
6550
6686
|
pid: input.pid ?? process.pid,
|
|
6551
6687
|
cwd: input.cwd,
|
|
6552
6688
|
...session ? { session } : {}
|
|
@@ -6627,7 +6763,7 @@ function decideWorktreeRemoval(input) {
|
|
|
6627
6763
|
}
|
|
6628
6764
|
function readFreshWorktreeLease(path2, now) {
|
|
6629
6765
|
try {
|
|
6630
|
-
const candidate = JSON.parse((0,
|
|
6766
|
+
const candidate = JSON.parse((0, import_node_fs10.readFileSync)((0, import_node_path8.join)(path2, WORKTREE_LEASE_MARKER), "utf8"));
|
|
6631
6767
|
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return void 0;
|
|
6632
6768
|
const lease = candidate;
|
|
6633
6769
|
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;
|
|
@@ -6646,7 +6782,7 @@ function describeActorShort(actor) {
|
|
|
6646
6782
|
}
|
|
6647
6783
|
function readOwners(primaryRoot) {
|
|
6648
6784
|
try {
|
|
6649
|
-
return parseWorktreeOwners((0,
|
|
6785
|
+
return parseWorktreeOwners((0, import_node_fs10.readFileSync)(worktreeOwnersPath(primaryRoot), "utf8"));
|
|
6650
6786
|
} catch {
|
|
6651
6787
|
return [];
|
|
6652
6788
|
}
|
|
@@ -6654,8 +6790,8 @@ function readOwners(primaryRoot) {
|
|
|
6654
6790
|
function writeOwners(primaryRoot, entries) {
|
|
6655
6791
|
try {
|
|
6656
6792
|
const path2 = worktreeOwnersPath(primaryRoot);
|
|
6657
|
-
(0,
|
|
6658
|
-
(0,
|
|
6793
|
+
(0, import_node_fs10.mkdirSync)((0, import_node_path8.dirname)(path2), { recursive: true });
|
|
6794
|
+
(0, import_node_fs10.writeFileSync)(path2, serializeWorktreeOwners(entries), "utf8");
|
|
6659
6795
|
} catch {
|
|
6660
6796
|
}
|
|
6661
6797
|
}
|
|
@@ -6690,8 +6826,8 @@ function dropWorktreeOwner(primaryRoot, path2, expectedCreatedAt) {
|
|
|
6690
6826
|
function appendWorktreeEvent(primaryRoot, event) {
|
|
6691
6827
|
try {
|
|
6692
6828
|
const path2 = worktreeEventsPath(primaryRoot);
|
|
6693
|
-
(0,
|
|
6694
|
-
(0,
|
|
6829
|
+
(0, import_node_fs10.mkdirSync)((0, import_node_path8.dirname)(path2), { recursive: true });
|
|
6830
|
+
(0, import_node_fs10.appendFileSync)(path2, `${JSON.stringify({ at: event.at ?? (/* @__PURE__ */ new Date()).toISOString(), ...event })}
|
|
6695
6831
|
`, "utf8");
|
|
6696
6832
|
} catch {
|
|
6697
6833
|
}
|
|
@@ -6708,7 +6844,7 @@ function recordWorktreeRemoval(primaryRoot, event) {
|
|
|
6708
6844
|
function readWorktreeEvents(primaryRoot, limit = 50) {
|
|
6709
6845
|
let text;
|
|
6710
6846
|
try {
|
|
6711
|
-
text = (0,
|
|
6847
|
+
text = (0, import_node_fs10.readFileSync)(worktreeEventsPath(primaryRoot), "utf8");
|
|
6712
6848
|
} catch {
|
|
6713
6849
|
return [];
|
|
6714
6850
|
}
|
|
@@ -6805,7 +6941,12 @@ async function isCommitUnreferenced(oid, git2) {
|
|
|
6805
6941
|
return refs !== void 0 && !refs.trim();
|
|
6806
6942
|
}
|
|
6807
6943
|
var DEFERRED_SWEEP_COMMAND = "mmi-cli worktree gc sweep-deferred";
|
|
6808
|
-
|
|
6944
|
+
function deferredNoteFor(reason) {
|
|
6945
|
+
if (reason === "active-workspace") {
|
|
6946
|
+
return "Worktree cleanup queued (active Cursor workspace). Open the primary checkout in Cursor first \u2014 then detached sweep / `worktree gc sweep-deferred` can remove it.";
|
|
6947
|
+
}
|
|
6948
|
+
return "Worktree cleanup queued (IDE lock). Detached sweep will retry automatically \u2014 no human action required.";
|
|
6949
|
+
}
|
|
6809
6950
|
var PRESERVED_WORKTREE_CONFIG = "mmi.preservedWorktreeBranch";
|
|
6810
6951
|
var WORKTREE_LOCK_RE = /EPERM|EBUSY|EACCES|ENOTEMPTY|permission denied|access is denied|used by another process|resource busy|directory not empty/i;
|
|
6811
6952
|
function isWorktreeLockError(error) {
|
|
@@ -6821,14 +6962,14 @@ function deferredWorktreesRegistryPath(gitDir) {
|
|
|
6821
6962
|
function parseDeferredWorktreesFile(text) {
|
|
6822
6963
|
const parsed = JSON.parse(text.replace(/^\uFEFF/, ""));
|
|
6823
6964
|
if (!parsed || !Array.isArray(parsed.entries)) return [];
|
|
6824
|
-
return parsed.entries.filter((e) => Boolean(e) && typeof e === "object" && typeof e.path === "string" && typeof e.branch === "string" && e.reason === "lock-held").map((e) => ({ ...e, registeredAt: e.registeredAt || (/* @__PURE__ */ new Date(0)).toISOString() }));
|
|
6965
|
+
return parsed.entries.filter((e) => Boolean(e) && typeof e === "object" && typeof e.path === "string" && typeof e.branch === "string" && (e.reason === "lock-held" || e.reason === "active-workspace")).map((e) => ({ ...e, registeredAt: e.registeredAt || (/* @__PURE__ */ new Date(0)).toISOString() }));
|
|
6825
6966
|
}
|
|
6826
6967
|
function serializeDeferredWorktrees(entries) {
|
|
6827
6968
|
return `${JSON.stringify({ entries }, null, 2)}
|
|
6828
6969
|
`;
|
|
6829
6970
|
}
|
|
6830
6971
|
function deferredPathKey(path2) {
|
|
6831
|
-
return
|
|
6972
|
+
return normPath2(path2);
|
|
6832
6973
|
}
|
|
6833
6974
|
function isPersistentWorktreeLockFailure(outcome) {
|
|
6834
6975
|
return outcome.status === "failed" && isWorktreeLockError(outcome.error);
|
|
@@ -6838,7 +6979,7 @@ async function registerDeferredWorktree(store, entry) {
|
|
|
6838
6979
|
const built = {
|
|
6839
6980
|
...entry,
|
|
6840
6981
|
registeredAt: entry.registeredAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
6841
|
-
reason: "lock-held"
|
|
6982
|
+
reason: entry.reason ?? "lock-held"
|
|
6842
6983
|
};
|
|
6843
6984
|
let newlyRegistered = false;
|
|
6844
6985
|
const mutate = (existing2) => {
|
|
@@ -6879,6 +7020,21 @@ async function sweepDeferredWorktrees(store, deps, removalContext) {
|
|
|
6879
7020
|
}
|
|
6880
7021
|
const owner = removalContext ? lookupWorktreeOwner(removalContext.primaryRoot, entry.path) : void 0;
|
|
6881
7022
|
if (removalContext) {
|
|
7023
|
+
const activeRoot = removalContext.activeWorkspaceRoot ?? resolveActiveWorkspaceRoot();
|
|
7024
|
+
const activeGuard = decideActiveWorkspaceGuard(entry.path, activeRoot);
|
|
7025
|
+
if (activeGuard.action === "refuse") {
|
|
7026
|
+
stillDeferred.push({ ...entry, reason: "active-workspace" });
|
|
7027
|
+
recordWorktreeRemoval(removalContext.primaryRoot, {
|
|
7028
|
+
action: "refused",
|
|
7029
|
+
command: removalContext.command,
|
|
7030
|
+
target: entry.path,
|
|
7031
|
+
branch: entry.branch,
|
|
7032
|
+
actor: removalContext.actor,
|
|
7033
|
+
owner,
|
|
7034
|
+
reason: activeGuard.message
|
|
7035
|
+
});
|
|
7036
|
+
continue;
|
|
7037
|
+
}
|
|
6882
7038
|
const verdict = decideWorktreeRemoval({
|
|
6883
7039
|
path: entry.path,
|
|
6884
7040
|
owner,
|
|
@@ -7002,10 +7158,10 @@ function baseName(p) {
|
|
|
7002
7158
|
return p.replace(/[/\\]+$/, "").split(/[/\\]/).pop() ?? "";
|
|
7003
7159
|
}
|
|
7004
7160
|
function assertSafeWorktreeTarget(worktreePath, primaryCheckout) {
|
|
7005
|
-
const wt =
|
|
7161
|
+
const wt = normPath2(worktreePath);
|
|
7006
7162
|
if (!wt) throw new Error("worktree teardown refused: empty target path");
|
|
7007
7163
|
if (!primaryCheckout) return;
|
|
7008
|
-
const primary =
|
|
7164
|
+
const primary = normPath2(primaryCheckout);
|
|
7009
7165
|
if (wt === primary) throw new Error(`worktree teardown refused: target is the primary checkout (${worktreePath})`);
|
|
7010
7166
|
if (primary.startsWith(`${wt}/`)) {
|
|
7011
7167
|
throw new Error(`worktree teardown refused: target ${worktreePath} contains the primary checkout`);
|
|
@@ -7161,7 +7317,7 @@ function resolveSafeSiblingWorktreeCleanupTarget(worktreePath, siblingRoot, deps
|
|
|
7161
7317
|
} catch {
|
|
7162
7318
|
return { ok: false, reason: "sibling root could not be resolved" };
|
|
7163
7319
|
}
|
|
7164
|
-
if (!
|
|
7320
|
+
if (!isPathUnderDirectory2(rootReal, siblingRoot) || !isPathUnderDirectory2(siblingRoot, rootReal)) {
|
|
7165
7321
|
return { ok: false, reason: "sibling root resolves outside expected path" };
|
|
7166
7322
|
}
|
|
7167
7323
|
try {
|
|
@@ -7169,26 +7325,26 @@ function resolveSafeSiblingWorktreeCleanupTarget(worktreePath, siblingRoot, deps
|
|
|
7169
7325
|
} catch {
|
|
7170
7326
|
return { ok: false, reason: "worktree path could not be resolved" };
|
|
7171
7327
|
}
|
|
7172
|
-
if (!
|
|
7328
|
+
if (!isPathUnderDirectory2(worktreeReal, rootReal)) {
|
|
7173
7329
|
return { ok: false, reason: "resolved worktree path outside sibling root" };
|
|
7174
7330
|
}
|
|
7175
7331
|
return { ok: true, path: worktreePath };
|
|
7176
7332
|
}
|
|
7177
7333
|
function siblingMmiWorktreesRoot(repoRoot2) {
|
|
7178
|
-
const parent = (0,
|
|
7179
|
-
if ((0,
|
|
7180
|
-
const grandparent = (0,
|
|
7181
|
-
if ((0,
|
|
7182
|
-
return (0,
|
|
7334
|
+
const parent = (0, import_node_path9.dirname)(repoRoot2);
|
|
7335
|
+
if ((0, import_node_path9.basename)(parent).toLowerCase() === "mmi-worktrees") return parent;
|
|
7336
|
+
const grandparent = (0, import_node_path9.dirname)(parent);
|
|
7337
|
+
if ((0, import_node_path9.basename)(grandparent).toLowerCase() === "mmi-worktrees") return grandparent;
|
|
7338
|
+
return (0, import_node_path9.join)(parent, "mmi-worktrees");
|
|
7183
7339
|
}
|
|
7184
7340
|
function worktreeScanDirs(root, repoRoot2, listDirs, isRepoCheckout) {
|
|
7185
|
-
const projectsDir = (0,
|
|
7186
|
-
const ownName = (0,
|
|
7341
|
+
const projectsDir = (0, import_node_path9.dirname)(root);
|
|
7342
|
+
const ownName = (0, import_node_path9.basename)(repoRoot2).toLowerCase();
|
|
7187
7343
|
const flat = [];
|
|
7188
7344
|
let ownContainer = null;
|
|
7189
7345
|
for (const dir of listDirs(root)) {
|
|
7190
|
-
const name = (0,
|
|
7191
|
-
if (isRepoCheckout((0,
|
|
7346
|
+
const name = (0, import_node_path9.basename)(dir);
|
|
7347
|
+
if (isRepoCheckout((0, import_node_path9.join)(projectsDir, name))) {
|
|
7192
7348
|
if (name.toLowerCase() === ownName) ownContainer = dir;
|
|
7193
7349
|
continue;
|
|
7194
7350
|
}
|
|
@@ -7197,9 +7353,9 @@ function worktreeScanDirs(root, repoRoot2, listDirs, isRepoCheckout) {
|
|
|
7197
7353
|
return ownContainer ? [...flat, ...listDirs(ownContainer)] : flat;
|
|
7198
7354
|
}
|
|
7199
7355
|
function explicitRepoWorktreesRoot(root, repoRoot2, rootDirs) {
|
|
7200
|
-
const repoName = (0,
|
|
7356
|
+
const repoName = (0, import_node_path9.basename)(repoRoot2);
|
|
7201
7357
|
const repoDir = rootDirs.find((name) => name.toLowerCase() === repoName.toLowerCase());
|
|
7202
|
-
return repoDir ? (0,
|
|
7358
|
+
return repoDir ? (0, import_node_path9.join)(root, repoDir) : root;
|
|
7203
7359
|
}
|
|
7204
7360
|
function classifySiblingWorktreeDir(entry) {
|
|
7205
7361
|
if (!entry.ownedByCurrentRepo) {
|
|
@@ -7475,12 +7631,12 @@ function parseWorktreePorcelain(stdout) {
|
|
|
7475
7631
|
function pathsAreCaseInsensitive(platform2 = process.platform) {
|
|
7476
7632
|
return platform2 === "win32" || platform2 === "darwin";
|
|
7477
7633
|
}
|
|
7478
|
-
function
|
|
7634
|
+
function normPath2(p, platform2 = process.platform) {
|
|
7479
7635
|
const unified = p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
7480
7636
|
return pathsAreCaseInsensitive(platform2) ? unified.toLowerCase() : unified;
|
|
7481
7637
|
}
|
|
7482
7638
|
function samePath(a, b, platform2 = process.platform) {
|
|
7483
|
-
return
|
|
7639
|
+
return normPath2(a, platform2) === normPath2(b, platform2);
|
|
7484
7640
|
}
|
|
7485
7641
|
function toNativePath(p) {
|
|
7486
7642
|
return process.platform === "win32" ? p.replace(/\//g, "\\") : p;
|
|
@@ -7515,12 +7671,12 @@ function parseComposeLs(stdout) {
|
|
|
7515
7671
|
}).filter((p) => Boolean(p));
|
|
7516
7672
|
}
|
|
7517
7673
|
function selectWorktreeComposeProjects(worktreePath, projects) {
|
|
7518
|
-
const root =
|
|
7674
|
+
const root = normPath2(worktreePath);
|
|
7519
7675
|
if (!root) return [];
|
|
7520
7676
|
const names = [];
|
|
7521
7677
|
for (const project2 of projects) {
|
|
7522
7678
|
const inside = project2.configFiles.some((file) => {
|
|
7523
|
-
const f =
|
|
7679
|
+
const f = normPath2(file);
|
|
7524
7680
|
return f === root || f.startsWith(`${root}/`);
|
|
7525
7681
|
});
|
|
7526
7682
|
if (inside && !names.includes(project2.name)) names.push(project2.name);
|
|
@@ -7528,7 +7684,7 @@ function selectWorktreeComposeProjects(worktreePath, projects) {
|
|
|
7528
7684
|
return names;
|
|
7529
7685
|
}
|
|
7530
7686
|
function deriveComposeProjectName(worktreePath) {
|
|
7531
|
-
const norm =
|
|
7687
|
+
const norm = normPath2(worktreePath).toLowerCase();
|
|
7532
7688
|
if (!norm) return void 0;
|
|
7533
7689
|
const base = norm.slice(norm.lastIndexOf("/") + 1);
|
|
7534
7690
|
const name = base.replace(/[^a-z0-9_-]+/g, "_").replace(/^[^a-z0-9]+/, "");
|
|
@@ -7589,15 +7745,15 @@ function selectSafeWorktreeCwd(worktrees, targetPath, options) {
|
|
|
7589
7745
|
const exists = options?.pathExists ?? (() => true);
|
|
7590
7746
|
return worktrees.find((w) => !samePath(w.path, targetPath) && exists(w.path))?.path;
|
|
7591
7747
|
}
|
|
7592
|
-
function
|
|
7593
|
-
const child2 =
|
|
7594
|
-
const parent =
|
|
7748
|
+
function isPathUnderDirectory2(childPath, parentPath) {
|
|
7749
|
+
const child2 = normPath2(childPath);
|
|
7750
|
+
const parent = normPath2(parentPath);
|
|
7595
7751
|
if (!child2 || !parent) return false;
|
|
7596
7752
|
if (child2 === parent) return true;
|
|
7597
7753
|
return child2.startsWith(`${parent}/`);
|
|
7598
7754
|
}
|
|
7599
7755
|
function planReleaseCwdBeforeWorktreeRemoval(targetPath, safeCwd, currentCwd) {
|
|
7600
|
-
if (!safeCwd || !
|
|
7756
|
+
if (!safeCwd || !isPathUnderDirectory2(currentCwd, targetPath)) return void 0;
|
|
7601
7757
|
if (samePath(currentCwd, safeCwd)) return void 0;
|
|
7602
7758
|
return safeCwd;
|
|
7603
7759
|
}
|
|
@@ -7748,6 +7904,57 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
7748
7904
|
if (wtPath && mainWorktreeTarget) {
|
|
7749
7905
|
report.worktree = { path: wtPath, status: "not-attempted", reason: "main-worktree" };
|
|
7750
7906
|
} else if (wtPath) {
|
|
7907
|
+
const activeRoot = options.removalContext?.activeWorkspaceRoot ?? resolveActiveWorkspaceRoot();
|
|
7908
|
+
const activeGuard = decideActiveWorkspaceGuard(wtPath, activeRoot);
|
|
7909
|
+
if (activeGuard.action === "refuse") {
|
|
7910
|
+
if (options.deferredStore) {
|
|
7911
|
+
try {
|
|
7912
|
+
const { newlyRegistered } = await registerDeferredWorktree(options.deferredStore, {
|
|
7913
|
+
path: wtPath,
|
|
7914
|
+
branch,
|
|
7915
|
+
reason: "active-workspace"
|
|
7916
|
+
});
|
|
7917
|
+
report.worktree = {
|
|
7918
|
+
path: wtPath,
|
|
7919
|
+
status: "deferred",
|
|
7920
|
+
reason: "active-workspace",
|
|
7921
|
+
deferredNote: deferredNoteFor("active-workspace"),
|
|
7922
|
+
deferredSweepCommand: DEFERRED_SWEEP_COMMAND,
|
|
7923
|
+
...newlyRegistered ? { safeCleanupCommand: safeWorktreeRemoveCommand(safeCwd, wtPath) } : {}
|
|
7924
|
+
};
|
|
7925
|
+
if (options.removalContext) {
|
|
7926
|
+
recordWorktreeRemoval(options.removalContext.primaryRoot, {
|
|
7927
|
+
action: "refused",
|
|
7928
|
+
command: options.removalContext.command,
|
|
7929
|
+
target: wtPath,
|
|
7930
|
+
branch,
|
|
7931
|
+
actor: options.removalContext.actor,
|
|
7932
|
+
owner: lookupWorktreeOwner(options.removalContext.primaryRoot, wtPath),
|
|
7933
|
+
reason: activeGuard.message
|
|
7934
|
+
});
|
|
7935
|
+
}
|
|
7936
|
+
noteBranchBlocked("active-workspace");
|
|
7937
|
+
return report;
|
|
7938
|
+
} catch (e) {
|
|
7939
|
+
report.worktree = {
|
|
7940
|
+
path: wtPath,
|
|
7941
|
+
status: "not-attempted",
|
|
7942
|
+
reason: "active-workspace",
|
|
7943
|
+
error: `${activeGuard.message}; deferred registry unavailable: ${errorMessage(e)}`
|
|
7944
|
+
};
|
|
7945
|
+
noteBranchBlocked("active-workspace");
|
|
7946
|
+
return report;
|
|
7947
|
+
}
|
|
7948
|
+
}
|
|
7949
|
+
report.worktree = {
|
|
7950
|
+
path: wtPath,
|
|
7951
|
+
status: "not-attempted",
|
|
7952
|
+
reason: "active-workspace",
|
|
7953
|
+
error: activeGuard.message
|
|
7954
|
+
};
|
|
7955
|
+
noteBranchBlocked("active-workspace");
|
|
7956
|
+
return report;
|
|
7957
|
+
}
|
|
7751
7958
|
const dirt = await worktreeDirtyAtActTime(wtPath);
|
|
7752
7959
|
if (dirt !== void 0 && !isRemovableDirt(dirt)) {
|
|
7753
7960
|
const reason = dirt === "untracked-files" ? "untracked-files" : "dirty-worktree";
|
|
@@ -7813,7 +8020,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
7813
8020
|
status: "deferred",
|
|
7814
8021
|
reason: "lock-held",
|
|
7815
8022
|
error: outcome.error,
|
|
7816
|
-
deferredNote:
|
|
8023
|
+
deferredNote: deferredNoteFor("lock-held"),
|
|
7817
8024
|
deferredSweepCommand: DEFERRED_SWEEP_COMMAND,
|
|
7818
8025
|
...newlyRegistered ? { safeCleanupCommand: safeWorktreeRemoveCommand(safeCwd, wtPath) } : {},
|
|
7819
8026
|
stageTeardown
|
|
@@ -8027,13 +8234,13 @@ async function gatherStaleWorktreeWarning(gitRun = defaultGitRun) {
|
|
|
8027
8234
|
}
|
|
8028
8235
|
|
|
8029
8236
|
// src/released-version-cache.ts
|
|
8030
|
-
var
|
|
8031
|
-
var
|
|
8237
|
+
var import_node_fs11 = require("node:fs");
|
|
8238
|
+
var import_node_path10 = require("node:path");
|
|
8032
8239
|
var RELEASED_VERSION_CACHE_MS = 24 * 36e5;
|
|
8033
8240
|
function releasedVersionCachePath(runtimeRoot) {
|
|
8034
|
-
return (0,
|
|
8241
|
+
return (0, import_node_path10.join)(runtimeRoot, "head-ts", ".released-version");
|
|
8035
8242
|
}
|
|
8036
|
-
function readReleasedVersionCache(cachePath, now = Date.now(), read =
|
|
8243
|
+
function readReleasedVersionCache(cachePath, now = Date.now(), read = import_node_fs11.readFileSync) {
|
|
8037
8244
|
let parsed;
|
|
8038
8245
|
try {
|
|
8039
8246
|
parsed = JSON.parse(read(cachePath, "utf8"));
|
|
@@ -8050,8 +8257,8 @@ function readReleasedVersionCache(cachePath, now = Date.now(), read = import_nod
|
|
|
8050
8257
|
}
|
|
8051
8258
|
function writeReleasedVersionCache(cachePath, version, now = Date.now()) {
|
|
8052
8259
|
try {
|
|
8053
|
-
(0,
|
|
8054
|
-
(0,
|
|
8260
|
+
(0, import_node_fs11.mkdirSync)((0, import_node_path10.dirname)(cachePath), { recursive: true });
|
|
8261
|
+
(0, import_node_fs11.writeFileSync)(cachePath, JSON.stringify({ version, at: now }), "utf8");
|
|
8055
8262
|
} catch {
|
|
8056
8263
|
}
|
|
8057
8264
|
}
|
|
@@ -8268,8 +8475,8 @@ function marketplaceRows(name, known, settings, healable = false) {
|
|
|
8268
8475
|
}
|
|
8269
8476
|
|
|
8270
8477
|
// src/hook-activity.ts
|
|
8271
|
-
var
|
|
8272
|
-
var
|
|
8478
|
+
var import_node_fs12 = require("node:fs");
|
|
8479
|
+
var import_node_path11 = require("node:path");
|
|
8273
8480
|
var DEFAULT_SURFACE = "claude";
|
|
8274
8481
|
function activityLogPath(cwd) {
|
|
8275
8482
|
return repoRuntimeStatePath(cwd, "hooks", "activity.jsonl");
|
|
@@ -8282,20 +8489,20 @@ function appendHookActivity(cwd, entry) {
|
|
|
8282
8489
|
surface: DEFAULT_SURFACE,
|
|
8283
8490
|
...entry
|
|
8284
8491
|
};
|
|
8285
|
-
(0,
|
|
8286
|
-
(0,
|
|
8492
|
+
(0, import_node_fs12.mkdirSync)((0, import_node_path11.dirname)(path2), { recursive: true });
|
|
8493
|
+
(0, import_node_fs12.appendFileSync)(path2, `${JSON.stringify(line)}
|
|
8287
8494
|
`, "utf8");
|
|
8288
8495
|
} catch {
|
|
8289
8496
|
}
|
|
8290
8497
|
}
|
|
8291
8498
|
|
|
8292
8499
|
// src/worktree.ts
|
|
8293
|
-
var
|
|
8294
|
-
var
|
|
8500
|
+
var import_node_fs13 = require("node:fs");
|
|
8501
|
+
var import_node_path13 = require("node:path");
|
|
8295
8502
|
|
|
8296
8503
|
// src/file-lock.ts
|
|
8297
8504
|
var import_promises2 = require("node:fs/promises");
|
|
8298
|
-
var
|
|
8505
|
+
var import_node_path12 = require("node:path");
|
|
8299
8506
|
var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
8300
8507
|
var IMMEDIATE_RETRY_BUDGET = 3;
|
|
8301
8508
|
var FileLockBusyError = class extends Error {
|
|
@@ -8380,7 +8587,7 @@ async function releaseFileLock(lockPath, guard) {
|
|
|
8380
8587
|
}
|
|
8381
8588
|
async function withFileLock(lockPath, opts, fn) {
|
|
8382
8589
|
const resolved = resolveFileLockOpts(opts);
|
|
8383
|
-
await (0, import_promises2.mkdir)((0,
|
|
8590
|
+
await (0, import_promises2.mkdir)((0, import_node_path12.dirname)(lockPath), { recursive: true }).catch(() => void 0);
|
|
8384
8591
|
const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
|
|
8385
8592
|
try {
|
|
8386
8593
|
return await fn();
|
|
@@ -8407,35 +8614,35 @@ var PROVISION_ENV_MARKER = "MMI_PROVISION_RUNNING";
|
|
|
8407
8614
|
var realFsProbe = {
|
|
8408
8615
|
isDir: (p) => {
|
|
8409
8616
|
try {
|
|
8410
|
-
return (0,
|
|
8617
|
+
return (0, import_node_fs13.statSync)(p).isDirectory();
|
|
8411
8618
|
} catch {
|
|
8412
8619
|
return false;
|
|
8413
8620
|
}
|
|
8414
8621
|
},
|
|
8415
8622
|
isFile: (p) => {
|
|
8416
8623
|
try {
|
|
8417
|
-
return (0,
|
|
8624
|
+
return (0, import_node_fs13.statSync)(p).isFile();
|
|
8418
8625
|
} catch {
|
|
8419
8626
|
return false;
|
|
8420
8627
|
}
|
|
8421
8628
|
},
|
|
8422
8629
|
listDirs: (p) => {
|
|
8423
8630
|
try {
|
|
8424
|
-
return (0,
|
|
8631
|
+
return (0, import_node_fs13.readdirSync)(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
8425
8632
|
} catch {
|
|
8426
8633
|
return [];
|
|
8427
8634
|
}
|
|
8428
8635
|
},
|
|
8429
8636
|
readFile: (p) => {
|
|
8430
8637
|
try {
|
|
8431
|
-
return (0,
|
|
8638
|
+
return (0, import_node_fs13.readFileSync)(p, "utf8");
|
|
8432
8639
|
} catch {
|
|
8433
8640
|
return void 0;
|
|
8434
8641
|
}
|
|
8435
8642
|
}
|
|
8436
8643
|
};
|
|
8437
8644
|
function declaredProvision(fs2, abs) {
|
|
8438
|
-
const raw = fs2.readFile?.((0,
|
|
8645
|
+
const raw = fs2.readFile?.((0, import_node_path13.join)(abs, PKG));
|
|
8439
8646
|
if (raw === void 0) return void 0;
|
|
8440
8647
|
try {
|
|
8441
8648
|
const scripts = JSON.parse(raw).scripts;
|
|
@@ -8447,13 +8654,13 @@ function declaredProvision(fs2, abs) {
|
|
|
8447
8654
|
}
|
|
8448
8655
|
function scanInstallDirs(root, fs2 = realFsProbe) {
|
|
8449
8656
|
const factsFor = (dir) => {
|
|
8450
|
-
const abs = dir ? (0,
|
|
8451
|
-
const match = LOCKFILE_INSTALLS.find((c) => fs2.isFile((0,
|
|
8452
|
-
const hasPackageJson = fs2.isFile((0,
|
|
8657
|
+
const abs = dir ? (0, import_node_path13.join)(root, dir) : root;
|
|
8658
|
+
const match = LOCKFILE_INSTALLS.find((c) => fs2.isFile((0, import_node_path13.join)(abs, c.lockfile)));
|
|
8659
|
+
const hasPackageJson = fs2.isFile((0, import_node_path13.join)(abs, PKG));
|
|
8453
8660
|
return {
|
|
8454
8661
|
dir,
|
|
8455
8662
|
hasPackageJson,
|
|
8456
|
-
hasNodeModules: fs2.isDir((0,
|
|
8663
|
+
hasNodeModules: fs2.isDir((0, import_node_path13.join)(abs, NODE_MODULES)),
|
|
8457
8664
|
install: match?.command,
|
|
8458
8665
|
provision: hasPackageJson ? declaredProvision(fs2, abs) : void 0
|
|
8459
8666
|
};
|
|
@@ -8469,7 +8676,7 @@ function npmInstallTargets(dirs) {
|
|
|
8469
8676
|
}));
|
|
8470
8677
|
}
|
|
8471
8678
|
function isLinkedWorktree(root, fs2 = realFsProbe) {
|
|
8472
|
-
return fs2.isFile((0,
|
|
8679
|
+
return fs2.isFile((0, import_node_path13.join)(root, ".git"));
|
|
8473
8680
|
}
|
|
8474
8681
|
function worktreeAutoProvisionBanner(root, fs2 = realFsProbe) {
|
|
8475
8682
|
if (!isLinkedWorktree(root, fs2)) return null;
|
|
@@ -8479,8 +8686,8 @@ function worktreeAutoProvisionBanner(root, fs2 = realFsProbe) {
|
|
|
8479
8686
|
return `[worktree] provisioning tooling in the background (deps in ${where} + local config) \u2014 \`mmi-cli worktree setup\` to redo`;
|
|
8480
8687
|
}
|
|
8481
8688
|
function defaultCopyFile(from, to) {
|
|
8482
|
-
(0,
|
|
8483
|
-
(0,
|
|
8689
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path13.dirname)(to), { recursive: true });
|
|
8690
|
+
(0, import_node_fs13.copyFileSync)(from, to);
|
|
8484
8691
|
}
|
|
8485
8692
|
async function runDeclaredProvision(target, cwd, runInstall) {
|
|
8486
8693
|
const previous = process.env[PROVISION_ENV_MARKER];
|
|
@@ -8510,7 +8717,7 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
8510
8717
|
const targets = npmInstallTargets(allDirs);
|
|
8511
8718
|
if (deps.validateInstall) {
|
|
8512
8719
|
for (const dir of allDirs.filter((d) => d.hasPackageJson && (d.provision ?? d.install) && d.hasNodeModules)) {
|
|
8513
|
-
const cwd = dir.dir ? (0,
|
|
8720
|
+
const cwd = dir.dir ? (0, import_node_path13.join)(worktreeRoot, dir.dir) : worktreeRoot;
|
|
8514
8721
|
if (!await deps.validateInstall(cwd)) {
|
|
8515
8722
|
targets.push({
|
|
8516
8723
|
dir: dir.dir,
|
|
@@ -8524,7 +8731,7 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
8524
8731
|
const skippedInstall = allDirs.filter((d) => d.hasPackageJson && d.hasNodeModules && !targetDirs.has(d.dir)).map((d) => d.dir);
|
|
8525
8732
|
const installed = [];
|
|
8526
8733
|
for (const target of targets) {
|
|
8527
|
-
const cwd = target.dir ? (0,
|
|
8734
|
+
const cwd = target.dir ? (0, import_node_path13.join)(worktreeRoot, target.dir) : worktreeRoot;
|
|
8528
8735
|
log(`installing deps: ${target.command} in ${target.dir || "."}`);
|
|
8529
8736
|
if (target.declared) await runDeclaredProvision(target, cwd, deps.runInstall);
|
|
8530
8737
|
else await deps.runInstall(target.command, cwd);
|
|
@@ -8534,7 +8741,7 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
8534
8741
|
const copySkipped = [];
|
|
8535
8742
|
const primary = await deps.primaryCheckout();
|
|
8536
8743
|
for (const rel of LOCAL_ONLY_FILES) {
|
|
8537
|
-
const dest = (0,
|
|
8744
|
+
const dest = (0, import_node_path13.join)(worktreeRoot, rel);
|
|
8538
8745
|
if (fs2.isFile(dest)) {
|
|
8539
8746
|
copySkipped.push({ file: rel, reason: "already-present" });
|
|
8540
8747
|
continue;
|
|
@@ -8543,11 +8750,11 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
8543
8750
|
copySkipped.push({ file: rel, reason: "no-primary" });
|
|
8544
8751
|
continue;
|
|
8545
8752
|
}
|
|
8546
|
-
if (!fs2.isFile((0,
|
|
8753
|
+
if (!fs2.isFile((0, import_node_path13.join)(primary, rel))) {
|
|
8547
8754
|
copySkipped.push({ file: rel, reason: "absent-in-primary" });
|
|
8548
8755
|
continue;
|
|
8549
8756
|
}
|
|
8550
|
-
copyFile((0,
|
|
8757
|
+
copyFile((0, import_node_path13.join)(primary, rel), dest);
|
|
8551
8758
|
copied.push(rel);
|
|
8552
8759
|
log(`copied local config: ${rel}`);
|
|
8553
8760
|
}
|
|
@@ -8561,12 +8768,12 @@ function capWorktreeDirName(name, max = 40) {
|
|
|
8561
8768
|
}
|
|
8562
8769
|
function defaultWorktreePath(repoRoot2, branch) {
|
|
8563
8770
|
const safe = capWorktreeDirName(branch.replace(/[/\\]+/g, "-"));
|
|
8564
|
-
return (0,
|
|
8771
|
+
return (0, import_node_path13.join)((0, import_node_path13.dirname)(repoRoot2), "mmi-worktrees", (0, import_node_path13.basename)(repoRoot2), safe);
|
|
8565
8772
|
}
|
|
8566
8773
|
async function primaryCheckoutRootOf(git2) {
|
|
8567
8774
|
try {
|
|
8568
8775
|
const out = (await git2(["rev-parse", "--path-format=absolute", "--git-common-dir"])).trim();
|
|
8569
|
-
return out ? (0,
|
|
8776
|
+
return out ? (0, import_node_path13.dirname)(out) : void 0;
|
|
8570
8777
|
} catch {
|
|
8571
8778
|
return void 0;
|
|
8572
8779
|
}
|
|
@@ -8704,7 +8911,7 @@ function commandLadderHint() {
|
|
|
8704
8911
|
}
|
|
8705
8912
|
|
|
8706
8913
|
// src/index.ts
|
|
8707
|
-
var
|
|
8914
|
+
var import_node_path41 = require("node:path");
|
|
8708
8915
|
|
|
8709
8916
|
// src/merge-ci-policy.ts
|
|
8710
8917
|
function resolveMergeCiPolicy(input) {
|
|
@@ -9364,13 +9571,13 @@ function planManagedGitignore(current) {
|
|
|
9364
9571
|
}
|
|
9365
9572
|
|
|
9366
9573
|
// src/docs-index-command.ts
|
|
9367
|
-
var
|
|
9368
|
-
var
|
|
9574
|
+
var import_node_fs15 = require("node:fs");
|
|
9575
|
+
var import_node_path15 = require("node:path");
|
|
9369
9576
|
|
|
9370
9577
|
// src/doc-refs-core.ts
|
|
9371
9578
|
var import_node_child_process5 = require("node:child_process");
|
|
9372
|
-
var
|
|
9373
|
-
var
|
|
9579
|
+
var import_node_fs14 = require("node:fs");
|
|
9580
|
+
var import_node_path14 = require("node:path");
|
|
9374
9581
|
var PIN_RE = /<!--\s*pinned by\s+([^:]+?)\s*:\s*"([^"]+)"[^"]*-->/;
|
|
9375
9582
|
var PIN_MENTION_RE = /<!--\s*pinned by\b/;
|
|
9376
9583
|
var FWD_RE = /<!--\s*forward-ref:\s*(\S+?)\s*-->/;
|
|
@@ -9429,7 +9636,7 @@ function checkPins(root, readFile9, docs2) {
|
|
|
9429
9636
|
findings.push({ kind: "malformed-pin", doc, line: pin.line, detail: pin.text });
|
|
9430
9637
|
continue;
|
|
9431
9638
|
}
|
|
9432
|
-
const source = readFile9((0,
|
|
9639
|
+
const source = readFile9((0, import_node_path14.join)(root, pin.file));
|
|
9433
9640
|
if (source == null) {
|
|
9434
9641
|
findings.push({ kind: "missing-test", doc, line: pin.line, detail: pin.file });
|
|
9435
9642
|
continue;
|
|
@@ -9491,11 +9698,11 @@ function checkRefs(root, deps, docs2) {
|
|
|
9491
9698
|
for (const { ref } of extractRefs(markdown)) allFirstSegments.add(refFirstSegment(ref));
|
|
9492
9699
|
}
|
|
9493
9700
|
const tracked = allFirstSegments.size ? deps.trackedFirstSegments?.([...allFirstSegments]) ?? null : null;
|
|
9494
|
-
const firstVerifiable = (first) => tracked ? tracked.has(first) : exists((0,
|
|
9701
|
+
const firstVerifiable = (first) => tracked ? tracked.has(first) : exists((0, import_node_path14.join)(root, first));
|
|
9495
9702
|
const candidates = [];
|
|
9496
9703
|
const direct = [];
|
|
9497
9704
|
for (const [doc, markdown] of Object.entries(docs2)) {
|
|
9498
|
-
const docDir =
|
|
9705
|
+
const docDir = import_node_path14.posix.dirname(doc);
|
|
9499
9706
|
const base = docDir === "." ? "" : docDir;
|
|
9500
9707
|
const covered = /* @__PURE__ */ new Set();
|
|
9501
9708
|
const markers = [];
|
|
@@ -9504,21 +9711,21 @@ function checkRefs(root, deps, docs2) {
|
|
|
9504
9711
|
direct.push({ kind: "malformed-forward-ref", doc, line: fwd.line, detail: fwd.text });
|
|
9505
9712
|
continue;
|
|
9506
9713
|
}
|
|
9507
|
-
const docRel =
|
|
9508
|
-
const rootRel =
|
|
9714
|
+
const docRel = import_node_path14.posix.normalize(import_node_path14.posix.join(base, fwd.target));
|
|
9715
|
+
const rootRel = import_node_path14.posix.normalize(fwd.target.replace(/^\/+/, ""));
|
|
9509
9716
|
markers.push({ target: fwd.target, line: fwd.line, docRel, rootRel });
|
|
9510
9717
|
covered.add(docRel);
|
|
9511
9718
|
covered.add(rootRel);
|
|
9512
9719
|
}
|
|
9513
9720
|
const links = extractLinks(markdown).map(({ target, line }) => {
|
|
9514
|
-
const resolved =
|
|
9515
|
-
return { target, line, resolved, missing: !exists((0,
|
|
9721
|
+
const resolved = import_node_path14.posix.normalize(import_node_path14.posix.join(base, target));
|
|
9722
|
+
return { target, line, resolved, missing: !exists((0, import_node_path14.join)(root, resolved)) };
|
|
9516
9723
|
});
|
|
9517
9724
|
for (const marker of markers) {
|
|
9518
9725
|
const coversMissing = links.some(
|
|
9519
9726
|
(l) => l.missing && (l.resolved === marker.docRel || l.resolved === marker.rootRel)
|
|
9520
9727
|
);
|
|
9521
|
-
if (!coversMissing && (exists((0,
|
|
9728
|
+
if (!coversMissing && (exists((0, import_node_path14.join)(root, marker.docRel)) || exists((0, import_node_path14.join)(root, marker.rootRel)))) {
|
|
9522
9729
|
direct.push({
|
|
9523
9730
|
kind: "stale-forward-ref",
|
|
9524
9731
|
doc,
|
|
@@ -9529,7 +9736,7 @@ function checkRefs(root, deps, docs2) {
|
|
|
9529
9736
|
}
|
|
9530
9737
|
for (const { ref, line } of extractRefs(markdown)) {
|
|
9531
9738
|
if (!firstVerifiable(refFirstSegment(ref))) continue;
|
|
9532
|
-
if (!exists((0,
|
|
9739
|
+
if (!exists((0, import_node_path14.join)(root, ref))) candidates.push({ kind: "missing-path", doc, line, detail: ref });
|
|
9533
9740
|
}
|
|
9534
9741
|
for (const { target, line, resolved, missing } of links) {
|
|
9535
9742
|
if (resolved.startsWith("..")) {
|
|
@@ -9577,22 +9784,22 @@ function checkCommands(docs2, commandPaths) {
|
|
|
9577
9784
|
return { ok: findings.length === 0, findings, warnings: [] };
|
|
9578
9785
|
}
|
|
9579
9786
|
function readFileOrNull(path2) {
|
|
9580
|
-
return (0,
|
|
9787
|
+
return (0, import_node_fs14.existsSync)(path2) ? (0, import_node_fs14.readFileSync)(path2, "utf8") : null;
|
|
9581
9788
|
}
|
|
9582
9789
|
function walk(dir, root, out) {
|
|
9583
|
-
for (const entry of (0,
|
|
9584
|
-
const full = (0,
|
|
9585
|
-
if ((0,
|
|
9790
|
+
for (const entry of (0, import_node_fs14.readdirSync)(dir)) {
|
|
9791
|
+
const full = (0, import_node_path14.join)(dir, entry);
|
|
9792
|
+
if ((0, import_node_fs14.statSync)(full).isDirectory()) walk(full, root, out);
|
|
9586
9793
|
else if (entry.endsWith(".md")) out.push(full.slice(root.length + 1).replaceAll("\\", "/"));
|
|
9587
9794
|
}
|
|
9588
9795
|
return out;
|
|
9589
9796
|
}
|
|
9590
9797
|
function defaultListDocs(root) {
|
|
9591
|
-
const docsDir = (0,
|
|
9592
|
-
const docs2 = ((0,
|
|
9798
|
+
const docsDir = (0, import_node_path14.join)(root, "docs");
|
|
9799
|
+
const docs2 = ((0, import_node_fs14.existsSync)(docsDir) ? walk(docsDir, root, []) : []).filter(
|
|
9593
9800
|
(rel) => !SKIP_WALK.some((skip) => rel.startsWith(skip))
|
|
9594
9801
|
);
|
|
9595
|
-
return [...ROOT_DOCS.filter((rel) => (0,
|
|
9802
|
+
return [...ROOT_DOCS.filter((rel) => (0, import_node_fs14.existsSync)((0, import_node_path14.join)(root, rel))), ...docs2];
|
|
9596
9803
|
}
|
|
9597
9804
|
var CHECK_IGNORE_MAX_BUFFER = 32 * 1024 * 1024;
|
|
9598
9805
|
function defaultIsIgnored(root, relPaths, exec = import_node_child_process5.execFileSync) {
|
|
@@ -9644,7 +9851,7 @@ function defaultTrackedFirstSegments(root, firstSegments, exec = import_node_chi
|
|
|
9644
9851
|
}
|
|
9645
9852
|
function runDocRefs(root, deps = {}) {
|
|
9646
9853
|
const readFile9 = deps.readFile ?? readFileOrNull;
|
|
9647
|
-
const exists = deps.exists ??
|
|
9854
|
+
const exists = deps.exists ?? import_node_fs14.existsSync;
|
|
9648
9855
|
const listDocs = deps.listDocs ?? defaultListDocs;
|
|
9649
9856
|
const isIgnored = deps.isIgnored ?? ((paths) => defaultIsIgnored(root, paths));
|
|
9650
9857
|
const trackedFirstSegments = deps.trackedFirstSegments ?? ((segs) => defaultTrackedFirstSegments(root, segs));
|
|
@@ -9652,7 +9859,7 @@ function runDocRefs(root, deps = {}) {
|
|
|
9652
9859
|
const walked = listDocs(root);
|
|
9653
9860
|
const ignoredDocs = walked.length ? isIgnored(walked) : /* @__PURE__ */ new Set();
|
|
9654
9861
|
const docs2 = Object.fromEntries(
|
|
9655
|
-
walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile9((0,
|
|
9862
|
+
walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile9((0, import_node_path14.join)(root, rel))]).filter(([, body]) => body != null)
|
|
9656
9863
|
);
|
|
9657
9864
|
const refResult = checkRefs(root, { exists, isIgnored, trackedFirstSegments }, docs2);
|
|
9658
9865
|
const findings = [
|
|
@@ -9759,31 +9966,31 @@ function walkMarkdown(dir) {
|
|
|
9759
9966
|
const stack = [dir];
|
|
9760
9967
|
while (stack.length) {
|
|
9761
9968
|
const current = stack.pop();
|
|
9762
|
-
for (const entry of (0,
|
|
9763
|
-
const full = (0,
|
|
9969
|
+
for (const entry of (0, import_node_fs15.readdirSync)(current, { withFileTypes: true })) {
|
|
9970
|
+
const full = (0, import_node_path15.join)(current, entry.name);
|
|
9764
9971
|
if (entry.isDirectory()) {
|
|
9765
9972
|
stack.push(full);
|
|
9766
9973
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
9767
|
-
out.push((0,
|
|
9974
|
+
out.push((0, import_node_path15.relative)(dir, full).split(import_node_path15.sep).join("/"));
|
|
9768
9975
|
}
|
|
9769
9976
|
}
|
|
9770
9977
|
}
|
|
9771
9978
|
return out;
|
|
9772
9979
|
}
|
|
9773
9980
|
function createDocsIndexDeps(repoRoot2) {
|
|
9774
|
-
const docsDir = (0,
|
|
9775
|
-
const indexPath = (0,
|
|
9981
|
+
const docsDir = (0, import_node_path15.join)(repoRoot2, "docs");
|
|
9982
|
+
const indexPath = (0, import_node_path15.join)(repoRoot2, DOCS_INDEX_PATH);
|
|
9776
9983
|
return {
|
|
9777
9984
|
listDocs: () => {
|
|
9778
|
-
if (!(0,
|
|
9985
|
+
if (!(0, import_node_fs15.existsSync)(docsDir)) return [];
|
|
9779
9986
|
const walked = walkMarkdown(docsDir).filter(isRoutableDocsPath);
|
|
9780
9987
|
if (!walked.length) return [];
|
|
9781
9988
|
const ignored = defaultIsIgnored(repoRoot2, walked.map((rel) => `docs/${rel}`));
|
|
9782
9989
|
return walked.filter((rel) => !ignored.has(`docs/${rel}`)).sort();
|
|
9783
9990
|
},
|
|
9784
|
-
readDoc: (relPath) => (0,
|
|
9785
|
-
readIndex: () => (0,
|
|
9786
|
-
writeIndex: (content) => (0,
|
|
9991
|
+
readDoc: (relPath) => (0, import_node_fs15.readFileSync)((0, import_node_path15.join)(docsDir, relPath), "utf8"),
|
|
9992
|
+
readIndex: () => (0, import_node_fs15.existsSync)(indexPath) ? (0, import_node_fs15.readFileSync)(indexPath, "utf8") : null,
|
|
9993
|
+
writeIndex: (content) => (0, import_node_fs15.writeFileSync)(indexPath, content, "utf8")
|
|
9787
9994
|
};
|
|
9788
9995
|
}
|
|
9789
9996
|
|
|
@@ -10430,15 +10637,15 @@ function parseVerifyBroker(stdout) {
|
|
|
10430
10637
|
}
|
|
10431
10638
|
|
|
10432
10639
|
// src/train-apply.ts
|
|
10433
|
-
var
|
|
10640
|
+
var import_node_fs17 = require("node:fs");
|
|
10434
10641
|
var import_promises3 = require("node:fs/promises");
|
|
10435
|
-
var
|
|
10642
|
+
var import_node_path17 = require("node:path");
|
|
10436
10643
|
|
|
10437
10644
|
// src/plugin-guard-io.ts
|
|
10438
|
-
var
|
|
10645
|
+
var import_node_fs16 = require("node:fs");
|
|
10439
10646
|
var import_node_child_process6 = require("node:child_process");
|
|
10440
|
-
var
|
|
10441
|
-
var
|
|
10647
|
+
var import_node_path16 = require("node:path");
|
|
10648
|
+
var import_node_os5 = require("node:os");
|
|
10442
10649
|
var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
|
|
10443
10650
|
|
|
10444
10651
|
// src/version-lag.ts
|
|
@@ -10727,20 +10934,20 @@ function runHostBin(bin, args, opts) {
|
|
|
10727
10934
|
const argv = isWin ? ["/c", bin, ...args] : args;
|
|
10728
10935
|
return step ? execFileHard(file, argv, { ...shared, step }) : execFileP2(file, argv, shared);
|
|
10729
10936
|
}
|
|
10730
|
-
function surfaceConfigRoot(surface, env = process.env, home = (0,
|
|
10731
|
-
if (surface === "codex") return env.CODEX_HOME?.trim() || (0,
|
|
10732
|
-
if (surface === "kimi") return env.KIMI_CODE_HOME?.trim() || (0,
|
|
10733
|
-
if (surface === "kilo") return env.KILO_CONFIG_DIR?.trim() || (0,
|
|
10734
|
-
if (surface === "cursor") return (0,
|
|
10735
|
-
if (surface === "jervcode") return env.PI_CODING_AGENT_DIR?.trim() || (0,
|
|
10736
|
-
return (0,
|
|
10937
|
+
function surfaceConfigRoot(surface, env = process.env, home = (0, import_node_os5.homedir)()) {
|
|
10938
|
+
if (surface === "codex") return env.CODEX_HOME?.trim() || (0, import_node_path16.join)(home, ".codex");
|
|
10939
|
+
if (surface === "kimi") return env.KIMI_CODE_HOME?.trim() || (0, import_node_path16.join)(home, ".kimi-code");
|
|
10940
|
+
if (surface === "kilo") return env.KILO_CONFIG_DIR?.trim() || (0, import_node_path16.join)(home, ".config", "kilo");
|
|
10941
|
+
if (surface === "cursor") return (0, import_node_path16.join)(home, ".cursor");
|
|
10942
|
+
if (surface === "jervcode") return env.PI_CODING_AGENT_DIR?.trim() || (0, import_node_path16.join)(home, ".pi", "agent");
|
|
10943
|
+
return (0, import_node_path16.join)(home, ".claude");
|
|
10737
10944
|
}
|
|
10738
10945
|
var installedPluginsPath = (surface = detectSurface(process.env)) => {
|
|
10739
|
-
return (0,
|
|
10946
|
+
return (0, import_node_path16.join)(surfaceConfigRoot(surface), "plugins", "installed_plugins.json");
|
|
10740
10947
|
};
|
|
10741
10948
|
function readInstalledPlugins(surface = detectSurface(process.env)) {
|
|
10742
10949
|
try {
|
|
10743
|
-
return JSON.parse((0,
|
|
10950
|
+
return JSON.parse((0, import_node_fs16.readFileSync)(installedPluginsPath(surface), "utf8"));
|
|
10744
10951
|
} catch {
|
|
10745
10952
|
return null;
|
|
10746
10953
|
}
|
|
@@ -10749,17 +10956,17 @@ function marketplaceCloneCandidates(surface, home, env = process.env) {
|
|
|
10749
10956
|
if (surface === "codex") {
|
|
10750
10957
|
const root = surfaceConfigRoot(surface, env, home);
|
|
10751
10958
|
return [
|
|
10752
|
-
(0,
|
|
10753
|
-
(0,
|
|
10959
|
+
(0, import_node_path16.join)(root, ".tmp", "marketplaces", CODEX_MARKETPLACE),
|
|
10960
|
+
(0, import_node_path16.join)(root, "plugins", "marketplaces", CODEX_MARKETPLACE)
|
|
10754
10961
|
];
|
|
10755
10962
|
}
|
|
10756
10963
|
if (surface === "kimi") return [];
|
|
10757
10964
|
if (surface === "kilo") return [];
|
|
10758
10965
|
if (surface === "cursor") return [];
|
|
10759
10966
|
if (surface === "jervcode") return [];
|
|
10760
|
-
return [(0,
|
|
10967
|
+
return [(0, import_node_path16.join)(home, ".claude", "plugins", "marketplaces", "mutmutco")];
|
|
10761
10968
|
}
|
|
10762
|
-
function marketplaceClonePresent(surface, home, exists =
|
|
10969
|
+
function marketplaceClonePresent(surface, home, exists = import_node_fs16.existsSync, env = process.env) {
|
|
10763
10970
|
return marketplaceCloneCandidates(surface, home, env).some(exists);
|
|
10764
10971
|
}
|
|
10765
10972
|
function runHostBinSync(bin, args) {
|
|
@@ -10791,7 +10998,7 @@ function codexPluginStatus() {
|
|
|
10791
10998
|
}
|
|
10792
10999
|
function countCodexHookCommands(path2) {
|
|
10793
11000
|
try {
|
|
10794
|
-
const parsed = JSON.parse((0,
|
|
11001
|
+
const parsed = JSON.parse((0, import_node_fs16.readFileSync)(path2, "utf8"));
|
|
10795
11002
|
let count = 0;
|
|
10796
11003
|
for (const groups of Object.values(parsed.hooks ?? {})) {
|
|
10797
11004
|
for (const group of groups) {
|
|
@@ -10808,11 +11015,11 @@ function codexHookTrustState(status = codexPluginStatus()) {
|
|
|
10808
11015
|
return { applicable: false, trusted: false, trustedCount: 0, requiredCount: 0 };
|
|
10809
11016
|
}
|
|
10810
11017
|
const root = surfaceConfigRoot("codex");
|
|
10811
|
-
const hooksPath = (0,
|
|
11018
|
+
const hooksPath = (0, import_node_path16.join)(root, "plugins", "cache", CODEX_MARKETPLACE, "mmi", status.version, "hooks", "codex-hooks.json");
|
|
10812
11019
|
const requiredCount = countCodexHookCommands(hooksPath);
|
|
10813
11020
|
let config = "";
|
|
10814
11021
|
try {
|
|
10815
|
-
config = (0,
|
|
11022
|
+
config = (0, import_node_fs16.readFileSync)((0, import_node_path16.join)(root, "config.toml"), "utf8");
|
|
10816
11023
|
} catch {
|
|
10817
11024
|
return { applicable: true, trusted: false, trustedCount: 0, requiredCount };
|
|
10818
11025
|
}
|
|
@@ -10846,11 +11053,11 @@ async function npmSelfUpdateCli(target, onStep) {
|
|
|
10846
11053
|
return { ok: false, detail: e.message.trim().slice(0, 200).replace(/\s+/g, " ") };
|
|
10847
11054
|
}
|
|
10848
11055
|
}
|
|
10849
|
-
function kiloConfigListsPlugin(configRoot, home = (0,
|
|
11056
|
+
function kiloConfigListsPlugin(configRoot, home = (0, import_node_os5.homedir)(), read = (p) => (0, import_node_fs16.readFileSync)(p, "utf8"), exists = import_node_fs16.existsSync) {
|
|
10850
11057
|
const candidates = ["kilo.json", "kilo.jsonc", "opencode.json", "opencode.jsonc", "config.json"];
|
|
10851
|
-
for (const dir of [configRoot, (0,
|
|
11058
|
+
for (const dir of [configRoot, (0, import_node_path16.join)(home, ".kilo")]) {
|
|
10852
11059
|
for (const file of candidates) {
|
|
10853
|
-
const path2 = (0,
|
|
11060
|
+
const path2 = (0, import_node_path16.join)(dir, file);
|
|
10854
11061
|
if (!exists(path2)) continue;
|
|
10855
11062
|
try {
|
|
10856
11063
|
const stripped = read(path2).replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "");
|
|
@@ -10866,24 +11073,24 @@ function kiloConfigListsPlugin(configRoot, home = (0, import_node_os4.homedir)()
|
|
|
10866
11073
|
}
|
|
10867
11074
|
return false;
|
|
10868
11075
|
}
|
|
10869
|
-
function cursorLocalPluginRoot(env = process.env, home = (0,
|
|
10870
|
-
return (0,
|
|
11076
|
+
function cursorLocalPluginRoot(env = process.env, home = (0, import_node_os5.homedir)()) {
|
|
11077
|
+
return (0, import_node_path16.join)(surfaceConfigRoot("cursor", env, home), "plugins", "local", "mmi");
|
|
10871
11078
|
}
|
|
10872
|
-
function cursorPluginTreeHealthy(root, exists =
|
|
11079
|
+
function cursorPluginTreeHealthy(root, exists = import_node_fs16.existsSync) {
|
|
10873
11080
|
return [
|
|
10874
11081
|
".cursor-plugin/plugin.json",
|
|
10875
11082
|
"skills/mmi/SKILL.md",
|
|
10876
11083
|
"hooks/cursor-hooks.json",
|
|
10877
11084
|
"scripts/hook-run.mjs",
|
|
10878
11085
|
"scripts/hook-policy.mjs"
|
|
10879
|
-
].every((path2) => exists((0,
|
|
11086
|
+
].every((path2) => exists((0, import_node_path16.join)(root, ...path2.split("/"))));
|
|
10880
11087
|
}
|
|
10881
|
-
function kimiPluginTreeHealthy(root, exists =
|
|
11088
|
+
function kimiPluginTreeHealthy(root, exists = import_node_fs16.existsSync) {
|
|
10882
11089
|
return [
|
|
10883
11090
|
".kimi-plugin/plugin.json",
|
|
10884
11091
|
"skills/mmi/SKILL.md",
|
|
10885
11092
|
"scripts/hook-run.mjs"
|
|
10886
|
-
].every((path2) => exists((0,
|
|
11093
|
+
].every((path2) => exists((0, import_node_path16.join)(root, ...path2.split("/"))));
|
|
10887
11094
|
}
|
|
10888
11095
|
var JERVCODE_WRAPPER_DIR = ".pi-plugin";
|
|
10889
11096
|
function normalizePiEntry(value) {
|
|
@@ -10906,7 +11113,7 @@ function jervcodePackageFamily(entry) {
|
|
|
10906
11113
|
function isMmiOwnedPiEntry(entry) {
|
|
10907
11114
|
if (typeof entry !== "string") return false;
|
|
10908
11115
|
try {
|
|
10909
|
-
const pkg = JSON.parse((0,
|
|
11116
|
+
const pkg = JSON.parse((0, import_node_fs16.readFileSync)((0, import_node_path16.join)(piEntryFsPath(entry), "package.json"), "utf8"));
|
|
10910
11117
|
return pkg.name === "mmi";
|
|
10911
11118
|
} catch {
|
|
10912
11119
|
return false;
|
|
@@ -10928,17 +11135,17 @@ function mergeMmiPiPackageEntries(entries, packagePath) {
|
|
|
10928
11135
|
};
|
|
10929
11136
|
}
|
|
10930
11137
|
function readPiSettings(path2) {
|
|
10931
|
-
if (!(0,
|
|
11138
|
+
if (!(0, import_node_fs16.existsSync)(path2)) return void 0;
|
|
10932
11139
|
try {
|
|
10933
|
-
const parsed = JSON.parse((0,
|
|
11140
|
+
const parsed = JSON.parse((0, import_node_fs16.readFileSync)(path2, "utf8"));
|
|
10934
11141
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
10935
11142
|
return parsed;
|
|
10936
11143
|
} catch {
|
|
10937
11144
|
return null;
|
|
10938
11145
|
}
|
|
10939
11146
|
}
|
|
10940
|
-
function mmiPiWrapperEntry(env = process.env, home = (0,
|
|
10941
|
-
const settings = readPiSettings((0,
|
|
11147
|
+
function mmiPiWrapperEntry(env = process.env, home = (0, import_node_os5.homedir)()) {
|
|
11148
|
+
const settings = readPiSettings((0, import_node_path16.join)(surfaceConfigRoot("jervcode", env, home), "settings.json"));
|
|
10942
11149
|
const entries = Array.isArray(settings?.packages) ? settings.packages : [];
|
|
10943
11150
|
for (const entry of entries) {
|
|
10944
11151
|
if (typeof entry !== "string") continue;
|
|
@@ -10951,17 +11158,17 @@ function mmiPiWrapperEntry(env = process.env, home = (0, import_node_os4.homedir
|
|
|
10951
11158
|
function mmiPiWrapperHealthy(entry) {
|
|
10952
11159
|
if (!entry) return false;
|
|
10953
11160
|
const wrapper = piEntryFsPath(entry);
|
|
10954
|
-
return isMmiOwnedPiEntry(entry) && (0,
|
|
11161
|
+
return isMmiOwnedPiEntry(entry) && (0, import_node_fs16.existsSync)((0, import_node_path16.join)((0, import_node_path16.dirname)(wrapper), "skills", "mmi", "SKILL.md"));
|
|
10955
11162
|
}
|
|
10956
|
-
function findMmiPiSourceClone(home = (0,
|
|
10957
|
-
const cacheRoot = (0,
|
|
11163
|
+
function findMmiPiSourceClone(home = (0, import_node_os5.homedir)()) {
|
|
11164
|
+
const cacheRoot = (0, import_node_path16.join)(home, ".claude", "plugins", "cache", "mutmutco", "mmi");
|
|
10958
11165
|
let best = null;
|
|
10959
11166
|
try {
|
|
10960
|
-
for (const entry of (0,
|
|
11167
|
+
for (const entry of (0, import_node_fs16.readdirSync)(cacheRoot, { withFileTypes: true })) {
|
|
10961
11168
|
if (!entry.isDirectory() || !/^\d+\.\d+\.\d+$/.test(entry.name)) continue;
|
|
10962
|
-
if (!(0,
|
|
11169
|
+
if (!(0, import_node_fs16.existsSync)((0, import_node_path16.join)(cacheRoot, entry.name, JERVCODE_WRAPPER_DIR, "package.json"))) continue;
|
|
10963
11170
|
if (!best || compareVersions(entry.name, best.version) > 0) {
|
|
10964
|
-
best = { path: (0,
|
|
11171
|
+
best = { path: (0, import_node_path16.join)(cacheRoot, entry.name), version: entry.name };
|
|
10965
11172
|
}
|
|
10966
11173
|
}
|
|
10967
11174
|
} catch {
|
|
@@ -10989,9 +11196,9 @@ function healJervCodePackageRegistration(opts = {}) {
|
|
|
10989
11196
|
const env = opts.env ?? process.env;
|
|
10990
11197
|
const inSeat = !!env.PI_SESSION_ID?.trim();
|
|
10991
11198
|
const nextLaunch = inSeat ? " \u2014 takes effect on the next seat launch" : "";
|
|
10992
|
-
const home = opts.home ?? (0,
|
|
11199
|
+
const home = opts.home ?? (0, import_node_os5.homedir)();
|
|
10993
11200
|
const agentDir = surfaceConfigRoot("jervcode", env, home);
|
|
10994
|
-
if (!(0,
|
|
11201
|
+
if (!(0, import_node_fs16.existsSync)(agentDir)) {
|
|
10995
11202
|
return { available: false, ok: true, changed: false, version: null, detail: "skipped \u2014 no Pi/JervCode install (no agent config dir)" };
|
|
10996
11203
|
}
|
|
10997
11204
|
const clone = opts.clone === void 0 ? findMmiPiSourceClone(home) : opts.clone;
|
|
@@ -10999,9 +11206,9 @@ function healJervCodePackageRegistration(opts = {}) {
|
|
|
10999
11206
|
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)" };
|
|
11000
11207
|
}
|
|
11001
11208
|
const packagePath = `${clone.path.replace(/\\/g, "/").replace(/\/+$/, "")}/${JERVCODE_WRAPPER_DIR}`;
|
|
11002
|
-
const settingsPath2 = (0,
|
|
11003
|
-
const release = (0,
|
|
11004
|
-
if ((0,
|
|
11209
|
+
const settingsPath2 = (0, import_node_path16.join)(agentDir, "settings.json");
|
|
11210
|
+
const release = (0, import_node_fs16.existsSync)(settingsPath2) ? acquirePiSettingsLock(settingsPath2) : void 0;
|
|
11211
|
+
if ((0, import_node_fs16.existsSync)(settingsPath2) && !release) {
|
|
11005
11212
|
return {
|
|
11006
11213
|
available: true,
|
|
11007
11214
|
ok: false,
|
|
@@ -11029,14 +11236,14 @@ function healJervCodePackageRegistration(opts = {}) {
|
|
|
11029
11236
|
}
|
|
11030
11237
|
current.packages = merged.next;
|
|
11031
11238
|
try {
|
|
11032
|
-
(0,
|
|
11239
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path16.dirname)(settingsPath2), { recursive: true });
|
|
11033
11240
|
const tmp = `${settingsPath2}.tmp-${process.pid}`;
|
|
11034
|
-
(0,
|
|
11241
|
+
(0, import_node_fs16.writeFileSync)(tmp, `${JSON.stringify(current, null, 2)}
|
|
11035
11242
|
`, "utf8");
|
|
11036
11243
|
try {
|
|
11037
|
-
(0,
|
|
11244
|
+
(0, import_node_fs16.renameSync)(tmp, settingsPath2);
|
|
11038
11245
|
} catch (renameError) {
|
|
11039
|
-
(0,
|
|
11246
|
+
(0, import_node_fs16.rmSync)(tmp, { force: true });
|
|
11040
11247
|
throw renameError;
|
|
11041
11248
|
}
|
|
11042
11249
|
} catch (error) {
|
|
@@ -11062,18 +11269,18 @@ function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRep
|
|
|
11062
11269
|
return {
|
|
11063
11270
|
isOrgRepo,
|
|
11064
11271
|
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.
|
|
11065
|
-
surface === "kimi" && (0,
|
|
11272
|
+
surface === "kimi" && (0, import_node_fs16.existsSync)((0, import_node_path16.join)(root, "plugins", "managed", "mmi")) || // kilo-p1: the install record is the config file itself.
|
|
11066
11273
|
surface === "kilo" && kiloConfigListsPlugin(root) || // #4188: jervcode's install record is the settings-file packages[] entry itself.
|
|
11067
|
-
surface === "jervcode" && piEntry !== null || surface === "cursor" && (0,
|
|
11274
|
+
surface === "jervcode" && piEntry !== null || surface === "cursor" && (0, import_node_fs16.existsSync)(cursorLocalPluginRoot()),
|
|
11068
11275
|
// Kilo has no marketplace to clone — the config file IS the install record, so this dimension of
|
|
11069
11276
|
// the shared guard table is vacuously satisfied. Same for jervcode's settings entry.
|
|
11070
|
-
marketplaceClonePresent: surface === "kimi" || surface === "kilo" || surface === "cursor" || surface === "jervcode" ? true : marketplaceClonePresent(surface, (0,
|
|
11277
|
+
marketplaceClonePresent: surface === "kimi" || surface === "kilo" || surface === "cursor" || surface === "jervcode" ? true : marketplaceClonePresent(surface, (0, import_node_os5.homedir)()),
|
|
11071
11278
|
// Kimi keeps no plugin cache dir — installs are copied to plugins/managed/<id> and run from there.
|
|
11072
11279
|
// Kilo (kilo-p1) keeps no cache dir either: the plugin's server() provisions ~/.kilo behind the
|
|
11073
11280
|
// version stamp, so the stamp's presence is the cache signal.
|
|
11074
|
-
pluginCachePresent: surface === "jervcode" ? mmiPiWrapperHealthy(piEntry) : surface === "kilo" ? (0,
|
|
11075
|
-
codexStatus?.installed && codexStatus.enabled && codexStatus.version && (0,
|
|
11076
|
-
) : (0,
|
|
11281
|
+
pluginCachePresent: surface === "jervcode" ? mmiPiWrapperHealthy(piEntry) : surface === "kilo" ? (0, import_node_fs16.existsSync)((0, import_node_path16.join)((0, import_node_os5.homedir)(), ".kilo", ".mmi-kilo-version")) : surface === "kimi" ? kimiPluginTreeHealthy((0, import_node_path16.join)(root, "plugins", "managed", "mmi")) : surface === "cursor" ? cursorPluginTreeHealthy(cursorLocalPluginRoot()) : surface === "codex" ? Boolean(
|
|
11282
|
+
codexStatus?.installed && codexStatus.enabled && codexStatus.version && (0, import_node_fs16.existsSync)((0, import_node_path16.join)(root, "plugins", "cache", CODEX_MARKETPLACE, "mmi", codexStatus.version))
|
|
11283
|
+
) : (0, import_node_fs16.existsSync)((0, import_node_path16.join)(root, "plugins", "cache", "mutmutco", "mmi"))
|
|
11077
11284
|
};
|
|
11078
11285
|
}
|
|
11079
11286
|
async function runHostBinLogged(bin, args, opts) {
|
|
@@ -11093,11 +11300,11 @@ async function runPluginCli(bin, args, log) {
|
|
|
11093
11300
|
function captureCodexHookLauncher() {
|
|
11094
11301
|
const status = codexPluginStatus();
|
|
11095
11302
|
if (!status.installed || !status.enabled || !status.version) return void 0;
|
|
11096
|
-
const root = (0,
|
|
11303
|
+
const root = (0, import_node_path16.join)(surfaceConfigRoot("codex"), "plugins", "cache", CODEX_MARKETPLACE, "mmi", status.version);
|
|
11097
11304
|
const files = ["mmi-hook", "mmi-hook.exe"].flatMap((name) => {
|
|
11098
|
-
const path2 = (0,
|
|
11305
|
+
const path2 = (0, import_node_path16.join)(root, "bin", name);
|
|
11099
11306
|
try {
|
|
11100
|
-
return [{ name, content: (0,
|
|
11307
|
+
return [{ name, content: (0, import_node_fs16.readFileSync)(path2) }];
|
|
11101
11308
|
} catch {
|
|
11102
11309
|
return [];
|
|
11103
11310
|
}
|
|
@@ -11105,13 +11312,13 @@ function captureCodexHookLauncher() {
|
|
|
11105
11312
|
return files.length === 2 ? { root, files } : void 0;
|
|
11106
11313
|
}
|
|
11107
11314
|
function restoreCodexHookLauncher(snapshot) {
|
|
11108
|
-
if (!snapshot || (0,
|
|
11109
|
-
const bin = (0,
|
|
11110
|
-
(0,
|
|
11315
|
+
if (!snapshot || (0, import_node_fs16.existsSync)((0, import_node_path16.join)(snapshot.root, "scripts", "hook-run.mjs"))) return false;
|
|
11316
|
+
const bin = (0, import_node_path16.join)(snapshot.root, "bin");
|
|
11317
|
+
(0, import_node_fs16.mkdirSync)(bin, { recursive: true });
|
|
11111
11318
|
for (const file of snapshot.files) {
|
|
11112
|
-
const path2 = (0,
|
|
11113
|
-
(0,
|
|
11114
|
-
if (file.name === "mmi-hook") (0,
|
|
11319
|
+
const path2 = (0, import_node_path16.join)(bin, file.name);
|
|
11320
|
+
(0, import_node_fs16.writeFileSync)(path2, file.content);
|
|
11321
|
+
if (file.name === "mmi-hook") (0, import_node_fs16.chmodSync)(path2, 493);
|
|
11115
11322
|
}
|
|
11116
11323
|
return true;
|
|
11117
11324
|
}
|
|
@@ -11120,10 +11327,10 @@ function canonicalCursorRemote(remote) {
|
|
|
11120
11327
|
}
|
|
11121
11328
|
async function installCursorPluginCheckout(env = process.env) {
|
|
11122
11329
|
const configRoot = surfaceConfigRoot("cursor", env);
|
|
11123
|
-
const pluginsRoot = (0,
|
|
11124
|
-
const target = (0,
|
|
11330
|
+
const pluginsRoot = (0, import_node_path16.join)(configRoot, "plugins");
|
|
11331
|
+
const target = (0, import_node_path16.join)(pluginsRoot, "local", "mmi");
|
|
11125
11332
|
const source = env.MMI_CURSOR_PLUGIN_SOURCE?.trim();
|
|
11126
|
-
if ((0,
|
|
11333
|
+
if ((0, import_node_fs16.existsSync)(target) && !source) {
|
|
11127
11334
|
try {
|
|
11128
11335
|
const { stdout } = await runHostBin("git", ["-C", target, "remote", "get-url", "origin"], { timeout: 15e3 });
|
|
11129
11336
|
if (!canonicalCursorRemote(stdout)) {
|
|
@@ -11133,15 +11340,15 @@ async function installCursorPluginCheckout(env = process.env) {
|
|
|
11133
11340
|
return { ok: false, detail: `refused to replace unmanaged Cursor plugin directory at ${target}` };
|
|
11134
11341
|
}
|
|
11135
11342
|
}
|
|
11136
|
-
(0,
|
|
11137
|
-
(0,
|
|
11138
|
-
(0,
|
|
11343
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path16.join)(pluginsRoot, "local"), { recursive: true });
|
|
11344
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path16.join)(pluginsRoot, "staging"), { recursive: true });
|
|
11345
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path16.join)(pluginsRoot, "quarantine"), { recursive: true });
|
|
11139
11346
|
const suffix = `${Date.now()}-${process.pid}`;
|
|
11140
|
-
const staged = (0,
|
|
11141
|
-
const quarantined = (0,
|
|
11347
|
+
const staged = (0, import_node_path16.join)(pluginsRoot, "staging", `mmi-${suffix}`);
|
|
11348
|
+
const quarantined = (0, import_node_path16.join)(pluginsRoot, "quarantine", `mmi-${suffix}`);
|
|
11142
11349
|
try {
|
|
11143
11350
|
if (source) {
|
|
11144
|
-
(0,
|
|
11351
|
+
(0, import_node_fs16.cpSync)(source, staged, {
|
|
11145
11352
|
recursive: true,
|
|
11146
11353
|
filter: (path2) => !path2.split(/[\\/]/).some((part) => part === ".git" || part === "node_modules")
|
|
11147
11354
|
});
|
|
@@ -11152,18 +11359,18 @@ async function installCursorPluginCheckout(env = process.env) {
|
|
|
11152
11359
|
});
|
|
11153
11360
|
}
|
|
11154
11361
|
if (!cursorPluginTreeHealthy(staged)) {
|
|
11155
|
-
(0,
|
|
11362
|
+
(0, import_node_fs16.rmSync)(staged, { recursive: true, force: true });
|
|
11156
11363
|
return { ok: false, detail: "downloaded Cursor plugin is incomplete; existing install was preserved" };
|
|
11157
11364
|
}
|
|
11158
11365
|
let movedOld = false;
|
|
11159
|
-
if ((0,
|
|
11160
|
-
(0,
|
|
11366
|
+
if ((0, import_node_fs16.existsSync)(target)) {
|
|
11367
|
+
(0, import_node_fs16.renameSync)(target, quarantined);
|
|
11161
11368
|
movedOld = true;
|
|
11162
11369
|
}
|
|
11163
11370
|
try {
|
|
11164
|
-
(0,
|
|
11371
|
+
(0, import_node_fs16.renameSync)(staged, target);
|
|
11165
11372
|
} catch (error) {
|
|
11166
|
-
if (movedOld && !(0,
|
|
11373
|
+
if (movedOld && !(0, import_node_fs16.existsSync)(target)) (0, import_node_fs16.renameSync)(quarantined, target);
|
|
11167
11374
|
throw error;
|
|
11168
11375
|
}
|
|
11169
11376
|
return {
|
|
@@ -11171,7 +11378,7 @@ async function installCursorPluginCheckout(env = process.env) {
|
|
|
11171
11378
|
detail: movedOld ? `installed canonical Cursor plugin; previous checkout quarantined at ${quarantined}` : `installed canonical Cursor plugin at ${target}`
|
|
11172
11379
|
};
|
|
11173
11380
|
} catch (error) {
|
|
11174
|
-
if ((0,
|
|
11381
|
+
if ((0, import_node_fs16.existsSync)(staged)) (0, import_node_fs16.rmSync)(staged, { recursive: true, force: true });
|
|
11175
11382
|
return { ok: false, detail: error.message.trim().slice(0, 240).replace(/\s+/g, " ") };
|
|
11176
11383
|
}
|
|
11177
11384
|
}
|
|
@@ -11221,7 +11428,7 @@ async function applyPluginHeal(surface, log, opts) {
|
|
|
11221
11428
|
const refSupported = await marketplaceAddRefSupported(bin);
|
|
11222
11429
|
const { steps } = adaptHealStepsForRefSupport(tableSteps, refSupported);
|
|
11223
11430
|
log(healBannerLine(bin, token, refSupported));
|
|
11224
|
-
const pinsPath = (0,
|
|
11431
|
+
const pinsPath = (0, import_node_path16.join)((0, import_node_os5.homedir)(), ...KNOWN_MARKETPLACES_RELATIVE);
|
|
11225
11432
|
const pins = token === "claude" ? captureMarketplacePins(readKnownMarketplacesFile(pinsPath), [MMI_MARKETPLACE_NAME, JERV_MARKETPLACE_NAME]) : /* @__PURE__ */ new Map();
|
|
11226
11433
|
try {
|
|
11227
11434
|
for (const step of steps) {
|
|
@@ -11296,7 +11503,7 @@ async function healActivePluginForDoctor(surface = detectSurface(process.env), o
|
|
|
11296
11503
|
}
|
|
11297
11504
|
function readKnownMarketplacesFile(path2) {
|
|
11298
11505
|
try {
|
|
11299
|
-
return (0,
|
|
11506
|
+
return (0, import_node_fs16.existsSync)(path2) ? (0, import_node_fs16.readFileSync)(path2, "utf8") : void 0;
|
|
11300
11507
|
} catch {
|
|
11301
11508
|
return void 0;
|
|
11302
11509
|
}
|
|
@@ -11323,7 +11530,7 @@ function writeMarketplacePinsOnDisk(path2, pins, succeeded, failedVerb, declineW
|
|
|
11323
11530
|
const declined = declineWhileHostLive?.();
|
|
11324
11531
|
if (declined) return declined;
|
|
11325
11532
|
try {
|
|
11326
|
-
(0,
|
|
11533
|
+
(0, import_node_fs16.writeFileSync)(path2, next, "utf8");
|
|
11327
11534
|
} catch {
|
|
11328
11535
|
return `could NOT ${failedVerb} ${[...pins.keys()].join(", ")} \u2014 set it by hand`;
|
|
11329
11536
|
}
|
|
@@ -11358,15 +11565,15 @@ function applyOrgMarketplacePins(path2, names, hostIsRunning = claudeCodeIsRunni
|
|
|
11358
11565
|
}
|
|
11359
11566
|
function writeMarketplacePinPending(path2, names, now = Date.now()) {
|
|
11360
11567
|
try {
|
|
11361
|
-
(0,
|
|
11362
|
-
(0,
|
|
11568
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path16.dirname)(path2), { recursive: true });
|
|
11569
|
+
(0, import_node_fs16.writeFileSync)(path2, `${JSON.stringify({ v: 1, names: [...names], at: new Date(now).toISOString() })}
|
|
11363
11570
|
`, "utf8");
|
|
11364
11571
|
} catch {
|
|
11365
11572
|
}
|
|
11366
11573
|
}
|
|
11367
11574
|
function readMarketplacePinPending(path2, name, now = Date.now()) {
|
|
11368
11575
|
try {
|
|
11369
|
-
const parsed = JSON.parse((0,
|
|
11576
|
+
const parsed = JSON.parse((0, import_node_fs16.readFileSync)(path2, "utf8"));
|
|
11370
11577
|
const at = typeof parsed.at === "string" ? Date.parse(parsed.at) : Number.NaN;
|
|
11371
11578
|
if (parsed.v !== 1 || !Array.isArray(parsed.names) || !parsed.names.includes(name) || !Number.isFinite(at)) return void 0;
|
|
11372
11579
|
if (now - at < 0 || now - at > 12 * 60 * 6e4) return void 0;
|
|
@@ -12498,17 +12705,17 @@ async function dispatchDeploy(deps, ctx, stage, ref, model, watch, autoRunSince,
|
|
|
12498
12705
|
return { note: `no manual dispatch: ${model} repo deploys via its own push-triggered workflow`, deployStatus: "pending" };
|
|
12499
12706
|
}
|
|
12500
12707
|
function readLocalGateWorkflows() {
|
|
12501
|
-
const dir = (0,
|
|
12708
|
+
const dir = (0, import_node_path17.join)(".github", "workflows");
|
|
12502
12709
|
let names;
|
|
12503
12710
|
try {
|
|
12504
|
-
names = (0,
|
|
12711
|
+
names = (0, import_node_fs17.readdirSync)(dir);
|
|
12505
12712
|
} catch {
|
|
12506
12713
|
return null;
|
|
12507
12714
|
}
|
|
12508
12715
|
const files = [];
|
|
12509
12716
|
for (const name of names.filter(isGateWorkflowPath)) {
|
|
12510
12717
|
try {
|
|
12511
|
-
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0,
|
|
12718
|
+
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs17.readFileSync)((0, import_node_path17.join)(dir, name), "utf8") });
|
|
12512
12719
|
} catch {
|
|
12513
12720
|
}
|
|
12514
12721
|
}
|
|
@@ -14610,12 +14817,12 @@ async function runPrLand(prNumber, options, deps) {
|
|
|
14610
14817
|
}
|
|
14611
14818
|
|
|
14612
14819
|
// src/wave-status.ts
|
|
14613
|
-
var
|
|
14820
|
+
var import_node_fs19 = require("node:fs");
|
|
14614
14821
|
|
|
14615
14822
|
// src/stage-runner.ts
|
|
14616
14823
|
var import_node_child_process7 = require("node:child_process");
|
|
14617
|
-
var
|
|
14618
|
-
var
|
|
14824
|
+
var import_node_fs18 = require("node:fs");
|
|
14825
|
+
var import_node_path18 = require("node:path");
|
|
14619
14826
|
var import_node_net = require("node:net");
|
|
14620
14827
|
var import_node_util5 = require("node:util");
|
|
14621
14828
|
|
|
@@ -14730,7 +14937,7 @@ function containersPublishingPort(containers, port) {
|
|
|
14730
14937
|
return containers.filter((c) => c.publishedHostPorts.includes(port));
|
|
14731
14938
|
}
|
|
14732
14939
|
function isOwnComposeContainer(container, cwd) {
|
|
14733
|
-
if (container.composeWorkingDir &&
|
|
14940
|
+
if (container.composeWorkingDir && normPath3(container.composeWorkingDir) === normPath3(cwd)) return true;
|
|
14734
14941
|
if (container.composeWorkingDir && pathUnder(container.composeWorkingDir, cwd)) return true;
|
|
14735
14942
|
const derived = deriveComposeProjectName(cwd);
|
|
14736
14943
|
return Boolean(derived && container.composeProject === derived);
|
|
@@ -14754,13 +14961,13 @@ function appendForceRecreate(up) {
|
|
|
14754
14961
|
return `${up.trimEnd()} --force-recreate`;
|
|
14755
14962
|
}
|
|
14756
14963
|
function stageStatePath(cwd = process.cwd()) {
|
|
14757
|
-
return (0,
|
|
14964
|
+
return (0, import_node_path18.join)(cwd, "tmp", "stage", "state.json");
|
|
14758
14965
|
}
|
|
14759
14966
|
function stageGlobalStatePath(cwd = process.cwd(), gitCommonDir = ".git") {
|
|
14760
|
-
const dir = (0,
|
|
14761
|
-
return (0,
|
|
14967
|
+
const dir = (0, import_node_path18.isAbsolute)(gitCommonDir) ? gitCommonDir : (0, import_node_path18.resolve)(cwd, gitCommonDir);
|
|
14968
|
+
return (0, import_node_path18.join)(dir, "mmi", "stage", "state.json");
|
|
14762
14969
|
}
|
|
14763
|
-
function
|
|
14970
|
+
function normPath3(path2) {
|
|
14764
14971
|
return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
14765
14972
|
}
|
|
14766
14973
|
function parseWorktreeListPaths(stdout) {
|
|
@@ -14791,8 +14998,8 @@ function parseStagePortFlag(raw) {
|
|
|
14791
14998
|
return port;
|
|
14792
14999
|
}
|
|
14793
15000
|
function pathUnder(childPath, parentPath) {
|
|
14794
|
-
const child2 =
|
|
14795
|
-
const parent =
|
|
15001
|
+
const child2 = normPath3(childPath);
|
|
15002
|
+
const parent = normPath3(parentPath);
|
|
14796
15003
|
return Boolean(child2 && parent && (child2 === parent || child2.startsWith(`${parent}/`)));
|
|
14797
15004
|
}
|
|
14798
15005
|
function stageStateMatchesRequiredCwd(state, requiredCwd) {
|
|
@@ -14956,8 +15163,8 @@ async function resolveStagePort(config, guard, reservedPorts = /* @__PURE__ */ n
|
|
|
14956
15163
|
return pickStagePort(config.portRange, (p) => free.has(p));
|
|
14957
15164
|
}
|
|
14958
15165
|
async function reservedPortsForWorktree(cwd) {
|
|
14959
|
-
const self =
|
|
14960
|
-
const siblings = (await listRepoWorktreePaths(cwd)).filter((p) =>
|
|
15166
|
+
const self = normPath3(cwd);
|
|
15167
|
+
const siblings = (await listRepoWorktreePaths(cwd)).filter((p) => normPath3(p) !== self);
|
|
14961
15168
|
return collectReservedStagePorts(siblings);
|
|
14962
15169
|
}
|
|
14963
15170
|
async function assertStagePortAvailable(port, cwd, guard, reserved) {
|
|
@@ -14982,14 +15189,14 @@ function stageProcessEnv(stagePort, extraEnv) {
|
|
|
14982
15189
|
}
|
|
14983
15190
|
async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
14984
15191
|
if (!config.ensureEnv) return;
|
|
14985
|
-
const target = (0,
|
|
14986
|
-
const example = (0,
|
|
14987
|
-
if (!(0,
|
|
14988
|
-
(0,
|
|
14989
|
-
} else if ((0,
|
|
14990
|
-
const stale = detectStaleEnvFile((0,
|
|
14991
|
-
exampleMtimeMs: (0,
|
|
14992
|
-
targetMtimeMs: (0,
|
|
15192
|
+
const target = (0, import_node_path18.join)(cwd, config.ensureEnv.target);
|
|
15193
|
+
const example = (0, import_node_path18.join)(cwd, config.ensureEnv.example);
|
|
15194
|
+
if (!(0, import_node_fs18.existsSync)(target) && (0, import_node_fs18.existsSync)(example)) {
|
|
15195
|
+
(0, import_node_fs18.copyFileSync)(example, target);
|
|
15196
|
+
} else if ((0, import_node_fs18.existsSync)(target) && (0, import_node_fs18.existsSync)(example)) {
|
|
15197
|
+
const stale = detectStaleEnvFile((0, import_node_fs18.readFileSync)(example, "utf8"), (0, import_node_fs18.readFileSync)(target, "utf8"), {
|
|
15198
|
+
exampleMtimeMs: (0, import_node_fs18.statSync)(example).mtimeMs,
|
|
15199
|
+
targetMtimeMs: (0, import_node_fs18.statSync)(target).mtimeMs
|
|
14993
15200
|
});
|
|
14994
15201
|
if (stale) {
|
|
14995
15202
|
const msg = `stale ${config.ensureEnv.target} (${stale}) \u2014 delete it or refresh from ${config.ensureEnv.example} before re-running /stage`;
|
|
@@ -14997,8 +15204,8 @@ async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
|
14997
15204
|
console.error(`mmi-cli stage: ${msg} (allowed via --allow-stale-env)`);
|
|
14998
15205
|
}
|
|
14999
15206
|
}
|
|
15000
|
-
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0,
|
|
15001
|
-
(0,
|
|
15207
|
+
if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0, import_node_fs18.existsSync)(target)) {
|
|
15208
|
+
(0, import_node_fs18.writeFileSync)(target, mergeEnvSecretsIntoFile((0, import_node_fs18.readFileSync)(target, "utf8"), opts.vaultEnvMerge), "utf8");
|
|
15002
15209
|
}
|
|
15003
15210
|
}
|
|
15004
15211
|
async function gitText(cwd, args) {
|
|
@@ -15028,20 +15235,20 @@ async function resolveGlobalStatePath(cwd, explicit) {
|
|
|
15028
15235
|
return void 0;
|
|
15029
15236
|
}
|
|
15030
15237
|
function readState(path2) {
|
|
15031
|
-
if (!(0,
|
|
15238
|
+
if (!(0, import_node_fs18.existsSync)(path2)) return null;
|
|
15032
15239
|
try {
|
|
15033
|
-
return JSON.parse((0,
|
|
15240
|
+
return JSON.parse((0, import_node_fs18.readFileSync)(path2, "utf8"));
|
|
15034
15241
|
} catch {
|
|
15035
15242
|
return null;
|
|
15036
15243
|
}
|
|
15037
15244
|
}
|
|
15038
15245
|
function mkdirFor(path2) {
|
|
15039
15246
|
const dir = path2.slice(0, Math.max(path2.lastIndexOf("/"), path2.lastIndexOf("\\")));
|
|
15040
|
-
(0,
|
|
15247
|
+
(0, import_node_fs18.mkdirSync)(dir, { recursive: true });
|
|
15041
15248
|
}
|
|
15042
15249
|
function writeState(path2, state) {
|
|
15043
15250
|
mkdirFor(path2);
|
|
15044
|
-
(0,
|
|
15251
|
+
(0, import_node_fs18.writeFileSync)(path2, JSON.stringify(state, null, 2), "utf8");
|
|
15045
15252
|
}
|
|
15046
15253
|
function writeStagePortReservation(port, cwd, statePath, globalStatePath, now) {
|
|
15047
15254
|
const reservation = {
|
|
@@ -15061,7 +15268,7 @@ async function cleanupStageState(state, paths, timeoutMs, fallbackCwd) {
|
|
|
15061
15268
|
await shell(state.teardown.command.trim(), state.teardown.cwd || state.cwd || fallbackCwd, Math.max(timeoutMs, 1e4));
|
|
15062
15269
|
}
|
|
15063
15270
|
for (const path2 of [...new Set(paths.filter((p) => Boolean(p)))]) {
|
|
15064
|
-
(0,
|
|
15271
|
+
(0, import_node_fs18.rmSync)(path2, { force: true });
|
|
15065
15272
|
}
|
|
15066
15273
|
}
|
|
15067
15274
|
async function killTree(pid) {
|
|
@@ -15219,8 +15426,8 @@ async function runStage(config = {}, opts = {}) {
|
|
|
15219
15426
|
await ensureStageRuntimeEnv(config, opts, cwd);
|
|
15220
15427
|
if (build) await shell(sub(build), cwd, timeoutMs, stageProcessEnv(stagePort, extraEnv));
|
|
15221
15428
|
} catch (e) {
|
|
15222
|
-
(0,
|
|
15223
|
-
if (globalStatePath && globalStatePath !== statePath) (0,
|
|
15429
|
+
(0, import_node_fs18.rmSync)(statePath, { force: true });
|
|
15430
|
+
if (globalStatePath && globalStatePath !== statePath) (0, import_node_fs18.rmSync)(globalStatePath, { force: true });
|
|
15224
15431
|
throw e;
|
|
15225
15432
|
}
|
|
15226
15433
|
const started = await startStage(config, {
|
|
@@ -15241,9 +15448,9 @@ function parseNextFromHead(headText) {
|
|
|
15241
15448
|
}
|
|
15242
15449
|
function readStageSummary(worktreePath) {
|
|
15243
15450
|
const statePath = stageStatePath(worktreePath);
|
|
15244
|
-
if (!(0,
|
|
15451
|
+
if (!(0, import_node_fs19.existsSync)(statePath)) return void 0;
|
|
15245
15452
|
try {
|
|
15246
|
-
const state = JSON.parse((0,
|
|
15453
|
+
const state = JSON.parse((0, import_node_fs19.readFileSync)(statePath, "utf8"));
|
|
15247
15454
|
const port = typeof state.port === "number" ? state.port : void 0;
|
|
15248
15455
|
if (port == null || !Number.isInteger(port) || port <= 0) return void 0;
|
|
15249
15456
|
return { port, url: typeof state.url === "string" ? state.url : void 0 };
|
|
@@ -15348,13 +15555,13 @@ async function executeWaveLand(plan, deps, opts = { preserveWorktree: true }) {
|
|
|
15348
15555
|
}
|
|
15349
15556
|
|
|
15350
15557
|
// src/index.ts
|
|
15351
|
-
var
|
|
15558
|
+
var import_node_os18 = require("node:os");
|
|
15352
15559
|
|
|
15353
15560
|
// src/board.ts
|
|
15354
15561
|
var import_node_child_process9 = require("node:child_process");
|
|
15355
|
-
var
|
|
15356
|
-
var
|
|
15357
|
-
var
|
|
15562
|
+
var import_node_fs22 = require("node:fs");
|
|
15563
|
+
var import_node_os8 = require("node:os");
|
|
15564
|
+
var import_node_path21 = require("node:path");
|
|
15358
15565
|
var import_node_util6 = require("node:util");
|
|
15359
15566
|
|
|
15360
15567
|
// src/board-priority.ts
|
|
@@ -15708,9 +15915,9 @@ function boardConfigFromProject(meta, floor = {}) {
|
|
|
15708
15915
|
}
|
|
15709
15916
|
|
|
15710
15917
|
// src/cli-doctor-shared.ts
|
|
15711
|
-
var import_node_fs19 = require("node:fs");
|
|
15712
|
-
var import_node_path19 = require("node:path");
|
|
15713
15918
|
var import_node_fs20 = require("node:fs");
|
|
15919
|
+
var import_node_path20 = require("node:path");
|
|
15920
|
+
var import_node_fs21 = require("node:fs");
|
|
15714
15921
|
|
|
15715
15922
|
// src/readiness-audit.ts
|
|
15716
15923
|
var TENANT_DEPLOY_RUN_SCAN_LIMIT = 100;
|
|
@@ -15827,12 +16034,12 @@ ${lines.join("\n")}`;
|
|
|
15827
16034
|
|
|
15828
16035
|
// src/secrets.ts
|
|
15829
16036
|
var import_node_child_process8 = require("node:child_process");
|
|
15830
|
-
var
|
|
16037
|
+
var import_node_os7 = require("node:os");
|
|
15831
16038
|
|
|
15832
16039
|
// src/gh-create.ts
|
|
15833
16040
|
var import_promises4 = require("node:fs/promises");
|
|
15834
|
-
var
|
|
15835
|
-
var
|
|
16041
|
+
var import_node_os6 = require("node:os");
|
|
16042
|
+
var import_node_path19 = require("node:path");
|
|
15836
16043
|
var import_node_crypto3 = require("node:crypto");
|
|
15837
16044
|
var ISSUE_TYPES = ["bug", "feature", "task"];
|
|
15838
16045
|
var GH_MUTATION_TIMEOUT_MS = 12e4;
|
|
@@ -15885,9 +16092,9 @@ async function bodyArgsViaFile(args, deps = {}) {
|
|
|
15885
16092
|
const write = deps.write ?? import_promises4.writeFile;
|
|
15886
16093
|
const remove2 = deps.remove ?? import_promises4.unlink;
|
|
15887
16094
|
const ensureDir = deps.ensureDir ?? import_promises4.mkdir;
|
|
15888
|
-
const dir = deps.dir ?? (0,
|
|
15889
|
-
const file = (0,
|
|
15890
|
-
await ensureDir((0,
|
|
16095
|
+
const dir = deps.dir ?? (0, import_node_os6.tmpdir)();
|
|
16096
|
+
const file = (0, import_node_path19.join)(dir, `mmi-gh-body-${process.pid}-${(0, import_node_crypto3.randomBytes)(4).toString("hex")}.md`);
|
|
16097
|
+
await ensureDir((0, import_node_path19.dirname)(file), { recursive: true }).catch(() => {
|
|
15891
16098
|
});
|
|
15892
16099
|
await write(file, args[i + 1], "utf8");
|
|
15893
16100
|
return {
|
|
@@ -17064,7 +17271,7 @@ function resolveSpawnTarget(command, args, platform2 = process.platform) {
|
|
|
17064
17271
|
}
|
|
17065
17272
|
function spawnExitCode(status, signal) {
|
|
17066
17273
|
if (status != null) return status;
|
|
17067
|
-
const signo = signal ?
|
|
17274
|
+
const signo = signal ? import_node_os7.constants.signals[signal] : void 0;
|
|
17068
17275
|
return signo ? 128 + signo : 1;
|
|
17069
17276
|
}
|
|
17070
17277
|
function defaultSpawn(command, args, env) {
|
|
@@ -17411,7 +17618,7 @@ async function localBranchHeads() {
|
|
|
17411
17618
|
}
|
|
17412
17619
|
async function currentRepoWorktreeGitRoot(repoRoot2) {
|
|
17413
17620
|
const gitCommonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
17414
|
-
return gitCommonDir ? (0,
|
|
17621
|
+
return gitCommonDir ? (0, import_node_path20.resolve)(repoRoot2, gitCommonDir, "worktrees") : "";
|
|
17415
17622
|
}
|
|
17416
17623
|
async function worktreeBranches() {
|
|
17417
17624
|
const { stdout } = await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS });
|
|
@@ -17431,18 +17638,18 @@ function resolveGitdirForWorktreeFile(worktreePath, content) {
|
|
|
17431
17638
|
const match = /^gitdir:\s*(.+)\s*$/im.exec(content);
|
|
17432
17639
|
if (!match?.[1]) return void 0;
|
|
17433
17640
|
const raw = match[1].trim();
|
|
17434
|
-
return (0,
|
|
17641
|
+
return (0, import_node_path20.isAbsolute)(raw) ? raw : (0, import_node_path20.resolve)(worktreePath, raw);
|
|
17435
17642
|
}
|
|
17436
17643
|
function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
17437
17644
|
if (!worktreeGitRoot) return false;
|
|
17438
17645
|
try {
|
|
17439
|
-
const entries = (0,
|
|
17646
|
+
const entries = (0, import_node_fs21.readdirSync)(worktreeGitRoot, { withFileTypes: true });
|
|
17440
17647
|
for (const ent of entries) {
|
|
17441
17648
|
if (!ent.isDirectory()) continue;
|
|
17442
17649
|
try {
|
|
17443
|
-
const gitdirPath = (0,
|
|
17444
|
-
const resolvedGitdir = (0,
|
|
17445
|
-
if (sameWorktreeMetadataPath((0,
|
|
17650
|
+
const gitdirPath = (0, import_node_fs20.readFileSync)((0, import_node_path20.join)(worktreeGitRoot, ent.name, "gitdir"), "utf8").trim();
|
|
17651
|
+
const resolvedGitdir = (0, import_node_path20.isAbsolute)(gitdirPath) ? gitdirPath : (0, import_node_path20.resolve)(worktreeGitRoot, ent.name, gitdirPath);
|
|
17652
|
+
if (sameWorktreeMetadataPath((0, import_node_path20.dirname)(resolvedGitdir), worktreePath)) return true;
|
|
17446
17653
|
} catch {
|
|
17447
17654
|
}
|
|
17448
17655
|
}
|
|
@@ -17452,7 +17659,7 @@ function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
|
17452
17659
|
}
|
|
17453
17660
|
function pathExistsKnown(path2) {
|
|
17454
17661
|
try {
|
|
17455
|
-
(0,
|
|
17662
|
+
(0, import_node_fs21.statSync)(path2);
|
|
17456
17663
|
return true;
|
|
17457
17664
|
} catch (e) {
|
|
17458
17665
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
@@ -17461,10 +17668,10 @@ function pathExistsKnown(path2) {
|
|
|
17461
17668
|
}
|
|
17462
17669
|
}
|
|
17463
17670
|
function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
17464
|
-
const gitPath = (0,
|
|
17671
|
+
const gitPath = (0, import_node_path20.join)(path2, ".git");
|
|
17465
17672
|
let st;
|
|
17466
17673
|
try {
|
|
17467
|
-
st = (0,
|
|
17674
|
+
st = (0, import_node_fs21.lstatSync)(gitPath);
|
|
17468
17675
|
} catch (e) {
|
|
17469
17676
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
17470
17677
|
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
@@ -17481,7 +17688,7 @@ function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
|
17481
17688
|
if (st.isDirectory()) return { path: path2, gitType: "dir" };
|
|
17482
17689
|
if (!st.isFile()) return { path: path2, gitType: "other" };
|
|
17483
17690
|
try {
|
|
17484
|
-
const gitFileContent = (0,
|
|
17691
|
+
const gitFileContent = (0, import_node_fs20.readFileSync)(gitPath, "utf8");
|
|
17485
17692
|
const gitdir = resolveGitdirForWorktreeFile(path2, gitFileContent);
|
|
17486
17693
|
const gitDirExists = gitdir ? pathExistsKnown(gitdir) : false;
|
|
17487
17694
|
return {
|
|
@@ -17489,7 +17696,7 @@ function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
|
17489
17696
|
gitType: "file",
|
|
17490
17697
|
gitFileContent,
|
|
17491
17698
|
gitDirExists,
|
|
17492
|
-
ownedByCurrentRepo: Boolean(gitdir && worktreeGitRoot &&
|
|
17699
|
+
ownedByCurrentRepo: Boolean(gitdir && worktreeGitRoot && isPathUnderDirectory2(gitdir, worktreeGitRoot)),
|
|
17493
17700
|
detail: gitDirExists === void 0 ? "gitdir existence could not be verified" : void 0
|
|
17494
17701
|
};
|
|
17495
17702
|
} catch {
|
|
@@ -17498,7 +17705,7 @@ function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
|
17498
17705
|
}
|
|
17499
17706
|
function inspectDeadWorktreeDirContent(path2) {
|
|
17500
17707
|
try {
|
|
17501
|
-
return { entries: (0,
|
|
17708
|
+
return { entries: (0, import_node_fs21.readdirSync)(path2) };
|
|
17502
17709
|
} catch (e) {
|
|
17503
17710
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
17504
17711
|
return { error: code ? `unable to inspect directory contents (${code})` : "unable to inspect directory contents" };
|
|
@@ -17515,7 +17722,7 @@ async function preservedBranches() {
|
|
|
17515
17722
|
async function siblingWorktreeDirs(explicitRoot) {
|
|
17516
17723
|
const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
17517
17724
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
17518
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
17725
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path20.dirname)((0, import_node_path20.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
17519
17726
|
try {
|
|
17520
17727
|
const dirs = explicitRoot ? listDirsIn(resolveExplicitScanRoot(explicitRoot, primaryRepoRoot)) : worktreeScanDirs(siblingMmiWorktreesRoot(primaryRepoRoot), primaryRepoRoot, listDirsIn, isRepoCheckoutDir);
|
|
17521
17728
|
return dirs.map((dir) => inspectSiblingWorktreeDir(dir, worktreeGitRoot)).filter((entry) => Boolean(entry));
|
|
@@ -17525,18 +17732,18 @@ async function siblingWorktreeDirs(explicitRoot) {
|
|
|
17525
17732
|
}
|
|
17526
17733
|
function listDirsIn(dir) {
|
|
17527
17734
|
try {
|
|
17528
|
-
return (0,
|
|
17735
|
+
return (0, import_node_fs21.readdirSync)(dir, { withFileTypes: true }).filter((ent) => ent.isDirectory()).map((ent) => (0, import_node_path20.join)(dir, ent.name));
|
|
17529
17736
|
} catch {
|
|
17530
17737
|
return [];
|
|
17531
17738
|
}
|
|
17532
17739
|
}
|
|
17533
17740
|
function isRepoCheckoutDir(dir) {
|
|
17534
|
-
return (0,
|
|
17741
|
+
return (0, import_node_fs21.existsSync)((0, import_node_path20.join)(dir, ".git"));
|
|
17535
17742
|
}
|
|
17536
17743
|
function resolveExplicitScanRoot(explicitRoot, repoRoot2) {
|
|
17537
17744
|
let rootDirs;
|
|
17538
17745
|
try {
|
|
17539
|
-
rootDirs = (0,
|
|
17746
|
+
rootDirs = (0, import_node_fs21.readdirSync)(explicitRoot, { withFileTypes: true }).filter((ent) => ent.isDirectory()).map((ent) => ent.name);
|
|
17540
17747
|
} catch {
|
|
17541
17748
|
return explicitRoot;
|
|
17542
17749
|
}
|
|
@@ -18884,7 +19091,7 @@ var CLAIM_SESSION_ACTIVITY_MS = 30 * 6e4;
|
|
|
18884
19091
|
var CLAIM_SESSION_PROBE_CACHE_MS = 6e4;
|
|
18885
19092
|
var claimSessionProbeCache = /* @__PURE__ */ new Map();
|
|
18886
19093
|
function probeLocalClaimSession(marker, now = Date.now()) {
|
|
18887
|
-
if (!marker.session || !marker.host || marker.host.toLowerCase() !== (0,
|
|
19094
|
+
if (!marker.session || !marker.host || marker.host.toLowerCase() !== (0, import_node_os8.hostname)().toLowerCase()) return void 0;
|
|
18888
19095
|
if (!marker.surface?.toLowerCase().startsWith("claude")) return void 0;
|
|
18889
19096
|
const cacheKey = `${marker.host.toLowerCase()}/${marker.session}`;
|
|
18890
19097
|
const cached = claimSessionProbeCache.get(cacheKey);
|
|
@@ -18893,17 +19100,17 @@ function probeLocalClaimSession(marker, now = Date.now()) {
|
|
|
18893
19100
|
claimSessionProbeCache.set(cacheKey, { checkedAt: now, state });
|
|
18894
19101
|
return state;
|
|
18895
19102
|
};
|
|
18896
|
-
const root = (0,
|
|
19103
|
+
const root = (0, import_node_path21.join)((0, import_node_os8.homedir)(), ".claude", "projects");
|
|
18897
19104
|
try {
|
|
18898
19105
|
const wanted = `${marker.session}.jsonl`.toLowerCase();
|
|
18899
19106
|
const pending = [root];
|
|
18900
19107
|
while (pending.length) {
|
|
18901
19108
|
const dir = pending.pop();
|
|
18902
|
-
for (const entry of (0,
|
|
18903
|
-
const path2 = (0,
|
|
19109
|
+
for (const entry of (0, import_node_fs22.readdirSync)(dir, { withFileTypes: true })) {
|
|
19110
|
+
const path2 = (0, import_node_path21.join)(dir, entry.name);
|
|
18904
19111
|
if (entry.isDirectory()) pending.push(path2);
|
|
18905
19112
|
else if (entry.isFile() && entry.name.toLowerCase() === wanted) {
|
|
18906
|
-
return remember(now - (0,
|
|
19113
|
+
return remember(now - (0, import_node_fs22.statSync)(path2).mtimeMs <= CLAIM_SESSION_ACTIVITY_MS ? "live" : "dead");
|
|
18907
19114
|
}
|
|
18908
19115
|
}
|
|
18909
19116
|
}
|
|
@@ -19182,7 +19389,7 @@ async function resolveAutoAddBoardAttach(client, cfg, selector, priority, warn =
|
|
|
19182
19389
|
}
|
|
19183
19390
|
|
|
19184
19391
|
// src/issue-body.ts
|
|
19185
|
-
var
|
|
19392
|
+
var import_node_os9 = require("node:os");
|
|
19186
19393
|
var TextArgError = class extends Error {
|
|
19187
19394
|
constructor(message, code, offendingFlag) {
|
|
19188
19395
|
super(message);
|
|
@@ -19194,7 +19401,7 @@ var TextArgError = class extends Error {
|
|
|
19194
19401
|
offendingFlag;
|
|
19195
19402
|
};
|
|
19196
19403
|
function emptyStdinMessage(fileFlag) {
|
|
19197
|
-
if ((0,
|
|
19404
|
+
if ((0, import_node_os9.platform)() === "win32") {
|
|
19198
19405
|
return `${fileFlag} - read empty stdin (on Windows, ${fileFlag} - is unreliable through the npm .cmd shim \u2014 use ${fileFlag} <path>, or pipe to \`node cli/dist/index.cjs\` directly)`;
|
|
19199
19406
|
}
|
|
19200
19407
|
return `${fileFlag} - read empty stdin (nothing piped \u2014 pass a heredoc/pipe, or ${fileFlag} <path>)`;
|
|
@@ -19299,6 +19506,102 @@ function applyChecklistCheck(body, query, checked) {
|
|
|
19299
19506
|
return { ok: true, edit: setChecklistMarker(body, sel.item, checked), item: sel.item };
|
|
19300
19507
|
}
|
|
19301
19508
|
|
|
19509
|
+
// src/house-map.ts
|
|
19510
|
+
var HOUSE_ROOTS = ["oracle", "harbour", "devops", "vault", "learning"];
|
|
19511
|
+
function isHouseRoot(token) {
|
|
19512
|
+
return HOUSE_ROOTS.includes(token);
|
|
19513
|
+
}
|
|
19514
|
+
var COMPAT_SHIM_DEATH_WAVE = "Wave 3";
|
|
19515
|
+
var COMPAT_SHIM_NOTE = `compatibility shim \u2014 the flat Wave 0 path; use the canonical house-prefixed form; dies in ${COMPAT_SHIM_DEATH_WAVE} (#4316)`;
|
|
19516
|
+
var HOUSE_QUESTIONS = {
|
|
19517
|
+
oracle: "Is it live?",
|
|
19518
|
+
harbour: "Is it bounded?",
|
|
19519
|
+
devops: "Is it legal?",
|
|
19520
|
+
vault: "Is it guarded?",
|
|
19521
|
+
learning: "Did it learn?",
|
|
19522
|
+
core: "Is it one door?"
|
|
19523
|
+
};
|
|
19524
|
+
var HOUSE_MAP = {
|
|
19525
|
+
// --- oracle — live truth ------------------------------------------------------------------------
|
|
19526
|
+
board: "oracle",
|
|
19527
|
+
// board management (read + the guarded mutations that keep it live)
|
|
19528
|
+
issue: "oracle",
|
|
19529
|
+
// issues are live org truth
|
|
19530
|
+
find: "oracle",
|
|
19531
|
+
// estate search door
|
|
19532
|
+
"repo-index": "oracle",
|
|
19533
|
+
// Hub cloud + local pointer index
|
|
19534
|
+
wave: "oracle",
|
|
19535
|
+
// read-side multi-worktree board visibility (`wave status`)
|
|
19536
|
+
next: "oracle",
|
|
19537
|
+
// read-side board: the next actionable item
|
|
19538
|
+
docs: "oracle",
|
|
19539
|
+
// generated docs surfaces — the live org knowledge routing index
|
|
19540
|
+
org: "oracle",
|
|
19541
|
+
// org projects/registry/access reads (subgroup overrides below)
|
|
19542
|
+
"org project": "oracle",
|
|
19543
|
+
// registry projections (declared + projected live truth)
|
|
19544
|
+
"org access": "oracle",
|
|
19545
|
+
// org access role/audit reads
|
|
19546
|
+
"org config": "oracle",
|
|
19547
|
+
// live org configuration read
|
|
19548
|
+
// --- harbour — declared lanes -------------------------------------------------------------------
|
|
19549
|
+
"org schedules": "harbour",
|
|
19550
|
+
// register/run/park schedule rows under the one lane contract
|
|
19551
|
+
// --- devops — shipping --------------------------------------------------------------------------
|
|
19552
|
+
pr: "devops",
|
|
19553
|
+
ci: "devops",
|
|
19554
|
+
// the CI/gate audit
|
|
19555
|
+
rcand: "devops",
|
|
19556
|
+
release: "devops",
|
|
19557
|
+
hotfix: "devops",
|
|
19558
|
+
train: "devops",
|
|
19559
|
+
"wave land": "devops",
|
|
19560
|
+
// the write-side serial merge train is shipping, not board observation
|
|
19561
|
+
bootstrap: "devops",
|
|
19562
|
+
// repo provisioning + propagate
|
|
19563
|
+
runtime: "devops",
|
|
19564
|
+
// tenant/deploy/box/edge — shipping and central deploy
|
|
19565
|
+
"org rules": "devops",
|
|
19566
|
+
// org-managed repository rule delivery (.gitignore)
|
|
19567
|
+
// --- vault — secrets ----------------------------------------------------------------------------
|
|
19568
|
+
secrets: "vault",
|
|
19569
|
+
"org oauth": "vault",
|
|
19570
|
+
// OAuth credential planning/set/verify
|
|
19571
|
+
// --- learning — self-improvement ----------------------------------------------------------------
|
|
19572
|
+
report: "learning",
|
|
19573
|
+
// friction reports
|
|
19574
|
+
"skill-lesson": "learning",
|
|
19575
|
+
// --- core — the front door itself ---------------------------------------------------------------
|
|
19576
|
+
commands: "core",
|
|
19577
|
+
whoami: "core",
|
|
19578
|
+
worktree: "core",
|
|
19579
|
+
spawn: "core",
|
|
19580
|
+
// this repo's process-spawn contract
|
|
19581
|
+
tests: "core",
|
|
19582
|
+
// this repo's test-policy contract
|
|
19583
|
+
doctor: "core",
|
|
19584
|
+
stage: "core",
|
|
19585
|
+
plugin: "core",
|
|
19586
|
+
// plugin lifecycle + guards (CLI house owns plugins)
|
|
19587
|
+
explain: "core",
|
|
19588
|
+
// command-surface help
|
|
19589
|
+
status: "core",
|
|
19590
|
+
// repo-orientation snapshot (front door — torn toward oracle, kept core)
|
|
19591
|
+
onboard: "core"
|
|
19592
|
+
// repo-readiness orientation (front door — torn toward oracle, kept core)
|
|
19593
|
+
};
|
|
19594
|
+
function houseForPath(path2) {
|
|
19595
|
+
if (path2 === "") return "core";
|
|
19596
|
+
const segments = path2.split(" ");
|
|
19597
|
+
for (let end = segments.length; end > 0; end -= 1) {
|
|
19598
|
+
const key = segments.slice(0, end).join(" ");
|
|
19599
|
+
const house = HOUSE_MAP[key];
|
|
19600
|
+
if (house) return house;
|
|
19601
|
+
}
|
|
19602
|
+
return void 0;
|
|
19603
|
+
}
|
|
19604
|
+
|
|
19302
19605
|
// src/command-taxonomy.ts
|
|
19303
19606
|
var COMMAND_METADATA = /* @__PURE__ */ Symbol.for("mmi.commandTaxonomy.metadata");
|
|
19304
19607
|
var PRIMARY_GROUPS = [
|
|
@@ -19476,9 +19779,6 @@ function applyCommandTaxonomy(program3) {
|
|
|
19476
19779
|
function commandTaxonomyRank(name) {
|
|
19477
19780
|
return TOP_LEVEL_ORDER.get(name) ?? Number.MAX_SAFE_INTEGER;
|
|
19478
19781
|
}
|
|
19479
|
-
function commandHelpGroupRank(group) {
|
|
19480
|
-
return HELP_GROUP_ORDER.get(group) ?? Number.MAX_SAFE_INTEGER;
|
|
19481
|
-
}
|
|
19482
19782
|
function isCanonicalSuggestion(metadata) {
|
|
19483
19783
|
return metadata.category !== "internal";
|
|
19484
19784
|
}
|
|
@@ -19559,9 +19859,16 @@ function buildCommand(cmd, path2) {
|
|
|
19559
19859
|
module_owner: "unclassified",
|
|
19560
19860
|
consumer: "unclassified"
|
|
19561
19861
|
};
|
|
19862
|
+
const house = houseForPath(path2);
|
|
19863
|
+
if (!house) {
|
|
19864
|
+
throw new Error(
|
|
19865
|
+
`command-manifest: command '${path2}' has no house assignment \u2014 add it to cli/src/house-map.ts (the six-house taxonomy; every top-level command maps to exactly one house).`
|
|
19866
|
+
);
|
|
19867
|
+
}
|
|
19562
19868
|
const out = {
|
|
19563
19869
|
name: cmd.name(),
|
|
19564
19870
|
path: path2,
|
|
19871
|
+
house,
|
|
19565
19872
|
arguments: cmd.registeredArguments.map(buildArgument),
|
|
19566
19873
|
// A hand-parsed command registers no Commander options, so its declared set is merged in (#3682).
|
|
19567
19874
|
options: [...cmd.options.map(buildOption), ...readDeclaredOptions(cmd)],
|
|
@@ -19571,6 +19878,7 @@ function buildCommand(cmd, path2) {
|
|
|
19571
19878
|
...metadata
|
|
19572
19879
|
};
|
|
19573
19880
|
if (cmd._allowUnknownOption) out.parses_own_argv = true;
|
|
19881
|
+
if (path2 && house !== "core") out.shim = true;
|
|
19574
19882
|
const description = cmd.description();
|
|
19575
19883
|
if (description) out.description = description;
|
|
19576
19884
|
const examples = readExamples(cmd);
|
|
@@ -19596,6 +19904,22 @@ function collectLeaves(node, acc) {
|
|
|
19596
19904
|
}
|
|
19597
19905
|
for (const child2 of node.subcommands) collectLeaves(child2, acc);
|
|
19598
19906
|
}
|
|
19907
|
+
function buildHouses(tree) {
|
|
19908
|
+
const collect = (node, parentHouse, root, out) => {
|
|
19909
|
+
if (node.house === root && parentHouse !== root) {
|
|
19910
|
+
const entry = { path: node.path };
|
|
19911
|
+
if (node.description) entry.description = node.description;
|
|
19912
|
+
out.push(entry);
|
|
19913
|
+
}
|
|
19914
|
+
for (const child2 of node.subcommands) collect(child2, node.house, root, out);
|
|
19915
|
+
};
|
|
19916
|
+
return HOUSE_ROOTS.map((root) => {
|
|
19917
|
+
const commands = [];
|
|
19918
|
+
for (const top of tree.subcommands) collect(top, void 0, root, commands);
|
|
19919
|
+
commands.sort((a, b) => a.path.localeCompare(b.path));
|
|
19920
|
+
return { root, question: HOUSE_QUESTIONS[root], commands };
|
|
19921
|
+
});
|
|
19922
|
+
}
|
|
19599
19923
|
function buildCommandManifest(program3) {
|
|
19600
19924
|
const tree = buildCommand(program3, "");
|
|
19601
19925
|
const index = [];
|
|
@@ -19609,6 +19933,13 @@ function buildCommandManifest(program3) {
|
|
|
19609
19933
|
name: tree.name,
|
|
19610
19934
|
tree,
|
|
19611
19935
|
index,
|
|
19936
|
+
houses: buildHouses(tree),
|
|
19937
|
+
shim_contract: {
|
|
19938
|
+
canonical: "mmi-cli <house> <command> \u2026",
|
|
19939
|
+
shim: "mmi-cli <command> \u2026",
|
|
19940
|
+
dies: COMPAT_SHIM_DEATH_WAVE,
|
|
19941
|
+
note: COMPAT_SHIM_NOTE
|
|
19942
|
+
},
|
|
19612
19943
|
primary_tree: primaryTree,
|
|
19613
19944
|
primary_index: primaryIndex,
|
|
19614
19945
|
error_codes: ERROR_CODE_REFERENCE
|
|
@@ -19645,16 +19976,26 @@ function formatManifestHuman(manifest, options = {}) {
|
|
|
19645
19976
|
}
|
|
19646
19977
|
if (options.all) for (const child2 of node.subcommands) render(child2, depth + 1);
|
|
19647
19978
|
};
|
|
19648
|
-
|
|
19649
|
-
|
|
19650
|
-
|
|
19651
|
-
|
|
19652
|
-
|
|
19653
|
-
|
|
19654
|
-
|
|
19655
|
-
|
|
19656
|
-
|
|
19657
|
-
|
|
19979
|
+
const forHouse = (node, house) => {
|
|
19980
|
+
const subcommands = node.subcommands.map((child2) => forHouse(child2, house)).filter((child2) => Boolean(child2));
|
|
19981
|
+
if (node.house === house) return { ...node, subcommands };
|
|
19982
|
+
return subcommands.length ? { ...node, subcommands } : void 0;
|
|
19983
|
+
};
|
|
19984
|
+
const renderHouse = (house, label) => {
|
|
19985
|
+
const members = root.subcommands.map((command) => forHouse(command, house)).filter((command) => Boolean(command)).sort((a, b) => commandTaxonomyRank(a.name) - commandTaxonomyRank(b.name));
|
|
19986
|
+
if (!members.length) return;
|
|
19987
|
+
lines.push("", `${label}:`);
|
|
19988
|
+
for (const member of members) render(member, 1);
|
|
19989
|
+
};
|
|
19990
|
+
for (const house of HOUSE_ROOTS) renderHouse(house, `${house} \u2014 ${HOUSE_QUESTIONS[house]}`);
|
|
19991
|
+
renderHouse("core", `core \u2014 ${HOUSE_QUESTIONS.core}`);
|
|
19992
|
+
lines.push(
|
|
19993
|
+
"",
|
|
19994
|
+
`Canonical form: \`mmi-cli <house> <command> \u2026\`. The flat paths above are compatibility shims,`,
|
|
19995
|
+
`kept for one window so fleet scripts keep working \u2014 they die in ${COMPAT_SHIM_DEATH_WAVE} (#4316).`,
|
|
19996
|
+
"",
|
|
19997
|
+
options.all ? "Use `mmi-cli explain <group|command>` for a focused map." : "Use `mmi-cli explain <group|command>` for detail or `mmi-cli commands --all` for operational depth."
|
|
19998
|
+
);
|
|
19658
19999
|
return lines.join("\n");
|
|
19659
20000
|
}
|
|
19660
20001
|
|
|
@@ -19719,8 +20060,8 @@ function consolidateCommandNamespaces(program3) {
|
|
|
19719
20060
|
}
|
|
19720
20061
|
|
|
19721
20062
|
// src/pi-plugin-registration.ts
|
|
19722
|
-
var
|
|
19723
|
-
var
|
|
20063
|
+
var import_node_fs23 = require("node:fs");
|
|
20064
|
+
var import_node_path22 = require("node:path");
|
|
19724
20065
|
|
|
19725
20066
|
// src/plugin-cache-prune.ts
|
|
19726
20067
|
var PLUGIN_CACHE_KEEP = 2;
|
|
@@ -19974,37 +20315,37 @@ function newestExistingPiPlugin(home) {
|
|
|
19974
20315
|
const cacheRoot = pluginCacheRoot(home);
|
|
19975
20316
|
let names;
|
|
19976
20317
|
try {
|
|
19977
|
-
names = (0,
|
|
20318
|
+
names = (0, import_node_fs23.readdirSync)(cacheRoot);
|
|
19978
20319
|
} catch {
|
|
19979
20320
|
return void 0;
|
|
19980
20321
|
}
|
|
19981
20322
|
for (const version of names.filter(isVersionDirName).sort((a, b) => compareVersions(b, a))) {
|
|
19982
|
-
const candidate = (0,
|
|
19983
|
-
if ((0,
|
|
20323
|
+
const candidate = (0, import_node_path22.join)(cacheRoot, version, ".pi-plugin");
|
|
20324
|
+
if ((0, import_node_fs23.existsSync)(candidate)) return candidate;
|
|
19984
20325
|
}
|
|
19985
20326
|
return void 0;
|
|
19986
20327
|
}
|
|
19987
20328
|
function expectedPiPluginPath(home, env, installedVersion) {
|
|
19988
20329
|
const root = env.CLAUDE_PLUGIN_ROOT?.trim();
|
|
19989
|
-
if (root && /[\\/]mutmutco[\\/]mmi[\\/]/.test(root)) return (0,
|
|
20330
|
+
if (root && /[\\/]mutmutco[\\/]mmi[\\/]/.test(root)) return (0, import_node_path22.join)(root, ".pi-plugin");
|
|
19990
20331
|
const version = installedVersion ?? runningPluginVersion(env);
|
|
19991
20332
|
if (version) {
|
|
19992
|
-
const pinned = (0,
|
|
19993
|
-
if ((0,
|
|
20333
|
+
const pinned = (0, import_node_path22.join)(home, ".claude", "plugins", "cache", "mutmutco", "mmi", version, ".pi-plugin");
|
|
20334
|
+
if ((0, import_node_fs23.existsSync)(pinned)) return pinned;
|
|
19994
20335
|
}
|
|
19995
20336
|
return newestExistingPiPlugin(home);
|
|
19996
20337
|
}
|
|
19997
20338
|
function settingsPath(home) {
|
|
19998
|
-
return (0,
|
|
20339
|
+
return (0, import_node_path22.join)(home, ".pi", "agent", "settings.json");
|
|
19999
20340
|
}
|
|
20000
20341
|
function readPiPluginState(home, env, installedVersion) {
|
|
20001
|
-
if (!(0,
|
|
20342
|
+
if (!(0, import_node_fs23.existsSync)((0, import_node_path22.join)(home, ".pi", "agent"))) return void 0;
|
|
20002
20343
|
const expectedPath = expectedPiPluginPath(home, env, installedVersion);
|
|
20003
20344
|
if (!expectedPath) return void 0;
|
|
20004
20345
|
const file = settingsPath(home);
|
|
20005
|
-
if (!(0,
|
|
20346
|
+
if (!(0, import_node_fs23.existsSync)(file)) return { expectedPath, settingsReadable: true };
|
|
20006
20347
|
try {
|
|
20007
|
-
const parsed = JSON.parse((0,
|
|
20348
|
+
const parsed = JSON.parse((0, import_node_fs23.readFileSync)(file, "utf8"));
|
|
20008
20349
|
const packages = Array.isArray(parsed.packages) ? parsed.packages : [];
|
|
20009
20350
|
return { expectedPath, registeredPath: packages.find((p) => typeof p === "string" && isMmiPiPackage(p)), settingsReadable: true };
|
|
20010
20351
|
} catch {
|
|
@@ -20017,11 +20358,11 @@ function healPiPluginRegistration(home, env, installedVersion) {
|
|
|
20017
20358
|
if (!state.settingsReadable) return { ok: false, detail: "settings.json unreadable \u2014 nothing written (fail closed)" };
|
|
20018
20359
|
const file = settingsPath(home);
|
|
20019
20360
|
try {
|
|
20020
|
-
const parsed = (0,
|
|
20361
|
+
const parsed = (0, import_node_fs23.existsSync)(file) ? JSON.parse((0, import_node_fs23.readFileSync)(file, "utf8")) : {};
|
|
20021
20362
|
const packages = Array.isArray(parsed.packages) ? parsed.packages : [];
|
|
20022
20363
|
const next = packages.filter((p) => !(typeof p === "string" && isMmiPiPackage(p)));
|
|
20023
20364
|
next.push(state.expectedPath);
|
|
20024
|
-
(0,
|
|
20365
|
+
(0, import_node_fs23.writeFileSync)(file, `${JSON.stringify({ ...parsed, packages: next }, null, 2)}
|
|
20025
20366
|
`);
|
|
20026
20367
|
return { ok: true, detail: state.registeredPath ? `replaced ${state.registeredPath}` : `registered ${state.expectedPath}` };
|
|
20027
20368
|
} catch (e) {
|
|
@@ -21666,8 +22007,8 @@ function renderAccessReport(report) {
|
|
|
21666
22007
|
// src/repo-index.ts
|
|
21667
22008
|
var import_node_crypto4 = require("node:crypto");
|
|
21668
22009
|
var import_node_child_process12 = require("node:child_process");
|
|
21669
|
-
var
|
|
21670
|
-
var
|
|
22010
|
+
var import_node_fs24 = require("node:fs");
|
|
22011
|
+
var import_node_path23 = require("node:path");
|
|
21671
22012
|
var REPO_INDEX_SCHEMA = 1;
|
|
21672
22013
|
var HARD_DENY = [
|
|
21673
22014
|
/(^|\/)\.env(\.|$)/i,
|
|
@@ -21792,11 +22133,11 @@ function loadReadmeHints(cwd, candidatePaths) {
|
|
|
21792
22133
|
}
|
|
21793
22134
|
for (const rel of readmes) {
|
|
21794
22135
|
if (isHardDeniedPath(rel)) continue;
|
|
21795
|
-
const abs = (0,
|
|
21796
|
-
if (!(0,
|
|
22136
|
+
const abs = (0, import_node_path23.join)(cwd, ...rel.split("/"));
|
|
22137
|
+
if (!(0, import_node_fs24.existsSync)(abs)) continue;
|
|
21797
22138
|
let text;
|
|
21798
22139
|
try {
|
|
21799
|
-
text = (0,
|
|
22140
|
+
text = (0, import_node_fs24.readFileSync)(abs, "utf8");
|
|
21800
22141
|
} catch {
|
|
21801
22142
|
continue;
|
|
21802
22143
|
}
|
|
@@ -21809,7 +22150,7 @@ function loadReadmeHints(cwd, candidatePaths) {
|
|
|
21809
22150
|
return hints;
|
|
21810
22151
|
}
|
|
21811
22152
|
function toPosix(p) {
|
|
21812
|
-
return p.split(
|
|
22153
|
+
return p.split(import_node_path23.sep).join("/");
|
|
21813
22154
|
}
|
|
21814
22155
|
function listCandidatePaths(cwd, exec = import_node_child_process12.execFileSync) {
|
|
21815
22156
|
try {
|
|
@@ -21831,11 +22172,11 @@ function rebuildRepoIndex(cwd, repoSlug2) {
|
|
|
21831
22172
|
for (const rel of candidates) {
|
|
21832
22173
|
if (ignored.has(rel)) continue;
|
|
21833
22174
|
if (isHardDeniedPath(rel)) continue;
|
|
21834
|
-
const abs = (0,
|
|
21835
|
-
if (!(0,
|
|
22175
|
+
const abs = (0, import_node_path23.join)(cwd, ...rel.split("/"));
|
|
22176
|
+
if (!(0, import_node_fs24.existsSync)(abs)) continue;
|
|
21836
22177
|
let text;
|
|
21837
22178
|
try {
|
|
21838
|
-
text = (0,
|
|
22179
|
+
text = (0, import_node_fs24.readFileSync)(abs, "utf8");
|
|
21839
22180
|
} catch {
|
|
21840
22181
|
continue;
|
|
21841
22182
|
}
|
|
@@ -21860,16 +22201,16 @@ function rebuildRepoIndex(cwd, repoSlug2) {
|
|
|
21860
22201
|
entries
|
|
21861
22202
|
};
|
|
21862
22203
|
const store = repoIndexStorePath(cwd);
|
|
21863
|
-
(0,
|
|
21864
|
-
(0,
|
|
22204
|
+
(0, import_node_fs24.mkdirSync)((0, import_node_path23.dirname)(store), { recursive: true });
|
|
22205
|
+
(0, import_node_fs24.writeFileSync)(store, `${JSON.stringify(projection, null, 2)}
|
|
21865
22206
|
`, "utf8");
|
|
21866
22207
|
return projection;
|
|
21867
22208
|
}
|
|
21868
22209
|
function loadRepoIndex(cwd) {
|
|
21869
22210
|
const store = repoIndexStorePath(cwd);
|
|
21870
|
-
if (!(0,
|
|
22211
|
+
if (!(0, import_node_fs24.existsSync)(store)) return null;
|
|
21871
22212
|
try {
|
|
21872
|
-
const raw = JSON.parse((0,
|
|
22213
|
+
const raw = JSON.parse((0, import_node_fs24.readFileSync)(store, "utf8"));
|
|
21873
22214
|
if (raw?.schema !== REPO_INDEX_SCHEMA || !Array.isArray(raw.entries)) return null;
|
|
21874
22215
|
return raw;
|
|
21875
22216
|
} catch {
|
|
@@ -21941,7 +22282,7 @@ function inferRepoSlug(cwd, exec = import_node_child_process12.execFileSync) {
|
|
|
21941
22282
|
if (m?.[1]) return m[1].toLowerCase();
|
|
21942
22283
|
} catch {
|
|
21943
22284
|
}
|
|
21944
|
-
return ((0,
|
|
22285
|
+
return ((0, import_node_path23.basename)(cwd) || "local").toLowerCase();
|
|
21945
22286
|
}
|
|
21946
22287
|
|
|
21947
22288
|
// src/repo-index-cloud-client.ts
|
|
@@ -22081,9 +22422,9 @@ async function gcRepoIndexCloud(deps) {
|
|
|
22081
22422
|
}
|
|
22082
22423
|
|
|
22083
22424
|
// src/repo-index-sync.ts
|
|
22084
|
-
var
|
|
22085
|
-
var
|
|
22086
|
-
var
|
|
22425
|
+
var import_node_fs25 = require("node:fs");
|
|
22426
|
+
var import_node_os10 = require("node:os");
|
|
22427
|
+
var import_node_path24 = require("node:path");
|
|
22087
22428
|
var import_node_child_process13 = require("node:child_process");
|
|
22088
22429
|
var MAX_EMBED_BACKFILL_ROUNDS = 40;
|
|
22089
22430
|
function normalizeRepo(raw) {
|
|
@@ -22127,7 +22468,7 @@ async function syncEstateRepoIndex(opts) {
|
|
|
22127
22468
|
const failed = [];
|
|
22128
22469
|
const skipped = [];
|
|
22129
22470
|
for (const repo of repos) {
|
|
22130
|
-
const dir = (0,
|
|
22471
|
+
const dir = (0, import_node_fs25.mkdtempSync)((0, import_node_path24.join)((0, import_node_os10.tmpdir)(), "mmi-repo-index-"));
|
|
22131
22472
|
try {
|
|
22132
22473
|
shallowClone(repo, dir, opts.githubToken);
|
|
22133
22474
|
const built = rebuildRepoIndex(dir, repo);
|
|
@@ -22177,7 +22518,7 @@ async function syncEstateRepoIndex(opts) {
|
|
|
22177
22518
|
failed.push({ repo, error: e.message });
|
|
22178
22519
|
} finally {
|
|
22179
22520
|
try {
|
|
22180
|
-
(0,
|
|
22521
|
+
(0, import_node_fs25.rmSync)(dir, { recursive: true, force: true });
|
|
22181
22522
|
} catch {
|
|
22182
22523
|
}
|
|
22183
22524
|
}
|
|
@@ -22186,7 +22527,7 @@ async function syncEstateRepoIndex(opts) {
|
|
|
22186
22527
|
}
|
|
22187
22528
|
|
|
22188
22529
|
// src/repo-index-health.ts
|
|
22189
|
-
var
|
|
22530
|
+
var import_node_fs26 = require("node:fs");
|
|
22190
22531
|
|
|
22191
22532
|
// testdata/repo-index-golden-queries.json
|
|
22192
22533
|
var repo_index_golden_queries_default = {
|
|
@@ -22228,7 +22569,7 @@ function assertGoldenSuite(raw, source) {
|
|
|
22228
22569
|
function loadGoldenSuite(path2) {
|
|
22229
22570
|
let text;
|
|
22230
22571
|
try {
|
|
22231
|
-
text = (0,
|
|
22572
|
+
text = (0, import_node_fs26.readFileSync)(path2, "utf8");
|
|
22232
22573
|
} catch (e) {
|
|
22233
22574
|
throw new Error(`golden suite unreadable at ${path2}: ${e.message}`);
|
|
22234
22575
|
}
|
|
@@ -22372,8 +22713,8 @@ async function runRepoIndexHealth(opts) {
|
|
|
22372
22713
|
|
|
22373
22714
|
// src/spawn-policy-core.ts
|
|
22374
22715
|
var import_node_child_process14 = require("node:child_process");
|
|
22375
|
-
var
|
|
22376
|
-
var
|
|
22716
|
+
var import_node_fs27 = require("node:fs");
|
|
22717
|
+
var import_node_path25 = require("node:path");
|
|
22377
22718
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
22378
22719
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
22379
22720
|
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
@@ -22459,7 +22800,7 @@ function runSpawnPolicy(root) {
|
|
|
22459
22800
|
for (const file of files) {
|
|
22460
22801
|
let raw;
|
|
22461
22802
|
try {
|
|
22462
|
-
raw = (0,
|
|
22803
|
+
raw = (0, import_node_fs27.readFileSync)((0, import_node_path25.join)(root, file), "utf8");
|
|
22463
22804
|
} catch {
|
|
22464
22805
|
continue;
|
|
22465
22806
|
}
|
|
@@ -22477,8 +22818,8 @@ function runSpawnPolicy(root) {
|
|
|
22477
22818
|
|
|
22478
22819
|
// src/test-policy-core.ts
|
|
22479
22820
|
var import_node_child_process15 = require("node:child_process");
|
|
22480
|
-
var
|
|
22481
|
-
var
|
|
22821
|
+
var import_node_fs28 = require("node:fs");
|
|
22822
|
+
var import_node_path26 = require("node:path");
|
|
22482
22823
|
var POLICY_FILE = "test-policy.json";
|
|
22483
22824
|
var TEST_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
|
22484
22825
|
var PY_TEST_RE = /(?:^|\/)test_[^/]*\.py$|_test\.py$/;
|
|
@@ -22531,7 +22872,7 @@ function isTestPath(path2) {
|
|
|
22531
22872
|
return TEST_RE.test(path2) || PY_TEST_RE.test(path2);
|
|
22532
22873
|
}
|
|
22533
22874
|
function loadPolicy(root, readFile9 = readFileOrNull2) {
|
|
22534
|
-
const raw = readFile9((0,
|
|
22875
|
+
const raw = readFile9((0, import_node_path26.join)(root, POLICY_FILE));
|
|
22535
22876
|
if (raw == null) return { mandatory: [], declared: false };
|
|
22536
22877
|
try {
|
|
22537
22878
|
return { ...JSON.parse(raw), declared: true };
|
|
@@ -22541,7 +22882,7 @@ function loadPolicy(root, readFile9 = readFileOrNull2) {
|
|
|
22541
22882
|
}
|
|
22542
22883
|
function readFileOrNull2(path2) {
|
|
22543
22884
|
try {
|
|
22544
|
-
return (0,
|
|
22885
|
+
return (0, import_node_fs28.readFileSync)(path2, "utf8");
|
|
22545
22886
|
} catch {
|
|
22546
22887
|
return null;
|
|
22547
22888
|
}
|
|
@@ -22568,12 +22909,12 @@ function classify(changed, policy, present = () => false) {
|
|
|
22568
22909
|
const removedProtected = [...removed].filter((p) => protectedBy.has(p)).map((p) => ({ path: p, why: protectedBy.get(p) ?? "" }));
|
|
22569
22910
|
return { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected };
|
|
22570
22911
|
}
|
|
22571
|
-
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0,
|
|
22572
|
-
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0,
|
|
22912
|
+
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs28.existsSync)(path2)) {
|
|
22913
|
+
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path26.join)(root, p)));
|
|
22573
22914
|
}
|
|
22574
|
-
function unresolvedSatisfiers(policy, root, exists = (path2) => (0,
|
|
22915
|
+
function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs28.existsSync)(path2)) {
|
|
22575
22916
|
const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
|
|
22576
|
-
return [...new Set(declared)].filter((p) => !exists((0,
|
|
22917
|
+
return [...new Set(declared)].filter((p) => !exists((0, import_node_path26.join)(root, p)));
|
|
22577
22918
|
}
|
|
22578
22919
|
function evaluate(changed, policy, present = () => false) {
|
|
22579
22920
|
const { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected } = classify(changed, policy, present);
|
|
@@ -22755,13 +23096,13 @@ function changedFilesSince(base, cwd) {
|
|
|
22755
23096
|
}
|
|
22756
23097
|
function runTestPolicy(root, deps = {}) {
|
|
22757
23098
|
const policy = deps.policy ?? loadPolicy(root);
|
|
22758
|
-
const exists = deps.exists ?? ((path2) => (0,
|
|
23099
|
+
const exists = deps.exists ?? ((path2) => (0, import_node_fs28.existsSync)(path2));
|
|
22759
23100
|
const counts = { mandatoryCount: (policy.mandatory ?? []).length, protectedCount: (policy.protected ?? []).length };
|
|
22760
23101
|
const base = deps.changed ? "(injected)" : resolveBase(root, deps.base);
|
|
22761
23102
|
const refusal = deps.changed ? null : untrustworthyRange(root, base);
|
|
22762
23103
|
const changed = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
|
|
22763
23104
|
const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride(base, root) : { override: null, refusals: [] };
|
|
22764
|
-
const present = (path2) => exists((0,
|
|
23105
|
+
const present = (path2) => exists((0, import_node_path26.join)(root, path2));
|
|
22765
23106
|
const removedByThisDiff = removedPaths(changed);
|
|
22766
23107
|
const staleFindings = [];
|
|
22767
23108
|
const unresolved = unresolvedProtectedEntries(policy, root, exists).filter((p) => !removedByThisDiff.has(p));
|
|
@@ -22799,8 +23140,8 @@ function runTestPolicy(root, deps = {}) {
|
|
|
22799
23140
|
}
|
|
22800
23141
|
|
|
22801
23142
|
// src/project-info-sync.ts
|
|
22802
|
-
var
|
|
22803
|
-
var
|
|
23143
|
+
var import_node_fs29 = require("node:fs");
|
|
23144
|
+
var import_node_path27 = require("node:path");
|
|
22804
23145
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
22805
23146
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
22806
23147
|
projectV2 { id }
|
|
@@ -22845,14 +23186,14 @@ function sharedName(entries, fallback) {
|
|
|
22845
23186
|
}
|
|
22846
23187
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
22847
23188
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
22848
|
-
const readmePath = (0,
|
|
22849
|
-
if (!(0,
|
|
23189
|
+
const readmePath = (0, import_node_path27.join)(repoRoot2, "README.md");
|
|
23190
|
+
if (!(0, import_node_fs29.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
22850
23191
|
const entries = entriesFor(project2, projects);
|
|
22851
23192
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
22852
23193
|
const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
|
|
22853
23194
|
if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
|
|
22854
23195
|
const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
|
|
22855
|
-
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0,
|
|
23196
|
+
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs29.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
|
|
22856
23197
|
const lines = [
|
|
22857
23198
|
`# ${projectName}`,
|
|
22858
23199
|
"",
|
|
@@ -22871,8 +23212,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
22871
23212
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
22872
23213
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
22873
23214
|
const orgDocs = [
|
|
22874
|
-
(0,
|
|
22875
|
-
(0,
|
|
23215
|
+
(0, import_node_fs29.existsSync)((0, import_node_path27.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
|
|
23216
|
+
(0, import_node_fs29.existsSync)((0, import_node_path27.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
|
|
22876
23217
|
].filter(Boolean);
|
|
22877
23218
|
if (orgDocs.length) lines.push("", "## Organisation docs", "", ...orgDocs);
|
|
22878
23219
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines.join("\n")}
|
|
@@ -23749,9 +24090,9 @@ function writeError(res) {
|
|
|
23749
24090
|
}
|
|
23750
24091
|
|
|
23751
24092
|
// src/secrets-commands.ts
|
|
23752
|
-
var
|
|
23753
|
-
var
|
|
23754
|
-
var
|
|
24093
|
+
var import_node_fs30 = require("node:fs");
|
|
24094
|
+
var import_node_path28 = require("node:path");
|
|
24095
|
+
var import_node_os11 = require("node:os");
|
|
23755
24096
|
|
|
23756
24097
|
// src/project-runtime.ts
|
|
23757
24098
|
function hasRuntimeSecretContract(contract) {
|
|
@@ -23874,18 +24215,18 @@ function collectMap(value, previous = []) {
|
|
|
23874
24215
|
return [...previous, value];
|
|
23875
24216
|
}
|
|
23876
24217
|
async function decryptRailsCredentials(input) {
|
|
23877
|
-
const appDir = (0,
|
|
24218
|
+
const appDir = (0, import_node_path28.resolve)(input.appDir ?? process.cwd());
|
|
23878
24219
|
const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
23879
24220
|
const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
23880
|
-
const credentialsPath = (0,
|
|
23881
|
-
const masterKeyPath = (0,
|
|
24221
|
+
const credentialsPath = (0, import_node_path28.resolve)(appDir, credentialsFile);
|
|
24222
|
+
const masterKeyPath = (0, import_node_path28.resolve)(appDir, masterKeyFile);
|
|
23882
24223
|
const env = {
|
|
23883
24224
|
...process.env,
|
|
23884
24225
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
23885
24226
|
MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
|
|
23886
24227
|
};
|
|
23887
|
-
if ((0,
|
|
23888
|
-
env.RAILS_MASTER_KEY = (0,
|
|
24228
|
+
if ((0, import_node_fs30.existsSync)(masterKeyPath)) {
|
|
24229
|
+
env.RAILS_MASTER_KEY = (0, import_node_fs30.readFileSync)(masterKeyPath, "utf8").trim();
|
|
23889
24230
|
}
|
|
23890
24231
|
const script = [
|
|
23891
24232
|
'require "json"',
|
|
@@ -23895,9 +24236,9 @@ async function decryptRailsCredentials(input) {
|
|
|
23895
24236
|
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
23896
24237
|
"puts JSON.generate(config.config)"
|
|
23897
24238
|
].join("\n");
|
|
23898
|
-
const scriptDir = (0,
|
|
23899
|
-
const scriptPath = (0,
|
|
23900
|
-
(0,
|
|
24239
|
+
const scriptDir = (0, import_node_fs30.mkdtempSync)((0, import_node_path28.join)((0, import_node_os11.tmpdir)(), "mmi-rails-decrypt-"));
|
|
24240
|
+
const scriptPath = (0, import_node_path28.join)(scriptDir, "decrypt.rb");
|
|
24241
|
+
(0, import_node_fs30.writeFileSync)(scriptPath, script, "utf8");
|
|
23901
24242
|
try {
|
|
23902
24243
|
const args = ["exec", "ruby", scriptPath];
|
|
23903
24244
|
const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
|
|
@@ -23909,7 +24250,7 @@ async function decryptRailsCredentials(input) {
|
|
|
23909
24250
|
});
|
|
23910
24251
|
return JSON.parse(stdout);
|
|
23911
24252
|
} finally {
|
|
23912
|
-
(0,
|
|
24253
|
+
(0, import_node_fs30.rmSync)(scriptDir, { recursive: true, force: true });
|
|
23913
24254
|
}
|
|
23914
24255
|
}
|
|
23915
24256
|
async function readSecretStdin() {
|
|
@@ -23999,7 +24340,7 @@ function registerSecretsCommands(program3) {
|
|
|
23999
24340
|
let body;
|
|
24000
24341
|
if (o.file) {
|
|
24001
24342
|
try {
|
|
24002
|
-
body = (0,
|
|
24343
|
+
body = (0, import_node_fs30.readFileSync)((0, import_node_path28.resolve)(o.file), "utf8");
|
|
24003
24344
|
} catch (e) {
|
|
24004
24345
|
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
24005
24346
|
}
|
|
@@ -24104,7 +24445,7 @@ function registerSecretsCommands(program3) {
|
|
|
24104
24445
|
{
|
|
24105
24446
|
...d,
|
|
24106
24447
|
decryptRailsCredentials,
|
|
24107
|
-
removeFile: (path2) => (0,
|
|
24448
|
+
removeFile: (path2) => (0, import_node_fs30.unlinkSync)((0, import_node_path28.resolve)(o.appDir ?? process.cwd(), path2))
|
|
24108
24449
|
},
|
|
24109
24450
|
{
|
|
24110
24451
|
repo: o.repo,
|
|
@@ -24268,7 +24609,7 @@ async function activateAppActor(commandPath3, env, mint) {
|
|
|
24268
24609
|
}
|
|
24269
24610
|
|
|
24270
24611
|
// src/box-commands.ts
|
|
24271
|
-
var
|
|
24612
|
+
var import_node_fs31 = require("node:fs");
|
|
24272
24613
|
|
|
24273
24614
|
// src/box.ts
|
|
24274
24615
|
var BOX_KEYS = {
|
|
@@ -24471,7 +24812,7 @@ function registerBoxCommands(program3) {
|
|
|
24471
24812
|
}
|
|
24472
24813
|
if (o.json) console.log(JSON.stringify({ box: found, incomplete }, null, 2));
|
|
24473
24814
|
else if (o.ssh && o.script) {
|
|
24474
|
-
(0,
|
|
24815
|
+
(0, import_node_fs31.writeFileSync)(o.script, sshRecipeScript(found), "utf8");
|
|
24475
24816
|
console.log(`wrote ${o.script} \u2014 run: bash "${o.script}"`);
|
|
24476
24817
|
} else if (o.ssh) console.log(`${formatSshRecipe(found)}
|
|
24477
24818
|
${SSH_RECIPE_AGENT_NOTE}`);
|
|
@@ -25237,7 +25578,7 @@ function registerSchedulesCommands(program3) {
|
|
|
25237
25578
|
|
|
25238
25579
|
// src/schedules-lift-command.ts
|
|
25239
25580
|
var import_promises6 = require("node:fs/promises");
|
|
25240
|
-
var
|
|
25581
|
+
var import_node_path29 = require("node:path");
|
|
25241
25582
|
|
|
25242
25583
|
// src/schedules-lift.ts
|
|
25243
25584
|
var SCHEDULE_HEADER_FIELDS = ["schedule", "what", "owner", "cadence", "executor", "llm", "output", "breaks", "kill"];
|
|
@@ -25343,7 +25684,7 @@ async function readWorkflowFiles(dir) {
|
|
|
25343
25684
|
const files = [];
|
|
25344
25685
|
for (const name of names.sort()) {
|
|
25345
25686
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
25346
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises6.readFile)((0,
|
|
25687
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises6.readFile)((0, import_node_path29.join)(dir, name), "utf8") });
|
|
25347
25688
|
}
|
|
25348
25689
|
return files;
|
|
25349
25690
|
}
|
|
@@ -26038,9 +26379,9 @@ function registerQueryCommands(program3) {
|
|
|
26038
26379
|
}
|
|
26039
26380
|
|
|
26040
26381
|
// src/bootstrap-commands.ts
|
|
26041
|
-
var
|
|
26042
|
-
var
|
|
26043
|
-
var
|
|
26382
|
+
var import_node_fs32 = require("node:fs");
|
|
26383
|
+
var import_node_os12 = require("node:os");
|
|
26384
|
+
var import_node_path30 = require("node:path");
|
|
26044
26385
|
|
|
26045
26386
|
// src/bootstrap-drift.ts
|
|
26046
26387
|
var import_node_crypto6 = require("node:crypto");
|
|
@@ -26944,13 +27285,13 @@ function registerBootstrapCommands(program3) {
|
|
|
26944
27285
|
client: defaultGitHubClient(),
|
|
26945
27286
|
projectMeta: meta,
|
|
26946
27287
|
deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
|
|
26947
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
27288
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs32.existsSync)(path2) ? (0, import_node_fs32.readFileSync)(path2, "utf8") : null,
|
|
26948
27289
|
// requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
|
|
26949
27290
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
26950
27291
|
// #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
|
|
26951
27292
|
// permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
|
|
26952
27293
|
// sanction, which is the pre-#3664 behaviour.
|
|
26953
|
-
sanctionedAdmins: (0,
|
|
27294
|
+
sanctionedAdmins: (0, import_node_fs32.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs32.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
|
|
26954
27295
|
requiredGcpApis: (() => {
|
|
26955
27296
|
const v = meta?.requiredGcpApis;
|
|
26956
27297
|
if (Array.isArray(v)) return v;
|
|
@@ -27003,14 +27344,14 @@ function registerBootstrapCommands(program3) {
|
|
|
27003
27344
|
bootstrap.command("drift").description("#3818: compare every org-owned whole-file seed against MMI-Hub's copy across the registry roster; read-only").option("--repo <owner/repo>", "audit one repo instead of the roster (never a fleet verdict)").option("--json", "machine-readable output").action(async () => {
|
|
27004
27345
|
const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
|
|
27005
27346
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
27006
|
-
if (!(0,
|
|
27347
|
+
if (!(0, import_node_fs32.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
|
|
27007
27348
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
27008
27349
|
if (!seedSource.ok) return fail(`bootstrap drift: ${seedSource.reason}`);
|
|
27009
|
-
const manifest = loadBootstrapSeeds((0,
|
|
27350
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs32.readFileSync)(manifestPath, "utf8"));
|
|
27010
27351
|
const hubContents = /* @__PURE__ */ new Map();
|
|
27011
27352
|
for (const s of manifest.seeds) {
|
|
27012
27353
|
if (s.ownership !== "org" || s.source !== "self") continue;
|
|
27013
|
-
hubContents.set(s.target, (0,
|
|
27354
|
+
hubContents.set(s.target, (0, import_node_fs32.existsSync)(s.target) ? (0, import_node_fs32.readFileSync)(s.target, "utf8") : null);
|
|
27014
27355
|
}
|
|
27015
27356
|
let targets;
|
|
27016
27357
|
let classOf = (_repo) => "deployable";
|
|
@@ -27089,10 +27430,10 @@ function registerBootstrapCommands(program3) {
|
|
|
27089
27430
|
return fail(`bootstrap apply: ${e.message}`);
|
|
27090
27431
|
}
|
|
27091
27432
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
27092
|
-
if (!(0,
|
|
27433
|
+
if (!(0, import_node_fs32.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
|
|
27093
27434
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
27094
27435
|
if (!seedSource.ok) return fail(`bootstrap apply: ${seedSource.reason}`);
|
|
27095
|
-
const manifest = loadBootstrapSeeds((0,
|
|
27436
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs32.readFileSync)(manifestPath, "utf8"));
|
|
27096
27437
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
27097
27438
|
const slug = parsedRepo.slug;
|
|
27098
27439
|
const onlyTarget = o.only.trim();
|
|
@@ -27103,16 +27444,16 @@ function registerBootstrapCommands(program3) {
|
|
|
27103
27444
|
${known}`);
|
|
27104
27445
|
}
|
|
27105
27446
|
const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
|
|
27106
|
-
const readFile9 = (p) => (0,
|
|
27447
|
+
const readFile9 = (p) => (0, import_node_fs32.existsSync)(p) ? (0, import_node_fs32.readFileSync)(p, "utf8") : null;
|
|
27107
27448
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
27108
27449
|
const putSeed = async (target, content, ref, sha) => {
|
|
27109
|
-
const tmp = (0,
|
|
27110
|
-
(0,
|
|
27450
|
+
const tmp = (0, import_node_path30.join)((0, import_node_os12.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
27451
|
+
(0, import_node_fs32.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
27111
27452
|
try {
|
|
27112
27453
|
await gh(contentPutInputArgs(repo, target, tmp));
|
|
27113
27454
|
} finally {
|
|
27114
27455
|
try {
|
|
27115
|
-
(0,
|
|
27456
|
+
(0, import_node_fs32.unlinkSync)(tmp);
|
|
27116
27457
|
} catch {
|
|
27117
27458
|
}
|
|
27118
27459
|
}
|
|
@@ -27377,10 +27718,10 @@ LIVE apply to ${repo}:
|
|
|
27377
27718
|
bootstrap.command("propagate").description("#4238: re-entrant canary\u2192wave tick \u2014 plan (or, with --execute, open) per-repo PRs fanning an org-owned seed out to the fleet").option("--target <path>", "the manifest target to propagate (an ownership:org + source:self seed, e.g. .github/workflows/agent-pr.yml)").option("--execute", "LIVE tick via gh (master-gated) \u2014 opens/reuses per-repo seed-propagate PRs; dry-run prints the plan only").option("--json", "machine-readable output").action(async () => {
|
|
27378
27719
|
const o = { target: rawValue("--target", ""), execute: rawFlag("--execute"), json: rawFlag("--json") };
|
|
27379
27720
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
27380
|
-
if (!(0,
|
|
27721
|
+
if (!(0, import_node_fs32.existsSync)(manifestPath)) return fail(`bootstrap propagate: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the desired state this tick propagates`);
|
|
27381
27722
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
27382
27723
|
if (!seedSource.ok) return fail(`bootstrap propagate: ${seedSource.reason}`);
|
|
27383
|
-
const manifest = loadBootstrapSeeds((0,
|
|
27724
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs32.readFileSync)(manifestPath, "utf8"));
|
|
27384
27725
|
const propagatable = manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self");
|
|
27385
27726
|
if (!o.target) {
|
|
27386
27727
|
return fail(`bootstrap propagate: --target <path> is required \u2014 one of:
|
|
@@ -27389,8 +27730,8 @@ LIVE apply to ${repo}:
|
|
|
27389
27730
|
const seed = propagatable.find((s) => s.target === o.target);
|
|
27390
27731
|
if (!seed) return fail(`bootstrap propagate: --target '${o.target}' names no ownership:org + source:self seed in ${manifestPath}. Propagatable targets:
|
|
27391
27732
|
${propagatable.map((s) => s.target).join("\n ")}`);
|
|
27392
|
-
if (!(0,
|
|
27393
|
-
const hubContent = (0,
|
|
27733
|
+
if (!(0, import_node_fs32.existsSync)(seed.target)) return fail(`bootstrap propagate: the Hub's own copy of '${seed.target}' is missing \u2014 nothing to propagate`);
|
|
27734
|
+
const hubContent = (0, import_node_fs32.readFileSync)(seed.target, "utf8");
|
|
27394
27735
|
const isWorkflowSeed = seed.target.startsWith(".github/workflows/");
|
|
27395
27736
|
const cfg = await loadConfig();
|
|
27396
27737
|
const projects = await fetchProjectsList(registryClientDeps(cfg));
|
|
@@ -27399,9 +27740,9 @@ LIVE apply to ${repo}:
|
|
|
27399
27740
|
}
|
|
27400
27741
|
const rosterRepos2 = collectRegistryRepos(projects).filter((r) => r.toLowerCase() !== "mutmutco/mmi-hub");
|
|
27401
27742
|
let independentCount = rosterRepos2.length;
|
|
27402
|
-
if ((0,
|
|
27743
|
+
if ((0, import_node_fs32.existsSync)("projects.json")) {
|
|
27403
27744
|
try {
|
|
27404
|
-
const local = JSON.parse((0,
|
|
27745
|
+
const local = JSON.parse((0, import_node_fs32.readFileSync)("projects.json", "utf8"));
|
|
27405
27746
|
const localRepos = /* @__PURE__ */ new Set();
|
|
27406
27747
|
for (const p of local.projects ?? []) for (const r of p.repos ?? []) {
|
|
27407
27748
|
const full = (r.includes("/") ? r : `mutmutco/${r}`).toLowerCase();
|
|
@@ -27517,13 +27858,13 @@ LIVE apply to ${repo}:
|
|
|
27517
27858
|
} catch {
|
|
27518
27859
|
existingSha = void 0;
|
|
27519
27860
|
}
|
|
27520
|
-
const tmp = (0,
|
|
27521
|
-
(0,
|
|
27861
|
+
const tmp = (0, import_node_path30.join)((0, import_node_os12.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
27862
|
+
(0, import_node_fs32.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, hubContent, branch, existingSha)), "utf8");
|
|
27522
27863
|
try {
|
|
27523
27864
|
await gh(contentPutInputArgs(rec.repo, seed.target, tmp));
|
|
27524
27865
|
} finally {
|
|
27525
27866
|
try {
|
|
27526
|
-
(0,
|
|
27867
|
+
(0, import_node_fs32.unlinkSync)(tmp);
|
|
27527
27868
|
} catch {
|
|
27528
27869
|
}
|
|
27529
27870
|
}
|
|
@@ -27581,10 +27922,10 @@ Rollback: \`mmi-cli bootstrap rollback ${rec.repo} --target ${seed.target} --exe
|
|
|
27581
27922
|
return fail(`bootstrap rollback: ${e.message}`);
|
|
27582
27923
|
}
|
|
27583
27924
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
27584
|
-
if (!(0,
|
|
27925
|
+
if (!(0, import_node_fs32.existsSync)(manifestPath)) return fail(`bootstrap rollback: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the manifest names which targets are org-owned and therefore propagated (and rollback-able)`);
|
|
27585
27926
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
27586
27927
|
if (!seedSource.ok) return fail(`bootstrap rollback: ${seedSource.reason}`);
|
|
27587
|
-
const manifest = loadBootstrapSeeds((0,
|
|
27928
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs32.readFileSync)(manifestPath, "utf8"));
|
|
27588
27929
|
const propagatable = manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self");
|
|
27589
27930
|
if (!o.target) {
|
|
27590
27931
|
return fail(`bootstrap rollback: --target <path> is required \u2014 one of:
|
|
@@ -27601,10 +27942,10 @@ Rollback: \`mmi-cli bootstrap rollback ${rec.repo} --target ${seed.target} --exe
|
|
|
27601
27942
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
27602
27943
|
let candidates;
|
|
27603
27944
|
if (o.record) {
|
|
27604
|
-
if (!(0,
|
|
27945
|
+
if (!(0, import_node_fs32.existsSync)(o.record)) return fail(`bootstrap rollback: --record '${o.record}' not found`);
|
|
27605
27946
|
let parsed;
|
|
27606
27947
|
try {
|
|
27607
|
-
parsed = JSON.parse((0,
|
|
27948
|
+
parsed = JSON.parse((0, import_node_fs32.readFileSync)(o.record, "utf8"));
|
|
27608
27949
|
} catch (e) {
|
|
27609
27950
|
return fail(`bootstrap rollback: --record '${o.record}' is not valid JSON: ${e.message}`);
|
|
27610
27951
|
}
|
|
@@ -27673,13 +28014,13 @@ Rollback: \`mmi-cli bootstrap rollback ${rec.repo} --target ${seed.target} --exe
|
|
|
27673
28014
|
} catch {
|
|
27674
28015
|
existingSha = void 0;
|
|
27675
28016
|
}
|
|
27676
|
-
const tmp = (0,
|
|
27677
|
-
(0,
|
|
28017
|
+
const tmp = (0, import_node_path30.join)((0, import_node_os12.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
28018
|
+
(0, import_node_fs32.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
|
|
27678
28019
|
try {
|
|
27679
28020
|
await gh(contentPutInputArgs(repo, seed.target, tmp));
|
|
27680
28021
|
} finally {
|
|
27681
28022
|
try {
|
|
27682
|
-
(0,
|
|
28023
|
+
(0, import_node_fs32.unlinkSync)(tmp);
|
|
27683
28024
|
} catch {
|
|
27684
28025
|
}
|
|
27685
28026
|
}
|
|
@@ -27701,12 +28042,12 @@ Rollback: \`mmi-cli bootstrap rollback ${rec.repo} --target ${seed.target} --exe
|
|
|
27701
28042
|
}
|
|
27702
28043
|
|
|
27703
28044
|
// src/stage-commands.ts
|
|
27704
|
-
var
|
|
27705
|
-
var
|
|
28045
|
+
var import_node_fs34 = require("node:fs");
|
|
28046
|
+
var import_node_path32 = require("node:path");
|
|
27706
28047
|
|
|
27707
28048
|
// src/port-registry.ts
|
|
27708
|
-
var
|
|
27709
|
-
var
|
|
28049
|
+
var import_node_fs33 = require("node:fs");
|
|
28050
|
+
var import_node_path31 = require("node:path");
|
|
27710
28051
|
|
|
27711
28052
|
// ../infra/port-geometry.mjs
|
|
27712
28053
|
var PORT_BLOCK = 100;
|
|
@@ -27720,8 +28061,8 @@ function nextPortBlock(registry2) {
|
|
|
27720
28061
|
return [base, base + PORT_SPAN];
|
|
27721
28062
|
}
|
|
27722
28063
|
function loadPortRegistry(path2) {
|
|
27723
|
-
if (!(0,
|
|
27724
|
-
const raw = JSON.parse((0,
|
|
28064
|
+
if (!(0, import_node_fs33.existsSync)(path2)) return {};
|
|
28065
|
+
const raw = JSON.parse((0, import_node_fs33.readFileSync)(path2, "utf8"));
|
|
27725
28066
|
const out = {};
|
|
27726
28067
|
for (const [key, value] of Object.entries(raw)) {
|
|
27727
28068
|
if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
|
|
@@ -27735,9 +28076,9 @@ function ensurePortRange(repo, path2) {
|
|
|
27735
28076
|
const existing = registry2[repo];
|
|
27736
28077
|
if (existing) return existing;
|
|
27737
28078
|
const range = nextPortBlock(registry2);
|
|
27738
|
-
const raw = (0,
|
|
28079
|
+
const raw = (0, import_node_fs33.existsSync)(path2) ? JSON.parse((0, import_node_fs33.readFileSync)(path2, "utf8")) : {};
|
|
27739
28080
|
raw[repo] = range;
|
|
27740
|
-
(0,
|
|
28081
|
+
(0, import_node_fs33.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
|
|
27741
28082
|
return range;
|
|
27742
28083
|
}
|
|
27743
28084
|
function portCursorSeed(registry2) {
|
|
@@ -27759,22 +28100,22 @@ function existingPortRange(repo, registry2) {
|
|
|
27759
28100
|
return registry2[repo] ?? null;
|
|
27760
28101
|
}
|
|
27761
28102
|
function portRangeInfraAt(root, source) {
|
|
27762
|
-
const registryPath = (0,
|
|
27763
|
-
const ddbScriptPath = (0,
|
|
27764
|
-
if (!(0,
|
|
28103
|
+
const registryPath = (0, import_node_path31.join)(root, "infra", "port-ranges.json");
|
|
28104
|
+
const ddbScriptPath = (0, import_node_path31.join)(root, "infra", "port-ddb.mjs");
|
|
28105
|
+
if (!(0, import_node_fs33.existsSync)(registryPath) || !(0, import_node_fs33.existsSync)(ddbScriptPath)) return null;
|
|
27765
28106
|
return { root, source, registryPath, ddbScriptPath };
|
|
27766
28107
|
}
|
|
27767
28108
|
function resolvePortRangeInfra(cwd, packageDir) {
|
|
27768
28109
|
const direct = portRangeInfraAt(cwd, "cwd");
|
|
27769
28110
|
if (direct) return direct;
|
|
27770
|
-
for (let dir = cwd; ; dir = (0,
|
|
27771
|
-
const sibling = portRangeInfraAt((0,
|
|
28111
|
+
for (let dir = cwd; ; dir = (0, import_node_path31.dirname)(dir)) {
|
|
28112
|
+
const sibling = portRangeInfraAt((0, import_node_path31.join)(dir, "MMI-Hub"), "sibling-hub");
|
|
27772
28113
|
if (sibling) return sibling;
|
|
27773
|
-
const parent = (0,
|
|
28114
|
+
const parent = (0, import_node_path31.dirname)(dir);
|
|
27774
28115
|
if (parent === dir) break;
|
|
27775
28116
|
}
|
|
27776
28117
|
if (packageDir) {
|
|
27777
|
-
const pkgRoot = (0,
|
|
28118
|
+
const pkgRoot = (0, import_node_path31.join)(packageDir, "..", "..");
|
|
27778
28119
|
const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
|
|
27779
28120
|
if (pkgFrom) return pkgFrom;
|
|
27780
28121
|
}
|
|
@@ -27968,8 +28309,8 @@ function registerStageCommands(program3) {
|
|
|
27968
28309
|
const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
|
|
27969
28310
|
return decideStage({
|
|
27970
28311
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
27971
|
-
hasCompose: (0,
|
|
27972
|
-
hasEnvExample: (0,
|
|
28312
|
+
hasCompose: (0, import_node_fs34.existsSync)((0, import_node_path32.join)(process.cwd(), "docker-compose.yml")),
|
|
28313
|
+
hasEnvExample: (0, import_node_fs34.existsSync)((0, import_node_path32.join)(process.cwd(), ".env.example"))
|
|
27973
28314
|
});
|
|
27974
28315
|
}
|
|
27975
28316
|
async function fetchStageVaultEnvMerge() {
|
|
@@ -28427,10 +28768,10 @@ function registerBoardCommands(program3) {
|
|
|
28427
28768
|
}
|
|
28428
28769
|
|
|
28429
28770
|
// src/merge-cleanup.ts
|
|
28430
|
-
var
|
|
28771
|
+
var import_node_fs36 = require("node:fs");
|
|
28431
28772
|
var import_promises8 = require("node:fs/promises");
|
|
28432
|
-
var
|
|
28433
|
-
var
|
|
28773
|
+
var import_node_path35 = require("node:path");
|
|
28774
|
+
var import_node_os14 = require("node:os");
|
|
28434
28775
|
var import_node_child_process17 = require("node:child_process");
|
|
28435
28776
|
|
|
28436
28777
|
// src/board-advance.ts
|
|
@@ -28517,7 +28858,7 @@ function boardAdvanceFailureMessage(result) {
|
|
|
28517
28858
|
|
|
28518
28859
|
// src/deferred-registry-store.ts
|
|
28519
28860
|
var import_promises7 = require("node:fs/promises");
|
|
28520
|
-
var
|
|
28861
|
+
var import_node_path33 = require("node:path");
|
|
28521
28862
|
var sleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
28522
28863
|
async function atomicWrite(target, contents) {
|
|
28523
28864
|
const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
@@ -28568,12 +28909,12 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
|
|
|
28568
28909
|
},
|
|
28569
28910
|
// Standalone atomic write — THROWS on failure (no best-effort swallow, #2846).
|
|
28570
28911
|
write: async (entries) => {
|
|
28571
|
-
await (0, import_promises7.mkdir)((0,
|
|
28912
|
+
await (0, import_promises7.mkdir)((0, import_node_path33.dirname)(registryPath), { recursive: true });
|
|
28572
28913
|
await atomicWrite(registryPath, serializeDeferredWorktrees(entries));
|
|
28573
28914
|
},
|
|
28574
28915
|
// Serialized read-modify-write under the repo-wide lock (#2846).
|
|
28575
28916
|
update: async (mutate) => {
|
|
28576
|
-
await (0, import_promises7.mkdir)((0,
|
|
28917
|
+
await (0, import_promises7.mkdir)((0, import_node_path33.dirname)(registryPath), { recursive: true });
|
|
28577
28918
|
const deadline = Date.now() + opts.maxWaitMs;
|
|
28578
28919
|
for (; ; ) {
|
|
28579
28920
|
const guard = await acquireLock(lockPath, opts, deadline);
|
|
@@ -28597,15 +28938,15 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
|
|
|
28597
28938
|
}
|
|
28598
28939
|
|
|
28599
28940
|
// src/jerv-cli-spawn.ts
|
|
28600
|
-
var
|
|
28601
|
-
var
|
|
28602
|
-
var
|
|
28941
|
+
var import_node_fs35 = require("node:fs");
|
|
28942
|
+
var import_node_os13 = require("node:os");
|
|
28943
|
+
var import_node_path34 = require("node:path");
|
|
28603
28944
|
var WIN_NAMES = ["jerv-cli.cmd", "jerv-cli.exe", "jerv-cli"];
|
|
28604
28945
|
var POSIX_NAMES = ["jerv-cli"];
|
|
28605
|
-
var JERV_CLI_ENTRY = (0,
|
|
28946
|
+
var JERV_CLI_ENTRY = (0, import_node_path34.join)("node_modules", "@jervaise", "jerv-cli", "dist", "index.cjs");
|
|
28606
28947
|
function pathEnvEntries(pathEnv, platform2 = process.platform) {
|
|
28607
28948
|
if (platform2 !== "win32") {
|
|
28608
|
-
return pathEnv.split(
|
|
28949
|
+
return pathEnv.split(import_node_path34.delimiter).map((e) => e.trim()).filter(Boolean);
|
|
28609
28950
|
}
|
|
28610
28951
|
if (pathEnv.includes(";")) {
|
|
28611
28952
|
return pathEnv.split(";").map((e) => e.trim()).filter(Boolean);
|
|
@@ -28624,7 +28965,7 @@ function normalizeSpawnPathEntry(entry, platform2 = process.platform) {
|
|
|
28624
28965
|
if (msys) return `${msys[1].toUpperCase()}:\\${msys[2].replace(/\//g, "\\")}`;
|
|
28625
28966
|
return trimmed;
|
|
28626
28967
|
}
|
|
28627
|
-
function jervCliCandidateDirs(env = process.env, home = (0,
|
|
28968
|
+
function jervCliCandidateDirs(env = process.env, home = (0, import_node_os13.homedir)(), platform2 = process.platform) {
|
|
28628
28969
|
const seen = /* @__PURE__ */ new Set();
|
|
28629
28970
|
const out = [];
|
|
28630
28971
|
const push = (dir) => {
|
|
@@ -28638,35 +28979,35 @@ function jervCliCandidateDirs(env = process.env, home = (0, import_node_os12.hom
|
|
|
28638
28979
|
push(normalizeSpawnPathEntry(entry, platform2));
|
|
28639
28980
|
}
|
|
28640
28981
|
if (platform2 === "win32") {
|
|
28641
|
-
if (env.APPDATA) push((0,
|
|
28642
|
-
if (env.LOCALAPPDATA) push((0,
|
|
28982
|
+
if (env.APPDATA) push((0, import_node_path34.join)(env.APPDATA, "npm"));
|
|
28983
|
+
if (env.LOCALAPPDATA) push((0, import_node_path34.join)(env.LOCALAPPDATA, "npm"));
|
|
28643
28984
|
} else {
|
|
28644
|
-
push((0,
|
|
28985
|
+
push((0, import_node_path34.join)(home, ".local", "bin"));
|
|
28645
28986
|
}
|
|
28646
28987
|
return out;
|
|
28647
28988
|
}
|
|
28648
|
-
function jervCliCandidatePaths(env = process.env, home = (0,
|
|
28989
|
+
function jervCliCandidatePaths(env = process.env, home = (0, import_node_os13.homedir)(), platform2 = process.platform) {
|
|
28649
28990
|
const names = platform2 === "win32" ? WIN_NAMES : POSIX_NAMES;
|
|
28650
28991
|
const out = [];
|
|
28651
28992
|
for (const dir of jervCliCandidateDirs(env, home, platform2)) {
|
|
28652
|
-
for (const name of names) out.push((0,
|
|
28993
|
+
for (const name of names) out.push((0, import_node_path34.join)(dir, name));
|
|
28653
28994
|
}
|
|
28654
28995
|
return out;
|
|
28655
28996
|
}
|
|
28656
|
-
function resolveJervCliPath(env = process.env, home = (0,
|
|
28997
|
+
function resolveJervCliPath(env = process.env, home = (0, import_node_os13.homedir)(), platform2 = process.platform, exists = import_node_fs35.existsSync) {
|
|
28657
28998
|
for (const candidate of jervCliCandidatePaths(env, home, platform2)) {
|
|
28658
28999
|
if (exists(candidate)) return candidate;
|
|
28659
29000
|
}
|
|
28660
29001
|
return void 0;
|
|
28661
29002
|
}
|
|
28662
|
-
function resolveJervCliNodeEntry(shimPath, exists =
|
|
28663
|
-
const entry = (0,
|
|
29003
|
+
function resolveJervCliNodeEntry(shimPath, exists = import_node_fs35.existsSync) {
|
|
29004
|
+
const entry = (0, import_node_path34.join)((0, import_node_path34.dirname)(shimPath), JERV_CLI_ENTRY);
|
|
28664
29005
|
return exists(entry) ? entry : void 0;
|
|
28665
29006
|
}
|
|
28666
29007
|
function jervCliExecFileArgs(args, opts = {}) {
|
|
28667
29008
|
const platform2 = opts.platform ?? process.platform;
|
|
28668
|
-
const exists = opts.exists ??
|
|
28669
|
-
const resolved = resolveJervCliPath(opts.env ?? process.env, opts.home ?? (0,
|
|
29009
|
+
const exists = opts.exists ?? import_node_fs35.existsSync;
|
|
29010
|
+
const resolved = resolveJervCliPath(opts.env ?? process.env, opts.home ?? (0, import_node_os13.homedir)(), platform2, exists);
|
|
28670
29011
|
if (resolved) {
|
|
28671
29012
|
const entry = resolveJervCliNodeEntry(resolved, exists);
|
|
28672
29013
|
if (entry) {
|
|
@@ -28849,12 +29190,31 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
28849
29190
|
);
|
|
28850
29191
|
const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
28851
29192
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
28852
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
29193
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path35.dirname)((0, import_node_path35.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
28853
29194
|
const gcActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
|
|
28854
29195
|
const owners = readWorktreeOwners(primaryRepoRoot);
|
|
28855
29196
|
const removalNow = Date.now();
|
|
28856
|
-
const
|
|
29197
|
+
const activeWorkspaceRoot = resolveActiveWorkspaceRoot();
|
|
29198
|
+
const deferredStore = await createDeferredWorktreeStore();
|
|
29199
|
+
const activeWorkspaceDeferred = [];
|
|
29200
|
+
const refusesRemoval = (path2, branch) => {
|
|
28857
29201
|
if (!path2) return false;
|
|
29202
|
+
const activeGuard = decideActiveWorkspaceGuard(path2, activeWorkspaceRoot);
|
|
29203
|
+
if (activeGuard.action === "refuse") {
|
|
29204
|
+
result.refused.push(activeGuard.message);
|
|
29205
|
+
const owner2 = findWorktreeOwner(owners, path2);
|
|
29206
|
+
recordWorktreeRemoval(primaryRepoRoot, {
|
|
29207
|
+
action: "refused",
|
|
29208
|
+
command: "worktree gc",
|
|
29209
|
+
target: path2,
|
|
29210
|
+
branch: branch ?? owner2?.branch,
|
|
29211
|
+
actor: gcActor,
|
|
29212
|
+
owner: owner2,
|
|
29213
|
+
reason: activeGuard.message
|
|
29214
|
+
});
|
|
29215
|
+
activeWorkspaceDeferred.push({ path: path2, branch: branch ?? owner2?.branch ?? "(unknown)" });
|
|
29216
|
+
return true;
|
|
29217
|
+
}
|
|
28858
29218
|
const owner = findWorktreeOwner(owners, path2);
|
|
28859
29219
|
const verdict = decideWorktreeRemoval({ path: path2, owner, actor: gcActor, now: removalNow, force: opts.force });
|
|
28860
29220
|
if (verdict.action !== "refuse") return false;
|
|
@@ -28870,9 +29230,16 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
28870
29230
|
});
|
|
28871
29231
|
return true;
|
|
28872
29232
|
};
|
|
28873
|
-
const branchesToClean = plan.branches.filter((b) => !refusesRemoval(b.worktreePath));
|
|
29233
|
+
const branchesToClean = plan.branches.filter((b) => !refusesRemoval(b.worktreePath, b.branch));
|
|
28874
29234
|
const worktreeDirsToRemove = plan.worktreeDirs.filter((d) => !refusesRemoval(d.path));
|
|
28875
|
-
|
|
29235
|
+
if (deferredStore) {
|
|
29236
|
+
for (const entry of activeWorkspaceDeferred) {
|
|
29237
|
+
try {
|
|
29238
|
+
await registerDeferredWorktree(deferredStore, { ...entry, reason: "active-workspace" });
|
|
29239
|
+
} catch {
|
|
29240
|
+
}
|
|
29241
|
+
}
|
|
29242
|
+
}
|
|
28876
29243
|
const branchTracking = await applyBranchAndTrackingCleanup({ ...plan, branches: branchesToClean }, {
|
|
28877
29244
|
localBranchHeads,
|
|
28878
29245
|
cleanupBranch: async (branch, expectedHeadOid) => {
|
|
@@ -28880,7 +29247,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
28880
29247
|
const cleanup = await cleanupPrMergeLocalBranch(branch.branch, {
|
|
28881
29248
|
beforeWorktrees,
|
|
28882
29249
|
startingPath: branch.worktreePath,
|
|
28883
|
-
pathExists: (p) => (0,
|
|
29250
|
+
pathExists: (p) => (0, import_node_fs36.existsSync)(p),
|
|
28884
29251
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
28885
29252
|
teardownWorktreeStage,
|
|
28886
29253
|
deferredStore,
|
|
@@ -28888,7 +29255,13 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
28888
29255
|
detachReparsePoints: wtDeps.detachReparsePoints,
|
|
28889
29256
|
// #3064: junction-safe teardown
|
|
28890
29257
|
removeWorktreeDir: wtDeps.removeWorktreeDir,
|
|
28891
|
-
removalContext: {
|
|
29258
|
+
removalContext: {
|
|
29259
|
+
primaryRoot: primaryRepoRoot,
|
|
29260
|
+
actor: gcActor,
|
|
29261
|
+
command: "worktree gc",
|
|
29262
|
+
force: opts.force,
|
|
29263
|
+
activeWorkspaceRoot
|
|
29264
|
+
}
|
|
28892
29265
|
});
|
|
28893
29266
|
if (cleanup.worktree?.status === "removed") await bestEffortLeaseClose(cleanup.worktree.path);
|
|
28894
29267
|
return cleanup;
|
|
@@ -28909,7 +29282,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
28909
29282
|
let removalAttempted = false;
|
|
28910
29283
|
try {
|
|
28911
29284
|
const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, siblingRoot, {
|
|
28912
|
-
realpath: (path2) => (0,
|
|
29285
|
+
realpath: (path2) => (0, import_node_fs36.realpathSync)(path2)
|
|
28913
29286
|
});
|
|
28914
29287
|
if (!cleanupTarget.ok) {
|
|
28915
29288
|
result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
|
|
@@ -28996,13 +29369,13 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
|
|
|
28996
29369
|
const commits = JSON.parse(raw).commits ?? [];
|
|
28997
29370
|
const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
|
|
28998
29371
|
if (!body) return void 0;
|
|
28999
|
-
const dir = (0,
|
|
29000
|
-
const path2 = (0,
|
|
29001
|
-
(0,
|
|
29372
|
+
const dir = (0, import_node_fs36.mkdtempSync)((0, import_node_path35.join)((0, import_node_os14.tmpdir)(), "mmi-squash-body-"));
|
|
29373
|
+
const path2 = (0, import_node_path35.join)(dir, "body.txt");
|
|
29374
|
+
(0, import_node_fs36.writeFileSync)(path2, `${body}
|
|
29002
29375
|
`, "utf8");
|
|
29003
29376
|
return { path: path2, cleanup: () => {
|
|
29004
29377
|
try {
|
|
29005
|
-
(0,
|
|
29378
|
+
(0, import_node_fs36.rmSync)(dir, { recursive: true, force: true });
|
|
29006
29379
|
} catch {
|
|
29007
29380
|
}
|
|
29008
29381
|
} };
|
|
@@ -29124,13 +29497,13 @@ var realWorktreeDirRemover = {
|
|
|
29124
29497
|
probe: (p) => {
|
|
29125
29498
|
let st;
|
|
29126
29499
|
try {
|
|
29127
|
-
st = (0,
|
|
29500
|
+
st = (0, import_node_fs36.lstatSync)(p);
|
|
29128
29501
|
} catch {
|
|
29129
29502
|
return null;
|
|
29130
29503
|
}
|
|
29131
29504
|
if (st.isSymbolicLink()) return "link";
|
|
29132
29505
|
try {
|
|
29133
|
-
(0,
|
|
29506
|
+
(0, import_node_fs36.readlinkSync)(p);
|
|
29134
29507
|
return "link";
|
|
29135
29508
|
} catch {
|
|
29136
29509
|
}
|
|
@@ -29138,7 +29511,7 @@ var realWorktreeDirRemover = {
|
|
|
29138
29511
|
},
|
|
29139
29512
|
readdir: (p) => {
|
|
29140
29513
|
try {
|
|
29141
|
-
return (0,
|
|
29514
|
+
return (0, import_node_fs36.readdirSync)(p);
|
|
29142
29515
|
} catch {
|
|
29143
29516
|
return [];
|
|
29144
29517
|
}
|
|
@@ -29147,9 +29520,9 @@ var realWorktreeDirRemover = {
|
|
|
29147
29520
|
// leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
|
|
29148
29521
|
detachLink: (p) => {
|
|
29149
29522
|
try {
|
|
29150
|
-
(0,
|
|
29523
|
+
(0, import_node_fs36.rmdirSync)(p);
|
|
29151
29524
|
} catch {
|
|
29152
|
-
(0,
|
|
29525
|
+
(0, import_node_fs36.unlinkSync)(p);
|
|
29153
29526
|
}
|
|
29154
29527
|
},
|
|
29155
29528
|
removeTree: (p) => (0, import_promises8.rm)(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
|
|
@@ -29182,11 +29555,11 @@ async function worktreeHasStageState(worktreePath) {
|
|
|
29182
29555
|
}
|
|
29183
29556
|
}
|
|
29184
29557
|
function stageStateFileBelongsToWorktree(statePath, worktreePath) {
|
|
29185
|
-
if (!(0,
|
|
29558
|
+
if (!(0, import_node_fs36.existsSync)(statePath)) return false;
|
|
29186
29559
|
try {
|
|
29187
|
-
const state = JSON.parse((0,
|
|
29560
|
+
const state = JSON.parse((0, import_node_fs36.readFileSync)(statePath, "utf8"));
|
|
29188
29561
|
const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
|
|
29189
|
-
return Boolean(recordedCwd &&
|
|
29562
|
+
return Boolean(recordedCwd && isPathUnderDirectory2(recordedCwd, worktreePath));
|
|
29190
29563
|
} catch {
|
|
29191
29564
|
return false;
|
|
29192
29565
|
}
|
|
@@ -29566,9 +29939,9 @@ async function checkDocsIndexAtHead(opts, deps) {
|
|
|
29566
29939
|
}
|
|
29567
29940
|
|
|
29568
29941
|
// src/worktree-lifecycle-commands.ts
|
|
29569
|
-
var
|
|
29942
|
+
var import_node_fs37 = require("node:fs");
|
|
29570
29943
|
var import_promises9 = require("node:fs/promises");
|
|
29571
|
-
var
|
|
29944
|
+
var import_node_path36 = require("node:path");
|
|
29572
29945
|
var GH_TIMEOUT_MS = 2e4;
|
|
29573
29946
|
var STALE_PR_LOOKUP_LIMIT = 20;
|
|
29574
29947
|
var DEFAULT_BASE = "origin/development";
|
|
@@ -29715,7 +30088,7 @@ function classifyStaleLeaks(input) {
|
|
|
29715
30088
|
var defaultOrphanDirScanDeps = {
|
|
29716
30089
|
listDirs: (root) => {
|
|
29717
30090
|
try {
|
|
29718
|
-
return (0,
|
|
30091
|
+
return (0, import_node_fs37.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path36.join)(root, e.name));
|
|
29719
30092
|
} catch {
|
|
29720
30093
|
return [];
|
|
29721
30094
|
}
|
|
@@ -29871,13 +30244,13 @@ function registerWorktreeCommands(program3) {
|
|
|
29871
30244
|
const detached = headBorn && !symbolicBranch;
|
|
29872
30245
|
const branch = symbolicBranch || (detached ? "HEAD" : "");
|
|
29873
30246
|
if (!wtPath || !branch) return fail("worktree land: not inside a git worktree");
|
|
29874
|
-
const gitFile = (0,
|
|
29875
|
-
const isLinked = (0,
|
|
30247
|
+
const gitFile = (0, import_node_path36.join)(wtPath, ".git");
|
|
30248
|
+
const isLinked = (0, import_node_fs37.existsSync)(gitFile) && (0, import_node_fs37.statSync)(gitFile).isFile();
|
|
29876
30249
|
if (apply && !isLinked) {
|
|
29877
30250
|
return fail("worktree land: run from inside the linked worktree you want to land (this is the primary checkout)");
|
|
29878
30251
|
}
|
|
29879
30252
|
const commonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
29880
|
-
const primaryCheckout = commonDir ? (0,
|
|
30253
|
+
const primaryCheckout = commonDir ? (0, import_node_path36.dirname)(commonDir) : wtPath;
|
|
29881
30254
|
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);
|
|
29882
30255
|
const orphan = classifyOrphanedWorktree({
|
|
29883
30256
|
branch,
|
|
@@ -29940,6 +30313,47 @@ function registerWorktreeCommands(program3) {
|
|
|
29940
30313
|
if (landRefs.action === "keep-remote") console.warn(`worktree land: ${landRefs.message}.`);
|
|
29941
30314
|
const reportedMergeState = treeOnly ? "not-checked" : mergeVerdict.state;
|
|
29942
30315
|
const landOwner = lookupWorktreeOwner(primaryCheckout, wtPath);
|
|
30316
|
+
const activeWorkspaceRoot = resolveActiveWorkspaceRoot();
|
|
30317
|
+
const activeGuard = decideActiveWorkspaceGuard(wtPath, activeWorkspaceRoot);
|
|
30318
|
+
if (activeGuard.action === "refuse") {
|
|
30319
|
+
const deferredStore = await createDeferredWorktreeStore();
|
|
30320
|
+
if (deferredStore) {
|
|
30321
|
+
await registerDeferredWorktree(deferredStore, {
|
|
30322
|
+
path: toNativePath(wtPath),
|
|
30323
|
+
branch,
|
|
30324
|
+
reason: "active-workspace"
|
|
30325
|
+
}).catch(() => void 0);
|
|
30326
|
+
}
|
|
30327
|
+
const landActor2 = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: wtPath });
|
|
30328
|
+
appendWorktreeEvent(primaryCheckout, {
|
|
30329
|
+
action: "refused",
|
|
30330
|
+
command: "worktree land",
|
|
30331
|
+
target: toNativePath(wtPath),
|
|
30332
|
+
branch,
|
|
30333
|
+
actor: landActor2,
|
|
30334
|
+
owner: landOwner ? { createdAt: landOwner.createdAt, lastSeenAt: landOwner.lastSeenAt, actor: landOwner.actor } : void 0,
|
|
30335
|
+
reason: activeGuard.message
|
|
30336
|
+
});
|
|
30337
|
+
const result2 = {
|
|
30338
|
+
dryRun: false,
|
|
30339
|
+
...plan,
|
|
30340
|
+
...o.keepRemote ? { keepRemote: true } : {},
|
|
30341
|
+
mergeState: reportedMergeState,
|
|
30342
|
+
prNumbers: mergeVerdict.numbers,
|
|
30343
|
+
cleanupState: "deferred",
|
|
30344
|
+
report: [
|
|
30345
|
+
{ step: "remove worktree", status: `deferred: ${activeGuard.message}` },
|
|
30346
|
+
{ step: "delete branch refs", status: "skipped: active Cursor workspace \u2014 open the primary checkout first" }
|
|
30347
|
+
]
|
|
30348
|
+
};
|
|
30349
|
+
if (o.json) console.log(JSON.stringify(result2, null, 2));
|
|
30350
|
+
else {
|
|
30351
|
+
console.error(`worktree land: ${activeGuard.message}`);
|
|
30352
|
+
for (const row of result2.report) console.log(` ${row.step}: ${row.status}`);
|
|
30353
|
+
}
|
|
30354
|
+
process.exitCode = 1;
|
|
30355
|
+
return;
|
|
30356
|
+
}
|
|
29943
30357
|
const report = [];
|
|
29944
30358
|
if (hasStage) {
|
|
29945
30359
|
try {
|
|
@@ -30086,10 +30500,10 @@ async function gatherWorktreeContext() {
|
|
|
30086
30500
|
if (s) stages.push({ path: wt.path, port: s.port });
|
|
30087
30501
|
}
|
|
30088
30502
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
30089
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
30503
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path36.dirname)((0, import_node_path36.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
30090
30504
|
const wtRoot = siblingMmiWorktreesRoot(primaryRepoRoot);
|
|
30091
30505
|
let orphanDirs = [];
|
|
30092
|
-
if ((0,
|
|
30506
|
+
if ((0, import_node_fs37.existsSync)(wtRoot)) {
|
|
30093
30507
|
orphanDirs = scanOrphanDirs(wtRoot, worktreeGitRoot, {
|
|
30094
30508
|
...defaultOrphanDirScanDeps,
|
|
30095
30509
|
listDirs: (root) => worktreeScanDirs(root, primaryRepoRoot, defaultOrphanDirScanDeps.listDirs, isRepoCheckoutDir)
|
|
@@ -30115,7 +30529,7 @@ ${err.stderr ?? ""}`;
|
|
|
30115
30529
|
}
|
|
30116
30530
|
|
|
30117
30531
|
// src/issue-commands.ts
|
|
30118
|
-
var
|
|
30532
|
+
var import_node_fs38 = require("node:fs");
|
|
30119
30533
|
var import_node_crypto7 = require("node:crypto");
|
|
30120
30534
|
var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
|
|
30121
30535
|
var ReparentConflictError = class extends Error {
|
|
@@ -30133,7 +30547,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
30133
30547
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
30134
30548
|
const patch = {};
|
|
30135
30549
|
let bodyChanged = false;
|
|
30136
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
30550
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs38.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
30137
30551
|
if (options.titleFile !== void 0) {
|
|
30138
30552
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
30139
30553
|
} else if (options.title !== void 0) {
|
|
@@ -30738,7 +31152,7 @@ function extendCreateCommand(issue2, batchAttach) {
|
|
|
30738
31152
|
if (opts.batch) {
|
|
30739
31153
|
let specs;
|
|
30740
31154
|
try {
|
|
30741
|
-
const raw = (0,
|
|
31155
|
+
const raw = (0, import_node_fs38.readFileSync)(opts.batch, "utf8");
|
|
30742
31156
|
specs = JSON.parse(raw);
|
|
30743
31157
|
if (!Array.isArray(specs)) throw new Error("batch file must contain a JSON array");
|
|
30744
31158
|
} catch (e) {
|
|
@@ -30813,8 +31227,8 @@ ${lines}`, {
|
|
|
30813
31227
|
}
|
|
30814
31228
|
|
|
30815
31229
|
// src/train-commands.ts
|
|
30816
|
-
var
|
|
30817
|
-
var
|
|
31230
|
+
var import_node_fs39 = require("node:fs");
|
|
31231
|
+
var import_node_path37 = require("node:path");
|
|
30818
31232
|
|
|
30819
31233
|
// src/train-status.ts
|
|
30820
31234
|
function buildTrainStatusReport(input) {
|
|
@@ -30854,7 +31268,7 @@ function formatTrainStatus(r) {
|
|
|
30854
31268
|
// src/train-commands.ts
|
|
30855
31269
|
function readRepoVersion() {
|
|
30856
31270
|
try {
|
|
30857
|
-
return JSON.parse((0,
|
|
31271
|
+
return JSON.parse((0, import_node_fs39.readFileSync)((0, import_node_path37.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
30858
31272
|
} catch {
|
|
30859
31273
|
return void 0;
|
|
30860
31274
|
}
|
|
@@ -31000,9 +31414,9 @@ function registerDeployCommands(program3) {
|
|
|
31000
31414
|
}
|
|
31001
31415
|
|
|
31002
31416
|
// src/discovery-commands.ts
|
|
31003
|
-
var
|
|
31004
|
-
var
|
|
31005
|
-
var
|
|
31417
|
+
var import_node_fs40 = require("node:fs");
|
|
31418
|
+
var import_node_os15 = require("node:os");
|
|
31419
|
+
var import_node_path38 = require("node:path");
|
|
31006
31420
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
31007
31421
|
async function collectStatus() {
|
|
31008
31422
|
const repo = await resolveRepo();
|
|
@@ -31190,10 +31604,10 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
31190
31604
|
else if (top) nextCommand = `mmi-cli board claim ${top.number} # ${top.title}`;
|
|
31191
31605
|
else nextCommand = "mmi-cli board read \u2014 no claimable items found";
|
|
31192
31606
|
}
|
|
31193
|
-
const home = (0,
|
|
31607
|
+
const home = (0, import_node_os15.homedir)();
|
|
31194
31608
|
const plugin = onboardPluginGate({
|
|
31195
|
-
readKnown: () => readFileSyncSafe((0,
|
|
31196
|
-
readSettings: () => readFileSyncSafe((0,
|
|
31609
|
+
readKnown: () => readFileSyncSafe((0, import_node_path38.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs40.readFileSync),
|
|
31610
|
+
readSettings: () => readFileSyncSafe((0, import_node_path38.join)(home, ".claude", "settings.json"), import_node_fs40.readFileSync)
|
|
31197
31611
|
});
|
|
31198
31612
|
return { track, board, registry: registry2, secrets, plugin, estateCli, nextCommand };
|
|
31199
31613
|
}
|
|
@@ -32097,19 +32511,19 @@ function registerSessionReport(program3) {
|
|
|
32097
32511
|
}
|
|
32098
32512
|
|
|
32099
32513
|
// src/plugin-release-catchup.ts
|
|
32100
|
-
var
|
|
32101
|
-
var
|
|
32102
|
-
var
|
|
32514
|
+
var import_node_fs41 = require("node:fs");
|
|
32515
|
+
var import_node_path39 = require("node:path");
|
|
32516
|
+
var import_node_os16 = require("node:os");
|
|
32103
32517
|
var RELEASE_CATCHUP_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
32104
32518
|
var RELEASE_CATCHUP_DISABLE_ENV = "MMI_NO_RELEASE_CATCHUP";
|
|
32105
32519
|
function releaseCatchupStatePath(env = process.env) {
|
|
32106
32520
|
if (env.MMI_RELEASE_CATCHUP_STATE) return env.MMI_RELEASE_CATCHUP_STATE;
|
|
32107
32521
|
if (process.platform === "win32") {
|
|
32108
|
-
const base2 = env.LOCALAPPDATA || (0,
|
|
32109
|
-
return (0,
|
|
32522
|
+
const base2 = env.LOCALAPPDATA || (0, import_node_path39.join)((0, import_node_os16.homedir)(), "AppData", "Local");
|
|
32523
|
+
return (0, import_node_path39.join)(base2, "MMI Future", "mmi-cli", "release-catchup.json");
|
|
32110
32524
|
}
|
|
32111
|
-
const base = env.XDG_STATE_HOME || (0,
|
|
32112
|
-
return (0,
|
|
32525
|
+
const base = env.XDG_STATE_HOME || (0, import_node_path39.join)((0, import_node_os16.homedir)(), ".local", "state");
|
|
32526
|
+
return (0, import_node_path39.join)(base, "mmi-cli", "release-catchup.json");
|
|
32113
32527
|
}
|
|
32114
32528
|
function releaseCatchupDue(state, now, force = false) {
|
|
32115
32529
|
if (force) return true;
|
|
@@ -32119,7 +32533,7 @@ function releaseCatchupDue(state, now, force = false) {
|
|
|
32119
32533
|
function newestCachedPluginVersion(home) {
|
|
32120
32534
|
let names;
|
|
32121
32535
|
try {
|
|
32122
|
-
names = (0,
|
|
32536
|
+
names = (0, import_node_fs41.readdirSync)(pluginCacheRoot(home));
|
|
32123
32537
|
} catch {
|
|
32124
32538
|
return void 0;
|
|
32125
32539
|
}
|
|
@@ -32127,15 +32541,15 @@ function newestCachedPluginVersion(home) {
|
|
|
32127
32541
|
}
|
|
32128
32542
|
function marketplaceClonePath(home) {
|
|
32129
32543
|
try {
|
|
32130
|
-
const parsed = JSON.parse((0,
|
|
32544
|
+
const parsed = JSON.parse((0, import_node_fs41.readFileSync)((0, import_node_path39.join)(home, ".claude", "plugins", "known_marketplaces.json"), "utf8"));
|
|
32131
32545
|
if (parsed.mutmutco?.installLocation) return parsed.mutmutco.installLocation;
|
|
32132
32546
|
} catch {
|
|
32133
32547
|
}
|
|
32134
|
-
return (0,
|
|
32548
|
+
return (0, import_node_path39.join)(home, ".claude", "plugins", "marketplaces", "mutmutco");
|
|
32135
32549
|
}
|
|
32136
32550
|
function readCatalogVersion(home) {
|
|
32137
32551
|
try {
|
|
32138
|
-
const parsed = JSON.parse((0,
|
|
32552
|
+
const parsed = JSON.parse((0, import_node_fs41.readFileSync)((0, import_node_path39.join)(marketplaceClonePath(home), ".claude-plugin", "marketplace.json"), "utf8"));
|
|
32139
32553
|
return parsed.plugins?.find((p) => p.name === "mmi")?.version;
|
|
32140
32554
|
} catch {
|
|
32141
32555
|
return void 0;
|
|
@@ -32143,7 +32557,7 @@ function readCatalogVersion(home) {
|
|
|
32143
32557
|
}
|
|
32144
32558
|
function readMmiInstallRecord(home) {
|
|
32145
32559
|
try {
|
|
32146
|
-
const parsed = JSON.parse((0,
|
|
32560
|
+
const parsed = JSON.parse((0, import_node_fs41.readFileSync)((0, import_node_path39.join)(home, ".claude", "plugins", "installed_plugins.json"), "utf8"));
|
|
32147
32561
|
const record = parsed.plugins?.["mmi@mutmutco"]?.[0];
|
|
32148
32562
|
return record?.version ? { version: record.version, gitCommitSha: record.gitCommitSha } : void 0;
|
|
32149
32563
|
} catch {
|
|
@@ -32152,7 +32566,7 @@ function readMmiInstallRecord(home) {
|
|
|
32152
32566
|
}
|
|
32153
32567
|
async function runReleaseCatchup(home, env, deps, opts = {}) {
|
|
32154
32568
|
if (env[RELEASE_CATCHUP_DISABLE_ENV]) return { ok: true, skipped: true, detail: `disabled via ${RELEASE_CATCHUP_DISABLE_ENV}` };
|
|
32155
|
-
if (!(0,
|
|
32569
|
+
if (!(0, import_node_fs41.existsSync)(pluginCacheRoot(home))) return { ok: true, skipped: true, detail: "no mmi plugin cache on this machine \u2014 nothing to catch up" };
|
|
32156
32570
|
const statePath = releaseCatchupStatePath(env);
|
|
32157
32571
|
const state = deps.readState(statePath);
|
|
32158
32572
|
if (!releaseCatchupDue(state, deps.now(), opts.force)) {
|
|
@@ -32177,8 +32591,8 @@ async function runReleaseCatchup(home, env, deps, opts = {}) {
|
|
|
32177
32591
|
return { ok: false, detail: `released ${latest} but the install record could not be cleared \u2014 nothing changed (record still ${prior.version})` };
|
|
32178
32592
|
}
|
|
32179
32593
|
const installed = await deps.runClaude(["plugin", "install", "mmi@mutmutco"], "claude plugin install mmi@mutmutco");
|
|
32180
|
-
const payload = (0,
|
|
32181
|
-
if (!installed || !(0,
|
|
32594
|
+
const payload = (0, import_node_path39.join)(pluginCacheRoot(home), latest, ".pi-plugin");
|
|
32595
|
+
if (!installed || !(0, import_node_fs41.existsSync)(payload)) {
|
|
32182
32596
|
const why = installed ? `install of ${latest} did not produce a verifiable .pi-plugin payload` : `install of ${latest} failed`;
|
|
32183
32597
|
if (!prior) return { ok: false, detail: `${why}; no prior record to restore` };
|
|
32184
32598
|
const rollback = await restorePriorRecord(home, prior, deps);
|
|
@@ -32206,7 +32620,7 @@ async function restorePriorRecord(home, prior, deps) {
|
|
|
32206
32620
|
}
|
|
32207
32621
|
function shouldSpawnReleaseCatchup(home, env, readState2, now = Date.now()) {
|
|
32208
32622
|
if (env[RELEASE_CATCHUP_DISABLE_ENV]) return false;
|
|
32209
|
-
if (!(0,
|
|
32623
|
+
if (!(0, import_node_fs41.existsSync)(pluginCacheRoot(home))) return false;
|
|
32210
32624
|
return releaseCatchupDue(readState2(releaseCatchupStatePath(env)), now);
|
|
32211
32625
|
}
|
|
32212
32626
|
function defaultRegistrationHeal(home, env) {
|
|
@@ -34252,17 +34666,17 @@ function parseOriginRepo(remoteUrl) {
|
|
|
34252
34666
|
}
|
|
34253
34667
|
function ghHostsConfigPath(env, platform2) {
|
|
34254
34668
|
const sep3 = platform2 === "win32" ? "\\" : "/";
|
|
34255
|
-
const
|
|
34669
|
+
const join37 = (...parts) => parts.join(sep3);
|
|
34256
34670
|
const explicit = env.GH_CONFIG_DIR?.trim();
|
|
34257
|
-
if (explicit) return
|
|
34671
|
+
if (explicit) return join37(explicit, "hosts.yml");
|
|
34258
34672
|
if (platform2 === "win32") {
|
|
34259
34673
|
const appData = (env.AppData ?? env.APPDATA)?.trim();
|
|
34260
|
-
return appData ?
|
|
34674
|
+
return appData ? join37(appData, "GitHub CLI", "hosts.yml") : void 0;
|
|
34261
34675
|
}
|
|
34262
34676
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
34263
|
-
if (xdg) return
|
|
34677
|
+
if (xdg) return join37(xdg, "gh", "hosts.yml");
|
|
34264
34678
|
const home = env.HOME?.trim();
|
|
34265
|
-
return home ?
|
|
34679
|
+
return home ? join37(home, ".config", "gh", "hosts.yml") : void 0;
|
|
34266
34680
|
}
|
|
34267
34681
|
function parseGhHostsAccounts(yaml, host = "github.com") {
|
|
34268
34682
|
let hostIndent = null;
|
|
@@ -34312,9 +34726,9 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
34312
34726
|
}
|
|
34313
34727
|
|
|
34314
34728
|
// src/doctor-io.ts
|
|
34315
|
-
var
|
|
34316
|
-
var
|
|
34317
|
-
var
|
|
34729
|
+
var import_node_fs42 = require("node:fs");
|
|
34730
|
+
var import_node_os17 = require("node:os");
|
|
34731
|
+
var import_node_path40 = require("node:path");
|
|
34318
34732
|
var import_node_child_process18 = require("node:child_process");
|
|
34319
34733
|
var import_node_util8 = require("node:util");
|
|
34320
34734
|
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process18.execFile);
|
|
@@ -34322,7 +34736,7 @@ var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
|
34322
34736
|
function installedClaudePluginVersion() {
|
|
34323
34737
|
try {
|
|
34324
34738
|
const file = JSON.parse(
|
|
34325
|
-
(0,
|
|
34739
|
+
(0, import_node_fs42.readFileSync)((0, import_node_path40.join)((0, import_node_os17.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
34326
34740
|
);
|
|
34327
34741
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
34328
34742
|
if (versions.length === 0) return void 0;
|
|
@@ -34333,7 +34747,7 @@ function installedClaudePluginVersion() {
|
|
|
34333
34747
|
}
|
|
34334
34748
|
function manifestVersion(path2) {
|
|
34335
34749
|
try {
|
|
34336
|
-
const manifest = JSON.parse((0,
|
|
34750
|
+
const manifest = JSON.parse((0, import_node_fs42.readFileSync)(path2, "utf8"));
|
|
34337
34751
|
return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : void 0;
|
|
34338
34752
|
} catch {
|
|
34339
34753
|
return void 0;
|
|
@@ -34343,22 +34757,22 @@ function installedSurfacePluginVersion(surface) {
|
|
|
34343
34757
|
const token = surfaceToken(surface);
|
|
34344
34758
|
if (token === "kilo") {
|
|
34345
34759
|
try {
|
|
34346
|
-
const stamp = (0,
|
|
34760
|
+
const stamp = (0, import_node_fs42.readFileSync)((0, import_node_path40.join)((0, import_node_os17.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
|
|
34347
34761
|
return stamp || void 0;
|
|
34348
34762
|
} catch {
|
|
34349
34763
|
return void 0;
|
|
34350
34764
|
}
|
|
34351
34765
|
}
|
|
34352
34766
|
if (token === "cursor") {
|
|
34353
|
-
return manifestVersion((0,
|
|
34767
|
+
return manifestVersion((0, import_node_path40.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
|
|
34354
34768
|
}
|
|
34355
34769
|
if (token === "jervcode") {
|
|
34356
34770
|
const entry = mmiPiWrapperEntry();
|
|
34357
34771
|
if (!entry) return void 0;
|
|
34358
|
-
return manifestVersion((0,
|
|
34772
|
+
return manifestVersion((0, import_node_path40.join)(decodeURIComponent(entry.replace(/^file:\/\/\/?/, "")), "package.json"));
|
|
34359
34773
|
}
|
|
34360
34774
|
if (token === "kimi") {
|
|
34361
|
-
return manifestVersion((0,
|
|
34775
|
+
return manifestVersion((0, import_node_path40.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
|
|
34362
34776
|
}
|
|
34363
34777
|
if (token === "claude") return installedClaudePluginVersion();
|
|
34364
34778
|
if (token !== "codex") return void 0;
|
|
@@ -34396,13 +34810,13 @@ function worktreeRootSync() {
|
|
|
34396
34810
|
}
|
|
34397
34811
|
var gitignorePath = () => {
|
|
34398
34812
|
const root = worktreeRootSync();
|
|
34399
|
-
return root === null ? null : (0,
|
|
34813
|
+
return root === null ? null : (0, import_node_path40.join)(root, ".gitignore");
|
|
34400
34814
|
};
|
|
34401
34815
|
function readGitignore() {
|
|
34402
34816
|
const path2 = gitignorePath();
|
|
34403
34817
|
if (path2 === null) return null;
|
|
34404
34818
|
try {
|
|
34405
|
-
return (0,
|
|
34819
|
+
return (0, import_node_fs42.readFileSync)(path2, "utf8");
|
|
34406
34820
|
} catch {
|
|
34407
34821
|
return null;
|
|
34408
34822
|
}
|
|
@@ -34411,7 +34825,7 @@ function writeGitignore(content) {
|
|
|
34411
34825
|
const path2 = gitignorePath();
|
|
34412
34826
|
if (path2 === null) return false;
|
|
34413
34827
|
try {
|
|
34414
|
-
(0,
|
|
34828
|
+
(0, import_node_fs42.writeFileSync)(path2, content, "utf8");
|
|
34415
34829
|
return true;
|
|
34416
34830
|
} catch {
|
|
34417
34831
|
return false;
|
|
@@ -34435,7 +34849,7 @@ async function repoRoot() {
|
|
|
34435
34849
|
}
|
|
34436
34850
|
function hasRepoLocalWorktrees() {
|
|
34437
34851
|
const root = worktreeRootSync();
|
|
34438
|
-
return root !== null && (0,
|
|
34852
|
+
return root !== null && (0, import_node_fs42.existsSync)((0, import_node_path40.join)(root, ".worktrees"));
|
|
34439
34853
|
}
|
|
34440
34854
|
|
|
34441
34855
|
// src/index.ts
|
|
@@ -34454,8 +34868,8 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
34454
34868
|
function ghMultiAccountCaveat(announcedLogin) {
|
|
34455
34869
|
try {
|
|
34456
34870
|
const hostsPath = ghHostsConfigPath(process.env, process.platform);
|
|
34457
|
-
if (!hostsPath || !(0,
|
|
34458
|
-
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0,
|
|
34871
|
+
if (!hostsPath || !(0, import_node_fs43.existsSync)(hostsPath)) return void 0;
|
|
34872
|
+
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs43.readFileSync)(hostsPath, "utf8")));
|
|
34459
34873
|
} catch {
|
|
34460
34874
|
return void 0;
|
|
34461
34875
|
}
|
|
@@ -34463,12 +34877,12 @@ function ghMultiAccountCaveat(announcedLogin) {
|
|
|
34463
34877
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
34464
34878
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
34465
34879
|
function envHealLockPath(home) {
|
|
34466
|
-
return (0,
|
|
34880
|
+
return (0, import_node_path41.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
34467
34881
|
}
|
|
34468
34882
|
async function withEnvHealLock(what, run) {
|
|
34469
34883
|
try {
|
|
34470
34884
|
return await withFileLock(
|
|
34471
|
-
envHealLockPath((0,
|
|
34885
|
+
envHealLockPath((0, import_node_os18.homedir)()),
|
|
34472
34886
|
{ staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
|
|
34473
34887
|
run
|
|
34474
34888
|
);
|
|
@@ -34565,7 +34979,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
34565
34979
|
const configRoot = surfaceConfigRoot(surface);
|
|
34566
34980
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
34567
34981
|
const plan = buildPluginCachePlan(
|
|
34568
|
-
(0,
|
|
34982
|
+
(0, import_node_os18.homedir)(),
|
|
34569
34983
|
running,
|
|
34570
34984
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
34571
34985
|
{ configRoot, includeStaging: surface !== "codex" }
|
|
@@ -34589,14 +35003,14 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
34589
35003
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
34590
35004
|
const installed = installedActivePluginVersion(surface);
|
|
34591
35005
|
const plan = buildPluginCachePlan(
|
|
34592
|
-
(0,
|
|
35006
|
+
(0, import_node_os18.homedir)(),
|
|
34593
35007
|
running,
|
|
34594
35008
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
34595
35009
|
{ configRoot, includeStaging: surface !== "codex", installedVersion: installed }
|
|
34596
35010
|
);
|
|
34597
35011
|
const result = applyPluginCachePlan(
|
|
34598
35012
|
plan,
|
|
34599
|
-
(p) => (0,
|
|
35013
|
+
(p) => (0, import_node_fs43.rmSync)(p, { recursive: true }),
|
|
34600
35014
|
stagingApplyFsGuard(configRoot)
|
|
34601
35015
|
);
|
|
34602
35016
|
return {
|
|
@@ -34624,12 +35038,12 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
34624
35038
|
piPluginState: () => {
|
|
34625
35039
|
const env = { ...process.env };
|
|
34626
35040
|
delete env.CLAUDE_PLUGIN_ROOT;
|
|
34627
|
-
return readPiPluginState((0,
|
|
35041
|
+
return readPiPluginState((0, import_node_os18.homedir)(), env);
|
|
34628
35042
|
},
|
|
34629
35043
|
healPiPlugin: () => {
|
|
34630
35044
|
const env = { ...process.env };
|
|
34631
35045
|
delete env.CLAUDE_PLUGIN_ROOT;
|
|
34632
|
-
return healPiPluginRegistration((0,
|
|
35046
|
+
return healPiPluginRegistration((0, import_node_os18.homedir)(), env);
|
|
34633
35047
|
},
|
|
34634
35048
|
// #3470: what the SessionStart hook LAST emitted, measured at emission by the session-start verb below.
|
|
34635
35049
|
// A local record read ? cheap enough for every lane, including the banner.
|
|
@@ -34639,17 +35053,17 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
34639
35053
|
marketplaceRows: () => {
|
|
34640
35054
|
try {
|
|
34641
35055
|
if (detectSurface(process.env) === "codex") return [];
|
|
34642
|
-
const home = (0,
|
|
35056
|
+
const home = (0, import_node_os18.homedir)();
|
|
34643
35057
|
const rows = marketplaceRows(
|
|
34644
35058
|
MMI_MARKETPLACE_NAME,
|
|
34645
|
-
readFileSyncSafe((0,
|
|
34646
|
-
readFileSyncSafe((0,
|
|
35059
|
+
readFileSyncSafe((0, import_node_path41.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs43.readFileSync),
|
|
35060
|
+
readFileSyncSafe((0, import_node_path41.join)(home, ".claude", "settings.json"), import_node_fs43.readFileSync),
|
|
34647
35061
|
// #3974: this CLI heals these rows, so report mode names the doctor run rather than the hand
|
|
34648
35062
|
// edit. Unconditional ? it is a fact about mmi-cli, not about the lane this run is on.
|
|
34649
35063
|
true
|
|
34650
35064
|
);
|
|
34651
35065
|
const pending = readMarketplacePinPending(
|
|
34652
|
-
(0,
|
|
35066
|
+
(0, import_node_path41.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"),
|
|
34653
35067
|
MMI_MARKETPLACE_NAME
|
|
34654
35068
|
);
|
|
34655
35069
|
if (!pending) return rows;
|
|
@@ -34673,11 +35087,11 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
34673
35087
|
healMarketplacePins: () => {
|
|
34674
35088
|
try {
|
|
34675
35089
|
if (detectSurface(process.env) === "codex") return void 0;
|
|
34676
|
-
const home = (0,
|
|
35090
|
+
const home = (0, import_node_os18.homedir)();
|
|
34677
35091
|
const names = [MMI_MARKETPLACE_NAME];
|
|
34678
|
-
const result = applyOrgMarketplacePins((0,
|
|
35092
|
+
const result = applyOrgMarketplacePins((0, import_node_path41.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), names);
|
|
34679
35093
|
if (result?.wrote) {
|
|
34680
|
-
writeMarketplacePinPending((0,
|
|
35094
|
+
writeMarketplacePinPending((0, import_node_path41.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"), names);
|
|
34681
35095
|
}
|
|
34682
35096
|
return result;
|
|
34683
35097
|
} catch {
|
|
@@ -34693,7 +35107,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
34693
35107
|
// adopted the generated routing index (MMG-Unlive: `docs/Archive/**` only, no index, no gate.yml) would
|
|
34694
35108
|
// get a permanent ? demanding an artifact it never asked for.
|
|
34695
35109
|
docsIndexState: (root) => {
|
|
34696
|
-
if (!(0,
|
|
35110
|
+
if (!(0, import_node_fs43.existsSync)((0, import_node_path41.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
34697
35111
|
const real = createDocsIndexDeps(root);
|
|
34698
35112
|
let docs2;
|
|
34699
35113
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -34702,7 +35116,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
34702
35116
|
},
|
|
34703
35117
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
34704
35118
|
healDocsIndex: (root) => {
|
|
34705
|
-
if (!(0,
|
|
35119
|
+
if (!(0, import_node_fs43.existsSync)((0, import_node_path41.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
34706
35120
|
const real = createDocsIndexDeps(root);
|
|
34707
35121
|
let docs2;
|
|
34708
35122
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -35006,6 +35420,14 @@ function envelopeAwareWriteErr(str) {
|
|
|
35006
35420
|
}
|
|
35007
35421
|
var program2 = new Command();
|
|
35008
35422
|
program2.name("mmi-cli").description("MMI Future Hub CLI ? the org control plane for agentic coding.").version(resolveClientVersion()).configureOutput({ writeErr: envelopeAwareWriteErr }).showHelpAfterError(PARSE_HINT_SENTINEL);
|
|
35423
|
+
program2.addHelpText(
|
|
35424
|
+
"before",
|
|
35425
|
+
`Houses (canonical roots): oracle \xB7 harbour \xB7 devops \xB7 vault \xB7 learning
|
|
35426
|
+
\`mmi-cli <house> <command> \u2026\` is the canonical form; the flat \`mmi-cli <command> \u2026\` paths below
|
|
35427
|
+
are compatibility shims that die in ${COMPAT_SHIM_DEATH_WAVE} (#4316). \`mmi-cli commands\` shows the
|
|
35428
|
+
house-shaped tree; \`mmi-cli <house> --help\` lists one house.
|
|
35429
|
+
`
|
|
35430
|
+
);
|
|
35009
35431
|
function appActorDeps() {
|
|
35010
35432
|
return {
|
|
35011
35433
|
fetchSecret: async (key) => fetchSecretValue(makeSecretsDeps(await loadConfig()), key, { repo: APP_VAULT_REPO }),
|
|
@@ -35037,19 +35459,19 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
35037
35459
|
});
|
|
35038
35460
|
var rules = program2.command("rules").description("org-managed .gitignore delivery");
|
|
35039
35461
|
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) => {
|
|
35040
|
-
const path2 = (0,
|
|
35041
|
-
const current = (0,
|
|
35462
|
+
const path2 = (0, import_node_path41.join)(process.cwd(), ".gitignore");
|
|
35463
|
+
const current = (0, import_node_fs43.existsSync)(path2) ? (0, import_node_fs43.readFileSync)(path2, "utf8") : null;
|
|
35042
35464
|
const plan = planManagedGitignore(current);
|
|
35043
35465
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
35044
35466
|
if (opts.json) {
|
|
35045
|
-
if (opts.write && plan.changed) (0,
|
|
35467
|
+
if (opts.write && plan.changed) (0, import_node_fs43.writeFileSync)(path2, plan.content, "utf8");
|
|
35046
35468
|
console.log(JSON.stringify(plan, null, 2));
|
|
35047
35469
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
35048
35470
|
return;
|
|
35049
35471
|
}
|
|
35050
35472
|
if (opts.write) {
|
|
35051
35473
|
if (plan.changed) {
|
|
35052
|
-
(0,
|
|
35474
|
+
(0, import_node_fs43.writeFileSync)(path2, plan.content, "utf8");
|
|
35053
35475
|
console.log(`mmi-cli org rules gitignore: updated .gitignore (${drift})`);
|
|
35054
35476
|
} else {
|
|
35055
35477
|
console.log("mmi-cli org rules gitignore: up to date");
|
|
@@ -35207,10 +35629,10 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
|
|
|
35207
35629
|
if (!Number.isFinite(limit) || limit < 1) return fail("worktree gc: --limit must be a positive integer");
|
|
35208
35630
|
let root;
|
|
35209
35631
|
if (o.root !== void 0) {
|
|
35210
|
-
root = (0,
|
|
35211
|
-
if (!(0,
|
|
35632
|
+
root = (0, import_node_path41.resolve)(o.root);
|
|
35633
|
+
if (!(0, import_node_fs43.existsSync)(root) || !(0, import_node_fs43.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
|
|
35212
35634
|
const gcRepoRoot = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
35213
|
-
if (
|
|
35635
|
+
if (isPathUnderDirectory2(gcRepoRoot, root)) {
|
|
35214
35636
|
return fail(`worktree gc: --root ${root} contains this checkout ? name a worktrees root, not the repo or an ancestor of it`);
|
|
35215
35637
|
}
|
|
35216
35638
|
}
|
|
@@ -35280,15 +35702,17 @@ async function primaryCheckoutRoot(from) {
|
|
|
35280
35702
|
}
|
|
35281
35703
|
async function currentWorktreeRemovalContext(command, force) {
|
|
35282
35704
|
const cwd = process.cwd();
|
|
35705
|
+
const activeWorkspaceRoot = resolveActiveWorkspaceRoot();
|
|
35283
35706
|
return {
|
|
35284
35707
|
primaryRoot: await primaryCheckoutRoot(cwd) ?? cwd,
|
|
35285
35708
|
actor: describeActor({ env: process.env, surface: detectSurface(process.env), cwd }),
|
|
35286
35709
|
command,
|
|
35287
|
-
...force ? { force: true } : {}
|
|
35710
|
+
...force ? { force: true } : {},
|
|
35711
|
+
...activeWorkspaceRoot ? { activeWorkspaceRoot } : {}
|
|
35288
35712
|
};
|
|
35289
35713
|
}
|
|
35290
35714
|
async function unprovenWorktreeReason(wtPath, repoRoot2) {
|
|
35291
|
-
if (!(0,
|
|
35715
|
+
if (!(0, import_node_fs43.existsSync)(wtPath)) return `${wtPath} does not exist on disk`;
|
|
35292
35716
|
const porcelain = (await execFileP2("git", ["-C", repoRoot2, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
|
|
35293
35717
|
const registered = parseWorktreePorcelainEntries(porcelain);
|
|
35294
35718
|
if (!registered.length) {
|
|
@@ -35310,26 +35734,26 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
|
|
|
35310
35734
|
function acquireWorktreeSetupLock(worktreeRoot) {
|
|
35311
35735
|
const lockPath = repoRuntimeStatePath(worktreeRoot, "worktree-setup.lock");
|
|
35312
35736
|
const take = () => {
|
|
35313
|
-
const fd = (0,
|
|
35737
|
+
const fd = (0, import_node_fs43.openSync)(lockPath, "wx");
|
|
35314
35738
|
try {
|
|
35315
|
-
(0,
|
|
35739
|
+
(0, import_node_fs43.writeSync)(fd, String(Date.now()));
|
|
35316
35740
|
} finally {
|
|
35317
|
-
(0,
|
|
35741
|
+
(0, import_node_fs43.closeSync)(fd);
|
|
35318
35742
|
}
|
|
35319
35743
|
return () => {
|
|
35320
35744
|
try {
|
|
35321
|
-
(0,
|
|
35745
|
+
(0, import_node_fs43.rmSync)(lockPath, { force: true });
|
|
35322
35746
|
} catch {
|
|
35323
35747
|
}
|
|
35324
35748
|
};
|
|
35325
35749
|
};
|
|
35326
35750
|
try {
|
|
35327
|
-
(0,
|
|
35751
|
+
(0, import_node_fs43.mkdirSync)((0, import_node_path41.dirname)(lockPath), { recursive: true });
|
|
35328
35752
|
return take();
|
|
35329
35753
|
} catch {
|
|
35330
35754
|
try {
|
|
35331
|
-
if (Date.now() - (0,
|
|
35332
|
-
(0,
|
|
35755
|
+
if (Date.now() - (0, import_node_fs43.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
|
|
35756
|
+
(0, import_node_fs43.rmSync)(lockPath, { force: true });
|
|
35333
35757
|
return take();
|
|
35334
35758
|
}
|
|
35335
35759
|
} catch {
|
|
@@ -36182,7 +36606,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
36182
36606
|
if (dupe) return fail(`org project set: KEY "${dupe}" was passed to both --var and --set; --set is an alias of --var, so pass each KEY once`);
|
|
36183
36607
|
if (o.secretsFile) {
|
|
36184
36608
|
try {
|
|
36185
|
-
vars.push(`secrets=${(0,
|
|
36609
|
+
vars.push(`secrets=${(0, import_node_fs43.readFileSync)(o.secretsFile, "utf8")}`);
|
|
36186
36610
|
} catch (e) {
|
|
36187
36611
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
36188
36612
|
}
|
|
@@ -36937,11 +37361,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
|
|
|
36937
37361
|
}
|
|
36938
37362
|
});
|
|
36939
37363
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
36940
|
-
const wfDir = (0,
|
|
36941
|
-
if (!(0,
|
|
36942
|
-
return (0,
|
|
37364
|
+
const wfDir = (0, import_node_path41.join)(cwd, ".github", "workflows");
|
|
37365
|
+
if (!(0, import_node_fs43.existsSync)(wfDir)) return [];
|
|
37366
|
+
return (0, import_node_fs43.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
36943
37367
|
try {
|
|
36944
|
-
return workflowReportsPrChecks((0,
|
|
37368
|
+
return workflowReportsPrChecks((0, import_node_fs43.readFileSync)((0, import_node_path41.join)(wfDir, name), "utf8"));
|
|
36945
37369
|
} catch {
|
|
36946
37370
|
return true;
|
|
36947
37371
|
}
|
|
@@ -36973,16 +37397,16 @@ function ciAuditDeps() {
|
|
|
36973
37397
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
36974
37398
|
readSeedFile: (path2) => {
|
|
36975
37399
|
if (!root) return null;
|
|
36976
|
-
const fullPath = (0,
|
|
36977
|
-
return (0,
|
|
37400
|
+
const fullPath = (0, import_node_path41.join)(root, path2);
|
|
37401
|
+
return (0, import_node_fs43.existsSync)(fullPath) ? (0, import_node_fs43.readFileSync)(fullPath, "utf8") : null;
|
|
36978
37402
|
}
|
|
36979
37403
|
};
|
|
36980
37404
|
}
|
|
36981
37405
|
function hubRoot() {
|
|
36982
|
-
const fromPkg = (0,
|
|
37406
|
+
const fromPkg = (0, import_node_path41.join)(__dirname, "..", "..");
|
|
36983
37407
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
36984
|
-
if ((0,
|
|
36985
|
-
if ((0,
|
|
37408
|
+
if ((0, import_node_fs43.existsSync)((0, import_node_path41.join)(fromPkg, marker))) return fromPkg;
|
|
37409
|
+
if ((0, import_node_fs43.existsSync)((0, import_node_path41.join)(process.cwd(), marker))) return process.cwd();
|
|
36986
37410
|
return null;
|
|
36987
37411
|
}
|
|
36988
37412
|
pr.command("ci-policy").description("report merge CI policy: wait-for-checks vs no-ci (for grind/build agents)").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the current checkout)").action(async (o) => {
|
|
@@ -37302,7 +37726,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
37302
37726
|
localCleanup = await cleanupPrMergeLocalBranch(headRef, {
|
|
37303
37727
|
beforeWorktrees,
|
|
37304
37728
|
startingPath,
|
|
37305
|
-
pathExists: (p) => (0,
|
|
37729
|
+
pathExists: (p) => (0, import_node_fs43.existsSync)(p),
|
|
37306
37730
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
37307
37731
|
teardownWorktreeStage,
|
|
37308
37732
|
deferredStore,
|
|
@@ -37799,12 +38223,12 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
37799
38223
|
targets = resolution.targets;
|
|
37800
38224
|
}
|
|
37801
38225
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
37802
|
-
const fileMatrix = (0,
|
|
38226
|
+
const fileMatrix = (0, import_node_fs43.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs43.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
37803
38227
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
37804
38228
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
37805
|
-
const fileContracts = (0,
|
|
38229
|
+
const fileContracts = (0, import_node_fs43.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs43.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
37806
38230
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
37807
|
-
const sanctioned = (0,
|
|
38231
|
+
const sanctioned = (0, import_node_fs43.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs43.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
37808
38232
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
37809
38233
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
37810
38234
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -37836,16 +38260,16 @@ function directoryBytes(path2) {
|
|
|
37836
38260
|
let total = 0;
|
|
37837
38261
|
let entries;
|
|
37838
38262
|
try {
|
|
37839
|
-
entries = (0,
|
|
38263
|
+
entries = (0, import_node_fs43.readdirSync)(path2, { withFileTypes: true });
|
|
37840
38264
|
} catch {
|
|
37841
38265
|
return 0;
|
|
37842
38266
|
}
|
|
37843
38267
|
for (const entry of entries) {
|
|
37844
|
-
const child2 = (0,
|
|
38268
|
+
const child2 = (0, import_node_path41.join)(path2, entry.name);
|
|
37845
38269
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
37846
38270
|
else {
|
|
37847
38271
|
try {
|
|
37848
|
-
total += (0,
|
|
38272
|
+
total += (0, import_node_fs43.statSync)(child2).size;
|
|
37849
38273
|
} catch {
|
|
37850
38274
|
}
|
|
37851
38275
|
}
|
|
@@ -37853,25 +38277,25 @@ function directoryBytes(path2) {
|
|
|
37853
38277
|
return total;
|
|
37854
38278
|
}
|
|
37855
38279
|
function listDirEntries(dir) {
|
|
37856
|
-
return (0,
|
|
38280
|
+
return (0, import_node_fs43.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
37857
38281
|
}
|
|
37858
38282
|
function readInstalledPluginRefs(configRoot) {
|
|
37859
38283
|
const p = installedPluginsPathForConfig(configRoot);
|
|
37860
|
-
if (!(0,
|
|
38284
|
+
if (!(0, import_node_fs43.existsSync)(p)) return [];
|
|
37861
38285
|
try {
|
|
37862
|
-
return installedPluginPaths((0,
|
|
38286
|
+
return installedPluginPaths((0, import_node_fs43.readFileSync)(p, "utf8"));
|
|
37863
38287
|
} catch {
|
|
37864
38288
|
return null;
|
|
37865
38289
|
}
|
|
37866
38290
|
}
|
|
37867
38291
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
37868
38292
|
return {
|
|
37869
|
-
exists: (p) => (0,
|
|
37870
|
-
listVersionDirs: (root) => (0,
|
|
38293
|
+
exists: (p) => (0, import_node_fs43.existsSync)(p),
|
|
38294
|
+
listVersionDirs: (root) => (0, import_node_fs43.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
37871
38295
|
dirBytes,
|
|
37872
|
-
listStagingDirs: (root) => (0,
|
|
38296
|
+
listStagingDirs: (root) => (0, import_node_fs43.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
37873
38297
|
try {
|
|
37874
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
38298
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path41.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs43.statSync)(p).mtimeMs) };
|
|
37875
38299
|
} catch {
|
|
37876
38300
|
return { name: d.name, mtimeMs: Date.now() };
|
|
37877
38301
|
}
|
|
@@ -37885,10 +38309,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
37885
38309
|
return {
|
|
37886
38310
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
37887
38311
|
mtimeMs: (name) => {
|
|
37888
|
-
const p = (0,
|
|
37889
|
-
if (!(0,
|
|
38312
|
+
const p = (0, import_node_path41.join)(stagingRoot, name);
|
|
38313
|
+
if (!(0, import_node_fs43.existsSync)(p)) return null;
|
|
37890
38314
|
try {
|
|
37891
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
38315
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs43.statSync)(q).mtimeMs);
|
|
37892
38316
|
} catch {
|
|
37893
38317
|
return null;
|
|
37894
38318
|
}
|
|
@@ -37908,13 +38332,13 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
37908
38332
|
return;
|
|
37909
38333
|
}
|
|
37910
38334
|
const plan = buildPluginCachePlan(
|
|
37911
|
-
(0,
|
|
38335
|
+
(0, import_node_os18.homedir)(),
|
|
37912
38336
|
running,
|
|
37913
38337
|
pluginCacheFsDeps(configRoot, directoryBytes),
|
|
37914
38338
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
37915
38339
|
);
|
|
37916
38340
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
37917
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
38341
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs43.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
37918
38342
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
37919
38343
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
37920
38344
|
else console.log(renderPluginCachePlan(plan, result));
|
|
@@ -37922,7 +38346,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
37922
38346
|
});
|
|
37923
38347
|
function readReleaseCatchupState(path2) {
|
|
37924
38348
|
try {
|
|
37925
|
-
const parsed = JSON.parse((0,
|
|
38349
|
+
const parsed = JSON.parse((0, import_node_fs43.readFileSync)(path2, "utf8"));
|
|
37926
38350
|
return typeof parsed?.checkedAt === "number" ? parsed : void 0;
|
|
37927
38351
|
} catch {
|
|
37928
38352
|
return void 0;
|
|
@@ -37930,8 +38354,8 @@ function readReleaseCatchupState(path2) {
|
|
|
37930
38354
|
}
|
|
37931
38355
|
function writeReleaseCatchupState(path2, state) {
|
|
37932
38356
|
try {
|
|
37933
|
-
(0,
|
|
37934
|
-
(0,
|
|
38357
|
+
(0, import_node_fs43.mkdirSync)((0, import_node_path41.dirname)(path2), { recursive: true });
|
|
38358
|
+
(0, import_node_fs43.writeFileSync)(path2, `${JSON.stringify(state)}
|
|
37935
38359
|
`);
|
|
37936
38360
|
} catch {
|
|
37937
38361
|
}
|
|
@@ -37939,7 +38363,7 @@ function writeReleaseCatchupState(path2, state) {
|
|
|
37939
38363
|
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) => {
|
|
37940
38364
|
const outcome = await withEnvHealLock(
|
|
37941
38365
|
"plugin release catch-up",
|
|
37942
|
-
() => runReleaseCatchup((0,
|
|
38366
|
+
() => runReleaseCatchup((0, import_node_os18.homedir)(), process.env, {
|
|
37943
38367
|
fetchReleased: fetchNpmReleasedVersion,
|
|
37944
38368
|
runClaude: (args, step) => runPluginCli("claude", args, (msg) => {
|
|
37945
38369
|
if (!o.quiet && !o.json) console.log(msg);
|
|
@@ -37955,7 +38379,7 @@ program2.command("plugin-release-catchup").description("install a newer released
|
|
|
37955
38379
|
},
|
|
37956
38380
|
readState: readReleaseCatchupState,
|
|
37957
38381
|
writeState: writeReleaseCatchupState,
|
|
37958
|
-
healRegistration: defaultRegistrationHeal((0,
|
|
38382
|
+
healRegistration: defaultRegistrationHeal((0, import_node_os18.homedir)(), process.env),
|
|
37959
38383
|
now: () => Date.now()
|
|
37960
38384
|
}, { force: o.force })
|
|
37961
38385
|
);
|
|
@@ -38023,7 +38447,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
|
|
|
38023
38447
|
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process19.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
38024
38448
|
bannerIo.log(worktreeBanner);
|
|
38025
38449
|
}
|
|
38026
|
-
if (shouldSpawnReleaseCatchup((0,
|
|
38450
|
+
if (shouldSpawnReleaseCatchup((0, import_node_os18.homedir)(), process.env, readReleaseCatchupState)) {
|
|
38027
38451
|
spawnDetachedSelf(["plugin", "release-catchup", "--quiet"], { spawn: import_node_child_process19.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
38028
38452
|
}
|
|
38029
38453
|
if (isLinkedWorktree(process.cwd())) {
|
|
@@ -38037,6 +38461,61 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
|
|
|
38037
38461
|
installProcessBackstop();
|
|
38038
38462
|
consolidateCommandNamespaces(program2);
|
|
38039
38463
|
applyCommandTaxonomy(program2);
|
|
38464
|
+
function resolveHouseShim(argv) {
|
|
38465
|
+
const houseToken = argv[2];
|
|
38466
|
+
if (!houseToken) return;
|
|
38467
|
+
if (!isHouseRoot(houseToken)) {
|
|
38468
|
+
return;
|
|
38469
|
+
}
|
|
38470
|
+
const remainder = argv.slice(3);
|
|
38471
|
+
const first = remainder[0];
|
|
38472
|
+
if (!first || first === "--help" || first === "-h") {
|
|
38473
|
+
printHouseRootHelp(houseToken);
|
|
38474
|
+
hardExit(0);
|
|
38475
|
+
}
|
|
38476
|
+
const commandTokens = remainder.slice(0, 2).filter((tok) => !tok.startsWith("-"));
|
|
38477
|
+
const lookupPath = commandTokens.join(" ");
|
|
38478
|
+
if (!lookupPath) {
|
|
38479
|
+
printHouseRootHelp(houseToken);
|
|
38480
|
+
hardExit(2);
|
|
38481
|
+
}
|
|
38482
|
+
const actual = houseForPath(lookupPath);
|
|
38483
|
+
if (actual === houseToken) {
|
|
38484
|
+
argv.splice(2, 1);
|
|
38485
|
+
return;
|
|
38486
|
+
}
|
|
38487
|
+
const canonical = actual ? `mmi-cli ${actual === "core" ? "" : `${actual} `}${lookupPath}`.replace(/\s+/g, " ").trim() : void 0;
|
|
38488
|
+
if (actual) {
|
|
38489
|
+
process.stderr.write(
|
|
38490
|
+
`mmi-cli ${houseToken}: '${lookupPath}' lives in house '${actual}' \u2014 run: ${canonical} \u2026
|
|
38491
|
+
`
|
|
38492
|
+
);
|
|
38493
|
+
} else {
|
|
38494
|
+
process.stderr.write(
|
|
38495
|
+
`mmi-cli ${houseToken}: '${first}' is not a command of house '${houseToken}' \u2014 run \`mmi-cli ${houseToken} --help\` for its commands
|
|
38496
|
+
`
|
|
38497
|
+
);
|
|
38498
|
+
}
|
|
38499
|
+
hardExit(2);
|
|
38500
|
+
}
|
|
38501
|
+
function printHouseRootHelp(house) {
|
|
38502
|
+
const manifest = buildCommandManifest(program2);
|
|
38503
|
+
const commands = manifest.houses.find((entry) => entry.root === house)?.commands ?? [];
|
|
38504
|
+
const lines = [
|
|
38505
|
+
`mmi-cli ${house} \u2014 the ${house} house (${HOUSE_QUESTIONS[house]}), ${commands.length} command${commands.length === 1 ? "" : "s"}:`,
|
|
38506
|
+
""
|
|
38507
|
+
];
|
|
38508
|
+
for (const cmd of commands) {
|
|
38509
|
+
lines.push(cmd.description ? ` ${cmd.path} \u2014 ${cmd.description}` : ` ${cmd.path}`);
|
|
38510
|
+
}
|
|
38511
|
+
lines.push(
|
|
38512
|
+
"",
|
|
38513
|
+
`Canonical: \`mmi-cli ${house} <command> \u2026\`. The flat \`mmi-cli <command> \u2026\` form is a compatibility`,
|
|
38514
|
+
`shim keeping fleet scripts working for one window \u2014 shims die in ${COMPAT_SHIM_DEATH_WAVE} (#4316).`
|
|
38515
|
+
);
|
|
38516
|
+
consoleIo.log(lines.join("\n"));
|
|
38517
|
+
}
|
|
38518
|
+
resolveHouseShim(process.argv);
|
|
38040
38519
|
program2.parseAsync(process.argv).then(() => finishCliRun()).catch((e) => failGraceful(e.message));
|
|
38041
38520
|
// Annotate the CommonJS export names for ESM import in node:
|
|
38042
38521
|
0 && (module.exports = {
|