@mutmutco/cli 3.82.0 → 3.83.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.cjs +421 -253
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -3416,7 +3416,7 @@ var program = new Command();
|
|
|
3416
3416
|
|
|
3417
3417
|
// src/index.ts
|
|
3418
3418
|
var import_promises10 = require("node:fs/promises");
|
|
3419
|
-
var
|
|
3419
|
+
var import_node_fs36 = require("node:fs");
|
|
3420
3420
|
var import_node_child_process16 = require("node:child_process");
|
|
3421
3421
|
|
|
3422
3422
|
// src/cli-shared.ts
|
|
@@ -6431,7 +6431,14 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
6431
6431
|
});
|
|
6432
6432
|
const allDirs = scanInstallDirs(worktreeRoot, fs2);
|
|
6433
6433
|
const targets = npmInstallTargets(allDirs);
|
|
6434
|
-
|
|
6434
|
+
if (deps.validateInstall) {
|
|
6435
|
+
for (const dir of allDirs.filter((d) => d.hasPackageJson && d.hasLockfile && d.hasNodeModules)) {
|
|
6436
|
+
const cwd = dir.dir ? (0, import_node_path10.join)(worktreeRoot, dir.dir) : worktreeRoot;
|
|
6437
|
+
if (!await deps.validateInstall(cwd)) targets.push({ dir: dir.dir, command: "npm ci" });
|
|
6438
|
+
}
|
|
6439
|
+
}
|
|
6440
|
+
const targetDirs = new Set(targets.map((target) => target.dir));
|
|
6441
|
+
const skippedInstall = allDirs.filter((d) => d.hasPackageJson && d.hasNodeModules && !targetDirs.has(d.dir)).map((d) => d.dir);
|
|
6435
6442
|
const installed = [];
|
|
6436
6443
|
for (const target of targets) {
|
|
6437
6444
|
const cwd = target.dir ? (0, import_node_path10.join)(worktreeRoot, target.dir) : worktreeRoot;
|
|
@@ -6595,7 +6602,7 @@ function commandLadderHint() {
|
|
|
6595
6602
|
}
|
|
6596
6603
|
|
|
6597
6604
|
// src/index.ts
|
|
6598
|
-
var
|
|
6605
|
+
var import_node_path35 = require("node:path");
|
|
6599
6606
|
|
|
6600
6607
|
// src/merge-ci-policy.ts
|
|
6601
6608
|
function resolveMergeCiPolicy(input) {
|
|
@@ -8027,7 +8034,7 @@ async function foldReleaseVersion(deps, model, tag, foldPaths) {
|
|
|
8027
8034
|
if (foldPaths.length === 0) return "no version manifest to fold \u2014 the tag is the version";
|
|
8028
8035
|
const version = tag.replace(/^v/, "");
|
|
8029
8036
|
if (model === "hub-serverless") {
|
|
8030
|
-
await deps.run("node", ["scripts/release-distribution.mjs", "prepare", version]);
|
|
8037
|
+
await deps.run("node", ["scripts/release-distribution.mjs", "prepare", version, "--source-commit", "HEAD"]);
|
|
8031
8038
|
} else {
|
|
8032
8039
|
await installAppFoldDeps(deps);
|
|
8033
8040
|
await deps.run("npm", ["version", version, "--no-git-tag-version", "--allow-same-version"]);
|
|
@@ -11683,10 +11690,13 @@ async function executeWaveLand(plan, deps, opts = { preserveWorktree: true }) {
|
|
|
11683
11690
|
}
|
|
11684
11691
|
|
|
11685
11692
|
// src/index.ts
|
|
11686
|
-
var
|
|
11693
|
+
var import_node_os14 = require("node:os");
|
|
11687
11694
|
|
|
11688
11695
|
// src/board.ts
|
|
11689
11696
|
var import_node_child_process8 = require("node:child_process");
|
|
11697
|
+
var import_node_fs20 = require("node:fs");
|
|
11698
|
+
var import_node_os7 = require("node:os");
|
|
11699
|
+
var import_node_path18 = require("node:path");
|
|
11690
11700
|
var import_node_util6 = require("node:util");
|
|
11691
11701
|
|
|
11692
11702
|
// src/board-priority.ts
|
|
@@ -14841,6 +14851,25 @@ function applyOrgMarketplacePins(path2, names) {
|
|
|
14841
14851
|
"pin"
|
|
14842
14852
|
);
|
|
14843
14853
|
}
|
|
14854
|
+
function writeMarketplacePinPending(path2, names, now = Date.now()) {
|
|
14855
|
+
try {
|
|
14856
|
+
(0, import_node_fs18.mkdirSync)((0, import_node_path16.dirname)(path2), { recursive: true });
|
|
14857
|
+
(0, import_node_fs18.writeFileSync)(path2, `${JSON.stringify({ v: 1, names: [...names], at: new Date(now).toISOString() })}
|
|
14858
|
+
`, "utf8");
|
|
14859
|
+
} catch {
|
|
14860
|
+
}
|
|
14861
|
+
}
|
|
14862
|
+
function readMarketplacePinPending(path2, name, now = Date.now()) {
|
|
14863
|
+
try {
|
|
14864
|
+
const parsed = JSON.parse((0, import_node_fs18.readFileSync)(path2, "utf8"));
|
|
14865
|
+
const at = typeof parsed.at === "string" ? Date.parse(parsed.at) : Number.NaN;
|
|
14866
|
+
if (parsed.v !== 1 || !Array.isArray(parsed.names) || !parsed.names.includes(name) || !Number.isFinite(at)) return void 0;
|
|
14867
|
+
if (now - at < 0 || now - at > 12 * 60 * 6e4) return void 0;
|
|
14868
|
+
return { v: 1, names: parsed.names.filter((n) => typeof n === "string"), at: parsed.at };
|
|
14869
|
+
} catch {
|
|
14870
|
+
return void 0;
|
|
14871
|
+
}
|
|
14872
|
+
}
|
|
14844
14873
|
function restoredPinsLine(pins) {
|
|
14845
14874
|
const named = [...pins].map(([name, want]) => {
|
|
14846
14875
|
const fields = [
|
|
@@ -16129,6 +16158,38 @@ async function postClaimMarkerComment(client, item) {
|
|
|
16129
16158
|
`);
|
|
16130
16159
|
}
|
|
16131
16160
|
}
|
|
16161
|
+
var CLAIM_SESSION_ACTIVITY_MS = 30 * 6e4;
|
|
16162
|
+
var CLAIM_SESSION_PROBE_CACHE_MS = 6e4;
|
|
16163
|
+
var claimSessionProbeCache = /* @__PURE__ */ new Map();
|
|
16164
|
+
function probeLocalClaimSession(marker, now = Date.now()) {
|
|
16165
|
+
if (!marker.session || !marker.host || marker.host.toLowerCase() !== (0, import_node_os7.hostname)().toLowerCase()) return void 0;
|
|
16166
|
+
if (!marker.surface?.toLowerCase().startsWith("claude")) return void 0;
|
|
16167
|
+
const cacheKey = `${marker.host.toLowerCase()}/${marker.session}`;
|
|
16168
|
+
const cached = claimSessionProbeCache.get(cacheKey);
|
|
16169
|
+
if (cached && now - cached.checkedAt <= CLAIM_SESSION_PROBE_CACHE_MS) return cached.state;
|
|
16170
|
+
const remember = (state) => {
|
|
16171
|
+
claimSessionProbeCache.set(cacheKey, { checkedAt: now, state });
|
|
16172
|
+
return state;
|
|
16173
|
+
};
|
|
16174
|
+
const root = (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".claude", "projects");
|
|
16175
|
+
try {
|
|
16176
|
+
const wanted = `${marker.session}.jsonl`.toLowerCase();
|
|
16177
|
+
const pending = [root];
|
|
16178
|
+
while (pending.length) {
|
|
16179
|
+
const dir = pending.pop();
|
|
16180
|
+
for (const entry of (0, import_node_fs20.readdirSync)(dir, { withFileTypes: true })) {
|
|
16181
|
+
const path2 = (0, import_node_path18.join)(dir, entry.name);
|
|
16182
|
+
if (entry.isDirectory()) pending.push(path2);
|
|
16183
|
+
else if (entry.isFile() && entry.name.toLowerCase() === wanted) {
|
|
16184
|
+
return remember(now - (0, import_node_fs20.statSync)(path2).mtimeMs <= CLAIM_SESSION_ACTIVITY_MS ? "live" : "dead");
|
|
16185
|
+
}
|
|
16186
|
+
}
|
|
16187
|
+
}
|
|
16188
|
+
return remember("dead");
|
|
16189
|
+
} catch {
|
|
16190
|
+
return remember("unverifiable");
|
|
16191
|
+
}
|
|
16192
|
+
}
|
|
16132
16193
|
function openPullsFetcher(client) {
|
|
16133
16194
|
const byRepo = /* @__PURE__ */ new Map();
|
|
16134
16195
|
return (repo) => {
|
|
@@ -16146,6 +16207,11 @@ async function gatherClaimLiveness(client, repo, number, fetchOpenPulls) {
|
|
|
16146
16207
|
const comments = await client.restPaginate(`repos/${repo}/issues/${number}/comments`);
|
|
16147
16208
|
out.marker = latestClaimMarker(comments.map((comment) => ({ body: comment.body ?? "" })));
|
|
16148
16209
|
out.markerAgeMs = out.marker ? Date.now() - Date.parse(out.marker.ts) : void 0;
|
|
16210
|
+
if (out.marker) {
|
|
16211
|
+
const state = probeLocalClaimSession(out.marker);
|
|
16212
|
+
if (state === "live" || state === "dead") out.sessionState = state;
|
|
16213
|
+
else if (state === "unverifiable") out.failed.push("session");
|
|
16214
|
+
}
|
|
16149
16215
|
} catch {
|
|
16150
16216
|
out.failed.push("comments");
|
|
16151
16217
|
}
|
|
@@ -16177,8 +16243,10 @@ async function gatherClaimLiveness(client, repo, number, fetchOpenPulls) {
|
|
|
16177
16243
|
}
|
|
16178
16244
|
function liveEvidenceLines(evidence, repo) {
|
|
16179
16245
|
const live = [];
|
|
16180
|
-
if (evidence.marker && evidence.
|
|
16181
|
-
live.push(`claim
|
|
16246
|
+
if (evidence.marker && evidence.sessionState === "live") {
|
|
16247
|
+
live.push(`claim session ${describeClaimMarker(evidence.marker)} has recent local transcript activity`);
|
|
16248
|
+
} else if (evidence.marker && evidence.sessionState !== "dead" && evidence.markerAgeMs !== void 0 && evidence.markerAgeMs < CLAIM_MARKER_LIVE_MS) {
|
|
16249
|
+
live.push(`claim marker from ${describeClaimMarker(evidence.marker)} is only ${formatClaimAge(evidence.markerAgeMs)} old (session not locally probeable)`);
|
|
16182
16250
|
}
|
|
16183
16251
|
if (evidence.openPr) live.push(evidence.openPr);
|
|
16184
16252
|
if (evidence.branch) live.push(`live branch ${evidence.branch} on ${repo}`);
|
|
@@ -16274,9 +16342,9 @@ async function boardDoctor(options, deps = {}) {
|
|
|
16274
16342
|
while (next < claimedItems.length) {
|
|
16275
16343
|
const item = claimedItems[next++];
|
|
16276
16344
|
const evidence = await gatherClaimLiveness(client, item.repository, item.number, fetchOpenPulls);
|
|
16277
|
-
const freshMarker = evidence.markerAgeMs !== void 0 && evidence.markerAgeMs < CLAIM_MARKER_LIVE_MS;
|
|
16345
|
+
const freshMarker = evidence.sessionState === "live" || evidence.sessionState !== "dead" && evidence.markerAgeMs !== void 0 && evidence.markerAgeMs < CLAIM_MARKER_LIVE_MS;
|
|
16278
16346
|
const bits = [
|
|
16279
|
-
evidence.marker ? `claim marker ${describeClaimMarker(evidence.marker)} ${formatClaimAge(evidence.markerAgeMs)} old` : evidence.failed.includes("comments") ? "claim marker unreadable" : "no claim marker",
|
|
16347
|
+
evidence.marker ? `claim marker ${describeClaimMarker(evidence.marker)} ${formatClaimAge(evidence.markerAgeMs)} old` + (evidence.sessionState ? `; local session ${evidence.sessionState}` : "") : evidence.failed.includes("comments") ? "claim marker unreadable" : "no claim marker",
|
|
16280
16348
|
evidence.openPr,
|
|
16281
16349
|
evidence.branch ? `branch ${evidence.branch}` : void 0
|
|
16282
16350
|
].filter((bit) => Boolean(bit)).join("; ");
|
|
@@ -16380,7 +16448,7 @@ async function resolveAutoAddBoardAttach(client, cfg, selector, priority, warn =
|
|
|
16380
16448
|
}
|
|
16381
16449
|
|
|
16382
16450
|
// src/issue-body.ts
|
|
16383
|
-
var
|
|
16451
|
+
var import_node_os8 = require("node:os");
|
|
16384
16452
|
var TextArgError = class extends Error {
|
|
16385
16453
|
constructor(message, code, offendingFlag) {
|
|
16386
16454
|
super(message);
|
|
@@ -16392,7 +16460,7 @@ var TextArgError = class extends Error {
|
|
|
16392
16460
|
offendingFlag;
|
|
16393
16461
|
};
|
|
16394
16462
|
function emptyStdinMessage(fileFlag) {
|
|
16395
|
-
if ((0,
|
|
16463
|
+
if ((0, import_node_os8.platform)() === "win32") {
|
|
16396
16464
|
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)`;
|
|
16397
16465
|
}
|
|
16398
16466
|
return `${fileFlag} - read empty stdin (nothing piped \u2014 pass a heredoc/pipe, or ${fileFlag} <path>)`;
|
|
@@ -17717,7 +17785,7 @@ async function runHotfixStart(deps, options) {
|
|
|
17717
17785
|
}
|
|
17718
17786
|
notes.push(`cherry-picked ${label} onto ${branch} (from origin/main, -x trailer recorded)`);
|
|
17719
17787
|
if (deployModel === "hub-serverless") {
|
|
17720
|
-
await deps.run("node", ["scripts/release-distribution.mjs", "prepare", version]);
|
|
17788
|
+
await deps.run("node", ["scripts/release-distribution.mjs", "prepare", version, "--source-commit", "HEAD"]);
|
|
17721
17789
|
const changedFiles = (await deps.run("node", ["scripts/release-distribution.mjs", "changed-files"])).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
17722
17790
|
await deps.run("git", ["add", "-f", "--", ...changedFiles]);
|
|
17723
17791
|
const staged = await deps.run("git", ["diff", "--cached", "--name-only"]);
|
|
@@ -18575,8 +18643,8 @@ function renderAccessReport(report) {
|
|
|
18575
18643
|
|
|
18576
18644
|
// src/doc-refs-core.ts
|
|
18577
18645
|
var import_node_child_process10 = require("node:child_process");
|
|
18578
|
-
var
|
|
18579
|
-
var
|
|
18646
|
+
var import_node_fs21 = require("node:fs");
|
|
18647
|
+
var import_node_path19 = require("node:path");
|
|
18580
18648
|
var PIN_RE = /<!--\s*pinned by\s+([^:]+?)\s*:\s*"([^"]+)"[^"]*-->/;
|
|
18581
18649
|
var PIN_MENTION_RE = /<!--\s*pinned by\b/;
|
|
18582
18650
|
var FWD_RE = /<!--\s*forward-ref:\s*(\S+?)\s*-->/;
|
|
@@ -18632,7 +18700,7 @@ function checkPins(root, readFile9, docs2) {
|
|
|
18632
18700
|
findings.push({ kind: "malformed-pin", doc, line: pin.line, detail: pin.text });
|
|
18633
18701
|
continue;
|
|
18634
18702
|
}
|
|
18635
|
-
const source = readFile9((0,
|
|
18703
|
+
const source = readFile9((0, import_node_path19.join)(root, pin.file));
|
|
18636
18704
|
if (source == null) {
|
|
18637
18705
|
findings.push({ kind: "missing-test", doc, line: pin.line, detail: pin.file });
|
|
18638
18706
|
continue;
|
|
@@ -18692,7 +18760,7 @@ function checkRefs(root, deps, docs2) {
|
|
|
18692
18760
|
const candidates = [];
|
|
18693
18761
|
const direct = [];
|
|
18694
18762
|
for (const [doc, markdown] of Object.entries(docs2)) {
|
|
18695
|
-
const docDir =
|
|
18763
|
+
const docDir = import_node_path19.posix.dirname(doc);
|
|
18696
18764
|
const base = docDir === "." ? "" : docDir;
|
|
18697
18765
|
const covered = /* @__PURE__ */ new Set();
|
|
18698
18766
|
const markers = [];
|
|
@@ -18701,21 +18769,21 @@ function checkRefs(root, deps, docs2) {
|
|
|
18701
18769
|
direct.push({ kind: "malformed-forward-ref", doc, line: fwd.line, detail: fwd.text });
|
|
18702
18770
|
continue;
|
|
18703
18771
|
}
|
|
18704
|
-
const docRel =
|
|
18705
|
-
const rootRel =
|
|
18772
|
+
const docRel = import_node_path19.posix.normalize(import_node_path19.posix.join(base, fwd.target));
|
|
18773
|
+
const rootRel = import_node_path19.posix.normalize(fwd.target.replace(/^\/+/, ""));
|
|
18706
18774
|
markers.push({ target: fwd.target, line: fwd.line, docRel, rootRel });
|
|
18707
18775
|
covered.add(docRel);
|
|
18708
18776
|
covered.add(rootRel);
|
|
18709
18777
|
}
|
|
18710
18778
|
const links = extractLinks(markdown).map(({ target, line }) => {
|
|
18711
|
-
const resolved =
|
|
18712
|
-
return { target, line, resolved, missing: !exists((0,
|
|
18779
|
+
const resolved = import_node_path19.posix.normalize(import_node_path19.posix.join(base, target));
|
|
18780
|
+
return { target, line, resolved, missing: !exists((0, import_node_path19.join)(root, resolved)) };
|
|
18713
18781
|
});
|
|
18714
18782
|
for (const marker of markers) {
|
|
18715
18783
|
const coversMissing = links.some(
|
|
18716
18784
|
(l) => l.missing && (l.resolved === marker.docRel || l.resolved === marker.rootRel)
|
|
18717
18785
|
);
|
|
18718
|
-
if (!coversMissing && (exists((0,
|
|
18786
|
+
if (!coversMissing && (exists((0, import_node_path19.join)(root, marker.docRel)) || exists((0, import_node_path19.join)(root, marker.rootRel)))) {
|
|
18719
18787
|
direct.push({
|
|
18720
18788
|
kind: "stale-forward-ref",
|
|
18721
18789
|
doc,
|
|
@@ -18726,8 +18794,8 @@ function checkRefs(root, deps, docs2) {
|
|
|
18726
18794
|
}
|
|
18727
18795
|
for (const { ref, line } of extractRefs(markdown)) {
|
|
18728
18796
|
const first = ref.split("/")[0];
|
|
18729
|
-
if (!exists((0,
|
|
18730
|
-
if (!exists((0,
|
|
18797
|
+
if (!exists((0, import_node_path19.join)(root, first))) continue;
|
|
18798
|
+
if (!exists((0, import_node_path19.join)(root, ref))) candidates.push({ kind: "missing-path", doc, line, detail: ref });
|
|
18731
18799
|
}
|
|
18732
18800
|
for (const { target, line, resolved, missing } of links) {
|
|
18733
18801
|
if (resolved.startsWith("..")) {
|
|
@@ -18775,22 +18843,22 @@ function checkCommands(docs2, commandPaths) {
|
|
|
18775
18843
|
return { ok: findings.length === 0, findings, warnings: [] };
|
|
18776
18844
|
}
|
|
18777
18845
|
function readFileOrNull(path2) {
|
|
18778
|
-
return (0,
|
|
18846
|
+
return (0, import_node_fs21.existsSync)(path2) ? (0, import_node_fs21.readFileSync)(path2, "utf8") : null;
|
|
18779
18847
|
}
|
|
18780
18848
|
function walk(dir, root, out) {
|
|
18781
|
-
for (const entry of (0,
|
|
18782
|
-
const full = (0,
|
|
18783
|
-
if ((0,
|
|
18849
|
+
for (const entry of (0, import_node_fs21.readdirSync)(dir)) {
|
|
18850
|
+
const full = (0, import_node_path19.join)(dir, entry);
|
|
18851
|
+
if ((0, import_node_fs21.statSync)(full).isDirectory()) walk(full, root, out);
|
|
18784
18852
|
else if (entry.endsWith(".md")) out.push(full.slice(root.length + 1).replaceAll("\\", "/"));
|
|
18785
18853
|
}
|
|
18786
18854
|
return out;
|
|
18787
18855
|
}
|
|
18788
18856
|
function defaultListDocs(root) {
|
|
18789
|
-
const docsDir = (0,
|
|
18790
|
-
const docs2 = ((0,
|
|
18857
|
+
const docsDir = (0, import_node_path19.join)(root, "docs");
|
|
18858
|
+
const docs2 = ((0, import_node_fs21.existsSync)(docsDir) ? walk(docsDir, root, []) : []).filter(
|
|
18791
18859
|
(rel) => !SKIP_WALK.some((skip) => rel.startsWith(skip))
|
|
18792
18860
|
);
|
|
18793
|
-
return [...ROOT_DOCS.filter((rel) => (0,
|
|
18861
|
+
return [...ROOT_DOCS.filter((rel) => (0, import_node_fs21.existsSync)((0, import_node_path19.join)(root, rel))), ...docs2];
|
|
18794
18862
|
}
|
|
18795
18863
|
function defaultIsIgnored(root, relPaths, exec = import_node_child_process10.execFileSync) {
|
|
18796
18864
|
const inRepo = relPaths.filter((p) => !p.startsWith(".."));
|
|
@@ -18810,14 +18878,14 @@ function defaultIsIgnored(root, relPaths, exec = import_node_child_process10.exe
|
|
|
18810
18878
|
}
|
|
18811
18879
|
function runDocRefs(root, deps = {}) {
|
|
18812
18880
|
const readFile9 = deps.readFile ?? readFileOrNull;
|
|
18813
|
-
const exists = deps.exists ??
|
|
18881
|
+
const exists = deps.exists ?? import_node_fs21.existsSync;
|
|
18814
18882
|
const listDocs = deps.listDocs ?? defaultListDocs;
|
|
18815
18883
|
const isIgnored = deps.isIgnored ?? ((paths) => defaultIsIgnored(root, paths));
|
|
18816
18884
|
const commandPaths = deps.commandPaths ?? null;
|
|
18817
18885
|
const walked = listDocs(root);
|
|
18818
18886
|
const ignoredDocs = walked.length ? isIgnored(walked) : /* @__PURE__ */ new Set();
|
|
18819
18887
|
const docs2 = Object.fromEntries(
|
|
18820
|
-
walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile9((0,
|
|
18888
|
+
walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile9((0, import_node_path19.join)(root, rel))]).filter(([, body]) => body != null)
|
|
18821
18889
|
);
|
|
18822
18890
|
const refResult = checkRefs(root, { exists, isIgnored }, docs2);
|
|
18823
18891
|
const findings = [
|
|
@@ -18844,8 +18912,8 @@ function runDocRefs(root, deps = {}) {
|
|
|
18844
18912
|
|
|
18845
18913
|
// src/spawn-policy-core.ts
|
|
18846
18914
|
var import_node_child_process11 = require("node:child_process");
|
|
18847
|
-
var
|
|
18848
|
-
var
|
|
18915
|
+
var import_node_fs22 = require("node:fs");
|
|
18916
|
+
var import_node_path20 = require("node:path");
|
|
18849
18917
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
18850
18918
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
18851
18919
|
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
@@ -18931,7 +18999,7 @@ function runSpawnPolicy(root) {
|
|
|
18931
18999
|
for (const file of files) {
|
|
18932
19000
|
let raw;
|
|
18933
19001
|
try {
|
|
18934
|
-
raw = (0,
|
|
19002
|
+
raw = (0, import_node_fs22.readFileSync)((0, import_node_path20.join)(root, file), "utf8");
|
|
18935
19003
|
} catch {
|
|
18936
19004
|
continue;
|
|
18937
19005
|
}
|
|
@@ -18949,8 +19017,8 @@ function runSpawnPolicy(root) {
|
|
|
18949
19017
|
|
|
18950
19018
|
// src/test-policy-core.ts
|
|
18951
19019
|
var import_node_child_process12 = require("node:child_process");
|
|
18952
|
-
var
|
|
18953
|
-
var
|
|
19020
|
+
var import_node_fs23 = require("node:fs");
|
|
19021
|
+
var import_node_path21 = require("node:path");
|
|
18954
19022
|
var POLICY_FILE = "test-policy.json";
|
|
18955
19023
|
var TEST_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
|
18956
19024
|
var PY_TEST_RE = /(?:^|\/)test_[^/]*\.py$|_test\.py$/;
|
|
@@ -19003,7 +19071,7 @@ function isTestPath(path2) {
|
|
|
19003
19071
|
return TEST_RE.test(path2) || PY_TEST_RE.test(path2);
|
|
19004
19072
|
}
|
|
19005
19073
|
function loadPolicy(root, readFile9 = readFileOrNull2) {
|
|
19006
|
-
const raw = readFile9((0,
|
|
19074
|
+
const raw = readFile9((0, import_node_path21.join)(root, POLICY_FILE));
|
|
19007
19075
|
if (raw == null) return { mandatory: [], declared: false };
|
|
19008
19076
|
try {
|
|
19009
19077
|
return { ...JSON.parse(raw), declared: true };
|
|
@@ -19013,7 +19081,7 @@ function loadPolicy(root, readFile9 = readFileOrNull2) {
|
|
|
19013
19081
|
}
|
|
19014
19082
|
function readFileOrNull2(path2) {
|
|
19015
19083
|
try {
|
|
19016
|
-
return (0,
|
|
19084
|
+
return (0, import_node_fs23.readFileSync)(path2, "utf8");
|
|
19017
19085
|
} catch {
|
|
19018
19086
|
return null;
|
|
19019
19087
|
}
|
|
@@ -19040,12 +19108,12 @@ function classify(changed, policy, present = () => false) {
|
|
|
19040
19108
|
const removedProtected = [...removed].filter((p) => protectedBy.has(p)).map((p) => ({ path: p, why: protectedBy.get(p) ?? "" }));
|
|
19041
19109
|
return { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected };
|
|
19042
19110
|
}
|
|
19043
|
-
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0,
|
|
19044
|
-
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0,
|
|
19111
|
+
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs23.existsSync)(path2)) {
|
|
19112
|
+
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path21.join)(root, p)));
|
|
19045
19113
|
}
|
|
19046
|
-
function unresolvedSatisfiers(policy, root, exists = (path2) => (0,
|
|
19114
|
+
function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs23.existsSync)(path2)) {
|
|
19047
19115
|
const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
|
|
19048
|
-
return [...new Set(declared)].filter((p) => !exists((0,
|
|
19116
|
+
return [...new Set(declared)].filter((p) => !exists((0, import_node_path21.join)(root, p)));
|
|
19049
19117
|
}
|
|
19050
19118
|
function evaluate(changed, policy, present = () => false) {
|
|
19051
19119
|
const { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected } = classify(changed, policy, present);
|
|
@@ -19227,13 +19295,13 @@ function changedFilesSince(base, cwd) {
|
|
|
19227
19295
|
}
|
|
19228
19296
|
function runTestPolicy(root, deps = {}) {
|
|
19229
19297
|
const policy = deps.policy ?? loadPolicy(root);
|
|
19230
|
-
const exists = deps.exists ?? ((path2) => (0,
|
|
19298
|
+
const exists = deps.exists ?? ((path2) => (0, import_node_fs23.existsSync)(path2));
|
|
19231
19299
|
const counts = { mandatoryCount: (policy.mandatory ?? []).length, protectedCount: (policy.protected ?? []).length };
|
|
19232
19300
|
const base = deps.changed ? "(injected)" : resolveBase(root, deps.base);
|
|
19233
19301
|
const refusal = deps.changed ? null : untrustworthyRange(root, base);
|
|
19234
19302
|
const changed = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
|
|
19235
19303
|
const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride(base, root) : { override: null, refusals: [] };
|
|
19236
|
-
const present = (path2) => exists((0,
|
|
19304
|
+
const present = (path2) => exists((0, import_node_path21.join)(root, path2));
|
|
19237
19305
|
const removedByThisDiff = removedPaths(changed);
|
|
19238
19306
|
const staleFindings = [];
|
|
19239
19307
|
const unresolved = unresolvedProtectedEntries(policy, root, exists).filter((p) => !removedByThisDiff.has(p));
|
|
@@ -19389,8 +19457,8 @@ function docsAuditStatus(fetch2, opts) {
|
|
|
19389
19457
|
}
|
|
19390
19458
|
|
|
19391
19459
|
// src/project-info-sync.ts
|
|
19392
|
-
var
|
|
19393
|
-
var
|
|
19460
|
+
var import_node_fs24 = require("node:fs");
|
|
19461
|
+
var import_node_path22 = require("node:path");
|
|
19394
19462
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
19395
19463
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
19396
19464
|
projectV2 { id }
|
|
@@ -19435,14 +19503,14 @@ function sharedName(entries, fallback) {
|
|
|
19435
19503
|
}
|
|
19436
19504
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
19437
19505
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
19438
|
-
const readmePath = (0,
|
|
19439
|
-
if (!(0,
|
|
19506
|
+
const readmePath = (0, import_node_path22.join)(repoRoot2, "README.md");
|
|
19507
|
+
if (!(0, import_node_fs24.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
19440
19508
|
const entries = entriesFor(project2, projects);
|
|
19441
19509
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
19442
19510
|
const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
|
|
19443
19511
|
if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
|
|
19444
19512
|
const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
|
|
19445
|
-
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0,
|
|
19513
|
+
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs24.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
|
|
19446
19514
|
const lines = [
|
|
19447
19515
|
`# ${projectName}`,
|
|
19448
19516
|
"",
|
|
@@ -19461,8 +19529,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
19461
19529
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
19462
19530
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
19463
19531
|
const orgDocs = [
|
|
19464
|
-
(0,
|
|
19465
|
-
(0,
|
|
19532
|
+
(0, import_node_fs24.existsSync)((0, import_node_path22.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
|
|
19533
|
+
(0, import_node_fs24.existsSync)((0, import_node_path22.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
|
|
19466
19534
|
].filter(Boolean);
|
|
19467
19535
|
if (orgDocs.length) lines.push("", "## Organisation docs", "", ...orgDocs);
|
|
19468
19536
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines.join("\n")}
|
|
@@ -20321,9 +20389,9 @@ function writeError(res) {
|
|
|
20321
20389
|
}
|
|
20322
20390
|
|
|
20323
20391
|
// src/secrets-commands.ts
|
|
20324
|
-
var
|
|
20325
|
-
var
|
|
20326
|
-
var
|
|
20392
|
+
var import_node_fs25 = require("node:fs");
|
|
20393
|
+
var import_node_path23 = require("node:path");
|
|
20394
|
+
var import_node_os9 = require("node:os");
|
|
20327
20395
|
|
|
20328
20396
|
// src/project-runtime.ts
|
|
20329
20397
|
function hasRuntimeSecretContract(contract) {
|
|
@@ -20446,18 +20514,18 @@ function collectMap(value, previous = []) {
|
|
|
20446
20514
|
return [...previous, value];
|
|
20447
20515
|
}
|
|
20448
20516
|
async function decryptRailsCredentials(input) {
|
|
20449
|
-
const appDir = (0,
|
|
20517
|
+
const appDir = (0, import_node_path23.resolve)(input.appDir ?? process.cwd());
|
|
20450
20518
|
const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
20451
20519
|
const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
20452
|
-
const credentialsPath = (0,
|
|
20453
|
-
const masterKeyPath = (0,
|
|
20520
|
+
const credentialsPath = (0, import_node_path23.resolve)(appDir, credentialsFile);
|
|
20521
|
+
const masterKeyPath = (0, import_node_path23.resolve)(appDir, masterKeyFile);
|
|
20454
20522
|
const env = {
|
|
20455
20523
|
...process.env,
|
|
20456
20524
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
20457
20525
|
MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
|
|
20458
20526
|
};
|
|
20459
|
-
if ((0,
|
|
20460
|
-
env.RAILS_MASTER_KEY = (0,
|
|
20527
|
+
if ((0, import_node_fs25.existsSync)(masterKeyPath)) {
|
|
20528
|
+
env.RAILS_MASTER_KEY = (0, import_node_fs25.readFileSync)(masterKeyPath, "utf8").trim();
|
|
20461
20529
|
}
|
|
20462
20530
|
const script = [
|
|
20463
20531
|
'require "json"',
|
|
@@ -20467,9 +20535,9 @@ async function decryptRailsCredentials(input) {
|
|
|
20467
20535
|
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
20468
20536
|
"puts JSON.generate(config.config)"
|
|
20469
20537
|
].join("\n");
|
|
20470
|
-
const scriptDir = (0,
|
|
20471
|
-
const scriptPath = (0,
|
|
20472
|
-
(0,
|
|
20538
|
+
const scriptDir = (0, import_node_fs25.mkdtempSync)((0, import_node_path23.join)((0, import_node_os9.tmpdir)(), "mmi-rails-decrypt-"));
|
|
20539
|
+
const scriptPath = (0, import_node_path23.join)(scriptDir, "decrypt.rb");
|
|
20540
|
+
(0, import_node_fs25.writeFileSync)(scriptPath, script, "utf8");
|
|
20473
20541
|
try {
|
|
20474
20542
|
const args = ["exec", "ruby", scriptPath];
|
|
20475
20543
|
const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
|
|
@@ -20481,7 +20549,7 @@ async function decryptRailsCredentials(input) {
|
|
|
20481
20549
|
});
|
|
20482
20550
|
return JSON.parse(stdout);
|
|
20483
20551
|
} finally {
|
|
20484
|
-
(0,
|
|
20552
|
+
(0, import_node_fs25.rmSync)(scriptDir, { recursive: true, force: true });
|
|
20485
20553
|
}
|
|
20486
20554
|
}
|
|
20487
20555
|
async function readSecretStdin() {
|
|
@@ -20571,7 +20639,7 @@ function registerSecretsCommands(program3) {
|
|
|
20571
20639
|
let body;
|
|
20572
20640
|
if (o.file) {
|
|
20573
20641
|
try {
|
|
20574
|
-
body = (0,
|
|
20642
|
+
body = (0, import_node_fs25.readFileSync)((0, import_node_path23.resolve)(o.file), "utf8");
|
|
20575
20643
|
} catch (e) {
|
|
20576
20644
|
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
20577
20645
|
}
|
|
@@ -20676,7 +20744,7 @@ function registerSecretsCommands(program3) {
|
|
|
20676
20744
|
{
|
|
20677
20745
|
...d,
|
|
20678
20746
|
decryptRailsCredentials,
|
|
20679
|
-
removeFile: (path2) => (0,
|
|
20747
|
+
removeFile: (path2) => (0, import_node_fs25.unlinkSync)((0, import_node_path23.resolve)(o.appDir ?? process.cwd(), path2))
|
|
20680
20748
|
},
|
|
20681
20749
|
{
|
|
20682
20750
|
repo: o.repo,
|
|
@@ -20840,7 +20908,7 @@ async function activateAppActor(commandPath3, env, mint) {
|
|
|
20840
20908
|
}
|
|
20841
20909
|
|
|
20842
20910
|
// src/box-commands.ts
|
|
20843
|
-
var
|
|
20911
|
+
var import_node_fs26 = require("node:fs");
|
|
20844
20912
|
|
|
20845
20913
|
// src/box.ts
|
|
20846
20914
|
var BOX_KEYS = {
|
|
@@ -21043,7 +21111,7 @@ function registerBoxCommands(program3) {
|
|
|
21043
21111
|
}
|
|
21044
21112
|
if (o.json) console.log(JSON.stringify({ box: found, incomplete }, null, 2));
|
|
21045
21113
|
else if (o.ssh && o.script) {
|
|
21046
|
-
(0,
|
|
21114
|
+
(0, import_node_fs26.writeFileSync)(o.script, sshRecipeScript(found), "utf8");
|
|
21047
21115
|
console.log(`wrote ${o.script} \u2014 run: bash "${o.script}"`);
|
|
21048
21116
|
} else if (o.ssh) console.log(`${formatSshRecipe(found)}
|
|
21049
21117
|
${SSH_RECIPE_AGENT_NOTE}`);
|
|
@@ -21729,7 +21797,7 @@ function registerSchedulesCommands(program3) {
|
|
|
21729
21797
|
|
|
21730
21798
|
// src/file-lock.ts
|
|
21731
21799
|
var import_promises4 = require("node:fs/promises");
|
|
21732
|
-
var
|
|
21800
|
+
var import_node_path24 = require("node:path");
|
|
21733
21801
|
var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
21734
21802
|
var IMMEDIATE_RETRY_BUDGET = 3;
|
|
21735
21803
|
var FileLockBusyError = class extends Error {
|
|
@@ -21814,7 +21882,7 @@ async function releaseFileLock(lockPath, guard) {
|
|
|
21814
21882
|
}
|
|
21815
21883
|
async function withFileLock(lockPath, opts, fn) {
|
|
21816
21884
|
const resolved = resolveFileLockOpts(opts);
|
|
21817
|
-
await (0, import_promises4.mkdir)((0,
|
|
21885
|
+
await (0, import_promises4.mkdir)((0, import_node_path24.dirname)(lockPath), { recursive: true }).catch(() => void 0);
|
|
21818
21886
|
const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
|
|
21819
21887
|
try {
|
|
21820
21888
|
return await fn();
|
|
@@ -21825,7 +21893,7 @@ async function withFileLock(lockPath, opts, fn) {
|
|
|
21825
21893
|
|
|
21826
21894
|
// src/schedules-lift-command.ts
|
|
21827
21895
|
var import_promises5 = require("node:fs/promises");
|
|
21828
|
-
var
|
|
21896
|
+
var import_node_path25 = require("node:path");
|
|
21829
21897
|
|
|
21830
21898
|
// src/schedules-lift.ts
|
|
21831
21899
|
var SCHEDULE_HEADER_FIELDS = ["schedule", "what", "owner", "cadence", "executor", "llm", "output", "breaks", "kill"];
|
|
@@ -21931,7 +21999,7 @@ async function readWorkflowFiles(dir) {
|
|
|
21931
21999
|
const files = [];
|
|
21932
22000
|
for (const name of names.sort()) {
|
|
21933
22001
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
21934
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises5.readFile)((0,
|
|
22002
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises5.readFile)((0, import_node_path25.join)(dir, name), "utf8") });
|
|
21935
22003
|
}
|
|
21936
22004
|
return files;
|
|
21937
22005
|
}
|
|
@@ -22015,13 +22083,13 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
|
|
|
22015
22083
|
// src/edge-tunnel.ts
|
|
22016
22084
|
var HOSTNAME_RE = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/;
|
|
22017
22085
|
var UPSTREAM_RE = /^https?:\/\/[^/\s]+(?::\d+)?(?:\/.*)?$/;
|
|
22018
|
-
function tunnelNameFromHostname(
|
|
22019
|
-
return
|
|
22086
|
+
function tunnelNameFromHostname(hostname3) {
|
|
22087
|
+
return hostname3.replace(/\./g, "-").slice(0, 63);
|
|
22020
22088
|
}
|
|
22021
|
-
function planInfraTunnel(
|
|
22022
|
-
const host =
|
|
22089
|
+
function planInfraTunnel(hostname3, upstream) {
|
|
22090
|
+
const host = hostname3.trim().toLowerCase();
|
|
22023
22091
|
const origin = upstream.trim();
|
|
22024
|
-
if (!HOSTNAME_RE.test(host)) throw new Error(`invalid hostname ${JSON.stringify(
|
|
22092
|
+
if (!HOSTNAME_RE.test(host)) throw new Error(`invalid hostname ${JSON.stringify(hostname3)}`);
|
|
22025
22093
|
if (!UPSTREAM_RE.test(origin)) throw new Error(`invalid upstream ${JSON.stringify(upstream)} \u2014 expected http(s)://host:port`);
|
|
22026
22094
|
const tunnelName = tunnelNameFromHostname(host);
|
|
22027
22095
|
const configYaml = [
|
|
@@ -22473,9 +22541,9 @@ function registerQueryCommands(program3) {
|
|
|
22473
22541
|
}
|
|
22474
22542
|
|
|
22475
22543
|
// src/bootstrap-commands.ts
|
|
22476
|
-
var
|
|
22477
|
-
var
|
|
22478
|
-
var
|
|
22544
|
+
var import_node_fs27 = require("node:fs");
|
|
22545
|
+
var import_node_os10 = require("node:os");
|
|
22546
|
+
var import_node_path26 = require("node:path");
|
|
22479
22547
|
|
|
22480
22548
|
// src/bootstrap-drift.ts
|
|
22481
22549
|
function byteComparableSeeds(manifest, cls) {
|
|
@@ -23089,13 +23157,13 @@ function registerBootstrapCommands(program3) {
|
|
|
23089
23157
|
client: defaultGitHubClient(),
|
|
23090
23158
|
projectMeta: meta,
|
|
23091
23159
|
deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
|
|
23092
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
23160
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs27.existsSync)(path2) ? (0, import_node_fs27.readFileSync)(path2, "utf8") : null,
|
|
23093
23161
|
// requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
|
|
23094
23162
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
23095
23163
|
// #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
|
|
23096
23164
|
// permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
|
|
23097
23165
|
// sanction, which is the pre-#3664 behaviour.
|
|
23098
|
-
sanctionedAdmins: (0,
|
|
23166
|
+
sanctionedAdmins: (0, import_node_fs27.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs27.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
|
|
23099
23167
|
requiredGcpApis: (() => {
|
|
23100
23168
|
const v = meta?.requiredGcpApis;
|
|
23101
23169
|
if (Array.isArray(v)) return v;
|
|
@@ -23133,12 +23201,12 @@ function registerBootstrapCommands(program3) {
|
|
|
23133
23201
|
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 () => {
|
|
23134
23202
|
const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
|
|
23135
23203
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
23136
|
-
if (!(0,
|
|
23137
|
-
const manifest = loadBootstrapSeeds((0,
|
|
23204
|
+
if (!(0, import_node_fs27.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`);
|
|
23205
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs27.readFileSync)(manifestPath, "utf8"));
|
|
23138
23206
|
const hubContents = /* @__PURE__ */ new Map();
|
|
23139
23207
|
for (const s of manifest.seeds) {
|
|
23140
23208
|
if (s.ownership !== "org" || s.source !== "self") continue;
|
|
23141
|
-
hubContents.set(s.target, (0,
|
|
23209
|
+
hubContents.set(s.target, (0, import_node_fs27.existsSync)(s.target) ? (0, import_node_fs27.readFileSync)(s.target, "utf8") : null);
|
|
23142
23210
|
}
|
|
23143
23211
|
let targets;
|
|
23144
23212
|
let classOf = (_repo) => "deployable";
|
|
@@ -23215,8 +23283,8 @@ function registerBootstrapCommands(program3) {
|
|
|
23215
23283
|
return fail(`bootstrap apply: ${e.message}`);
|
|
23216
23284
|
}
|
|
23217
23285
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
23218
|
-
if (!(0,
|
|
23219
|
-
const manifest = loadBootstrapSeeds((0,
|
|
23286
|
+
if (!(0, import_node_fs27.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`);
|
|
23287
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs27.readFileSync)(manifestPath, "utf8"));
|
|
23220
23288
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
23221
23289
|
const slug = parsedRepo.slug;
|
|
23222
23290
|
const onlyTarget = o.only.trim();
|
|
@@ -23227,16 +23295,16 @@ function registerBootstrapCommands(program3) {
|
|
|
23227
23295
|
${known}`);
|
|
23228
23296
|
}
|
|
23229
23297
|
const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
|
|
23230
|
-
const readFile9 = (p) => (0,
|
|
23298
|
+
const readFile9 = (p) => (0, import_node_fs27.existsSync)(p) ? (0, import_node_fs27.readFileSync)(p, "utf8") : null;
|
|
23231
23299
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
23232
23300
|
const putSeed = async (target, content, ref, sha) => {
|
|
23233
|
-
const tmp = (0,
|
|
23234
|
-
(0,
|
|
23301
|
+
const tmp = (0, import_node_path26.join)((0, import_node_os10.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
23302
|
+
(0, import_node_fs27.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
23235
23303
|
try {
|
|
23236
23304
|
await gh(contentPutInputArgs(repo, target, tmp));
|
|
23237
23305
|
} finally {
|
|
23238
23306
|
try {
|
|
23239
|
-
(0,
|
|
23307
|
+
(0, import_node_fs27.unlinkSync)(tmp);
|
|
23240
23308
|
} catch {
|
|
23241
23309
|
}
|
|
23242
23310
|
}
|
|
@@ -23488,12 +23556,12 @@ LIVE apply to ${repo}:
|
|
|
23488
23556
|
}
|
|
23489
23557
|
|
|
23490
23558
|
// src/stage-commands.ts
|
|
23491
|
-
var
|
|
23492
|
-
var
|
|
23559
|
+
var import_node_fs29 = require("node:fs");
|
|
23560
|
+
var import_node_path28 = require("node:path");
|
|
23493
23561
|
|
|
23494
23562
|
// src/port-registry.ts
|
|
23495
|
-
var
|
|
23496
|
-
var
|
|
23563
|
+
var import_node_fs28 = require("node:fs");
|
|
23564
|
+
var import_node_path27 = require("node:path");
|
|
23497
23565
|
|
|
23498
23566
|
// ../infra/port-geometry.mjs
|
|
23499
23567
|
var PORT_BLOCK = 100;
|
|
@@ -23507,8 +23575,8 @@ function nextPortBlock(registry2) {
|
|
|
23507
23575
|
return [base, base + PORT_SPAN];
|
|
23508
23576
|
}
|
|
23509
23577
|
function loadPortRegistry(path2) {
|
|
23510
|
-
if (!(0,
|
|
23511
|
-
const raw = JSON.parse((0,
|
|
23578
|
+
if (!(0, import_node_fs28.existsSync)(path2)) return {};
|
|
23579
|
+
const raw = JSON.parse((0, import_node_fs28.readFileSync)(path2, "utf8"));
|
|
23512
23580
|
const out = {};
|
|
23513
23581
|
for (const [key, value] of Object.entries(raw)) {
|
|
23514
23582
|
if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
|
|
@@ -23522,9 +23590,9 @@ function ensurePortRange(repo, path2) {
|
|
|
23522
23590
|
const existing = registry2[repo];
|
|
23523
23591
|
if (existing) return existing;
|
|
23524
23592
|
const range = nextPortBlock(registry2);
|
|
23525
|
-
const raw = (0,
|
|
23593
|
+
const raw = (0, import_node_fs28.existsSync)(path2) ? JSON.parse((0, import_node_fs28.readFileSync)(path2, "utf8")) : {};
|
|
23526
23594
|
raw[repo] = range;
|
|
23527
|
-
(0,
|
|
23595
|
+
(0, import_node_fs28.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
|
|
23528
23596
|
return range;
|
|
23529
23597
|
}
|
|
23530
23598
|
function portCursorSeed(registry2) {
|
|
@@ -23546,22 +23614,22 @@ function existingPortRange(repo, registry2) {
|
|
|
23546
23614
|
return registry2[repo] ?? null;
|
|
23547
23615
|
}
|
|
23548
23616
|
function portRangeInfraAt(root, source) {
|
|
23549
|
-
const registryPath = (0,
|
|
23550
|
-
const ddbScriptPath = (0,
|
|
23551
|
-
if (!(0,
|
|
23617
|
+
const registryPath = (0, import_node_path27.join)(root, "infra", "port-ranges.json");
|
|
23618
|
+
const ddbScriptPath = (0, import_node_path27.join)(root, "infra", "port-ddb.mjs");
|
|
23619
|
+
if (!(0, import_node_fs28.existsSync)(registryPath) || !(0, import_node_fs28.existsSync)(ddbScriptPath)) return null;
|
|
23552
23620
|
return { root, source, registryPath, ddbScriptPath };
|
|
23553
23621
|
}
|
|
23554
23622
|
function resolvePortRangeInfra(cwd, packageDir) {
|
|
23555
23623
|
const direct = portRangeInfraAt(cwd, "cwd");
|
|
23556
23624
|
if (direct) return direct;
|
|
23557
|
-
for (let dir = cwd; ; dir = (0,
|
|
23558
|
-
const sibling = portRangeInfraAt((0,
|
|
23625
|
+
for (let dir = cwd; ; dir = (0, import_node_path27.dirname)(dir)) {
|
|
23626
|
+
const sibling = portRangeInfraAt((0, import_node_path27.join)(dir, "MMI-Hub"), "sibling-hub");
|
|
23559
23627
|
if (sibling) return sibling;
|
|
23560
|
-
const parent = (0,
|
|
23628
|
+
const parent = (0, import_node_path27.dirname)(dir);
|
|
23561
23629
|
if (parent === dir) break;
|
|
23562
23630
|
}
|
|
23563
23631
|
if (packageDir) {
|
|
23564
|
-
const pkgRoot = (0,
|
|
23632
|
+
const pkgRoot = (0, import_node_path27.join)(packageDir, "..", "..");
|
|
23565
23633
|
const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
|
|
23566
23634
|
if (pkgFrom) return pkgFrom;
|
|
23567
23635
|
}
|
|
@@ -23737,8 +23805,8 @@ function registerStageCommands(program3) {
|
|
|
23737
23805
|
const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
|
|
23738
23806
|
return decideStage({
|
|
23739
23807
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
23740
|
-
hasCompose: (0,
|
|
23741
|
-
hasEnvExample: (0,
|
|
23808
|
+
hasCompose: (0, import_node_fs29.existsSync)((0, import_node_path28.join)(process.cwd(), "docker-compose.yml")),
|
|
23809
|
+
hasEnvExample: (0, import_node_fs29.existsSync)((0, import_node_path28.join)(process.cwd(), ".env.example"))
|
|
23742
23810
|
});
|
|
23743
23811
|
}
|
|
23744
23812
|
async function fetchStageVaultEnvMerge() {
|
|
@@ -24176,10 +24244,10 @@ function registerBoardCommands(program3) {
|
|
|
24176
24244
|
}
|
|
24177
24245
|
|
|
24178
24246
|
// src/merge-cleanup.ts
|
|
24179
|
-
var
|
|
24247
|
+
var import_node_fs30 = require("node:fs");
|
|
24180
24248
|
var import_promises7 = require("node:fs/promises");
|
|
24181
|
-
var
|
|
24182
|
-
var
|
|
24249
|
+
var import_node_path30 = require("node:path");
|
|
24250
|
+
var import_node_os11 = require("node:os");
|
|
24183
24251
|
var import_node_child_process14 = require("node:child_process");
|
|
24184
24252
|
|
|
24185
24253
|
// src/board-advance.ts
|
|
@@ -24266,7 +24334,7 @@ function boardAdvanceFailureMessage(result) {
|
|
|
24266
24334
|
|
|
24267
24335
|
// src/deferred-registry-store.ts
|
|
24268
24336
|
var import_promises6 = require("node:fs/promises");
|
|
24269
|
-
var
|
|
24337
|
+
var import_node_path29 = require("node:path");
|
|
24270
24338
|
var sleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
24271
24339
|
async function atomicWrite(target, contents) {
|
|
24272
24340
|
const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
@@ -24317,12 +24385,12 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
|
|
|
24317
24385
|
},
|
|
24318
24386
|
// Standalone atomic write — THROWS on failure (no best-effort swallow, #2846).
|
|
24319
24387
|
write: async (entries) => {
|
|
24320
|
-
await (0, import_promises6.mkdir)((0,
|
|
24388
|
+
await (0, import_promises6.mkdir)((0, import_node_path29.dirname)(registryPath), { recursive: true });
|
|
24321
24389
|
await atomicWrite(registryPath, serializeDeferredWorktrees(entries));
|
|
24322
24390
|
},
|
|
24323
24391
|
// Serialized read-modify-write under the repo-wide lock (#2846).
|
|
24324
24392
|
update: async (mutate) => {
|
|
24325
|
-
await (0, import_promises6.mkdir)((0,
|
|
24393
|
+
await (0, import_promises6.mkdir)((0, import_node_path29.dirname)(registryPath), { recursive: true });
|
|
24326
24394
|
const deadline = Date.now() + opts.maxWaitMs;
|
|
24327
24395
|
for (; ; ) {
|
|
24328
24396
|
const guard = await acquireLock(lockPath, opts, deadline);
|
|
@@ -24483,7 +24551,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
24483
24551
|
);
|
|
24484
24552
|
const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
24485
24553
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
24486
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
24554
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path30.dirname)((0, import_node_path30.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
24487
24555
|
const gcActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
|
|
24488
24556
|
const owners = readWorktreeOwners(primaryRepoRoot);
|
|
24489
24557
|
const removalNow = Date.now();
|
|
@@ -24514,7 +24582,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
24514
24582
|
const cleanup = await cleanupPrMergeLocalBranch(branch.branch, {
|
|
24515
24583
|
beforeWorktrees,
|
|
24516
24584
|
startingPath: branch.worktreePath,
|
|
24517
|
-
pathExists: (p) => (0,
|
|
24585
|
+
pathExists: (p) => (0, import_node_fs30.existsSync)(p),
|
|
24518
24586
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
24519
24587
|
teardownWorktreeStage,
|
|
24520
24588
|
deferredStore,
|
|
@@ -24542,7 +24610,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
24542
24610
|
for (const wt of worktreeDirsToRemove) {
|
|
24543
24611
|
try {
|
|
24544
24612
|
const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, siblingRoot, {
|
|
24545
|
-
realpath: (path2) => (0,
|
|
24613
|
+
realpath: (path2) => (0, import_node_fs30.realpathSync)(path2)
|
|
24546
24614
|
});
|
|
24547
24615
|
if (!cleanupTarget.ok) {
|
|
24548
24616
|
result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
|
|
@@ -24607,13 +24675,13 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
|
|
|
24607
24675
|
const commits = JSON.parse(raw).commits ?? [];
|
|
24608
24676
|
const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
|
|
24609
24677
|
if (!body) return void 0;
|
|
24610
|
-
const dir = (0,
|
|
24611
|
-
const path2 = (0,
|
|
24612
|
-
(0,
|
|
24678
|
+
const dir = (0, import_node_fs30.mkdtempSync)((0, import_node_path30.join)((0, import_node_os11.tmpdir)(), "mmi-squash-body-"));
|
|
24679
|
+
const path2 = (0, import_node_path30.join)(dir, "body.txt");
|
|
24680
|
+
(0, import_node_fs30.writeFileSync)(path2, `${body}
|
|
24613
24681
|
`, "utf8");
|
|
24614
24682
|
return { path: path2, cleanup: () => {
|
|
24615
24683
|
try {
|
|
24616
|
-
(0,
|
|
24684
|
+
(0, import_node_fs30.rmSync)(dir, { recursive: true, force: true });
|
|
24617
24685
|
} catch {
|
|
24618
24686
|
}
|
|
24619
24687
|
} };
|
|
@@ -24735,13 +24803,13 @@ var realWorktreeDirRemover = {
|
|
|
24735
24803
|
probe: (p) => {
|
|
24736
24804
|
let st;
|
|
24737
24805
|
try {
|
|
24738
|
-
st = (0,
|
|
24806
|
+
st = (0, import_node_fs30.lstatSync)(p);
|
|
24739
24807
|
} catch {
|
|
24740
24808
|
return null;
|
|
24741
24809
|
}
|
|
24742
24810
|
if (st.isSymbolicLink()) return "link";
|
|
24743
24811
|
try {
|
|
24744
|
-
(0,
|
|
24812
|
+
(0, import_node_fs30.readlinkSync)(p);
|
|
24745
24813
|
return "link";
|
|
24746
24814
|
} catch {
|
|
24747
24815
|
}
|
|
@@ -24749,7 +24817,7 @@ var realWorktreeDirRemover = {
|
|
|
24749
24817
|
},
|
|
24750
24818
|
readdir: (p) => {
|
|
24751
24819
|
try {
|
|
24752
|
-
return (0,
|
|
24820
|
+
return (0, import_node_fs30.readdirSync)(p);
|
|
24753
24821
|
} catch {
|
|
24754
24822
|
return [];
|
|
24755
24823
|
}
|
|
@@ -24758,9 +24826,9 @@ var realWorktreeDirRemover = {
|
|
|
24758
24826
|
// leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
|
|
24759
24827
|
detachLink: (p) => {
|
|
24760
24828
|
try {
|
|
24761
|
-
(0,
|
|
24829
|
+
(0, import_node_fs30.rmdirSync)(p);
|
|
24762
24830
|
} catch {
|
|
24763
|
-
(0,
|
|
24831
|
+
(0, import_node_fs30.unlinkSync)(p);
|
|
24764
24832
|
}
|
|
24765
24833
|
},
|
|
24766
24834
|
removeTree: (p) => (0, import_promises7.rm)(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
|
|
@@ -24793,9 +24861,9 @@ async function worktreeHasStageState(worktreePath) {
|
|
|
24793
24861
|
}
|
|
24794
24862
|
}
|
|
24795
24863
|
function stageStateFileBelongsToWorktree(statePath, worktreePath) {
|
|
24796
|
-
if (!(0,
|
|
24864
|
+
if (!(0, import_node_fs30.existsSync)(statePath)) return false;
|
|
24797
24865
|
try {
|
|
24798
|
-
const state = JSON.parse((0,
|
|
24866
|
+
const state = JSON.parse((0, import_node_fs30.readFileSync)(statePath, "utf8"));
|
|
24799
24867
|
const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
|
|
24800
24868
|
return Boolean(recordedCwd && isPathUnderDirectory(recordedCwd, worktreePath));
|
|
24801
24869
|
} catch {
|
|
@@ -25019,9 +25087,9 @@ async function fetchRestCorePool(gh = defaultGhApi) {
|
|
|
25019
25087
|
}
|
|
25020
25088
|
|
|
25021
25089
|
// src/worktree-lifecycle-commands.ts
|
|
25022
|
-
var
|
|
25090
|
+
var import_node_fs31 = require("node:fs");
|
|
25023
25091
|
var import_promises8 = require("node:fs/promises");
|
|
25024
|
-
var
|
|
25092
|
+
var import_node_path31 = require("node:path");
|
|
25025
25093
|
var GH_TIMEOUT_MS = 2e4;
|
|
25026
25094
|
var DEFAULT_BASE = "origin/development";
|
|
25027
25095
|
var DEFAULT_REMOTE = "origin";
|
|
@@ -25093,6 +25161,16 @@ function orphanLandSteps(lostBranch, hasStage) {
|
|
|
25093
25161
|
steps.push("prune worktree metadata");
|
|
25094
25162
|
return steps;
|
|
25095
25163
|
}
|
|
25164
|
+
function detachedLandSteps(hasStage) {
|
|
25165
|
+
const steps = [];
|
|
25166
|
+
if (hasStage) steps.push("stop spawned dev stage");
|
|
25167
|
+
steps.push("remove worktree (detached HEAD)");
|
|
25168
|
+
steps.push("prune worktree metadata");
|
|
25169
|
+
return steps;
|
|
25170
|
+
}
|
|
25171
|
+
function shouldContinueLandCleanup(removeStatus) {
|
|
25172
|
+
return removeStatus === "removed";
|
|
25173
|
+
}
|
|
25096
25174
|
function classifyStaleLeaks(input) {
|
|
25097
25175
|
const protectedBranches = input.protectedBranches ?? PROTECTED_BRANCHES2;
|
|
25098
25176
|
const leaks = [];
|
|
@@ -25157,7 +25235,7 @@ function classifyStaleLeaks(input) {
|
|
|
25157
25235
|
var defaultOrphanDirScanDeps = {
|
|
25158
25236
|
listDirs: (root) => {
|
|
25159
25237
|
try {
|
|
25160
|
-
return (0,
|
|
25238
|
+
return (0, import_node_fs31.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path31.join)(root, e.name));
|
|
25161
25239
|
} catch {
|
|
25162
25240
|
return [];
|
|
25163
25241
|
}
|
|
@@ -25302,15 +25380,21 @@ function registerWorktreeCommands(program3) {
|
|
|
25302
25380
|
try {
|
|
25303
25381
|
const wtPath = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
|
|
25304
25382
|
const headBorn = await execFileP2("git", ["-C", wtPath || ".", "rev-parse", "--verify", "--quiet", "HEAD"], { timeout: GIT_TIMEOUT_MS }).then(() => true).catch(() => false);
|
|
25305
|
-
const
|
|
25383
|
+
const symbolicBranch = (await execFileP2(
|
|
25384
|
+
"git",
|
|
25385
|
+
["-C", wtPath || ".", "symbolic-ref", "--quiet", "--short", "HEAD"],
|
|
25386
|
+
{ timeout: GIT_TIMEOUT_MS }
|
|
25387
|
+
).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
25388
|
+
const detached = headBorn && !symbolicBranch;
|
|
25389
|
+
const branch = symbolicBranch || (detached ? "HEAD" : "");
|
|
25306
25390
|
if (!wtPath || !branch) return fail("worktree land: not inside a git worktree");
|
|
25307
|
-
const gitFile = (0,
|
|
25308
|
-
const isLinked = (0,
|
|
25391
|
+
const gitFile = (0, import_node_path31.join)(wtPath, ".git");
|
|
25392
|
+
const isLinked = (0, import_node_fs31.existsSync)(gitFile) && (0, import_node_fs31.statSync)(gitFile).isFile();
|
|
25309
25393
|
if (apply && !isLinked) {
|
|
25310
25394
|
return fail("worktree land: run from inside the linked worktree you want to land (this is the primary checkout)");
|
|
25311
25395
|
}
|
|
25312
25396
|
const commonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
25313
|
-
const primaryCheckout = commonDir ? (0,
|
|
25397
|
+
const primaryCheckout = commonDir ? (0, import_node_path31.dirname)(commonDir) : wtPath;
|
|
25314
25398
|
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);
|
|
25315
25399
|
const orphan = classifyOrphanedWorktree({
|
|
25316
25400
|
branch,
|
|
@@ -25323,7 +25407,7 @@ function registerWorktreeCommands(program3) {
|
|
|
25323
25407
|
}
|
|
25324
25408
|
const stageSummary = readStageSummary(wtPath);
|
|
25325
25409
|
const hasStage = stageSummary != null;
|
|
25326
|
-
const steps = orphan.orphan ? orphanLandSteps(orphan.lostBranch, hasStage) : landSteps(branch, true, hasStage);
|
|
25410
|
+
const steps = detached ? detachedLandSteps(hasStage) : orphan.orphan ? orphanLandSteps(orphan.lostBranch, hasStage) : landSteps(branch, true, hasStage);
|
|
25327
25411
|
const plan = {
|
|
25328
25412
|
command: "worktree land",
|
|
25329
25413
|
branch,
|
|
@@ -25332,13 +25416,15 @@ function registerWorktreeCommands(program3) {
|
|
|
25332
25416
|
remote: o.remote,
|
|
25333
25417
|
hasStage,
|
|
25334
25418
|
steps,
|
|
25335
|
-
...orphan.orphan ? { orphan: { kind: orphan.kind, lostBranch: orphan.lostBranch, reason: orphan.reason } } : {}
|
|
25419
|
+
...orphan.orphan ? { orphan: { kind: orphan.kind, lostBranch: orphan.lostBranch, reason: orphan.reason } } : {},
|
|
25420
|
+
...detached ? { detached: true } : {}
|
|
25336
25421
|
};
|
|
25337
25422
|
if (!apply) {
|
|
25338
25423
|
const preview = { dryRun: true, ...plan, ...o.keepRemote ? { keepRemote: true } : {} };
|
|
25339
25424
|
if (o.json) return console.log(JSON.stringify(preview, null, 2));
|
|
25340
25425
|
const lines = [`worktree land: dry-run (pass --apply to execute)`, ` branch: ${branch}`, ` worktree: ${wtPath}`];
|
|
25341
|
-
if (
|
|
25426
|
+
if (detached) lines.push(" detached HEAD: no branch refs will be deleted");
|
|
25427
|
+
else if (orphan.orphan) lines.push(` orphaned: ${orphan.reason} \u2014 no branch refs will be deleted`);
|
|
25342
25428
|
if (hasStage) lines.push(` stage: port ${stageSummary.port} (will be stopped)`);
|
|
25343
25429
|
if (o.keepRemote && !orphan.orphan) lines.push(` remote branch: kept (--keep-remote)`);
|
|
25344
25430
|
for (const s of steps) lines.push(` - ${s}`);
|
|
@@ -25360,15 +25446,16 @@ function registerWorktreeCommands(program3) {
|
|
|
25360
25446
|
unreferenced: await isCommitUnreferenced(orphanTip, async (args) => (await execFileP2("git", ["-C", primaryCheckout, ...args], { timeout: GIT_TIMEOUT_MS })).stdout),
|
|
25361
25447
|
recoverCommand: `git -C "${primaryCheckout}" branch <name> ${orphanTip}`
|
|
25362
25448
|
} : void 0;
|
|
25363
|
-
const
|
|
25449
|
+
const treeOnly = orphan.orphan || detached;
|
|
25450
|
+
const landPrs = treeOnly ? void 0 : await fetchLandBranchPrs(branch);
|
|
25364
25451
|
const mergeVerdict = classifyLandBranchMergeState(landPrs);
|
|
25365
|
-
const landRefs =
|
|
25452
|
+
const landRefs = treeOnly ? { action: "proceed" } : decideWorktreeLandRefCleanup({ branch, prs: landPrs, keepRemote: Boolean(o.keepRemote) });
|
|
25366
25453
|
if (landRefs.action === "refuse") {
|
|
25367
25454
|
return fail(`worktree land: ${landRefs.message}`, { code: ERROR_CODES.ERR_BAD_ENUM });
|
|
25368
25455
|
}
|
|
25369
25456
|
const keepRemoteBranch = Boolean(o.keepRemote) || landRefs.action === "keep-remote";
|
|
25370
25457
|
if (landRefs.action === "keep-remote") console.warn(`worktree land: ${landRefs.message}.`);
|
|
25371
|
-
const reportedMergeState =
|
|
25458
|
+
const reportedMergeState = treeOnly ? "not-checked" : mergeVerdict.state;
|
|
25372
25459
|
const landOwner = lookupWorktreeOwner(primaryCheckout, wtPath);
|
|
25373
25460
|
const report = [];
|
|
25374
25461
|
if (hasStage) {
|
|
@@ -25388,6 +25475,24 @@ function registerWorktreeCommands(program3) {
|
|
|
25388
25475
|
step: "remove worktree",
|
|
25389
25476
|
status: removeOutcome.status === "removed" ? removeOutcome.recovery ? `done (${removeOutcome.recovery})` : "done" : `failed: ${removeOutcome.error ?? "lock held"} \u2014 run: git -C "${primaryCheckout}" worktree remove --force "${wtPath}"`
|
|
25390
25477
|
});
|
|
25478
|
+
if (!shouldContinueLandCleanup(removeOutcome.status)) {
|
|
25479
|
+
const result2 = {
|
|
25480
|
+
dryRun: false,
|
|
25481
|
+
...plan,
|
|
25482
|
+
...o.keepRemote ? { keepRemote: true } : {},
|
|
25483
|
+
mergeState: reportedMergeState,
|
|
25484
|
+
prNumbers: mergeVerdict.numbers,
|
|
25485
|
+
cleanupState: "partial",
|
|
25486
|
+
report
|
|
25487
|
+
};
|
|
25488
|
+
if (o.json) console.log(JSON.stringify(result2, null, 2));
|
|
25489
|
+
else {
|
|
25490
|
+
console.error(`worktree land: cleanup stopped after the worktree removal failed; branch refs and metadata were left untouched`);
|
|
25491
|
+
for (const row of report) console.log(` ${row.step}: ${row.status}`);
|
|
25492
|
+
}
|
|
25493
|
+
process.exitCode = 1;
|
|
25494
|
+
return;
|
|
25495
|
+
}
|
|
25391
25496
|
const landActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: wtPath });
|
|
25392
25497
|
appendWorktreeEvent(primaryCheckout, {
|
|
25393
25498
|
action: removeOutcome.status === "removed" ? "removed" : "failed",
|
|
@@ -25402,8 +25507,11 @@ function registerWorktreeCommands(program3) {
|
|
|
25402
25507
|
reason: orphan.orphan ? `orphaned worktree \u2014 ${orphan.reason}${lastCommit ? `; last commit ${lastCommit.oid}${lastCommit.unreferenced ? " (on no ref)" : ""}` : ""}` : describeLandMergeState(mergeVerdict)
|
|
25403
25508
|
});
|
|
25404
25509
|
if (removeOutcome.status === "removed") dropWorktreeOwner(primaryCheckout, wtPath);
|
|
25405
|
-
if (
|
|
25406
|
-
report.push({
|
|
25510
|
+
if (treeOnly) {
|
|
25511
|
+
report.push({
|
|
25512
|
+
step: "delete branch refs",
|
|
25513
|
+
status: detached ? "skipped: detached HEAD has no branch refs" : `skipped: orphaned worktree \u2014 ${orphan.reason}`
|
|
25514
|
+
});
|
|
25407
25515
|
if (lastCommit) {
|
|
25408
25516
|
report.push({
|
|
25409
25517
|
step: "last commit",
|
|
@@ -25431,12 +25539,12 @@ function registerWorktreeCommands(program3) {
|
|
|
25431
25539
|
report
|
|
25432
25540
|
};
|
|
25433
25541
|
if (o.json) return console.log(JSON.stringify(result, null, 2));
|
|
25434
|
-
console.log(
|
|
25542
|
+
console.log(treeOnly ? `worktree land: removed ${detached ? "detached" : "orphaned"} worktree ${wtPath}${detached ? "" : ` (${orphan.reason})`}; no branch refs touched` : `worktree land: cleaned up branch ${branch} (${describeLandMergeState(mergeVerdict)})`);
|
|
25435
25543
|
for (const r of report) console.log(` ${r.step}: ${r.status}`);
|
|
25436
25544
|
if (lastCommit?.unreferenced) {
|
|
25437
25545
|
console.warn(`worktree land: commit ${lastCommit.oid} was on no ref but this worktree's reflog \u2014 normal after a squash merge, but if that work was NOT merged, recover it now: ${lastCommit.recoverCommand}`);
|
|
25438
25546
|
}
|
|
25439
|
-
if (!
|
|
25547
|
+
if (!treeOnly && mergeVerdict.state !== "merged") {
|
|
25440
25548
|
console.warn(`worktree land: '${branch}' was cleaned up but NOT merged \u2014 ${describeLandMergeState(mergeVerdict)}. Do not report this work as shipped.`);
|
|
25441
25549
|
}
|
|
25442
25550
|
if (report.some((r) => r.status.startsWith("failed"))) process.exitCode = 1;
|
|
@@ -25497,10 +25605,10 @@ async function gatherWorktreeContext() {
|
|
|
25497
25605
|
if (s) stages.push({ path: wt.path, port: s.port });
|
|
25498
25606
|
}
|
|
25499
25607
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
25500
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
25608
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path31.dirname)((0, import_node_path31.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
25501
25609
|
const wtRoot = siblingMmiWorktreesRoot(primaryRepoRoot);
|
|
25502
25610
|
let orphanDirs = [];
|
|
25503
|
-
if ((0,
|
|
25611
|
+
if ((0, import_node_fs31.existsSync)(wtRoot)) {
|
|
25504
25612
|
orphanDirs = scanOrphanDirs(wtRoot, worktreeGitRoot, {
|
|
25505
25613
|
...defaultOrphanDirScanDeps,
|
|
25506
25614
|
listDirs: (root) => worktreeScanDirs(root, primaryRepoRoot, defaultOrphanDirScanDeps.listDirs, isRepoCheckoutDir)
|
|
@@ -25523,7 +25631,7 @@ async function bestEffortGit(args, cwd, step, timeoutMs = GIT_TIMEOUT_MS) {
|
|
|
25523
25631
|
}
|
|
25524
25632
|
|
|
25525
25633
|
// src/issue-commands.ts
|
|
25526
|
-
var
|
|
25634
|
+
var import_node_fs32 = require("node:fs");
|
|
25527
25635
|
var import_node_crypto5 = require("node:crypto");
|
|
25528
25636
|
var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
|
|
25529
25637
|
var ReparentConflictError = class extends Error {
|
|
@@ -25541,7 +25649,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
25541
25649
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
25542
25650
|
const patch = {};
|
|
25543
25651
|
let bodyChanged = false;
|
|
25544
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
25652
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs32.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
25545
25653
|
if (options.titleFile !== void 0) {
|
|
25546
25654
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
25547
25655
|
} else if (options.title !== void 0) {
|
|
@@ -26104,7 +26212,7 @@ function extendCreateCommand(issue2, batchAttach) {
|
|
|
26104
26212
|
if (opts.batch) {
|
|
26105
26213
|
let specs;
|
|
26106
26214
|
try {
|
|
26107
|
-
const raw = (0,
|
|
26215
|
+
const raw = (0, import_node_fs32.readFileSync)(opts.batch, "utf8");
|
|
26108
26216
|
specs = JSON.parse(raw);
|
|
26109
26217
|
if (!Array.isArray(specs)) throw new Error("batch file must contain a JSON array");
|
|
26110
26218
|
} catch (e) {
|
|
@@ -26178,8 +26286,8 @@ ${lines}`, {
|
|
|
26178
26286
|
}
|
|
26179
26287
|
|
|
26180
26288
|
// src/train-commands.ts
|
|
26181
|
-
var
|
|
26182
|
-
var
|
|
26289
|
+
var import_node_fs33 = require("node:fs");
|
|
26290
|
+
var import_node_path32 = require("node:path");
|
|
26183
26291
|
|
|
26184
26292
|
// src/train-status.ts
|
|
26185
26293
|
function buildTrainStatusReport(input) {
|
|
@@ -26219,7 +26327,7 @@ function formatTrainStatus(r) {
|
|
|
26219
26327
|
// src/train-commands.ts
|
|
26220
26328
|
function readRepoVersion() {
|
|
26221
26329
|
try {
|
|
26222
|
-
return JSON.parse((0,
|
|
26330
|
+
return JSON.parse((0, import_node_fs33.readFileSync)((0, import_node_path32.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
26223
26331
|
} catch {
|
|
26224
26332
|
return void 0;
|
|
26225
26333
|
}
|
|
@@ -26365,11 +26473,12 @@ function registerDeployCommands(program3) {
|
|
|
26365
26473
|
}
|
|
26366
26474
|
|
|
26367
26475
|
// src/discovery-commands.ts
|
|
26368
|
-
var
|
|
26369
|
-
var
|
|
26370
|
-
var
|
|
26476
|
+
var import_node_fs34 = require("node:fs");
|
|
26477
|
+
var import_node_os12 = require("node:os");
|
|
26478
|
+
var import_node_path33 = require("node:path");
|
|
26371
26479
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
26372
26480
|
async function collectStatus() {
|
|
26481
|
+
const repo = await resolveRepo();
|
|
26373
26482
|
let branch = "";
|
|
26374
26483
|
try {
|
|
26375
26484
|
branch = (await execFileP2("git", ["rev-parse", "--abbrev-ref", "HEAD"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
|
|
@@ -26398,7 +26507,7 @@ async function collectStatus() {
|
|
|
26398
26507
|
}
|
|
26399
26508
|
let claimedItems = [];
|
|
26400
26509
|
try {
|
|
26401
|
-
const cfg = await
|
|
26510
|
+
const cfg = await loadConfigOrDiscover();
|
|
26402
26511
|
if (cfg.sagaApiUrl) {
|
|
26403
26512
|
const report = await readBoard({ config: cfg });
|
|
26404
26513
|
claimedItems = report.primary.userOwned.map((item) => ({
|
|
@@ -26421,7 +26530,7 @@ async function collectStatus() {
|
|
|
26421
26530
|
} catch {
|
|
26422
26531
|
stage = { running: false };
|
|
26423
26532
|
}
|
|
26424
|
-
return { branch, worktrees, myOpenPrs, claimedItems, stage };
|
|
26533
|
+
return { repo, branch, worktrees, myOpenPrs, claimedItems, stage };
|
|
26425
26534
|
}
|
|
26426
26535
|
var PRIORITY_RANK = {
|
|
26427
26536
|
Urgent: 0,
|
|
@@ -26429,14 +26538,14 @@ var PRIORITY_RANK = {
|
|
|
26429
26538
|
Medium: 2,
|
|
26430
26539
|
Low: 3
|
|
26431
26540
|
};
|
|
26432
|
-
async function recommendNext(deps) {
|
|
26433
|
-
const load = deps?.loadConfig ??
|
|
26541
|
+
async function recommendNext(repo, deps) {
|
|
26542
|
+
const load = deps?.loadConfig ?? loadConfigForRepo;
|
|
26434
26543
|
const reader = deps?.readBoard ?? readBoard;
|
|
26435
|
-
const cfg = await load();
|
|
26544
|
+
const cfg = await load(repo);
|
|
26436
26545
|
if (!cfg.sagaApiUrl) throw new Error("Hub API URL not configured \u2014 the board was NOT read (run `mmi-cli doctor`)");
|
|
26437
26546
|
let report;
|
|
26438
26547
|
try {
|
|
26439
|
-
report = await reader({ config: cfg });
|
|
26548
|
+
report = await reader({ config: cfg, repo });
|
|
26440
26549
|
} catch (e) {
|
|
26441
26550
|
throw new Error(
|
|
26442
26551
|
`board unreachable \u2014 ${e.message}. This is NOT an empty board: confirm the ACTIVE gh account can see this repo (\`gh auth status\`, \`gh repo view <owner/repo>\`; \`gh auth switch --user <other>\` when more than one account is present).`
|
|
@@ -26542,10 +26651,10 @@ async function collectOnboardStatus() {
|
|
|
26542
26651
|
else if (top) nextCommand = `mmi-cli board claim ${top.number} # ${top.title}`;
|
|
26543
26652
|
else nextCommand = "mmi-cli board read \u2014 no claimable items found";
|
|
26544
26653
|
}
|
|
26545
|
-
const home = (0,
|
|
26654
|
+
const home = (0, import_node_os12.homedir)();
|
|
26546
26655
|
const plugin = onboardPluginGate({
|
|
26547
|
-
readKnown: () => readFileSyncSafe((0,
|
|
26548
|
-
readSettings: () => readFileSyncSafe((0,
|
|
26656
|
+
readKnown: () => readFileSyncSafe((0, import_node_path33.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs34.readFileSync),
|
|
26657
|
+
readSettings: () => readFileSyncSafe((0, import_node_path33.join)(home, ".claude", "settings.json"), import_node_fs34.readFileSync)
|
|
26549
26658
|
});
|
|
26550
26659
|
return { track, board, registry: registry2, secrets, plugin, nextCommand };
|
|
26551
26660
|
}
|
|
@@ -26556,6 +26665,7 @@ function registerDiscoveryCommands(program3) {
|
|
|
26556
26665
|
if (o.json) {
|
|
26557
26666
|
console.log(JSON.stringify(report, null, 2));
|
|
26558
26667
|
} else {
|
|
26668
|
+
console.log(`repo: ${report.repo ?? "unknown"}`);
|
|
26559
26669
|
console.log(`branch: ${report.branch}`);
|
|
26560
26670
|
console.log(`worktrees: ${report.worktrees.length} linked`);
|
|
26561
26671
|
console.log(`my open PRs: ${report.myOpenPrs.length}`);
|
|
@@ -26566,9 +26676,9 @@ function registerDiscoveryCommands(program3) {
|
|
|
26566
26676
|
fail(`status: ${e.message}`);
|
|
26567
26677
|
}
|
|
26568
26678
|
});
|
|
26569
|
-
program3.command("next").description("recommend the next actionable board item (claimable, unblocked, priority-ranked)").action(async () => {
|
|
26679
|
+
program3.command("next").description("recommend the next actionable board item (claimable, unblocked, priority-ranked)").option("--repo <owner/repo>", "current repo (defaults to git origin)").action(async (o) => {
|
|
26570
26680
|
try {
|
|
26571
|
-
const item = await recommendNext();
|
|
26681
|
+
const item = await recommendNext(o.repo);
|
|
26572
26682
|
if (!item) {
|
|
26573
26683
|
console.log("No claimable board items found (board read OK).");
|
|
26574
26684
|
return;
|
|
@@ -28688,17 +28798,17 @@ function parseOriginRepo(remoteUrl) {
|
|
|
28688
28798
|
}
|
|
28689
28799
|
function ghHostsConfigPath(env, platform2) {
|
|
28690
28800
|
const sep2 = platform2 === "win32" ? "\\" : "/";
|
|
28691
|
-
const
|
|
28801
|
+
const join30 = (...parts) => parts.join(sep2);
|
|
28692
28802
|
const explicit = env.GH_CONFIG_DIR?.trim();
|
|
28693
|
-
if (explicit) return
|
|
28803
|
+
if (explicit) return join30(explicit, "hosts.yml");
|
|
28694
28804
|
if (platform2 === "win32") {
|
|
28695
28805
|
const appData = (env.AppData ?? env.APPDATA)?.trim();
|
|
28696
|
-
return appData ?
|
|
28806
|
+
return appData ? join30(appData, "GitHub CLI", "hosts.yml") : void 0;
|
|
28697
28807
|
}
|
|
28698
28808
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
28699
|
-
if (xdg) return
|
|
28809
|
+
if (xdg) return join30(xdg, "gh", "hosts.yml");
|
|
28700
28810
|
const home = env.HOME?.trim();
|
|
28701
|
-
return home ?
|
|
28811
|
+
return home ? join30(home, ".config", "gh", "hosts.yml") : void 0;
|
|
28702
28812
|
}
|
|
28703
28813
|
function parseGhHostsAccounts(yaml, host = "github.com") {
|
|
28704
28814
|
let hostIndent = null;
|
|
@@ -28748,9 +28858,9 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
28748
28858
|
}
|
|
28749
28859
|
|
|
28750
28860
|
// src/doctor-io.ts
|
|
28751
|
-
var
|
|
28752
|
-
var
|
|
28753
|
-
var
|
|
28861
|
+
var import_node_fs35 = require("node:fs");
|
|
28862
|
+
var import_node_os13 = require("node:os");
|
|
28863
|
+
var import_node_path34 = require("node:path");
|
|
28754
28864
|
var import_node_child_process15 = require("node:child_process");
|
|
28755
28865
|
var import_node_util8 = require("node:util");
|
|
28756
28866
|
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process15.execFile);
|
|
@@ -28758,7 +28868,7 @@ var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
|
28758
28868
|
function installedClaudePluginVersion() {
|
|
28759
28869
|
try {
|
|
28760
28870
|
const file = JSON.parse(
|
|
28761
|
-
(0,
|
|
28871
|
+
(0, import_node_fs35.readFileSync)((0, import_node_path34.join)((0, import_node_os13.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
28762
28872
|
);
|
|
28763
28873
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
28764
28874
|
if (versions.length === 0) return void 0;
|
|
@@ -28769,7 +28879,7 @@ function installedClaudePluginVersion() {
|
|
|
28769
28879
|
}
|
|
28770
28880
|
function manifestVersion(path2) {
|
|
28771
28881
|
try {
|
|
28772
|
-
const manifest = JSON.parse((0,
|
|
28882
|
+
const manifest = JSON.parse((0, import_node_fs35.readFileSync)(path2, "utf8"));
|
|
28773
28883
|
return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : void 0;
|
|
28774
28884
|
} catch {
|
|
28775
28885
|
return void 0;
|
|
@@ -28779,17 +28889,17 @@ function installedSurfacePluginVersion(surface) {
|
|
|
28779
28889
|
const token = surfaceToken(surface);
|
|
28780
28890
|
if (token === "kilo") {
|
|
28781
28891
|
try {
|
|
28782
|
-
const stamp = (0,
|
|
28892
|
+
const stamp = (0, import_node_fs35.readFileSync)((0, import_node_path34.join)((0, import_node_os13.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
|
|
28783
28893
|
return stamp || void 0;
|
|
28784
28894
|
} catch {
|
|
28785
28895
|
return void 0;
|
|
28786
28896
|
}
|
|
28787
28897
|
}
|
|
28788
28898
|
if (token === "cursor") {
|
|
28789
|
-
return manifestVersion((0,
|
|
28899
|
+
return manifestVersion((0, import_node_path34.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
|
|
28790
28900
|
}
|
|
28791
28901
|
if (token === "kimi") {
|
|
28792
|
-
return manifestVersion((0,
|
|
28902
|
+
return manifestVersion((0, import_node_path34.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
|
|
28793
28903
|
}
|
|
28794
28904
|
if (token === "claude") return installedClaudePluginVersion();
|
|
28795
28905
|
if (token !== "codex") return void 0;
|
|
@@ -28827,13 +28937,13 @@ function worktreeRootSync() {
|
|
|
28827
28937
|
}
|
|
28828
28938
|
var gitignorePath = () => {
|
|
28829
28939
|
const root = worktreeRootSync();
|
|
28830
|
-
return root === null ? null : (0,
|
|
28940
|
+
return root === null ? null : (0, import_node_path34.join)(root, ".gitignore");
|
|
28831
28941
|
};
|
|
28832
28942
|
function readGitignore() {
|
|
28833
28943
|
const path2 = gitignorePath();
|
|
28834
28944
|
if (path2 === null) return null;
|
|
28835
28945
|
try {
|
|
28836
|
-
return (0,
|
|
28946
|
+
return (0, import_node_fs35.readFileSync)(path2, "utf8");
|
|
28837
28947
|
} catch {
|
|
28838
28948
|
return null;
|
|
28839
28949
|
}
|
|
@@ -28842,7 +28952,7 @@ function writeGitignore(content) {
|
|
|
28842
28952
|
const path2 = gitignorePath();
|
|
28843
28953
|
if (path2 === null) return false;
|
|
28844
28954
|
try {
|
|
28845
|
-
(0,
|
|
28955
|
+
(0, import_node_fs35.writeFileSync)(path2, content, "utf8");
|
|
28846
28956
|
return true;
|
|
28847
28957
|
} catch {
|
|
28848
28958
|
return false;
|
|
@@ -28866,7 +28976,7 @@ async function repoRoot() {
|
|
|
28866
28976
|
}
|
|
28867
28977
|
function hasRepoLocalWorktrees() {
|
|
28868
28978
|
const root = worktreeRootSync();
|
|
28869
|
-
return root !== null && (0,
|
|
28979
|
+
return root !== null && (0, import_node_fs35.existsSync)((0, import_node_path34.join)(root, ".worktrees"));
|
|
28870
28980
|
}
|
|
28871
28981
|
|
|
28872
28982
|
// src/index.ts
|
|
@@ -28902,8 +29012,8 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
28902
29012
|
function ghMultiAccountCaveat(announcedLogin) {
|
|
28903
29013
|
try {
|
|
28904
29014
|
const hostsPath = ghHostsConfigPath(process.env, process.platform);
|
|
28905
|
-
if (!hostsPath || !(0,
|
|
28906
|
-
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0,
|
|
29015
|
+
if (!hostsPath || !(0, import_node_fs36.existsSync)(hostsPath)) return void 0;
|
|
29016
|
+
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs36.readFileSync)(hostsPath, "utf8")));
|
|
28907
29017
|
} catch {
|
|
28908
29018
|
return void 0;
|
|
28909
29019
|
}
|
|
@@ -28911,12 +29021,12 @@ function ghMultiAccountCaveat(announcedLogin) {
|
|
|
28911
29021
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
28912
29022
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
28913
29023
|
function envHealLockPath(home) {
|
|
28914
|
-
return (0,
|
|
29024
|
+
return (0, import_node_path35.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
28915
29025
|
}
|
|
28916
29026
|
async function withEnvHealLock(what, run) {
|
|
28917
29027
|
try {
|
|
28918
29028
|
return await withFileLock(
|
|
28919
|
-
envHealLockPath((0,
|
|
29029
|
+
envHealLockPath((0, import_node_os14.homedir)()),
|
|
28920
29030
|
{ staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
|
|
28921
29031
|
run
|
|
28922
29032
|
);
|
|
@@ -29013,7 +29123,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
29013
29123
|
const configRoot = surfaceConfigRoot(surface);
|
|
29014
29124
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
29015
29125
|
const plan = buildPluginCachePlan(
|
|
29016
|
-
(0,
|
|
29126
|
+
(0, import_node_os14.homedir)(),
|
|
29017
29127
|
running,
|
|
29018
29128
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
29019
29129
|
{ configRoot, includeStaging: surface !== "codex" }
|
|
@@ -29034,15 +29144,31 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
29034
29144
|
marketplaceRows: () => {
|
|
29035
29145
|
try {
|
|
29036
29146
|
if (detectSurface(process.env) === "codex") return [];
|
|
29037
|
-
const home = (0,
|
|
29038
|
-
|
|
29147
|
+
const home = (0, import_node_os14.homedir)();
|
|
29148
|
+
const rows = marketplaceRows(
|
|
29039
29149
|
MMI_MARKETPLACE_NAME,
|
|
29040
|
-
readFileSyncSafe((0,
|
|
29041
|
-
readFileSyncSafe((0,
|
|
29150
|
+
readFileSyncSafe((0, import_node_path35.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs36.readFileSync),
|
|
29151
|
+
readFileSyncSafe((0, import_node_path35.join)(home, ".claude", "settings.json"), import_node_fs36.readFileSync),
|
|
29042
29152
|
// #3974: this CLI heals these rows, so report mode names the doctor run rather than the hand
|
|
29043
29153
|
// edit. Unconditional — it is a fact about mmi-cli, not about the lane this run is on.
|
|
29044
29154
|
true
|
|
29045
29155
|
);
|
|
29156
|
+
const pending = readMarketplacePinPending(
|
|
29157
|
+
(0, import_node_path35.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"),
|
|
29158
|
+
MMI_MARKETPLACE_NAME
|
|
29159
|
+
);
|
|
29160
|
+
if (!pending) return rows;
|
|
29161
|
+
return rows.map((row) => {
|
|
29162
|
+
if (row.id !== "marketplace-catalog-ref" || row.ok) return row;
|
|
29163
|
+
const { fix: _fix, reportOnly: _reportOnly, ...rest } = row;
|
|
29164
|
+
return {
|
|
29165
|
+
...rest,
|
|
29166
|
+
ok: true,
|
|
29167
|
+
warn: true,
|
|
29168
|
+
detail: `pending restart \u2014 doctor pinned main at ${pending.at}; restart Claude Code to load it`,
|
|
29169
|
+
verbose: [...row.verbose ?? [], "known_marketplaces.json was rewritten by the running Claude host after the verified doctor write"]
|
|
29170
|
+
};
|
|
29171
|
+
});
|
|
29046
29172
|
} catch {
|
|
29047
29173
|
return [];
|
|
29048
29174
|
}
|
|
@@ -29052,7 +29178,13 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
29052
29178
|
healMarketplacePins: () => {
|
|
29053
29179
|
try {
|
|
29054
29180
|
if (detectSurface(process.env) === "codex") return void 0;
|
|
29055
|
-
|
|
29181
|
+
const home = (0, import_node_os14.homedir)();
|
|
29182
|
+
const names = [MMI_MARKETPLACE_NAME];
|
|
29183
|
+
const result = applyOrgMarketplacePins((0, import_node_path35.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), names);
|
|
29184
|
+
if (result?.startsWith("pinned ")) {
|
|
29185
|
+
writeMarketplacePinPending((0, import_node_path35.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"), names);
|
|
29186
|
+
}
|
|
29187
|
+
return result;
|
|
29056
29188
|
} catch {
|
|
29057
29189
|
return void 0;
|
|
29058
29190
|
}
|
|
@@ -29258,19 +29390,19 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
29258
29390
|
});
|
|
29259
29391
|
var rules = program2.command("rules").description("org-managed .gitignore delivery");
|
|
29260
29392
|
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) => {
|
|
29261
|
-
const path2 = (0,
|
|
29262
|
-
const current = (0,
|
|
29393
|
+
const path2 = (0, import_node_path35.join)(process.cwd(), ".gitignore");
|
|
29394
|
+
const current = (0, import_node_fs36.existsSync)(path2) ? (0, import_node_fs36.readFileSync)(path2, "utf8") : null;
|
|
29263
29395
|
const plan = planManagedGitignore(current);
|
|
29264
29396
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
29265
29397
|
if (opts.json) {
|
|
29266
|
-
if (opts.write && plan.changed) (0,
|
|
29398
|
+
if (opts.write && plan.changed) (0, import_node_fs36.writeFileSync)(path2, plan.content, "utf8");
|
|
29267
29399
|
console.log(JSON.stringify(plan, null, 2));
|
|
29268
29400
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
29269
29401
|
return;
|
|
29270
29402
|
}
|
|
29271
29403
|
if (opts.write) {
|
|
29272
29404
|
if (plan.changed) {
|
|
29273
|
-
(0,
|
|
29405
|
+
(0, import_node_fs36.writeFileSync)(path2, plan.content, "utf8");
|
|
29274
29406
|
console.log(`mmi-cli org rules gitignore: updated .gitignore (${drift})`);
|
|
29275
29407
|
} else {
|
|
29276
29408
|
console.log("mmi-cli org rules gitignore: up to date");
|
|
@@ -29425,8 +29557,8 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
|
|
|
29425
29557
|
if (!Number.isFinite(limit) || limit < 1) return fail("worktree gc: --limit must be a positive integer");
|
|
29426
29558
|
let root;
|
|
29427
29559
|
if (o.root !== void 0) {
|
|
29428
|
-
root = (0,
|
|
29429
|
-
if (!(0,
|
|
29560
|
+
root = (0, import_node_path35.resolve)(o.root);
|
|
29561
|
+
if (!(0, import_node_fs36.existsSync)(root) || !(0, import_node_fs36.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
|
|
29430
29562
|
const gcRepoRoot = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
29431
29563
|
if (isPathUnderDirectory(gcRepoRoot, root)) {
|
|
29432
29564
|
return fail(`worktree gc: --root ${root} contains this checkout \u2014 name a worktrees root, not the repo or an ancestor of it`);
|
|
@@ -29491,7 +29623,7 @@ async function primaryCheckoutRoot(from) {
|
|
|
29491
29623
|
return primaryCheckoutRootOf(async (args) => (await execFileP2("git", ["-C", from, ...args], { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
29492
29624
|
}
|
|
29493
29625
|
async function unprovenWorktreeReason(wtPath, repoRoot2) {
|
|
29494
|
-
if (!(0,
|
|
29626
|
+
if (!(0, import_node_fs36.existsSync)(wtPath)) return `${wtPath} does not exist on disk`;
|
|
29495
29627
|
const porcelain = (await execFileP2("git", ["-C", repoRoot2, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
|
|
29496
29628
|
const registered = parseWorktreePorcelainEntries(porcelain);
|
|
29497
29629
|
if (!registered.length) {
|
|
@@ -29505,6 +29637,7 @@ async function unprovenWorktreeReason(wtPath, repoRoot2) {
|
|
|
29505
29637
|
function makeProvisionDeps(worktreeRoot, quiet, log) {
|
|
29506
29638
|
return {
|
|
29507
29639
|
runInstall: (command, cwd) => runWorktreeInstall(command, cwd, quiet),
|
|
29640
|
+
validateInstall: (cwd) => runWorktreeInstall("npm ls --depth=0 --json", cwd, true).then(() => true).catch(() => false),
|
|
29508
29641
|
primaryCheckout: () => primaryCheckoutRoot(worktreeRoot),
|
|
29509
29642
|
log
|
|
29510
29643
|
};
|
|
@@ -29512,26 +29645,26 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
|
|
|
29512
29645
|
function acquireWorktreeSetupLock(worktreeRoot) {
|
|
29513
29646
|
const lockPath = repoRuntimeStatePath(worktreeRoot, "worktree-setup.lock");
|
|
29514
29647
|
const take = () => {
|
|
29515
|
-
const fd = (0,
|
|
29648
|
+
const fd = (0, import_node_fs36.openSync)(lockPath, "wx");
|
|
29516
29649
|
try {
|
|
29517
|
-
(0,
|
|
29650
|
+
(0, import_node_fs36.writeSync)(fd, String(Date.now()));
|
|
29518
29651
|
} finally {
|
|
29519
|
-
(0,
|
|
29652
|
+
(0, import_node_fs36.closeSync)(fd);
|
|
29520
29653
|
}
|
|
29521
29654
|
return () => {
|
|
29522
29655
|
try {
|
|
29523
|
-
(0,
|
|
29656
|
+
(0, import_node_fs36.rmSync)(lockPath, { force: true });
|
|
29524
29657
|
} catch {
|
|
29525
29658
|
}
|
|
29526
29659
|
};
|
|
29527
29660
|
};
|
|
29528
29661
|
try {
|
|
29529
|
-
(0,
|
|
29662
|
+
(0, import_node_fs36.mkdirSync)((0, import_node_path35.dirname)(lockPath), { recursive: true });
|
|
29530
29663
|
return take();
|
|
29531
29664
|
} catch {
|
|
29532
29665
|
try {
|
|
29533
|
-
if (Date.now() - (0,
|
|
29534
|
-
(0,
|
|
29666
|
+
if (Date.now() - (0, import_node_fs36.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
|
|
29667
|
+
(0, import_node_fs36.rmSync)(lockPath, { force: true });
|
|
29535
29668
|
return take();
|
|
29536
29669
|
}
|
|
29537
29670
|
} catch {
|
|
@@ -29587,22 +29720,52 @@ withExamples(mutating(
|
|
|
29587
29720
|
const localOnly = preferRemote && base !== preferRemote ? ` (no ${preferRemote} \u2014 local ref)` : "";
|
|
29588
29721
|
console.error(` base ${base} ${baseSha}${localOnly}`);
|
|
29589
29722
|
}
|
|
29590
|
-
|
|
29591
|
-
|
|
29592
|
-
|
|
29593
|
-
|
|
29594
|
-
|
|
29595
|
-
|
|
29596
|
-
|
|
29597
|
-
|
|
29723
|
+
const registered = parseWorktreePorcelainEntries((await execFileP2(
|
|
29724
|
+
"git",
|
|
29725
|
+
["-C", repoRoot2, "worktree", "list", "--porcelain"],
|
|
29726
|
+
{ timeout: GIT_TIMEOUT_MS }
|
|
29727
|
+
)).stdout);
|
|
29728
|
+
const exact = registered.find((entry) => samePath(entry.path, wtPath));
|
|
29729
|
+
let resumed = false;
|
|
29730
|
+
if (exact) {
|
|
29731
|
+
step = `resume existing worktree ${wtPath}`;
|
|
29732
|
+
if (exact.branch !== branch) {
|
|
29733
|
+
return fail(`worktree create: ${wtPath} is already registered for '${exact.branch ?? "detached HEAD"}', not '${branch}'`);
|
|
29734
|
+
}
|
|
29735
|
+
const status = (await execFileP2("git", ["-C", wtPath, "status", "--porcelain"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
|
|
29736
|
+
if (status) return fail(`worktree create: refusing to resume ${wtPath} \u2014 it has uncommitted changes`);
|
|
29737
|
+
const head = await revParseRef(`refs/heads/${branch}`);
|
|
29738
|
+
const baseOid = await revParseRef(base);
|
|
29739
|
+
if (!head || !baseOid) return fail(`worktree create: could not prove '${branch}' and '${base}' before resume`);
|
|
29740
|
+
const canFastForward = await execFileP2(
|
|
29741
|
+
"git",
|
|
29742
|
+
["-C", repoRoot2, "merge-base", "--is-ancestor", head, baseOid],
|
|
29743
|
+
{ timeout: GIT_TIMEOUT_MS }
|
|
29744
|
+
).then(() => true).catch(() => false);
|
|
29745
|
+
if (!canFastForward) {
|
|
29746
|
+
return fail(`worktree create: refusing to resume '${branch}' \u2014 it has local commits or diverges from ${base}`);
|
|
29747
|
+
}
|
|
29748
|
+
await execFileP2("git", ["-C", wtPath, "merge", "--ff-only", base], { timeout: GIT_TIMEOUT_MS });
|
|
29749
|
+
resumed = true;
|
|
29750
|
+
}
|
|
29751
|
+
if (!resumed) {
|
|
29752
|
+
step = `git worktree add ${wtPath}`;
|
|
29753
|
+
await addWorktreeRobust(wtPath, branch, base, {
|
|
29754
|
+
git: async (args) => (await execFileP2("git", args, { timeout: GH_MUTATION_TIMEOUT_MS })).stdout,
|
|
29755
|
+
revParse: async (ref) => {
|
|
29756
|
+
try {
|
|
29757
|
+
return (await execFileP2("git", ["rev-parse", "--verify", ref], { timeout: GIT_TIMEOUT_MS })).stdout.trim() || void 0;
|
|
29758
|
+
} catch {
|
|
29759
|
+
return void 0;
|
|
29760
|
+
}
|
|
29761
|
+
},
|
|
29762
|
+
deleteBranch: (b) => execFileP2("git", ["branch", "-D", b], { timeout: GIT_TIMEOUT_MS }).then(() => void 0),
|
|
29763
|
+
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
|
|
29764
|
+
log: (m) => {
|
|
29765
|
+
if (!o.json) console.error(` ${m}`);
|
|
29598
29766
|
}
|
|
29599
|
-
}
|
|
29600
|
-
|
|
29601
|
-
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
|
|
29602
|
-
log: (m) => {
|
|
29603
|
-
if (!o.json) console.error(` ${m}`);
|
|
29604
|
-
}
|
|
29605
|
-
});
|
|
29767
|
+
});
|
|
29768
|
+
}
|
|
29606
29769
|
step = "install deps + copy local-only config";
|
|
29607
29770
|
const report = await provisionWorktree(wtPath, makeProvisionDeps(wtPath, Boolean(o.json), (m) => {
|
|
29608
29771
|
if (!o.json) console.error(` ${m}`);
|
|
@@ -29642,12 +29805,13 @@ withExamples(mutating(
|
|
|
29642
29805
|
branch,
|
|
29643
29806
|
path: wtPath,
|
|
29644
29807
|
base,
|
|
29808
|
+
resumed,
|
|
29645
29809
|
...report,
|
|
29646
29810
|
...issueForm && selector ? { issue: `${selector.repo}#${selector.number}` } : {},
|
|
29647
29811
|
...issueForm && o.claim ? { claim: claimError ? { ok: false, error: claimError } : claim } : {}
|
|
29648
29812
|
}, null, 2));
|
|
29649
29813
|
}
|
|
29650
|
-
console.log(`worktree ready: ${wtPath} (branch ${branch} from ${base})`);
|
|
29814
|
+
console.log(`worktree ready: ${wtPath} (branch ${branch} ${resumed ? "resumed at" : "from"} ${base})`);
|
|
29651
29815
|
console.log(` installed: ${report.installed.map((i) => i.dir || ".").join(", ") || "none"}`);
|
|
29652
29816
|
console.log(` copied: ${report.copied.join(", ") || "none"}`);
|
|
29653
29817
|
if (issueForm && o.claim && selector) {
|
|
@@ -30133,7 +30297,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
30133
30297
|
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`);
|
|
30134
30298
|
if (o.secretsFile) {
|
|
30135
30299
|
try {
|
|
30136
|
-
vars.push(`secrets=${(0,
|
|
30300
|
+
vars.push(`secrets=${(0, import_node_fs36.readFileSync)(o.secretsFile, "utf8")}`);
|
|
30137
30301
|
} catch (e) {
|
|
30138
30302
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
30139
30303
|
}
|
|
@@ -30881,11 +31045,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
|
|
|
30881
31045
|
}
|
|
30882
31046
|
});
|
|
30883
31047
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
30884
|
-
const wfDir = (0,
|
|
30885
|
-
if (!(0,
|
|
30886
|
-
return (0,
|
|
31048
|
+
const wfDir = (0, import_node_path35.join)(cwd, ".github", "workflows");
|
|
31049
|
+
if (!(0, import_node_fs36.existsSync)(wfDir)) return [];
|
|
31050
|
+
return (0, import_node_fs36.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
30887
31051
|
try {
|
|
30888
|
-
return workflowReportsPrChecks((0,
|
|
31052
|
+
return workflowReportsPrChecks((0, import_node_fs36.readFileSync)((0, import_node_path35.join)(wfDir, name), "utf8"));
|
|
30889
31053
|
} catch {
|
|
30890
31054
|
return true;
|
|
30891
31055
|
}
|
|
@@ -30917,16 +31081,16 @@ function ciAuditDeps() {
|
|
|
30917
31081
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
30918
31082
|
readSeedFile: (path2) => {
|
|
30919
31083
|
if (!root) return null;
|
|
30920
|
-
const fullPath = (0,
|
|
30921
|
-
return (0,
|
|
31084
|
+
const fullPath = (0, import_node_path35.join)(root, path2);
|
|
31085
|
+
return (0, import_node_fs36.existsSync)(fullPath) ? (0, import_node_fs36.readFileSync)(fullPath, "utf8") : null;
|
|
30922
31086
|
}
|
|
30923
31087
|
};
|
|
30924
31088
|
}
|
|
30925
31089
|
function hubRoot() {
|
|
30926
|
-
const fromPkg = (0,
|
|
31090
|
+
const fromPkg = (0, import_node_path35.join)(__dirname, "..", "..");
|
|
30927
31091
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
30928
|
-
if ((0,
|
|
30929
|
-
if ((0,
|
|
31092
|
+
if ((0, import_node_fs36.existsSync)((0, import_node_path35.join)(fromPkg, marker))) return fromPkg;
|
|
31093
|
+
if ((0, import_node_fs36.existsSync)((0, import_node_path35.join)(process.cwd(), marker))) return process.cwd();
|
|
30930
31094
|
return null;
|
|
30931
31095
|
}
|
|
30932
31096
|
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) => {
|
|
@@ -31055,6 +31219,10 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
|
|
|
31055
31219
|
return false;
|
|
31056
31220
|
}
|
|
31057
31221
|
});
|
|
31222
|
+
if (result.status !== "failed") {
|
|
31223
|
+
const repoFlag = result.repo ? ` --repo ${result.repo}` : "";
|
|
31224
|
+
console.warn(`pr land: merge confirmed; cleanup pending \u2014 if this process is interrupted, resume with: mmi-cli pr merge ${number}${repoFlag} --squash`);
|
|
31225
|
+
}
|
|
31058
31226
|
if (result.status !== "failed") {
|
|
31059
31227
|
try {
|
|
31060
31228
|
const { stdout } = await execFileP2(process.execPath, [
|
|
@@ -31229,7 +31397,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
31229
31397
|
localCleanup = await cleanupPrMergeLocalBranch(headRef, {
|
|
31230
31398
|
beforeWorktrees,
|
|
31231
31399
|
startingPath,
|
|
31232
|
-
pathExists: (p) => (0,
|
|
31400
|
+
pathExists: (p) => (0, import_node_fs36.existsSync)(p),
|
|
31233
31401
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
31234
31402
|
teardownWorktreeStage,
|
|
31235
31403
|
deferredStore,
|
|
@@ -31704,12 +31872,12 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
31704
31872
|
targets = resolution.targets;
|
|
31705
31873
|
}
|
|
31706
31874
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
31707
|
-
const fileMatrix = (0,
|
|
31875
|
+
const fileMatrix = (0, import_node_fs36.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs36.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
31708
31876
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
31709
31877
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
31710
|
-
const fileContracts = (0,
|
|
31878
|
+
const fileContracts = (0, import_node_fs36.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs36.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
31711
31879
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
31712
|
-
const sanctioned = (0,
|
|
31880
|
+
const sanctioned = (0, import_node_fs36.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs36.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
31713
31881
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
31714
31882
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
31715
31883
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -31741,16 +31909,16 @@ function directoryBytes(path2) {
|
|
|
31741
31909
|
let total = 0;
|
|
31742
31910
|
let entries;
|
|
31743
31911
|
try {
|
|
31744
|
-
entries = (0,
|
|
31912
|
+
entries = (0, import_node_fs36.readdirSync)(path2, { withFileTypes: true });
|
|
31745
31913
|
} catch {
|
|
31746
31914
|
return 0;
|
|
31747
31915
|
}
|
|
31748
31916
|
for (const entry of entries) {
|
|
31749
|
-
const child2 = (0,
|
|
31917
|
+
const child2 = (0, import_node_path35.join)(path2, entry.name);
|
|
31750
31918
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
31751
31919
|
else {
|
|
31752
31920
|
try {
|
|
31753
|
-
total += (0,
|
|
31921
|
+
total += (0, import_node_fs36.statSync)(child2).size;
|
|
31754
31922
|
} catch {
|
|
31755
31923
|
}
|
|
31756
31924
|
}
|
|
@@ -31758,25 +31926,25 @@ function directoryBytes(path2) {
|
|
|
31758
31926
|
return total;
|
|
31759
31927
|
}
|
|
31760
31928
|
function listDirEntries(dir) {
|
|
31761
|
-
return (0,
|
|
31929
|
+
return (0, import_node_fs36.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
31762
31930
|
}
|
|
31763
31931
|
function readInstalledPluginRefs(configRoot) {
|
|
31764
31932
|
const p = installedPluginsPathForConfig(configRoot);
|
|
31765
|
-
if (!(0,
|
|
31933
|
+
if (!(0, import_node_fs36.existsSync)(p)) return [];
|
|
31766
31934
|
try {
|
|
31767
|
-
return installedPluginPaths((0,
|
|
31935
|
+
return installedPluginPaths((0, import_node_fs36.readFileSync)(p, "utf8"));
|
|
31768
31936
|
} catch {
|
|
31769
31937
|
return null;
|
|
31770
31938
|
}
|
|
31771
31939
|
}
|
|
31772
31940
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
31773
31941
|
return {
|
|
31774
|
-
exists: (p) => (0,
|
|
31775
|
-
listVersionDirs: (root) => (0,
|
|
31942
|
+
exists: (p) => (0, import_node_fs36.existsSync)(p),
|
|
31943
|
+
listVersionDirs: (root) => (0, import_node_fs36.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
31776
31944
|
dirBytes,
|
|
31777
|
-
listStagingDirs: (root) => (0,
|
|
31945
|
+
listStagingDirs: (root) => (0, import_node_fs36.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
31778
31946
|
try {
|
|
31779
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
31947
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path35.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs36.statSync)(p).mtimeMs) };
|
|
31780
31948
|
} catch {
|
|
31781
31949
|
return { name: d.name, mtimeMs: Date.now() };
|
|
31782
31950
|
}
|
|
@@ -31790,10 +31958,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
31790
31958
|
return {
|
|
31791
31959
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
31792
31960
|
mtimeMs: (name) => {
|
|
31793
|
-
const p = (0,
|
|
31794
|
-
if (!(0,
|
|
31961
|
+
const p = (0, import_node_path35.join)(stagingRoot, name);
|
|
31962
|
+
if (!(0, import_node_fs36.existsSync)(p)) return null;
|
|
31795
31963
|
try {
|
|
31796
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
31964
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs36.statSync)(q).mtimeMs);
|
|
31797
31965
|
} catch {
|
|
31798
31966
|
return null;
|
|
31799
31967
|
}
|
|
@@ -31813,13 +31981,13 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
31813
31981
|
return;
|
|
31814
31982
|
}
|
|
31815
31983
|
const plan = buildPluginCachePlan(
|
|
31816
|
-
(0,
|
|
31984
|
+
(0, import_node_os14.homedir)(),
|
|
31817
31985
|
running,
|
|
31818
31986
|
pluginCacheFsDeps(configRoot, directoryBytes),
|
|
31819
31987
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
31820
31988
|
);
|
|
31821
31989
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
31822
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
31990
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs36.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
31823
31991
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
31824
31992
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
31825
31993
|
else console.log(renderPluginCachePlan(plan, result));
|