@mutmutco/cli 3.105.0 → 3.105.3
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 +430 -147
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -5000,7 +5000,7 @@ var program = new Command();
|
|
|
5000
5000
|
|
|
5001
5001
|
// src/index.ts
|
|
5002
5002
|
var import_promises11 = require("node:fs/promises");
|
|
5003
|
-
var
|
|
5003
|
+
var import_node_fs42 = require("node:fs");
|
|
5004
5004
|
var import_node_child_process19 = require("node:child_process");
|
|
5005
5005
|
|
|
5006
5006
|
// src/cli-shared.ts
|
|
@@ -8590,7 +8590,7 @@ function commandLadderHint() {
|
|
|
8590
8590
|
}
|
|
8591
8591
|
|
|
8592
8592
|
// src/index.ts
|
|
8593
|
-
var
|
|
8593
|
+
var import_node_path40 = require("node:path");
|
|
8594
8594
|
|
|
8595
8595
|
// src/merge-ci-policy.ts
|
|
8596
8596
|
function resolveMergeCiPolicy(input) {
|
|
@@ -15206,7 +15206,7 @@ async function executeWaveLand(plan, deps, opts = { preserveWorktree: true }) {
|
|
|
15206
15206
|
}
|
|
15207
15207
|
|
|
15208
15208
|
// src/index.ts
|
|
15209
|
-
var
|
|
15209
|
+
var import_node_os17 = require("node:os");
|
|
15210
15210
|
|
|
15211
15211
|
// src/board.ts
|
|
15212
15212
|
var import_node_child_process9 = require("node:child_process");
|
|
@@ -18832,13 +18832,25 @@ function liveEvidenceLines(evidence, repo) {
|
|
|
18832
18832
|
if (evidence.branch) live.push(`live branch ${evidence.branch} on ${repo}`);
|
|
18833
18833
|
return live;
|
|
18834
18834
|
}
|
|
18835
|
-
function
|
|
18836
|
-
|
|
18835
|
+
function sameClaimHost(a, b) {
|
|
18836
|
+
return Boolean(a && b && a.toLowerCase() === b.toLowerCase());
|
|
18837
|
+
}
|
|
18838
|
+
function laneOwnership(marker, current) {
|
|
18839
|
+
if (!marker) return "unknown";
|
|
18840
|
+
if (marker.session && current.session) {
|
|
18841
|
+
return marker.session === current.session ? "mine" : "other";
|
|
18842
|
+
}
|
|
18843
|
+
if (marker.surface && current.surface && marker.surface !== current.surface) return "other";
|
|
18844
|
+
if (marker.host && current.host && !sameClaimHost(marker.host, current.host)) return "other";
|
|
18845
|
+
if (marker.surface && current.surface && marker.surface === current.surface && marker.host && current.host && sameClaimHost(marker.host, current.host)) {
|
|
18846
|
+
return "mine";
|
|
18847
|
+
}
|
|
18837
18848
|
return "unknown";
|
|
18838
18849
|
}
|
|
18839
18850
|
async function checkLaneContest(client, item) {
|
|
18840
18851
|
const evidence = await gatherClaimLiveness(client, item.repository, item.number, openPullsFetcher(client));
|
|
18841
|
-
const
|
|
18852
|
+
const actor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
|
|
18853
|
+
const ownership = laneOwnership(evidence.marker, actor);
|
|
18842
18854
|
const live = ownership === "mine" ? [] : liveEvidenceLines(evidence, item.repository);
|
|
18843
18855
|
const unverifiable = ownership === "mine" ? [] : evidence.failed;
|
|
18844
18856
|
return {
|
|
@@ -20462,7 +20474,8 @@ async function runHotfixStart(deps, options) {
|
|
|
20462
20474
|
}
|
|
20463
20475
|
notes.push(`cherry-picked ${label} onto ${branch} (from origin/main, -x trailer recorded)`);
|
|
20464
20476
|
if (deployModel === "hub-serverless") {
|
|
20465
|
-
await deps.run("
|
|
20477
|
+
const durableSource = clean3(await deps.run("git", ["merge-base", "HEAD", "origin/main"]));
|
|
20478
|
+
await deps.run("node", ["scripts/release-distribution.mjs", "prepare", version, "--source-commit", durableSource]);
|
|
20466
20479
|
const changedFiles = (await deps.run("node", ["scripts/release-distribution.mjs", "changed-files"])).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
20467
20480
|
await deps.run("git", ["add", "-f", "--", ...changedFiles]);
|
|
20468
20481
|
const staged = await deps.run("git", ["diff", "--cached", "--name-only"]);
|
|
@@ -25439,6 +25452,148 @@ async function runIssueChildren(deps, epic, opts) {
|
|
|
25439
25452
|
}
|
|
25440
25453
|
return out;
|
|
25441
25454
|
}
|
|
25455
|
+
var FRONTIER_BUCKET_ORDER = {
|
|
25456
|
+
in_flight: 0,
|
|
25457
|
+
partial: 1,
|
|
25458
|
+
ready: 2,
|
|
25459
|
+
landed: 3
|
|
25460
|
+
};
|
|
25461
|
+
function prStateIs(pr2, want) {
|
|
25462
|
+
return String(pr2.state ?? "").toUpperCase() === want;
|
|
25463
|
+
}
|
|
25464
|
+
function isBoardDone(boardStatus) {
|
|
25465
|
+
return /^done$/i.test(String(boardStatus ?? "").trim());
|
|
25466
|
+
}
|
|
25467
|
+
function isBoardInFlight(boardStatus) {
|
|
25468
|
+
return /^(in\s*progress|in\s*review|review|doing)$/i.test(String(boardStatus ?? "").trim());
|
|
25469
|
+
}
|
|
25470
|
+
function classifyFrontierChild(child2) {
|
|
25471
|
+
const mergedPrs = child2.linkedPrs.filter((p) => prStateIs(p, "MERGED"));
|
|
25472
|
+
const openPrs = child2.linkedPrs.filter((p) => prStateIs(p, "OPEN"));
|
|
25473
|
+
const otherPrs = child2.linkedPrs.filter((p) => !prStateIs(p, "MERGED") && !prStateIs(p, "OPEN"));
|
|
25474
|
+
const closed = String(child2.state ?? "").toUpperCase() === "CLOSED";
|
|
25475
|
+
let bucket;
|
|
25476
|
+
if (closed || isBoardDone(child2.boardStatus)) {
|
|
25477
|
+
bucket = "landed";
|
|
25478
|
+
} else if (openPrs.length > 0 || isBoardInFlight(child2.boardStatus)) {
|
|
25479
|
+
bucket = "in_flight";
|
|
25480
|
+
} else if (mergedPrs.length > 0) {
|
|
25481
|
+
bucket = "partial";
|
|
25482
|
+
} else {
|
|
25483
|
+
bucket = "ready";
|
|
25484
|
+
}
|
|
25485
|
+
return {
|
|
25486
|
+
number: child2.number,
|
|
25487
|
+
title: child2.title,
|
|
25488
|
+
state: child2.state,
|
|
25489
|
+
url: child2.url,
|
|
25490
|
+
repo: child2.repo,
|
|
25491
|
+
assignee: child2.assignee,
|
|
25492
|
+
boardStatus: child2.boardStatus,
|
|
25493
|
+
depth: child2.depth,
|
|
25494
|
+
bucket,
|
|
25495
|
+
mergedPrs,
|
|
25496
|
+
openPrs,
|
|
25497
|
+
otherPrs
|
|
25498
|
+
};
|
|
25499
|
+
}
|
|
25500
|
+
function sortFrontierChildren(rows) {
|
|
25501
|
+
return [...rows].sort((a, b) => {
|
|
25502
|
+
const byBucket = FRONTIER_BUCKET_ORDER[a.bucket] - FRONTIER_BUCKET_ORDER[b.bucket];
|
|
25503
|
+
if (byBucket !== 0) return byBucket;
|
|
25504
|
+
return a.number - b.number;
|
|
25505
|
+
});
|
|
25506
|
+
}
|
|
25507
|
+
function buildFrontierReport(epic, children) {
|
|
25508
|
+
const classified = children.map(classifyFrontierChild);
|
|
25509
|
+
const landed = classified.filter((c) => c.bucket === "landed").sort((a, b) => a.number - b.number);
|
|
25510
|
+
const frontier = sortFrontierChildren(classified.filter((c) => c.bucket !== "landed"));
|
|
25511
|
+
const inFlight = frontier.filter((c) => c.bucket === "in_flight").length;
|
|
25512
|
+
const partial = frontier.filter((c) => c.bucket === "partial").length;
|
|
25513
|
+
const ready = frontier.filter((c) => c.bucket === "ready").length;
|
|
25514
|
+
const report = {
|
|
25515
|
+
epic,
|
|
25516
|
+
counts: {
|
|
25517
|
+
total: classified.length,
|
|
25518
|
+
landed: landed.length,
|
|
25519
|
+
frontier: frontier.length,
|
|
25520
|
+
inFlight,
|
|
25521
|
+
partial,
|
|
25522
|
+
ready
|
|
25523
|
+
},
|
|
25524
|
+
landed,
|
|
25525
|
+
frontier,
|
|
25526
|
+
children: classified,
|
|
25527
|
+
summary: ""
|
|
25528
|
+
};
|
|
25529
|
+
report.summary = formatFrontierSummary(report);
|
|
25530
|
+
return report;
|
|
25531
|
+
}
|
|
25532
|
+
function fmtChildLine(c) {
|
|
25533
|
+
const board = c.boardStatus ? ` board=${c.boardStatus}` : "";
|
|
25534
|
+
const merged = c.mergedPrs.length ? ` merged=[${c.mergedPrs.map((p) => `#${p.number}`).join(",")}]` : "";
|
|
25535
|
+
const open2 = c.openPrs.length ? ` open=[${c.openPrs.map((p) => `#${p.number}`).join(",")}]` : "";
|
|
25536
|
+
return ` #${c.number} [${c.bucket}] ${c.title} (${c.state}${board}${merged}${open2})`;
|
|
25537
|
+
}
|
|
25538
|
+
function formatFrontierSummary(report) {
|
|
25539
|
+
const { epic, counts } = report;
|
|
25540
|
+
const lines = [
|
|
25541
|
+
`issue frontier \u2014 ${epic.repo}#${epic.number} ${epic.title} (${epic.state})`,
|
|
25542
|
+
` ${counts.total} children \xB7 ${counts.landed} landed \xB7 ${counts.frontier} frontier (${counts.inFlight} in-flight, ${counts.partial} partial, ${counts.ready} ready)`
|
|
25543
|
+
];
|
|
25544
|
+
if (report.frontier.length) {
|
|
25545
|
+
lines.push("frontier:");
|
|
25546
|
+
for (const c of report.frontier) lines.push(fmtChildLine(c));
|
|
25547
|
+
} else {
|
|
25548
|
+
lines.push("frontier: (empty \u2014 every walked child is landed)");
|
|
25549
|
+
}
|
|
25550
|
+
if (report.landed.length) {
|
|
25551
|
+
lines.push("landed:");
|
|
25552
|
+
for (const c of report.landed) lines.push(fmtChildLine(c));
|
|
25553
|
+
}
|
|
25554
|
+
return lines.join("\n");
|
|
25555
|
+
}
|
|
25556
|
+
function frontierEpicGraphqlArgs(owner, name, number) {
|
|
25557
|
+
const query = "query($owner:String!,$name:String!){repository(owner:$owner,name:$name){issue(number:" + number + "){number title state url}}}";
|
|
25558
|
+
return ["api", "graphql", "-f", `query=${query}`, "-f", `owner=${owner}`, "-f", `name=${name}`];
|
|
25559
|
+
}
|
|
25560
|
+
function extractFrontierEpicResponse(resp, repo) {
|
|
25561
|
+
const r = resp ?? {};
|
|
25562
|
+
const issue2 = r.data?.repository?.issue;
|
|
25563
|
+
if (!issue2 || (r.errors ?? []).some((e) => e?.type === "NOT_FOUND")) {
|
|
25564
|
+
throw new QueryReadError("NOT_FOUND", "issue not found");
|
|
25565
|
+
}
|
|
25566
|
+
return {
|
|
25567
|
+
number: Number(issue2.number),
|
|
25568
|
+
title: String(issue2.title ?? ""),
|
|
25569
|
+
state: String(issue2.state ?? ""),
|
|
25570
|
+
url: String(issue2.url ?? ""),
|
|
25571
|
+
repo
|
|
25572
|
+
};
|
|
25573
|
+
}
|
|
25574
|
+
async function runIssueFrontier(deps, epic, opts) {
|
|
25575
|
+
const ref = parseIssueRef(epic);
|
|
25576
|
+
const repo = ref.repo ?? await deps.resolveRepo(void 0);
|
|
25577
|
+
if (!repo) {
|
|
25578
|
+
throw new QueryReadError("BAD_INPUT", `could not resolve repo for #${ref.number} (pass --repo <owner/repo>)`);
|
|
25579
|
+
}
|
|
25580
|
+
const { owner, name } = splitRepo(repo);
|
|
25581
|
+
let headerResp;
|
|
25582
|
+
try {
|
|
25583
|
+
headerResp = await deps.ghJson(frontierEpicGraphqlArgs(owner, name, ref.number), GH_LIST_TIMEOUT_MS);
|
|
25584
|
+
} catch (e) {
|
|
25585
|
+
if (isGhNotFound(e)) {
|
|
25586
|
+
throw new QueryReadError("NOT_FOUND", `#${ref.number} not found in ${repo}`);
|
|
25587
|
+
}
|
|
25588
|
+
throw e;
|
|
25589
|
+
}
|
|
25590
|
+
const epicHeader = extractFrontierEpicResponse(headerResp, repo);
|
|
25591
|
+
const children = await runIssueChildren(deps, `${repo}#${ref.number}`, {
|
|
25592
|
+
recursive: opts.recursive,
|
|
25593
|
+
boardProjectId: opts.boardProjectId
|
|
25594
|
+
});
|
|
25595
|
+
return buildFrontierReport(epicHeader, children);
|
|
25596
|
+
}
|
|
25442
25597
|
function buildPrListArgs(opts, repo, author) {
|
|
25443
25598
|
const limit = clampLimit(opts.limit, DEFAULT_LIMIT);
|
|
25444
25599
|
const state = opts.state ?? "open";
|
|
@@ -25615,6 +25770,17 @@ function registerQueryCommands(program3) {
|
|
|
25615
25770
|
queryFail("issue children", e);
|
|
25616
25771
|
}
|
|
25617
25772
|
});
|
|
25773
|
+
issue2.command("frontier <epic>").description("compute an epic's landed-vs-open frontier from merged PRs + board Status (read-only; never mutates the epic body) \u2014 human summary by default, --json for the structured report").option("--recursive", "walk the full sub-issue tree (bounded depth + total cap)").option("--repo <owner/repo>", "repo for a bare epic ref (defaults to the current repo)").option("--json", "print the structured FrontierReport JSON (includes summary)").action(async (epic, o) => {
|
|
25774
|
+
try {
|
|
25775
|
+
const frontierDeps = { ...deps, resolveRepo: async (r) => deps.resolveRepo(r ?? o.repo) };
|
|
25776
|
+
const boardProjectId = await resolveBoardProjectId(o.repo);
|
|
25777
|
+
const report = await runIssueFrontier(frontierDeps, epic, { recursive: o.recursive, boardProjectId });
|
|
25778
|
+
if (o.json) console.log(JSON.stringify(report, null, 2));
|
|
25779
|
+
else console.log(report.summary);
|
|
25780
|
+
} catch (e) {
|
|
25781
|
+
queryFail("issue frontier", e);
|
|
25782
|
+
}
|
|
25783
|
+
});
|
|
25618
25784
|
pr2.command("list").description("bounded PR read \u2014 --mine scopes to the viewer's own PRs (resolves the viewer via gh); always prints JSON").option("--mine", "only the caller's own PRs (resolves the viewer via `gh api user`)").addOption(new Option("--state <state>", "open | all").default("open").choices(PR_STATES)).option("--limit <n>", "max PRs to return (capped at 100)", (v) => Number(v), DEFAULT_LIMIT).option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json", "machine-readable output (already the default \u2014 accepted for contract uniformity)").action(async (o) => {
|
|
25619
25785
|
try {
|
|
25620
25786
|
const state = validateEnum("--state", PR_STATES, o.state, "pr list");
|
|
@@ -28025,10 +28191,10 @@ function registerBoardCommands(program3) {
|
|
|
28025
28191
|
}
|
|
28026
28192
|
|
|
28027
28193
|
// src/merge-cleanup.ts
|
|
28028
|
-
var
|
|
28194
|
+
var import_node_fs35 = require("node:fs");
|
|
28029
28195
|
var import_promises8 = require("node:fs/promises");
|
|
28030
|
-
var
|
|
28031
|
-
var
|
|
28196
|
+
var import_node_path34 = require("node:path");
|
|
28197
|
+
var import_node_os13 = require("node:os");
|
|
28032
28198
|
var import_node_child_process17 = require("node:child_process");
|
|
28033
28199
|
|
|
28034
28200
|
// src/board-advance.ts
|
|
@@ -28194,6 +28360,117 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
|
|
|
28194
28360
|
};
|
|
28195
28361
|
}
|
|
28196
28362
|
|
|
28363
|
+
// src/jerv-cli-spawn.ts
|
|
28364
|
+
var import_node_fs34 = require("node:fs");
|
|
28365
|
+
var import_node_os12 = require("node:os");
|
|
28366
|
+
var import_node_path33 = require("node:path");
|
|
28367
|
+
var WIN_NAMES = ["jerv-cli.cmd", "jerv-cli.exe", "jerv-cli"];
|
|
28368
|
+
var POSIX_NAMES = ["jerv-cli"];
|
|
28369
|
+
var JERV_CLI_ENTRY = (0, import_node_path33.join)("node_modules", "@jervaise", "jerv-cli", "dist", "index.cjs");
|
|
28370
|
+
function pathEnvEntries(pathEnv, platform2 = process.platform) {
|
|
28371
|
+
if (platform2 !== "win32") {
|
|
28372
|
+
return pathEnv.split(import_node_path33.delimiter).map((e) => e.trim()).filter(Boolean);
|
|
28373
|
+
}
|
|
28374
|
+
if (pathEnv.includes(";")) {
|
|
28375
|
+
return pathEnv.split(";").map((e) => e.trim()).filter(Boolean);
|
|
28376
|
+
}
|
|
28377
|
+
const looksUnix = pathEnv.startsWith("/") || /(?:^|:)\/[a-zA-Z]\//.test(pathEnv);
|
|
28378
|
+
if (looksUnix && pathEnv.includes(":")) {
|
|
28379
|
+
return pathEnv.split(":").map((e) => e.trim()).filter(Boolean);
|
|
28380
|
+
}
|
|
28381
|
+
return pathEnv.trim() ? [pathEnv.trim()] : [];
|
|
28382
|
+
}
|
|
28383
|
+
function normalizeSpawnPathEntry(entry, platform2 = process.platform) {
|
|
28384
|
+
const trimmed = entry.trim();
|
|
28385
|
+
if (!trimmed) return void 0;
|
|
28386
|
+
if (platform2 !== "win32") return trimmed;
|
|
28387
|
+
const msys = /^\/([a-zA-Z])\/(.*)$/.exec(trimmed.replace(/\\/g, "/"));
|
|
28388
|
+
if (msys) return `${msys[1].toUpperCase()}:\\${msys[2].replace(/\//g, "\\")}`;
|
|
28389
|
+
return trimmed;
|
|
28390
|
+
}
|
|
28391
|
+
function jervCliCandidateDirs(env = process.env, home = (0, import_node_os12.homedir)(), platform2 = process.platform) {
|
|
28392
|
+
const seen = /* @__PURE__ */ new Set();
|
|
28393
|
+
const out = [];
|
|
28394
|
+
const push = (dir) => {
|
|
28395
|
+
if (!dir) return;
|
|
28396
|
+
const key = platform2 === "win32" ? dir.toLowerCase() : dir;
|
|
28397
|
+
if (seen.has(key)) return;
|
|
28398
|
+
seen.add(key);
|
|
28399
|
+
out.push(dir);
|
|
28400
|
+
};
|
|
28401
|
+
for (const entry of pathEnvEntries(env.PATH ?? "", platform2)) {
|
|
28402
|
+
push(normalizeSpawnPathEntry(entry, platform2));
|
|
28403
|
+
}
|
|
28404
|
+
if (platform2 === "win32") {
|
|
28405
|
+
if (env.APPDATA) push((0, import_node_path33.join)(env.APPDATA, "npm"));
|
|
28406
|
+
if (env.LOCALAPPDATA) push((0, import_node_path33.join)(env.LOCALAPPDATA, "npm"));
|
|
28407
|
+
} else {
|
|
28408
|
+
push((0, import_node_path33.join)(home, ".local", "bin"));
|
|
28409
|
+
}
|
|
28410
|
+
return out;
|
|
28411
|
+
}
|
|
28412
|
+
function jervCliCandidatePaths(env = process.env, home = (0, import_node_os12.homedir)(), platform2 = process.platform) {
|
|
28413
|
+
const names = platform2 === "win32" ? WIN_NAMES : POSIX_NAMES;
|
|
28414
|
+
const out = [];
|
|
28415
|
+
for (const dir of jervCliCandidateDirs(env, home, platform2)) {
|
|
28416
|
+
for (const name of names) out.push((0, import_node_path33.join)(dir, name));
|
|
28417
|
+
}
|
|
28418
|
+
return out;
|
|
28419
|
+
}
|
|
28420
|
+
function resolveJervCliPath(env = process.env, home = (0, import_node_os12.homedir)(), platform2 = process.platform, exists = import_node_fs34.existsSync) {
|
|
28421
|
+
for (const candidate of jervCliCandidatePaths(env, home, platform2)) {
|
|
28422
|
+
if (exists(candidate)) return candidate;
|
|
28423
|
+
}
|
|
28424
|
+
return void 0;
|
|
28425
|
+
}
|
|
28426
|
+
function resolveJervCliNodeEntry(shimPath, exists = import_node_fs34.existsSync) {
|
|
28427
|
+
const entry = (0, import_node_path33.join)((0, import_node_path33.dirname)(shimPath), JERV_CLI_ENTRY);
|
|
28428
|
+
return exists(entry) ? entry : void 0;
|
|
28429
|
+
}
|
|
28430
|
+
function jervCliExecFileArgs(args, opts = {}) {
|
|
28431
|
+
const platform2 = opts.platform ?? process.platform;
|
|
28432
|
+
const exists = opts.exists ?? import_node_fs34.existsSync;
|
|
28433
|
+
const resolved = resolveJervCliPath(opts.env ?? process.env, opts.home ?? (0, import_node_os12.homedir)(), platform2, exists);
|
|
28434
|
+
if (resolved) {
|
|
28435
|
+
const entry = resolveJervCliNodeEntry(resolved, exists);
|
|
28436
|
+
if (entry) {
|
|
28437
|
+
return { file: opts.execPath ?? process.execPath, args: [entry, ...args], via: "node-entry" };
|
|
28438
|
+
}
|
|
28439
|
+
}
|
|
28440
|
+
const bin = resolved ?? "jerv-cli";
|
|
28441
|
+
if (platform2 === "win32") {
|
|
28442
|
+
return { file: "cmd.exe", args: ["/c", bin, ...args], via: resolved ? "cmd-shim" : "bare" };
|
|
28443
|
+
}
|
|
28444
|
+
return { file: bin, args: [...args], via: resolved ? "posix" : "bare" };
|
|
28445
|
+
}
|
|
28446
|
+
function formatJervCliSpawnFailure(err, plan, candidates) {
|
|
28447
|
+
const base = (err.stderr?.trim() || err.message.trim()).split("\n")[0] || "spawn failed";
|
|
28448
|
+
const code = err.code;
|
|
28449
|
+
if (code !== "ENOENT" && !/\bENOENT\b/i.test(base)) return base;
|
|
28450
|
+
const tried = candidates.length > 0 ? candidates.join(" | ") : "(no candidates)";
|
|
28451
|
+
return `${base} [via=${plan.via} file=${plan.file}; tried: ${tried}]`;
|
|
28452
|
+
}
|
|
28453
|
+
function isEnoent(err) {
|
|
28454
|
+
const code = err.code;
|
|
28455
|
+
if (code === "ENOENT") return true;
|
|
28456
|
+
return /\bENOENT\b/i.test(err.message ?? "");
|
|
28457
|
+
}
|
|
28458
|
+
function execJervCli(args, options = {}) {
|
|
28459
|
+
const env = options.env ?? process.env;
|
|
28460
|
+
const candidates = jervCliCandidatePaths(env);
|
|
28461
|
+
const plan = jervCliExecFileArgs(args, { env });
|
|
28462
|
+
return execFileP2(plan.file, plan.args, options).catch((err) => {
|
|
28463
|
+
if (!isEnoent(err)) throw err;
|
|
28464
|
+
const wrapped = new Error(formatJervCliSpawnFailure(err, plan, candidates));
|
|
28465
|
+
Object.assign(wrapped, {
|
|
28466
|
+
code: "ENOENT",
|
|
28467
|
+
stderr: err.stderr,
|
|
28468
|
+
cause: err
|
|
28469
|
+
});
|
|
28470
|
+
throw wrapped;
|
|
28471
|
+
});
|
|
28472
|
+
}
|
|
28473
|
+
|
|
28197
28474
|
// src/merge-cleanup.ts
|
|
28198
28475
|
var GC_GH_TIMEOUT_MS2 = 2e4;
|
|
28199
28476
|
async function advanceClosedIssuesToDone2(prNumber, repoOption) {
|
|
@@ -28312,7 +28589,8 @@ function assertPrMergeHousekeepingClean(startingPath, context, options = {}) {
|
|
|
28312
28589
|
if (verdict.blocked) throw new Error(verdict.reason);
|
|
28313
28590
|
return housekeeping;
|
|
28314
28591
|
}
|
|
28315
|
-
|
|
28592
|
+
var defaultLeaseCloseExec = (_cmd, args) => execJervCli(args, { timeout: GIT_TIMEOUT_MS });
|
|
28593
|
+
async function bestEffortLeaseClose(wtPath, exec = defaultLeaseCloseExec) {
|
|
28316
28594
|
const step = "close jerv worktree lease";
|
|
28317
28595
|
try {
|
|
28318
28596
|
await exec("jerv-cli", ["lease", "close", "--ref", wtPath]);
|
|
@@ -28335,7 +28613,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
28335
28613
|
);
|
|
28336
28614
|
const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
28337
28615
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
28338
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
28616
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path34.dirname)((0, import_node_path34.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
28339
28617
|
const gcActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
|
|
28340
28618
|
const owners = readWorktreeOwners(primaryRepoRoot);
|
|
28341
28619
|
const removalNow = Date.now();
|
|
@@ -28366,7 +28644,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
28366
28644
|
const cleanup = await cleanupPrMergeLocalBranch(branch.branch, {
|
|
28367
28645
|
beforeWorktrees,
|
|
28368
28646
|
startingPath: branch.worktreePath,
|
|
28369
|
-
pathExists: (p) => (0,
|
|
28647
|
+
pathExists: (p) => (0, import_node_fs35.existsSync)(p),
|
|
28370
28648
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
28371
28649
|
teardownWorktreeStage,
|
|
28372
28650
|
deferredStore,
|
|
@@ -28395,7 +28673,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
28395
28673
|
let removalAttempted = false;
|
|
28396
28674
|
try {
|
|
28397
28675
|
const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, siblingRoot, {
|
|
28398
|
-
realpath: (path2) => (0,
|
|
28676
|
+
realpath: (path2) => (0, import_node_fs35.realpathSync)(path2)
|
|
28399
28677
|
});
|
|
28400
28678
|
if (!cleanupTarget.ok) {
|
|
28401
28679
|
result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
|
|
@@ -28482,13 +28760,13 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
|
|
|
28482
28760
|
const commits = JSON.parse(raw).commits ?? [];
|
|
28483
28761
|
const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
|
|
28484
28762
|
if (!body) return void 0;
|
|
28485
|
-
const dir = (0,
|
|
28486
|
-
const path2 = (0,
|
|
28487
|
-
(0,
|
|
28763
|
+
const dir = (0, import_node_fs35.mkdtempSync)((0, import_node_path34.join)((0, import_node_os13.tmpdir)(), "mmi-squash-body-"));
|
|
28764
|
+
const path2 = (0, import_node_path34.join)(dir, "body.txt");
|
|
28765
|
+
(0, import_node_fs35.writeFileSync)(path2, `${body}
|
|
28488
28766
|
`, "utf8");
|
|
28489
28767
|
return { path: path2, cleanup: () => {
|
|
28490
28768
|
try {
|
|
28491
|
-
(0,
|
|
28769
|
+
(0, import_node_fs35.rmSync)(dir, { recursive: true, force: true });
|
|
28492
28770
|
} catch {
|
|
28493
28771
|
}
|
|
28494
28772
|
} };
|
|
@@ -28610,13 +28888,13 @@ var realWorktreeDirRemover = {
|
|
|
28610
28888
|
probe: (p) => {
|
|
28611
28889
|
let st;
|
|
28612
28890
|
try {
|
|
28613
|
-
st = (0,
|
|
28891
|
+
st = (0, import_node_fs35.lstatSync)(p);
|
|
28614
28892
|
} catch {
|
|
28615
28893
|
return null;
|
|
28616
28894
|
}
|
|
28617
28895
|
if (st.isSymbolicLink()) return "link";
|
|
28618
28896
|
try {
|
|
28619
|
-
(0,
|
|
28897
|
+
(0, import_node_fs35.readlinkSync)(p);
|
|
28620
28898
|
return "link";
|
|
28621
28899
|
} catch {
|
|
28622
28900
|
}
|
|
@@ -28624,7 +28902,7 @@ var realWorktreeDirRemover = {
|
|
|
28624
28902
|
},
|
|
28625
28903
|
readdir: (p) => {
|
|
28626
28904
|
try {
|
|
28627
|
-
return (0,
|
|
28905
|
+
return (0, import_node_fs35.readdirSync)(p);
|
|
28628
28906
|
} catch {
|
|
28629
28907
|
return [];
|
|
28630
28908
|
}
|
|
@@ -28633,9 +28911,9 @@ var realWorktreeDirRemover = {
|
|
|
28633
28911
|
// leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
|
|
28634
28912
|
detachLink: (p) => {
|
|
28635
28913
|
try {
|
|
28636
|
-
(0,
|
|
28914
|
+
(0, import_node_fs35.rmdirSync)(p);
|
|
28637
28915
|
} catch {
|
|
28638
|
-
(0,
|
|
28916
|
+
(0, import_node_fs35.unlinkSync)(p);
|
|
28639
28917
|
}
|
|
28640
28918
|
},
|
|
28641
28919
|
removeTree: (p) => (0, import_promises8.rm)(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
|
|
@@ -28668,9 +28946,9 @@ async function worktreeHasStageState(worktreePath) {
|
|
|
28668
28946
|
}
|
|
28669
28947
|
}
|
|
28670
28948
|
function stageStateFileBelongsToWorktree(statePath, worktreePath) {
|
|
28671
|
-
if (!(0,
|
|
28949
|
+
if (!(0, import_node_fs35.existsSync)(statePath)) return false;
|
|
28672
28950
|
try {
|
|
28673
|
-
const state = JSON.parse((0,
|
|
28951
|
+
const state = JSON.parse((0, import_node_fs35.readFileSync)(statePath, "utf8"));
|
|
28674
28952
|
const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
|
|
28675
28953
|
return Boolean(recordedCwd && isPathUnderDirectory(recordedCwd, worktreePath));
|
|
28676
28954
|
} catch {
|
|
@@ -29001,9 +29279,9 @@ async function checkDocsIndexAtHead(opts, deps) {
|
|
|
29001
29279
|
}
|
|
29002
29280
|
|
|
29003
29281
|
// src/worktree-lifecycle-commands.ts
|
|
29004
|
-
var
|
|
29282
|
+
var import_node_fs36 = require("node:fs");
|
|
29005
29283
|
var import_promises9 = require("node:fs/promises");
|
|
29006
|
-
var
|
|
29284
|
+
var import_node_path35 = require("node:path");
|
|
29007
29285
|
var GH_TIMEOUT_MS = 2e4;
|
|
29008
29286
|
var STALE_PR_LOOKUP_LIMIT = 20;
|
|
29009
29287
|
var DEFAULT_BASE = "origin/development";
|
|
@@ -29150,7 +29428,7 @@ function classifyStaleLeaks(input) {
|
|
|
29150
29428
|
var defaultOrphanDirScanDeps = {
|
|
29151
29429
|
listDirs: (root) => {
|
|
29152
29430
|
try {
|
|
29153
|
-
return (0,
|
|
29431
|
+
return (0, import_node_fs36.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path35.join)(root, e.name));
|
|
29154
29432
|
} catch {
|
|
29155
29433
|
return [];
|
|
29156
29434
|
}
|
|
@@ -29306,13 +29584,13 @@ function registerWorktreeCommands(program3) {
|
|
|
29306
29584
|
const detached = headBorn && !symbolicBranch;
|
|
29307
29585
|
const branch = symbolicBranch || (detached ? "HEAD" : "");
|
|
29308
29586
|
if (!wtPath || !branch) return fail("worktree land: not inside a git worktree");
|
|
29309
|
-
const gitFile = (0,
|
|
29310
|
-
const isLinked = (0,
|
|
29587
|
+
const gitFile = (0, import_node_path35.join)(wtPath, ".git");
|
|
29588
|
+
const isLinked = (0, import_node_fs36.existsSync)(gitFile) && (0, import_node_fs36.statSync)(gitFile).isFile();
|
|
29311
29589
|
if (apply && !isLinked) {
|
|
29312
29590
|
return fail("worktree land: run from inside the linked worktree you want to land (this is the primary checkout)");
|
|
29313
29591
|
}
|
|
29314
29592
|
const commonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
29315
|
-
const primaryCheckout = commonDir ? (0,
|
|
29593
|
+
const primaryCheckout = commonDir ? (0, import_node_path35.dirname)(commonDir) : wtPath;
|
|
29316
29594
|
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);
|
|
29317
29595
|
const orphan = classifyOrphanedWorktree({
|
|
29318
29596
|
branch,
|
|
@@ -29521,10 +29799,10 @@ async function gatherWorktreeContext() {
|
|
|
29521
29799
|
if (s) stages.push({ path: wt.path, port: s.port });
|
|
29522
29800
|
}
|
|
29523
29801
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
29524
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
29802
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path35.dirname)((0, import_node_path35.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
29525
29803
|
const wtRoot = siblingMmiWorktreesRoot(primaryRepoRoot);
|
|
29526
29804
|
let orphanDirs = [];
|
|
29527
|
-
if ((0,
|
|
29805
|
+
if ((0, import_node_fs36.existsSync)(wtRoot)) {
|
|
29528
29806
|
orphanDirs = scanOrphanDirs(wtRoot, worktreeGitRoot, {
|
|
29529
29807
|
...defaultOrphanDirScanDeps,
|
|
29530
29808
|
listDirs: (root) => worktreeScanDirs(root, primaryRepoRoot, defaultOrphanDirScanDeps.listDirs, isRepoCheckoutDir)
|
|
@@ -29550,7 +29828,7 @@ ${err.stderr ?? ""}`;
|
|
|
29550
29828
|
}
|
|
29551
29829
|
|
|
29552
29830
|
// src/issue-commands.ts
|
|
29553
|
-
var
|
|
29831
|
+
var import_node_fs37 = require("node:fs");
|
|
29554
29832
|
var import_node_crypto7 = require("node:crypto");
|
|
29555
29833
|
var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
|
|
29556
29834
|
var ReparentConflictError = class extends Error {
|
|
@@ -29568,7 +29846,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
29568
29846
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
29569
29847
|
const patch = {};
|
|
29570
29848
|
let bodyChanged = false;
|
|
29571
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
29849
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs37.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
29572
29850
|
if (options.titleFile !== void 0) {
|
|
29573
29851
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
29574
29852
|
} else if (options.title !== void 0) {
|
|
@@ -30173,7 +30451,7 @@ function extendCreateCommand(issue2, batchAttach) {
|
|
|
30173
30451
|
if (opts.batch) {
|
|
30174
30452
|
let specs;
|
|
30175
30453
|
try {
|
|
30176
|
-
const raw = (0,
|
|
30454
|
+
const raw = (0, import_node_fs37.readFileSync)(opts.batch, "utf8");
|
|
30177
30455
|
specs = JSON.parse(raw);
|
|
30178
30456
|
if (!Array.isArray(specs)) throw new Error("batch file must contain a JSON array");
|
|
30179
30457
|
} catch (e) {
|
|
@@ -30248,8 +30526,8 @@ ${lines}`, {
|
|
|
30248
30526
|
}
|
|
30249
30527
|
|
|
30250
30528
|
// src/train-commands.ts
|
|
30251
|
-
var
|
|
30252
|
-
var
|
|
30529
|
+
var import_node_fs38 = require("node:fs");
|
|
30530
|
+
var import_node_path36 = require("node:path");
|
|
30253
30531
|
|
|
30254
30532
|
// src/train-status.ts
|
|
30255
30533
|
function buildTrainStatusReport(input) {
|
|
@@ -30289,7 +30567,7 @@ function formatTrainStatus(r) {
|
|
|
30289
30567
|
// src/train-commands.ts
|
|
30290
30568
|
function readRepoVersion() {
|
|
30291
30569
|
try {
|
|
30292
|
-
return JSON.parse((0,
|
|
30570
|
+
return JSON.parse((0, import_node_fs38.readFileSync)((0, import_node_path36.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
30293
30571
|
} catch {
|
|
30294
30572
|
return void 0;
|
|
30295
30573
|
}
|
|
@@ -30435,9 +30713,9 @@ function registerDeployCommands(program3) {
|
|
|
30435
30713
|
}
|
|
30436
30714
|
|
|
30437
30715
|
// src/discovery-commands.ts
|
|
30438
|
-
var
|
|
30439
|
-
var
|
|
30440
|
-
var
|
|
30716
|
+
var import_node_fs39 = require("node:fs");
|
|
30717
|
+
var import_node_os14 = require("node:os");
|
|
30718
|
+
var import_node_path37 = require("node:path");
|
|
30441
30719
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
30442
30720
|
async function collectStatus() {
|
|
30443
30721
|
const repo = await resolveRepo();
|
|
@@ -30625,10 +30903,10 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
30625
30903
|
else if (top) nextCommand = `mmi-cli board claim ${top.number} # ${top.title}`;
|
|
30626
30904
|
else nextCommand = "mmi-cli board read \u2014 no claimable items found";
|
|
30627
30905
|
}
|
|
30628
|
-
const home = (0,
|
|
30906
|
+
const home = (0, import_node_os14.homedir)();
|
|
30629
30907
|
const plugin = onboardPluginGate({
|
|
30630
|
-
readKnown: () => readFileSyncSafe((0,
|
|
30631
|
-
readSettings: () => readFileSyncSafe((0,
|
|
30908
|
+
readKnown: () => readFileSyncSafe((0, import_node_path37.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs39.readFileSync),
|
|
30909
|
+
readSettings: () => readFileSyncSafe((0, import_node_path37.join)(home, ".claude", "settings.json"), import_node_fs39.readFileSync)
|
|
30632
30910
|
});
|
|
30633
30911
|
return { track, board, registry: registry2, secrets, plugin, estateCli, nextCommand };
|
|
30634
30912
|
}
|
|
@@ -31532,19 +31810,19 @@ function registerSessionReport(program3) {
|
|
|
31532
31810
|
}
|
|
31533
31811
|
|
|
31534
31812
|
// src/plugin-release-catchup.ts
|
|
31535
|
-
var
|
|
31536
|
-
var
|
|
31537
|
-
var
|
|
31813
|
+
var import_node_fs40 = require("node:fs");
|
|
31814
|
+
var import_node_path38 = require("node:path");
|
|
31815
|
+
var import_node_os15 = require("node:os");
|
|
31538
31816
|
var RELEASE_CATCHUP_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
31539
31817
|
var RELEASE_CATCHUP_DISABLE_ENV = "MMI_NO_RELEASE_CATCHUP";
|
|
31540
31818
|
function releaseCatchupStatePath(env = process.env) {
|
|
31541
31819
|
if (env.MMI_RELEASE_CATCHUP_STATE) return env.MMI_RELEASE_CATCHUP_STATE;
|
|
31542
31820
|
if (process.platform === "win32") {
|
|
31543
|
-
const base2 = env.LOCALAPPDATA || (0,
|
|
31544
|
-
return (0,
|
|
31821
|
+
const base2 = env.LOCALAPPDATA || (0, import_node_path38.join)((0, import_node_os15.homedir)(), "AppData", "Local");
|
|
31822
|
+
return (0, import_node_path38.join)(base2, "MMI Future", "mmi-cli", "release-catchup.json");
|
|
31545
31823
|
}
|
|
31546
|
-
const base = env.XDG_STATE_HOME || (0,
|
|
31547
|
-
return (0,
|
|
31824
|
+
const base = env.XDG_STATE_HOME || (0, import_node_path38.join)((0, import_node_os15.homedir)(), ".local", "state");
|
|
31825
|
+
return (0, import_node_path38.join)(base, "mmi-cli", "release-catchup.json");
|
|
31548
31826
|
}
|
|
31549
31827
|
function releaseCatchupDue(state, now, force = false) {
|
|
31550
31828
|
if (force) return true;
|
|
@@ -31554,7 +31832,7 @@ function releaseCatchupDue(state, now, force = false) {
|
|
|
31554
31832
|
function newestCachedPluginVersion(home) {
|
|
31555
31833
|
let names;
|
|
31556
31834
|
try {
|
|
31557
|
-
names = (0,
|
|
31835
|
+
names = (0, import_node_fs40.readdirSync)(pluginCacheRoot(home));
|
|
31558
31836
|
} catch {
|
|
31559
31837
|
return void 0;
|
|
31560
31838
|
}
|
|
@@ -31562,15 +31840,15 @@ function newestCachedPluginVersion(home) {
|
|
|
31562
31840
|
}
|
|
31563
31841
|
function marketplaceClonePath(home) {
|
|
31564
31842
|
try {
|
|
31565
|
-
const parsed = JSON.parse((0,
|
|
31843
|
+
const parsed = JSON.parse((0, import_node_fs40.readFileSync)((0, import_node_path38.join)(home, ".claude", "plugins", "known_marketplaces.json"), "utf8"));
|
|
31566
31844
|
if (parsed.mutmutco?.installLocation) return parsed.mutmutco.installLocation;
|
|
31567
31845
|
} catch {
|
|
31568
31846
|
}
|
|
31569
|
-
return (0,
|
|
31847
|
+
return (0, import_node_path38.join)(home, ".claude", "plugins", "marketplaces", "mutmutco");
|
|
31570
31848
|
}
|
|
31571
31849
|
function readCatalogVersion(home) {
|
|
31572
31850
|
try {
|
|
31573
|
-
const parsed = JSON.parse((0,
|
|
31851
|
+
const parsed = JSON.parse((0, import_node_fs40.readFileSync)((0, import_node_path38.join)(marketplaceClonePath(home), ".claude-plugin", "marketplace.json"), "utf8"));
|
|
31574
31852
|
return parsed.plugins?.find((p) => p.name === "mmi")?.version;
|
|
31575
31853
|
} catch {
|
|
31576
31854
|
return void 0;
|
|
@@ -31578,7 +31856,7 @@ function readCatalogVersion(home) {
|
|
|
31578
31856
|
}
|
|
31579
31857
|
function readMmiInstallRecord(home) {
|
|
31580
31858
|
try {
|
|
31581
|
-
const parsed = JSON.parse((0,
|
|
31859
|
+
const parsed = JSON.parse((0, import_node_fs40.readFileSync)((0, import_node_path38.join)(home, ".claude", "plugins", "installed_plugins.json"), "utf8"));
|
|
31582
31860
|
const record = parsed.plugins?.["mmi@mutmutco"]?.[0];
|
|
31583
31861
|
return record?.version ? { version: record.version, gitCommitSha: record.gitCommitSha } : void 0;
|
|
31584
31862
|
} catch {
|
|
@@ -31587,7 +31865,7 @@ function readMmiInstallRecord(home) {
|
|
|
31587
31865
|
}
|
|
31588
31866
|
async function runReleaseCatchup(home, env, deps, opts = {}) {
|
|
31589
31867
|
if (env[RELEASE_CATCHUP_DISABLE_ENV]) return { ok: true, skipped: true, detail: `disabled via ${RELEASE_CATCHUP_DISABLE_ENV}` };
|
|
31590
|
-
if (!(0,
|
|
31868
|
+
if (!(0, import_node_fs40.existsSync)(pluginCacheRoot(home))) return { ok: true, skipped: true, detail: "no mmi plugin cache on this machine \u2014 nothing to catch up" };
|
|
31591
31869
|
const statePath = releaseCatchupStatePath(env);
|
|
31592
31870
|
const state = deps.readState(statePath);
|
|
31593
31871
|
if (!releaseCatchupDue(state, deps.now(), opts.force)) {
|
|
@@ -31612,8 +31890,8 @@ async function runReleaseCatchup(home, env, deps, opts = {}) {
|
|
|
31612
31890
|
return { ok: false, detail: `released ${latest} but the install record could not be cleared \u2014 nothing changed (record still ${prior.version})` };
|
|
31613
31891
|
}
|
|
31614
31892
|
const installed = await deps.runClaude(["plugin", "install", "mmi@mutmutco"], "claude plugin install mmi@mutmutco");
|
|
31615
|
-
const payload = (0,
|
|
31616
|
-
if (!installed || !(0,
|
|
31893
|
+
const payload = (0, import_node_path38.join)(pluginCacheRoot(home), latest, ".pi-plugin");
|
|
31894
|
+
if (!installed || !(0, import_node_fs40.existsSync)(payload)) {
|
|
31617
31895
|
const why = installed ? `install of ${latest} did not produce a verifiable .pi-plugin payload` : `install of ${latest} failed`;
|
|
31618
31896
|
if (!prior) return { ok: false, detail: `${why}; no prior record to restore` };
|
|
31619
31897
|
const rollback = await restorePriorRecord(home, prior, deps);
|
|
@@ -31641,7 +31919,7 @@ async function restorePriorRecord(home, prior, deps) {
|
|
|
31641
31919
|
}
|
|
31642
31920
|
function shouldSpawnReleaseCatchup(home, env, readState2, now = Date.now()) {
|
|
31643
31921
|
if (env[RELEASE_CATCHUP_DISABLE_ENV]) return false;
|
|
31644
|
-
if (!(0,
|
|
31922
|
+
if (!(0, import_node_fs40.existsSync)(pluginCacheRoot(home))) return false;
|
|
31645
31923
|
return releaseCatchupDue(readState2(releaseCatchupStatePath(env)), now);
|
|
31646
31924
|
}
|
|
31647
31925
|
function defaultRegistrationHeal(home, env) {
|
|
@@ -33687,17 +33965,17 @@ function parseOriginRepo(remoteUrl) {
|
|
|
33687
33965
|
}
|
|
33688
33966
|
function ghHostsConfigPath(env, platform2) {
|
|
33689
33967
|
const sep3 = platform2 === "win32" ? "\\" : "/";
|
|
33690
|
-
const
|
|
33968
|
+
const join36 = (...parts) => parts.join(sep3);
|
|
33691
33969
|
const explicit = env.GH_CONFIG_DIR?.trim();
|
|
33692
|
-
if (explicit) return
|
|
33970
|
+
if (explicit) return join36(explicit, "hosts.yml");
|
|
33693
33971
|
if (platform2 === "win32") {
|
|
33694
33972
|
const appData = (env.AppData ?? env.APPDATA)?.trim();
|
|
33695
|
-
return appData ?
|
|
33973
|
+
return appData ? join36(appData, "GitHub CLI", "hosts.yml") : void 0;
|
|
33696
33974
|
}
|
|
33697
33975
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
33698
|
-
if (xdg) return
|
|
33976
|
+
if (xdg) return join36(xdg, "gh", "hosts.yml");
|
|
33699
33977
|
const home = env.HOME?.trim();
|
|
33700
|
-
return home ?
|
|
33978
|
+
return home ? join36(home, ".config", "gh", "hosts.yml") : void 0;
|
|
33701
33979
|
}
|
|
33702
33980
|
function parseGhHostsAccounts(yaml, host = "github.com") {
|
|
33703
33981
|
let hostIndent = null;
|
|
@@ -33747,9 +34025,9 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
33747
34025
|
}
|
|
33748
34026
|
|
|
33749
34027
|
// src/doctor-io.ts
|
|
33750
|
-
var
|
|
33751
|
-
var
|
|
33752
|
-
var
|
|
34028
|
+
var import_node_fs41 = require("node:fs");
|
|
34029
|
+
var import_node_os16 = require("node:os");
|
|
34030
|
+
var import_node_path39 = require("node:path");
|
|
33753
34031
|
var import_node_child_process18 = require("node:child_process");
|
|
33754
34032
|
var import_node_util8 = require("node:util");
|
|
33755
34033
|
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process18.execFile);
|
|
@@ -33757,7 +34035,7 @@ var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
|
33757
34035
|
function installedClaudePluginVersion() {
|
|
33758
34036
|
try {
|
|
33759
34037
|
const file = JSON.parse(
|
|
33760
|
-
(0,
|
|
34038
|
+
(0, import_node_fs41.readFileSync)((0, import_node_path39.join)((0, import_node_os16.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
33761
34039
|
);
|
|
33762
34040
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
33763
34041
|
if (versions.length === 0) return void 0;
|
|
@@ -33768,7 +34046,7 @@ function installedClaudePluginVersion() {
|
|
|
33768
34046
|
}
|
|
33769
34047
|
function manifestVersion(path2) {
|
|
33770
34048
|
try {
|
|
33771
|
-
const manifest = JSON.parse((0,
|
|
34049
|
+
const manifest = JSON.parse((0, import_node_fs41.readFileSync)(path2, "utf8"));
|
|
33772
34050
|
return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : void 0;
|
|
33773
34051
|
} catch {
|
|
33774
34052
|
return void 0;
|
|
@@ -33778,22 +34056,22 @@ function installedSurfacePluginVersion(surface) {
|
|
|
33778
34056
|
const token = surfaceToken(surface);
|
|
33779
34057
|
if (token === "kilo") {
|
|
33780
34058
|
try {
|
|
33781
|
-
const stamp = (0,
|
|
34059
|
+
const stamp = (0, import_node_fs41.readFileSync)((0, import_node_path39.join)((0, import_node_os16.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
|
|
33782
34060
|
return stamp || void 0;
|
|
33783
34061
|
} catch {
|
|
33784
34062
|
return void 0;
|
|
33785
34063
|
}
|
|
33786
34064
|
}
|
|
33787
34065
|
if (token === "cursor") {
|
|
33788
|
-
return manifestVersion((0,
|
|
34066
|
+
return manifestVersion((0, import_node_path39.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
|
|
33789
34067
|
}
|
|
33790
34068
|
if (token === "jervcode") {
|
|
33791
34069
|
const entry = mmiPiWrapperEntry();
|
|
33792
34070
|
if (!entry) return void 0;
|
|
33793
|
-
return manifestVersion((0,
|
|
34071
|
+
return manifestVersion((0, import_node_path39.join)(decodeURIComponent(entry.replace(/^file:\/\/\/?/, "")), "package.json"));
|
|
33794
34072
|
}
|
|
33795
34073
|
if (token === "kimi") {
|
|
33796
|
-
return manifestVersion((0,
|
|
34074
|
+
return manifestVersion((0, import_node_path39.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
|
|
33797
34075
|
}
|
|
33798
34076
|
if (token === "claude") return installedClaudePluginVersion();
|
|
33799
34077
|
if (token !== "codex") return void 0;
|
|
@@ -33831,13 +34109,13 @@ function worktreeRootSync() {
|
|
|
33831
34109
|
}
|
|
33832
34110
|
var gitignorePath = () => {
|
|
33833
34111
|
const root = worktreeRootSync();
|
|
33834
|
-
return root === null ? null : (0,
|
|
34112
|
+
return root === null ? null : (0, import_node_path39.join)(root, ".gitignore");
|
|
33835
34113
|
};
|
|
33836
34114
|
function readGitignore() {
|
|
33837
34115
|
const path2 = gitignorePath();
|
|
33838
34116
|
if (path2 === null) return null;
|
|
33839
34117
|
try {
|
|
33840
|
-
return (0,
|
|
34118
|
+
return (0, import_node_fs41.readFileSync)(path2, "utf8");
|
|
33841
34119
|
} catch {
|
|
33842
34120
|
return null;
|
|
33843
34121
|
}
|
|
@@ -33846,7 +34124,7 @@ function writeGitignore(content) {
|
|
|
33846
34124
|
const path2 = gitignorePath();
|
|
33847
34125
|
if (path2 === null) return false;
|
|
33848
34126
|
try {
|
|
33849
|
-
(0,
|
|
34127
|
+
(0, import_node_fs41.writeFileSync)(path2, content, "utf8");
|
|
33850
34128
|
return true;
|
|
33851
34129
|
} catch {
|
|
33852
34130
|
return false;
|
|
@@ -33870,7 +34148,7 @@ async function repoRoot() {
|
|
|
33870
34148
|
}
|
|
33871
34149
|
function hasRepoLocalWorktrees() {
|
|
33872
34150
|
const root = worktreeRootSync();
|
|
33873
|
-
return root !== null && (0,
|
|
34151
|
+
return root !== null && (0, import_node_fs41.existsSync)((0, import_node_path39.join)(root, ".worktrees"));
|
|
33874
34152
|
}
|
|
33875
34153
|
|
|
33876
34154
|
// src/index.ts
|
|
@@ -33889,8 +34167,8 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
33889
34167
|
function ghMultiAccountCaveat(announcedLogin) {
|
|
33890
34168
|
try {
|
|
33891
34169
|
const hostsPath = ghHostsConfigPath(process.env, process.platform);
|
|
33892
|
-
if (!hostsPath || !(0,
|
|
33893
|
-
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0,
|
|
34170
|
+
if (!hostsPath || !(0, import_node_fs42.existsSync)(hostsPath)) return void 0;
|
|
34171
|
+
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs42.readFileSync)(hostsPath, "utf8")));
|
|
33894
34172
|
} catch {
|
|
33895
34173
|
return void 0;
|
|
33896
34174
|
}
|
|
@@ -33898,12 +34176,12 @@ function ghMultiAccountCaveat(announcedLogin) {
|
|
|
33898
34176
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
33899
34177
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
33900
34178
|
function envHealLockPath(home) {
|
|
33901
|
-
return (0,
|
|
34179
|
+
return (0, import_node_path40.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
33902
34180
|
}
|
|
33903
34181
|
async function withEnvHealLock(what, run) {
|
|
33904
34182
|
try {
|
|
33905
34183
|
return await withFileLock(
|
|
33906
|
-
envHealLockPath((0,
|
|
34184
|
+
envHealLockPath((0, import_node_os17.homedir)()),
|
|
33907
34185
|
{ staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
|
|
33908
34186
|
run
|
|
33909
34187
|
);
|
|
@@ -34000,7 +34278,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
34000
34278
|
const configRoot = surfaceConfigRoot(surface);
|
|
34001
34279
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
34002
34280
|
const plan = buildPluginCachePlan(
|
|
34003
|
-
(0,
|
|
34281
|
+
(0, import_node_os17.homedir)(),
|
|
34004
34282
|
running,
|
|
34005
34283
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
34006
34284
|
{ configRoot, includeStaging: surface !== "codex" }
|
|
@@ -34024,14 +34302,14 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
34024
34302
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
34025
34303
|
const installed = installedActivePluginVersion(surface);
|
|
34026
34304
|
const plan = buildPluginCachePlan(
|
|
34027
|
-
(0,
|
|
34305
|
+
(0, import_node_os17.homedir)(),
|
|
34028
34306
|
running,
|
|
34029
34307
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
34030
34308
|
{ configRoot, includeStaging: surface !== "codex", installedVersion: installed }
|
|
34031
34309
|
);
|
|
34032
34310
|
const result = applyPluginCachePlan(
|
|
34033
34311
|
plan,
|
|
34034
|
-
(p) => (0,
|
|
34312
|
+
(p) => (0, import_node_fs42.rmSync)(p, { recursive: true }),
|
|
34035
34313
|
stagingApplyFsGuard(configRoot)
|
|
34036
34314
|
);
|
|
34037
34315
|
return {
|
|
@@ -34059,12 +34337,12 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
34059
34337
|
piPluginState: () => {
|
|
34060
34338
|
const env = { ...process.env };
|
|
34061
34339
|
delete env.CLAUDE_PLUGIN_ROOT;
|
|
34062
|
-
return readPiPluginState((0,
|
|
34340
|
+
return readPiPluginState((0, import_node_os17.homedir)(), env);
|
|
34063
34341
|
},
|
|
34064
34342
|
healPiPlugin: () => {
|
|
34065
34343
|
const env = { ...process.env };
|
|
34066
34344
|
delete env.CLAUDE_PLUGIN_ROOT;
|
|
34067
|
-
return healPiPluginRegistration((0,
|
|
34345
|
+
return healPiPluginRegistration((0, import_node_os17.homedir)(), env);
|
|
34068
34346
|
},
|
|
34069
34347
|
// #3470: what the SessionStart hook LAST emitted, measured at emission by the session-start verb below.
|
|
34070
34348
|
// A local record read ? cheap enough for every lane, including the banner.
|
|
@@ -34074,17 +34352,17 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
34074
34352
|
marketplaceRows: () => {
|
|
34075
34353
|
try {
|
|
34076
34354
|
if (detectSurface(process.env) === "codex") return [];
|
|
34077
|
-
const home = (0,
|
|
34355
|
+
const home = (0, import_node_os17.homedir)();
|
|
34078
34356
|
const rows = marketplaceRows(
|
|
34079
34357
|
MMI_MARKETPLACE_NAME,
|
|
34080
|
-
readFileSyncSafe((0,
|
|
34081
|
-
readFileSyncSafe((0,
|
|
34358
|
+
readFileSyncSafe((0, import_node_path40.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs42.readFileSync),
|
|
34359
|
+
readFileSyncSafe((0, import_node_path40.join)(home, ".claude", "settings.json"), import_node_fs42.readFileSync),
|
|
34082
34360
|
// #3974: this CLI heals these rows, so report mode names the doctor run rather than the hand
|
|
34083
34361
|
// edit. Unconditional ? it is a fact about mmi-cli, not about the lane this run is on.
|
|
34084
34362
|
true
|
|
34085
34363
|
);
|
|
34086
34364
|
const pending = readMarketplacePinPending(
|
|
34087
|
-
(0,
|
|
34365
|
+
(0, import_node_path40.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"),
|
|
34088
34366
|
MMI_MARKETPLACE_NAME
|
|
34089
34367
|
);
|
|
34090
34368
|
if (!pending) return rows;
|
|
@@ -34108,11 +34386,11 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
34108
34386
|
healMarketplacePins: () => {
|
|
34109
34387
|
try {
|
|
34110
34388
|
if (detectSurface(process.env) === "codex") return void 0;
|
|
34111
|
-
const home = (0,
|
|
34389
|
+
const home = (0, import_node_os17.homedir)();
|
|
34112
34390
|
const names = [MMI_MARKETPLACE_NAME];
|
|
34113
|
-
const result = applyOrgMarketplacePins((0,
|
|
34391
|
+
const result = applyOrgMarketplacePins((0, import_node_path40.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), names);
|
|
34114
34392
|
if (result?.wrote) {
|
|
34115
|
-
writeMarketplacePinPending((0,
|
|
34393
|
+
writeMarketplacePinPending((0, import_node_path40.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"), names);
|
|
34116
34394
|
}
|
|
34117
34395
|
return result;
|
|
34118
34396
|
} catch {
|
|
@@ -34128,7 +34406,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
34128
34406
|
// adopted the generated routing index (MMG-Unlive: `docs/Archive/**` only, no index, no gate.yml) would
|
|
34129
34407
|
// get a permanent ? demanding an artifact it never asked for.
|
|
34130
34408
|
docsIndexState: (root) => {
|
|
34131
|
-
if (!(0,
|
|
34409
|
+
if (!(0, import_node_fs42.existsSync)((0, import_node_path40.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
34132
34410
|
const real = createDocsIndexDeps(root);
|
|
34133
34411
|
let docs2;
|
|
34134
34412
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -34137,7 +34415,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
34137
34415
|
},
|
|
34138
34416
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
34139
34417
|
healDocsIndex: (root) => {
|
|
34140
|
-
if (!(0,
|
|
34418
|
+
if (!(0, import_node_fs42.existsSync)((0, import_node_path40.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
34141
34419
|
const real = createDocsIndexDeps(root);
|
|
34142
34420
|
let docs2;
|
|
34143
34421
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -34472,19 +34750,19 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
34472
34750
|
});
|
|
34473
34751
|
var rules = program2.command("rules").description("org-managed .gitignore delivery");
|
|
34474
34752
|
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) => {
|
|
34475
|
-
const path2 = (0,
|
|
34476
|
-
const current = (0,
|
|
34753
|
+
const path2 = (0, import_node_path40.join)(process.cwd(), ".gitignore");
|
|
34754
|
+
const current = (0, import_node_fs42.existsSync)(path2) ? (0, import_node_fs42.readFileSync)(path2, "utf8") : null;
|
|
34477
34755
|
const plan = planManagedGitignore(current);
|
|
34478
34756
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
34479
34757
|
if (opts.json) {
|
|
34480
|
-
if (opts.write && plan.changed) (0,
|
|
34758
|
+
if (opts.write && plan.changed) (0, import_node_fs42.writeFileSync)(path2, plan.content, "utf8");
|
|
34481
34759
|
console.log(JSON.stringify(plan, null, 2));
|
|
34482
34760
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
34483
34761
|
return;
|
|
34484
34762
|
}
|
|
34485
34763
|
if (opts.write) {
|
|
34486
34764
|
if (plan.changed) {
|
|
34487
|
-
(0,
|
|
34765
|
+
(0, import_node_fs42.writeFileSync)(path2, plan.content, "utf8");
|
|
34488
34766
|
console.log(`mmi-cli org rules gitignore: updated .gitignore (${drift})`);
|
|
34489
34767
|
} else {
|
|
34490
34768
|
console.log("mmi-cli org rules gitignore: up to date");
|
|
@@ -34642,8 +34920,8 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
|
|
|
34642
34920
|
if (!Number.isFinite(limit) || limit < 1) return fail("worktree gc: --limit must be a positive integer");
|
|
34643
34921
|
let root;
|
|
34644
34922
|
if (o.root !== void 0) {
|
|
34645
|
-
root = (0,
|
|
34646
|
-
if (!(0,
|
|
34923
|
+
root = (0, import_node_path40.resolve)(o.root);
|
|
34924
|
+
if (!(0, import_node_fs42.existsSync)(root) || !(0, import_node_fs42.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
|
|
34647
34925
|
const gcRepoRoot = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
34648
34926
|
if (isPathUnderDirectory(gcRepoRoot, root)) {
|
|
34649
34927
|
return fail(`worktree gc: --root ${root} contains this checkout ? name a worktrees root, not the repo or an ancestor of it`);
|
|
@@ -34723,7 +35001,7 @@ async function currentWorktreeRemovalContext(command, force) {
|
|
|
34723
35001
|
};
|
|
34724
35002
|
}
|
|
34725
35003
|
async function unprovenWorktreeReason(wtPath, repoRoot2) {
|
|
34726
|
-
if (!(0,
|
|
35004
|
+
if (!(0, import_node_fs42.existsSync)(wtPath)) return `${wtPath} does not exist on disk`;
|
|
34727
35005
|
const porcelain = (await execFileP2("git", ["-C", repoRoot2, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
|
|
34728
35006
|
const registered = parseWorktreePorcelainEntries(porcelain);
|
|
34729
35007
|
if (!registered.length) {
|
|
@@ -34745,26 +35023,26 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
|
|
|
34745
35023
|
function acquireWorktreeSetupLock(worktreeRoot) {
|
|
34746
35024
|
const lockPath = repoRuntimeStatePath(worktreeRoot, "worktree-setup.lock");
|
|
34747
35025
|
const take = () => {
|
|
34748
|
-
const fd = (0,
|
|
35026
|
+
const fd = (0, import_node_fs42.openSync)(lockPath, "wx");
|
|
34749
35027
|
try {
|
|
34750
|
-
(0,
|
|
35028
|
+
(0, import_node_fs42.writeSync)(fd, String(Date.now()));
|
|
34751
35029
|
} finally {
|
|
34752
|
-
(0,
|
|
35030
|
+
(0, import_node_fs42.closeSync)(fd);
|
|
34753
35031
|
}
|
|
34754
35032
|
return () => {
|
|
34755
35033
|
try {
|
|
34756
|
-
(0,
|
|
35034
|
+
(0, import_node_fs42.rmSync)(lockPath, { force: true });
|
|
34757
35035
|
} catch {
|
|
34758
35036
|
}
|
|
34759
35037
|
};
|
|
34760
35038
|
};
|
|
34761
35039
|
try {
|
|
34762
|
-
(0,
|
|
35040
|
+
(0, import_node_fs42.mkdirSync)((0, import_node_path40.dirname)(lockPath), { recursive: true });
|
|
34763
35041
|
return take();
|
|
34764
35042
|
} catch {
|
|
34765
35043
|
try {
|
|
34766
|
-
if (Date.now() - (0,
|
|
34767
|
-
(0,
|
|
35044
|
+
if (Date.now() - (0, import_node_fs42.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
|
|
35045
|
+
(0, import_node_fs42.rmSync)(lockPath, { force: true });
|
|
34768
35046
|
return take();
|
|
34769
35047
|
}
|
|
34770
35048
|
} catch {
|
|
@@ -34790,6 +35068,11 @@ withExamples(mutating(
|
|
|
34790
35068
|
const resolvedRepo = await resolveRepo();
|
|
34791
35069
|
if (!resolvedRepo) return fail("worktree create: could not resolve the repo for the issue ref (run from a repo checkout)");
|
|
34792
35070
|
selector = parseIssueSelector(target, resolvedRepo);
|
|
35071
|
+
if (selector.repo.toLowerCase() !== resolvedRepo.toLowerCase()) {
|
|
35072
|
+
return fail(
|
|
35073
|
+
`worktree create: issue ${selector.repo}#${selector.number} belongs to ${selector.repo}, but this checkout is ${resolvedRepo} \u2014 re-run from ${selector.repo}'s primary checkout. Refusing to attach a foreign issue branch here (MMI-Hub#4351).`
|
|
35074
|
+
);
|
|
35075
|
+
}
|
|
34793
35076
|
const slug = o.slug ?? await fetchIssueTitle(selector.repo, selector.number).then((t) => t ? slugifyIssueTitle(t) : "");
|
|
34794
35077
|
branch = buildNewBranchName(selector.number, slug ?? "");
|
|
34795
35078
|
if (PROTECTED_BRANCHES2.has(branch)) {
|
|
@@ -34903,7 +35186,7 @@ withExamples(mutating(
|
|
|
34903
35186
|
appendWorktreeEvent(repoRoot2, { action: "created", command: "worktree create", target: wtPath, branch, actor: owner.actor });
|
|
34904
35187
|
let lease;
|
|
34905
35188
|
try {
|
|
34906
|
-
await
|
|
35189
|
+
await execJervCli([
|
|
34907
35190
|
"lease",
|
|
34908
35191
|
"open",
|
|
34909
35192
|
"--kind",
|
|
@@ -35612,7 +35895,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
35612
35895
|
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`);
|
|
35613
35896
|
if (o.secretsFile) {
|
|
35614
35897
|
try {
|
|
35615
|
-
vars.push(`secrets=${(0,
|
|
35898
|
+
vars.push(`secrets=${(0, import_node_fs42.readFileSync)(o.secretsFile, "utf8")}`);
|
|
35616
35899
|
} catch (e) {
|
|
35617
35900
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
35618
35901
|
}
|
|
@@ -36366,11 +36649,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
|
|
|
36366
36649
|
}
|
|
36367
36650
|
});
|
|
36368
36651
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
36369
|
-
const wfDir = (0,
|
|
36370
|
-
if (!(0,
|
|
36371
|
-
return (0,
|
|
36652
|
+
const wfDir = (0, import_node_path40.join)(cwd, ".github", "workflows");
|
|
36653
|
+
if (!(0, import_node_fs42.existsSync)(wfDir)) return [];
|
|
36654
|
+
return (0, import_node_fs42.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
36372
36655
|
try {
|
|
36373
|
-
return workflowReportsPrChecks((0,
|
|
36656
|
+
return workflowReportsPrChecks((0, import_node_fs42.readFileSync)((0, import_node_path40.join)(wfDir, name), "utf8"));
|
|
36374
36657
|
} catch {
|
|
36375
36658
|
return true;
|
|
36376
36659
|
}
|
|
@@ -36402,16 +36685,16 @@ function ciAuditDeps() {
|
|
|
36402
36685
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
36403
36686
|
readSeedFile: (path2) => {
|
|
36404
36687
|
if (!root) return null;
|
|
36405
|
-
const fullPath = (0,
|
|
36406
|
-
return (0,
|
|
36688
|
+
const fullPath = (0, import_node_path40.join)(root, path2);
|
|
36689
|
+
return (0, import_node_fs42.existsSync)(fullPath) ? (0, import_node_fs42.readFileSync)(fullPath, "utf8") : null;
|
|
36407
36690
|
}
|
|
36408
36691
|
};
|
|
36409
36692
|
}
|
|
36410
36693
|
function hubRoot() {
|
|
36411
|
-
const fromPkg = (0,
|
|
36694
|
+
const fromPkg = (0, import_node_path40.join)(__dirname, "..", "..");
|
|
36412
36695
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
36413
|
-
if ((0,
|
|
36414
|
-
if ((0,
|
|
36696
|
+
if ((0, import_node_fs42.existsSync)((0, import_node_path40.join)(fromPkg, marker))) return fromPkg;
|
|
36697
|
+
if ((0, import_node_fs42.existsSync)((0, import_node_path40.join)(process.cwd(), marker))) return process.cwd();
|
|
36415
36698
|
return null;
|
|
36416
36699
|
}
|
|
36417
36700
|
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) => {
|
|
@@ -36723,7 +37006,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
36723
37006
|
localCleanup = await cleanupPrMergeLocalBranch(headRef, {
|
|
36724
37007
|
beforeWorktrees,
|
|
36725
37008
|
startingPath,
|
|
36726
|
-
pathExists: (p) => (0,
|
|
37009
|
+
pathExists: (p) => (0, import_node_fs42.existsSync)(p),
|
|
36727
37010
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
36728
37011
|
teardownWorktreeStage,
|
|
36729
37012
|
deferredStore,
|
|
@@ -37219,12 +37502,12 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
37219
37502
|
targets = resolution.targets;
|
|
37220
37503
|
}
|
|
37221
37504
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
37222
|
-
const fileMatrix = (0,
|
|
37505
|
+
const fileMatrix = (0, import_node_fs42.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs42.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
37223
37506
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
37224
37507
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
37225
|
-
const fileContracts = (0,
|
|
37508
|
+
const fileContracts = (0, import_node_fs42.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs42.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
37226
37509
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
37227
|
-
const sanctioned = (0,
|
|
37510
|
+
const sanctioned = (0, import_node_fs42.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs42.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
37228
37511
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
37229
37512
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
37230
37513
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -37256,16 +37539,16 @@ function directoryBytes(path2) {
|
|
|
37256
37539
|
let total = 0;
|
|
37257
37540
|
let entries;
|
|
37258
37541
|
try {
|
|
37259
|
-
entries = (0,
|
|
37542
|
+
entries = (0, import_node_fs42.readdirSync)(path2, { withFileTypes: true });
|
|
37260
37543
|
} catch {
|
|
37261
37544
|
return 0;
|
|
37262
37545
|
}
|
|
37263
37546
|
for (const entry of entries) {
|
|
37264
|
-
const child2 = (0,
|
|
37547
|
+
const child2 = (0, import_node_path40.join)(path2, entry.name);
|
|
37265
37548
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
37266
37549
|
else {
|
|
37267
37550
|
try {
|
|
37268
|
-
total += (0,
|
|
37551
|
+
total += (0, import_node_fs42.statSync)(child2).size;
|
|
37269
37552
|
} catch {
|
|
37270
37553
|
}
|
|
37271
37554
|
}
|
|
@@ -37273,25 +37556,25 @@ function directoryBytes(path2) {
|
|
|
37273
37556
|
return total;
|
|
37274
37557
|
}
|
|
37275
37558
|
function listDirEntries(dir) {
|
|
37276
|
-
return (0,
|
|
37559
|
+
return (0, import_node_fs42.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
37277
37560
|
}
|
|
37278
37561
|
function readInstalledPluginRefs(configRoot) {
|
|
37279
37562
|
const p = installedPluginsPathForConfig(configRoot);
|
|
37280
|
-
if (!(0,
|
|
37563
|
+
if (!(0, import_node_fs42.existsSync)(p)) return [];
|
|
37281
37564
|
try {
|
|
37282
|
-
return installedPluginPaths((0,
|
|
37565
|
+
return installedPluginPaths((0, import_node_fs42.readFileSync)(p, "utf8"));
|
|
37283
37566
|
} catch {
|
|
37284
37567
|
return null;
|
|
37285
37568
|
}
|
|
37286
37569
|
}
|
|
37287
37570
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
37288
37571
|
return {
|
|
37289
|
-
exists: (p) => (0,
|
|
37290
|
-
listVersionDirs: (root) => (0,
|
|
37572
|
+
exists: (p) => (0, import_node_fs42.existsSync)(p),
|
|
37573
|
+
listVersionDirs: (root) => (0, import_node_fs42.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
37291
37574
|
dirBytes,
|
|
37292
|
-
listStagingDirs: (root) => (0,
|
|
37575
|
+
listStagingDirs: (root) => (0, import_node_fs42.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
37293
37576
|
try {
|
|
37294
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
37577
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path40.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs42.statSync)(p).mtimeMs) };
|
|
37295
37578
|
} catch {
|
|
37296
37579
|
return { name: d.name, mtimeMs: Date.now() };
|
|
37297
37580
|
}
|
|
@@ -37305,10 +37588,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
37305
37588
|
return {
|
|
37306
37589
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
37307
37590
|
mtimeMs: (name) => {
|
|
37308
|
-
const p = (0,
|
|
37309
|
-
if (!(0,
|
|
37591
|
+
const p = (0, import_node_path40.join)(stagingRoot, name);
|
|
37592
|
+
if (!(0, import_node_fs42.existsSync)(p)) return null;
|
|
37310
37593
|
try {
|
|
37311
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
37594
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs42.statSync)(q).mtimeMs);
|
|
37312
37595
|
} catch {
|
|
37313
37596
|
return null;
|
|
37314
37597
|
}
|
|
@@ -37328,13 +37611,13 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
37328
37611
|
return;
|
|
37329
37612
|
}
|
|
37330
37613
|
const plan = buildPluginCachePlan(
|
|
37331
|
-
(0,
|
|
37614
|
+
(0, import_node_os17.homedir)(),
|
|
37332
37615
|
running,
|
|
37333
37616
|
pluginCacheFsDeps(configRoot, directoryBytes),
|
|
37334
37617
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
37335
37618
|
);
|
|
37336
37619
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
37337
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
37620
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs42.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
37338
37621
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
37339
37622
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
37340
37623
|
else console.log(renderPluginCachePlan(plan, result));
|
|
@@ -37342,7 +37625,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
37342
37625
|
});
|
|
37343
37626
|
function readReleaseCatchupState(path2) {
|
|
37344
37627
|
try {
|
|
37345
|
-
const parsed = JSON.parse((0,
|
|
37628
|
+
const parsed = JSON.parse((0, import_node_fs42.readFileSync)(path2, "utf8"));
|
|
37346
37629
|
return typeof parsed?.checkedAt === "number" ? parsed : void 0;
|
|
37347
37630
|
} catch {
|
|
37348
37631
|
return void 0;
|
|
@@ -37350,8 +37633,8 @@ function readReleaseCatchupState(path2) {
|
|
|
37350
37633
|
}
|
|
37351
37634
|
function writeReleaseCatchupState(path2, state) {
|
|
37352
37635
|
try {
|
|
37353
|
-
(0,
|
|
37354
|
-
(0,
|
|
37636
|
+
(0, import_node_fs42.mkdirSync)((0, import_node_path40.dirname)(path2), { recursive: true });
|
|
37637
|
+
(0, import_node_fs42.writeFileSync)(path2, `${JSON.stringify(state)}
|
|
37355
37638
|
`);
|
|
37356
37639
|
} catch {
|
|
37357
37640
|
}
|
|
@@ -37359,7 +37642,7 @@ function writeReleaseCatchupState(path2, state) {
|
|
|
37359
37642
|
program2.command("plugin-release-catchup").description("install a newer released MMI plugin clone non-destructively and re-point the Pi registration (#4297); TTL-gated no-op when current").option("--force", "skip the 24h TTL (acceptance proof / manual run)").option("--quiet", "print only failures (the detached session-start lane)").option("--json", "machine-readable output").action(async (o) => {
|
|
37360
37643
|
const outcome = await withEnvHealLock(
|
|
37361
37644
|
"plugin release catch-up",
|
|
37362
|
-
() => runReleaseCatchup((0,
|
|
37645
|
+
() => runReleaseCatchup((0, import_node_os17.homedir)(), process.env, {
|
|
37363
37646
|
fetchReleased: fetchNpmReleasedVersion,
|
|
37364
37647
|
runClaude: (args, step) => runPluginCli("claude", args, (msg) => {
|
|
37365
37648
|
if (!o.quiet && !o.json) console.log(msg);
|
|
@@ -37375,7 +37658,7 @@ program2.command("plugin-release-catchup").description("install a newer released
|
|
|
37375
37658
|
},
|
|
37376
37659
|
readState: readReleaseCatchupState,
|
|
37377
37660
|
writeState: writeReleaseCatchupState,
|
|
37378
|
-
healRegistration: defaultRegistrationHeal((0,
|
|
37661
|
+
healRegistration: defaultRegistrationHeal((0, import_node_os17.homedir)(), process.env),
|
|
37379
37662
|
now: () => Date.now()
|
|
37380
37663
|
}, { force: o.force })
|
|
37381
37664
|
);
|
|
@@ -37443,7 +37726,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
|
|
|
37443
37726
|
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process19.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
37444
37727
|
bannerIo.log(worktreeBanner);
|
|
37445
37728
|
}
|
|
37446
|
-
if (shouldSpawnReleaseCatchup((0,
|
|
37729
|
+
if (shouldSpawnReleaseCatchup((0, import_node_os17.homedir)(), process.env, readReleaseCatchupState)) {
|
|
37447
37730
|
spawnDetachedSelf(["plugin", "release-catchup", "--quiet"], { spawn: import_node_child_process19.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
37448
37731
|
}
|
|
37449
37732
|
if (isLinkedWorktree(process.cwd())) {
|
package/package.json
CHANGED