@mutmutco/cli 3.80.0 → 3.82.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 +409 -534
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -3416,8 +3416,8 @@ var program = new Command();
|
|
|
3416
3416
|
|
|
3417
3417
|
// src/index.ts
|
|
3418
3418
|
var import_promises10 = require("node:fs/promises");
|
|
3419
|
-
var
|
|
3420
|
-
var
|
|
3419
|
+
var import_node_fs35 = require("node:fs");
|
|
3420
|
+
var import_node_child_process16 = require("node:child_process");
|
|
3421
3421
|
|
|
3422
3422
|
// src/cli-shared.ts
|
|
3423
3423
|
var import_node_child_process3 = require("node:child_process");
|
|
@@ -4483,6 +4483,7 @@ function trackedPathStatus(c, realPath, repoAnchor) {
|
|
|
4483
4483
|
const gitPath = c.kind === "dir" ? `${rel.replace(/\/+$/, "")}/` : rel;
|
|
4484
4484
|
try {
|
|
4485
4485
|
(0, import_node_child_process4.execFileSync)("git", ["-C", repoAnchor, "rev-parse", "--is-inside-work-tree"], {
|
|
4486
|
+
windowsHide: true,
|
|
4486
4487
|
stdio: ["ignore", "ignore", "ignore"],
|
|
4487
4488
|
timeout: 1e4
|
|
4488
4489
|
});
|
|
@@ -4491,6 +4492,7 @@ function trackedPathStatus(c, realPath, repoAnchor) {
|
|
|
4491
4492
|
}
|
|
4492
4493
|
try {
|
|
4493
4494
|
const out = (0, import_node_child_process4.execFileSync)("git", ["-C", repoAnchor, "ls-files", "-z", "--", gitPath], {
|
|
4495
|
+
windowsHide: true,
|
|
4494
4496
|
encoding: "utf8",
|
|
4495
4497
|
stdio: ["ignore", "pipe", "ignore"],
|
|
4496
4498
|
timeout: 1e4
|
|
@@ -5293,9 +5295,6 @@ function explicitRepoWorktreesRoot(root, repoRoot2, rootDirs) {
|
|
|
5293
5295
|
const repoDir = rootDirs.find((name) => name.toLowerCase() === repoName.toLowerCase());
|
|
5294
5296
|
return repoDir ? (0, import_node_path7.join)(root, repoDir) : root;
|
|
5295
5297
|
}
|
|
5296
|
-
function strayWorktreeRootPaths(container, names, authoritativeRoot) {
|
|
5297
|
-
return names.filter((name) => /worktrees/i.test(name)).map((name) => (0, import_node_path7.join)(container, name)).filter((path2) => !samePath(path2, authoritativeRoot));
|
|
5298
|
-
}
|
|
5299
5298
|
function classifySiblingWorktreeDir(entry) {
|
|
5300
5299
|
if (!entry.ownedByCurrentRepo) {
|
|
5301
5300
|
return { skip: { path: entry.path, reason: "unknown-git-state", detail: entry.detail ?? "not proven current-repo owned" } };
|
|
@@ -6259,6 +6258,8 @@ function readSettingsAutoUpdate(raw, name) {
|
|
|
6259
6258
|
var AUTO_UPDATE_TOGGLE_STEPS = "`/plugin` \u2192 Marketplaces tab \u2192 select the marketplace \u2192 Enable auto-update";
|
|
6260
6259
|
var CATALOG_CONTENT_REF = "main";
|
|
6261
6260
|
var CATALOG_REF_PIN_STEPS = `add \`"ref": "${CATALOG_CONTENT_REF}"\` to this marketplace's \`source\` in ~/.claude/plugins/known_marketplaces.json, then restart Claude`;
|
|
6261
|
+
var PIN_HEAL_COMMAND = "run `mmi-cli doctor`";
|
|
6262
|
+
var ORG_MARKETPLACE_PINS = { autoUpdate: true, ref: CATALOG_CONTENT_REF };
|
|
6262
6263
|
function resolveCatalogRef(probe) {
|
|
6263
6264
|
const { name, registered, ref, autoUpdate } = probe;
|
|
6264
6265
|
if (!registered) return void 0;
|
|
@@ -6281,7 +6282,7 @@ function resolveCatalogRef(probe) {
|
|
|
6281
6282
|
reason: `no ref pinned \u2014 the catalog is served from the repo's default branch while plugin content is pinned to ${CATALOG_CONTENT_REF}`
|
|
6282
6283
|
};
|
|
6283
6284
|
}
|
|
6284
|
-
function checkMarketplaceAutoUpdate(probe) {
|
|
6285
|
+
function checkMarketplaceAutoUpdate(probe, healable = false) {
|
|
6285
6286
|
if (!probe) return null;
|
|
6286
6287
|
const evidence = [`${probe.name}: auto-update ${probe.effective ? "on" : "off"} \u2014 ${probe.reason}`];
|
|
6287
6288
|
if (probe.effective) {
|
|
@@ -6292,11 +6293,11 @@ function checkMarketplaceAutoUpdate(probe) {
|
|
|
6292
6293
|
ok: true,
|
|
6293
6294
|
warn: true,
|
|
6294
6295
|
label: `marketplace auto-update (${probe.name})`,
|
|
6295
|
-
detail: `off \u2014 this machine will not pick up a new plugin release on its own; turn it on with ${AUTO_UPDATE_TOGGLE_STEPS}`,
|
|
6296
|
+
detail: `off \u2014 this machine will not pick up a new plugin release on its own; ${healable ? `${PIN_HEAL_COMMAND} to turn it on` : `turn it on with ${AUTO_UPDATE_TOGGLE_STEPS}`}`,
|
|
6296
6297
|
verbose: evidence
|
|
6297
6298
|
};
|
|
6298
6299
|
}
|
|
6299
|
-
function checkMarketplaceCatalogRef(probe) {
|
|
6300
|
+
function checkMarketplaceCatalogRef(probe, healable = false) {
|
|
6300
6301
|
if (!probe) return null;
|
|
6301
6302
|
const label = `marketplace catalog ref (${probe.name})`;
|
|
6302
6303
|
const evidence = [`${probe.name}: ${probe.reason}`, `auto-update: ${probe.autoUpdate ? "on" : "off"}`];
|
|
@@ -6313,7 +6314,7 @@ function checkMarketplaceCatalogRef(probe) {
|
|
|
6313
6314
|
// ✗ branch carried it in `fix` only because `renderCheckLine` discarded `detail` on failure — one
|
|
6314
6315
|
// row, two conventions, decided by which field would actually print.
|
|
6315
6316
|
detail: `${drift} while auto-update is ON`,
|
|
6316
|
-
fix: `this machine installs plugin releases advertised by a branch nobody released; ${CATALOG_REF_PIN_STEPS}`,
|
|
6317
|
+
fix: `this machine installs plugin releases advertised by a branch nobody released; ${healable ? `${PIN_HEAL_COMMAND} to pin it` : CATALOG_REF_PIN_STEPS}`,
|
|
6317
6318
|
verbose: evidence
|
|
6318
6319
|
};
|
|
6319
6320
|
}
|
|
@@ -6322,11 +6323,11 @@ function checkMarketplaceCatalogRef(probe) {
|
|
|
6322
6323
|
ok: true,
|
|
6323
6324
|
warn: true,
|
|
6324
6325
|
label,
|
|
6325
|
-
detail: `${drift} \u2014 harmless while auto-update is off, and the thing to fix before turning it on; ${CATALOG_REF_PIN_STEPS}`,
|
|
6326
|
+
detail: `${drift} \u2014 harmless while auto-update is off, and the thing to fix before turning it on; ${healable ? `${PIN_HEAL_COMMAND} to pin it` : CATALOG_REF_PIN_STEPS}`,
|
|
6326
6327
|
verbose: evidence
|
|
6327
6328
|
};
|
|
6328
6329
|
}
|
|
6329
|
-
function marketplaceRows(name, known, settings) {
|
|
6330
|
+
function marketplaceRows(name, known, settings, healable = false) {
|
|
6330
6331
|
const { registered, declared, ref } = readKnownMarketplace(known, name);
|
|
6331
6332
|
const autoUpdate = resolveAutoUpdate({
|
|
6332
6333
|
name,
|
|
@@ -6334,8 +6335,9 @@ function marketplaceRows(name, known, settings) {
|
|
|
6334
6335
|
declared,
|
|
6335
6336
|
settingsDeclared: readSettingsAutoUpdate(settings, name)
|
|
6336
6337
|
});
|
|
6337
|
-
const rows = [checkMarketplaceAutoUpdate(autoUpdate), checkMarketplaceCatalogRef(
|
|
6338
|
-
resolveCatalogRef({ name, registered, ref, autoUpdate: autoUpdate.effective })
|
|
6338
|
+
const rows = [checkMarketplaceAutoUpdate(autoUpdate, healable), checkMarketplaceCatalogRef(
|
|
6339
|
+
resolveCatalogRef({ name, registered, ref, autoUpdate: autoUpdate.effective }),
|
|
6340
|
+
healable
|
|
6339
6341
|
)];
|
|
6340
6342
|
return rows.filter((r) => r !== null);
|
|
6341
6343
|
}
|
|
@@ -6347,32 +6349,6 @@ var DEFAULT_SURFACE = "claude";
|
|
|
6347
6349
|
function activityLogPath(cwd) {
|
|
6348
6350
|
return repoRuntimeStatePath(cwd, "hooks", "activity.jsonl");
|
|
6349
6351
|
}
|
|
6350
|
-
var REDACTOR_WINDOW_MS = 48 * 60 * 60 * 1e3;
|
|
6351
|
-
var REDACTOR_SCAN_BYTES = 512 * 1024;
|
|
6352
|
-
function redactorLivenessProbe(cwd, now = /* @__PURE__ */ new Date()) {
|
|
6353
|
-
try {
|
|
6354
|
-
const raw = (0, import_node_fs10.readFileSync)(activityLogPath(cwd), "utf8");
|
|
6355
|
-
const tail = raw.length > REDACTOR_SCAN_BYTES ? raw.slice(raw.length - REDACTOR_SCAN_BYTES) : raw;
|
|
6356
|
-
const floor = now.getTime() - REDACTOR_WINDOW_MS;
|
|
6357
|
-
let failed = 0;
|
|
6358
|
-
let lastTs;
|
|
6359
|
-
for (const line of tail.split("\n")) {
|
|
6360
|
-
if (!line.includes('"secret-redact"') || !line.includes('"failed"')) continue;
|
|
6361
|
-
try {
|
|
6362
|
-
const row = JSON.parse(line);
|
|
6363
|
-
if (row.script !== "secret-redact" || row.outcome !== "failed") continue;
|
|
6364
|
-
const ts = row.ts ? Date.parse(row.ts) : Number.NaN;
|
|
6365
|
-
if (Number.isNaN(ts) || ts < floor) continue;
|
|
6366
|
-
failed += 1;
|
|
6367
|
-
if (!lastTs || row.ts > lastTs) lastTs = row.ts;
|
|
6368
|
-
} catch {
|
|
6369
|
-
}
|
|
6370
|
-
}
|
|
6371
|
-
return { failed, ...lastTs ? { lastTs } : {} };
|
|
6372
|
-
} catch {
|
|
6373
|
-
return void 0;
|
|
6374
|
-
}
|
|
6375
|
-
}
|
|
6376
6352
|
function appendHookActivity(cwd, entry) {
|
|
6377
6353
|
try {
|
|
6378
6354
|
const path2 = activityLogPath(cwd);
|
|
@@ -6619,7 +6595,7 @@ function commandLadderHint() {
|
|
|
6619
6595
|
}
|
|
6620
6596
|
|
|
6621
6597
|
// src/index.ts
|
|
6622
|
-
var
|
|
6598
|
+
var import_node_path34 = require("node:path");
|
|
6623
6599
|
|
|
6624
6600
|
// src/merge-ci-policy.ts
|
|
6625
6601
|
function resolveMergeCiPolicy(input) {
|
|
@@ -13440,6 +13416,7 @@ function spawnExitCode(status, signal) {
|
|
|
13440
13416
|
function defaultSpawn(command, args, env) {
|
|
13441
13417
|
const target = resolveSpawnTarget(command, args);
|
|
13442
13418
|
const r = (0, import_node_child_process6.spawnSync)(target.cmd, target.args, {
|
|
13419
|
+
windowsHide: true,
|
|
13443
13420
|
stdio: "inherit",
|
|
13444
13421
|
env,
|
|
13445
13422
|
windowsVerbatimArguments: target.windowsVerbatimArguments
|
|
@@ -13937,62 +13914,6 @@ async function branchesMergedIntoBase(remote) {
|
|
|
13937
13914
|
}
|
|
13938
13915
|
return [...out];
|
|
13939
13916
|
}
|
|
13940
|
-
var STRAY_ROOT_WALK_BUDGET_MS = 4e3;
|
|
13941
|
-
function measureWorktreeRoot(root, deadline) {
|
|
13942
|
-
let dirs = 0;
|
|
13943
|
-
let bytes = 0;
|
|
13944
|
-
let partial = false;
|
|
13945
|
-
const stack = [root];
|
|
13946
|
-
let depth0 = true;
|
|
13947
|
-
while (stack.length) {
|
|
13948
|
-
if (Date.now() > deadline) {
|
|
13949
|
-
partial = true;
|
|
13950
|
-
break;
|
|
13951
|
-
}
|
|
13952
|
-
const current = stack.pop();
|
|
13953
|
-
let entries;
|
|
13954
|
-
try {
|
|
13955
|
-
entries = (0, import_node_fs17.readdirSync)(current, { withFileTypes: true });
|
|
13956
|
-
} catch {
|
|
13957
|
-
continue;
|
|
13958
|
-
}
|
|
13959
|
-
for (const ent of entries) {
|
|
13960
|
-
const child2 = (0, import_node_path15.join)(current, ent.name);
|
|
13961
|
-
if (ent.isDirectory()) {
|
|
13962
|
-
if (depth0) dirs++;
|
|
13963
|
-
let isLink = false;
|
|
13964
|
-
try {
|
|
13965
|
-
(0, import_node_fs17.readlinkSync)(child2);
|
|
13966
|
-
isLink = true;
|
|
13967
|
-
} catch {
|
|
13968
|
-
}
|
|
13969
|
-
if (!isLink) stack.push(child2);
|
|
13970
|
-
} else if (ent.isFile()) {
|
|
13971
|
-
try {
|
|
13972
|
-
bytes += (0, import_node_fs17.statSync)(child2).size;
|
|
13973
|
-
} catch {
|
|
13974
|
-
}
|
|
13975
|
-
}
|
|
13976
|
-
}
|
|
13977
|
-
depth0 = false;
|
|
13978
|
-
}
|
|
13979
|
-
return partial ? { dirs, bytes, partial } : { dirs, bytes };
|
|
13980
|
-
}
|
|
13981
|
-
function worktreeRootsProbe(repoRoot2) {
|
|
13982
|
-
const authoritative = siblingMmiWorktreesRoot(repoRoot2);
|
|
13983
|
-
const container = (0, import_node_path15.dirname)(authoritative);
|
|
13984
|
-
let names;
|
|
13985
|
-
try {
|
|
13986
|
-
names = (0, import_node_fs17.readdirSync)(container, { withFileTypes: true }).filter((ent) => ent.isDirectory()).map((ent) => ent.name);
|
|
13987
|
-
} catch {
|
|
13988
|
-
return { authoritative, stray: [] };
|
|
13989
|
-
}
|
|
13990
|
-
const deadline = Date.now() + STRAY_ROOT_WALK_BUDGET_MS;
|
|
13991
|
-
return {
|
|
13992
|
-
authoritative,
|
|
13993
|
-
stray: strayWorktreeRootPaths(container, names, authoritative).map((path2) => ({ path: path2, ...measureWorktreeRoot(path2, deadline) }))
|
|
13994
|
-
};
|
|
13995
|
-
}
|
|
13996
13917
|
|
|
13997
13918
|
// src/repo-resolve.ts
|
|
13998
13919
|
function slugOf(repoOrSlug) {
|
|
@@ -14892,7 +14813,7 @@ function readKnownMarketplacesFile(path2) {
|
|
|
14892
14813
|
return void 0;
|
|
14893
14814
|
}
|
|
14894
14815
|
}
|
|
14895
|
-
function
|
|
14816
|
+
function writeMarketplacePinsOnDisk(path2, pins, succeeded, failedVerb) {
|
|
14896
14817
|
if (pins.size === 0) return void 0;
|
|
14897
14818
|
const after = readKnownMarketplacesFile(path2);
|
|
14898
14819
|
const next = restoreMarketplacePins(after, pins);
|
|
@@ -14900,14 +14821,35 @@ function restoreMarketplacePinsOnDisk(path2, pins) {
|
|
|
14900
14821
|
try {
|
|
14901
14822
|
(0, import_node_fs18.writeFileSync)(path2, next, "utf8");
|
|
14902
14823
|
} catch {
|
|
14903
|
-
return `could NOT
|
|
14824
|
+
return `could NOT ${failedVerb} ${[...pins.keys()].join(", ")} \u2014 set it by hand`;
|
|
14904
14825
|
}
|
|
14905
14826
|
const verify = readKnownMarketplacesFile(path2);
|
|
14906
14827
|
const failed = [...pins].filter(([name, want]) => {
|
|
14907
14828
|
const got = readKnownMarketplace(verify, name);
|
|
14908
14829
|
return typeof want.autoUpdate === "boolean" && got.declared !== want.autoUpdate || want.ref !== void 0 && got.ref !== want.ref;
|
|
14909
14830
|
});
|
|
14910
|
-
return failed.length ?
|
|
14831
|
+
return failed.length ? `${failedVerb} did NOT take for ${failed.map(([n]) => n).join(", ")} \u2014 set it by hand` : succeeded(pins);
|
|
14832
|
+
}
|
|
14833
|
+
function restoreMarketplacePinsOnDisk(path2, pins) {
|
|
14834
|
+
return writeMarketplacePinsOnDisk(path2, pins, restoredPinsLine, "restore");
|
|
14835
|
+
}
|
|
14836
|
+
function applyOrgMarketplacePins(path2, names) {
|
|
14837
|
+
return writeMarketplacePinsOnDisk(
|
|
14838
|
+
path2,
|
|
14839
|
+
new Map(names.map((name) => [name, ORG_MARKETPLACE_PINS])),
|
|
14840
|
+
(pins) => `pinned ${[...pins.keys()].join(", ")} to ${ORG_MARKETPLACE_PINS.ref} with auto-update on`,
|
|
14841
|
+
"pin"
|
|
14842
|
+
);
|
|
14843
|
+
}
|
|
14844
|
+
function restoredPinsLine(pins) {
|
|
14845
|
+
const named = [...pins].map(([name, want]) => {
|
|
14846
|
+
const fields = [
|
|
14847
|
+
...typeof want.autoUpdate === "boolean" ? ["auto-update"] : [],
|
|
14848
|
+
...want.ref ? ["catalog ref"] : []
|
|
14849
|
+
];
|
|
14850
|
+
return `${name} (${fields.join(" + ")})`;
|
|
14851
|
+
});
|
|
14852
|
+
return `re-pinned ${named.join(", ")} \u2014 restored across the reinstall`;
|
|
14911
14853
|
}
|
|
14912
14854
|
async function runGuard(readOrigin) {
|
|
14913
14855
|
const surface = detectSurface(process.env);
|
|
@@ -16562,12 +16504,13 @@ var PRIMARY_GROUPS = [
|
|
|
16562
16504
|
["Plan and work", ["board", "issue", "worktree", "stage"]],
|
|
16563
16505
|
["Review and ship", ["pr", "ci", "rcand", "release", "hotfix", "train"]],
|
|
16564
16506
|
// `tests` sits beside `docs` deliberately: both are deterministic, repo-local gates a workflow
|
|
16565
|
-
// step invokes (`docs refs`, `tests policy`), not org-plane operations (#3605).
|
|
16566
|
-
|
|
16507
|
+
// step invokes (`docs refs`, `tests policy`), not org-plane operations (#3605). `spawn policy`
|
|
16508
|
+
// joins them on the same footing (#3979).
|
|
16509
|
+
["Setup and support", ["bootstrap", "secrets", "docs", "tests", "spawn"]],
|
|
16567
16510
|
["Coordinate and improve", ["wave", "report", "skill-lesson"]]
|
|
16568
16511
|
];
|
|
16569
16512
|
var OPERATIONAL_TOP_LEVEL = /* @__PURE__ */ new Set(["org", "runtime", "plugin"]);
|
|
16570
|
-
var SUPPORT_PRIMARY = /* @__PURE__ */ new Set(["doctor", "whoami", "commands", "explain", "docs", "tests", "wave", "report", "skill-lesson"]);
|
|
16513
|
+
var SUPPORT_PRIMARY = /* @__PURE__ */ new Set(["doctor", "whoami", "commands", "explain", "docs", "tests", "spawn", "wave", "report", "skill-lesson"]);
|
|
16571
16514
|
var TOP_LEVEL_ORDER = /* @__PURE__ */ new Map();
|
|
16572
16515
|
var HELP_GROUP_ORDER = /* @__PURE__ */ new Map();
|
|
16573
16516
|
var topLevelPosition = 0;
|
|
@@ -16607,6 +16550,7 @@ var COMMAND_OWNERSHIP = {
|
|
|
16607
16550
|
secrets: { module_owner: "cli/src/secrets-commands.ts", consumer: "authenticated-operator" },
|
|
16608
16551
|
docs: { module_owner: "cli/src/docs-index-command.ts", consumer: "repo-gates" },
|
|
16609
16552
|
tests: { module_owner: "cli/src/test-policy-core.ts", consumer: "repo-gates" },
|
|
16553
|
+
spawn: { module_owner: "cli/src/spawn-policy-core.ts", consumer: "repo-gates" },
|
|
16610
16554
|
wave: { module_owner: "cli/src/wave-land.ts", consumer: "campaign-orchestrator" },
|
|
16611
16555
|
report: { module_owner: "cli/src/report.ts", consumer: "campaign-orchestrator" },
|
|
16612
16556
|
"skill-lesson": { module_owner: "cli/src/skill-lesson.ts", consumer: "campaign-orchestrator" },
|
|
@@ -18898,10 +18842,115 @@ function runDocRefs(root, deps = {}) {
|
|
|
18898
18842
|
};
|
|
18899
18843
|
}
|
|
18900
18844
|
|
|
18901
|
-
// src/
|
|
18845
|
+
// src/spawn-policy-core.ts
|
|
18902
18846
|
var import_node_child_process11 = require("node:child_process");
|
|
18903
18847
|
var import_node_fs21 = require("node:fs");
|
|
18904
18848
|
var import_node_path19 = require("node:path");
|
|
18849
|
+
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
18850
|
+
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
18851
|
+
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
18852
|
+
var EXCLUDED = [
|
|
18853
|
+
/(^|\/)node_modules\//,
|
|
18854
|
+
/(^|\/)(dist|build|coverage)\//,
|
|
18855
|
+
/(^|\/)(test|tests|__tests__|fixtures|__fixtures__)\//,
|
|
18856
|
+
/\.(test|spec)\.[cm]?[jt]s$/,
|
|
18857
|
+
/\.d\.[cm]?ts$/
|
|
18858
|
+
];
|
|
18859
|
+
function stripComments(src) {
|
|
18860
|
+
return src.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, " ")).replace(/(^|[^:])\/\/[^\n]*/g, (m, p) => p + " ".repeat(m.length - p.length));
|
|
18861
|
+
}
|
|
18862
|
+
function callArgs(src, open2) {
|
|
18863
|
+
let depth = 0;
|
|
18864
|
+
for (let i = open2; i < src.length; i++) {
|
|
18865
|
+
const c = src[i];
|
|
18866
|
+
if (c === "(") depth++;
|
|
18867
|
+
else if (c === ")") {
|
|
18868
|
+
depth--;
|
|
18869
|
+
if (depth === 0) return src.slice(open2, i + 1);
|
|
18870
|
+
}
|
|
18871
|
+
}
|
|
18872
|
+
return "";
|
|
18873
|
+
}
|
|
18874
|
+
function findViolationsInSource(raw) {
|
|
18875
|
+
if (!/from\s+["']node:child_process["']/.test(raw)) return [];
|
|
18876
|
+
const importBlock = /import\s*(?:type\s*)?\{([^}]*)\}\s*from\s+["']node:child_process["']/.exec(raw);
|
|
18877
|
+
const imported = new Set(
|
|
18878
|
+
(importBlock?.[1] ?? "").split(",").map((s) => s.trim().split(/\s+as\s+/)[0].trim()).filter(Boolean)
|
|
18879
|
+
);
|
|
18880
|
+
if (imported.size === 0) return [];
|
|
18881
|
+
const rawLines = raw.split("\n");
|
|
18882
|
+
const src = stripComments(raw);
|
|
18883
|
+
const call = new RegExp(CALL_SOURCE, "g");
|
|
18884
|
+
const found = [];
|
|
18885
|
+
let m;
|
|
18886
|
+
while (m = call.exec(src)) {
|
|
18887
|
+
const callee = m[2];
|
|
18888
|
+
if (!imported.has(callee)) continue;
|
|
18889
|
+
const open2 = m.index + m[0].length - 1;
|
|
18890
|
+
const args = callArgs(src, open2);
|
|
18891
|
+
if (!args) continue;
|
|
18892
|
+
if (args.includes("windowsHide")) continue;
|
|
18893
|
+
const line = src.slice(0, m.index + m[1].length).split("\n").length;
|
|
18894
|
+
if (/windows-hide-exempt:/.test(rawLines[line - 2] ?? "")) continue;
|
|
18895
|
+
if (/\.{3}\s*\w+/.test(args)) continue;
|
|
18896
|
+
if (/\b(opts|options|spawnOptions)\b/.test(args)) continue;
|
|
18897
|
+
const identifiers = new Set(args.match(/\b[A-Za-z_$][\w$]*\b/g) ?? []);
|
|
18898
|
+
let satisfied = false;
|
|
18899
|
+
for (const id of identifiers) {
|
|
18900
|
+
if (SPAWNERS.includes(id)) continue;
|
|
18901
|
+
const decl = new RegExp(String.raw`\b(?:const|let|var)\s+${id}\b[^;]*`, "s").exec(src);
|
|
18902
|
+
if (decl && decl[0].includes("windowsHide")) {
|
|
18903
|
+
satisfied = true;
|
|
18904
|
+
break;
|
|
18905
|
+
}
|
|
18906
|
+
if (new RegExp(String.raw`\b${id}\s*:\s*\{[^}]*windowsHide`, "s").test(src)) {
|
|
18907
|
+
satisfied = true;
|
|
18908
|
+
break;
|
|
18909
|
+
}
|
|
18910
|
+
}
|
|
18911
|
+
if (satisfied) continue;
|
|
18912
|
+
found.push({ line, callee });
|
|
18913
|
+
}
|
|
18914
|
+
return found;
|
|
18915
|
+
}
|
|
18916
|
+
function policedFiles(root) {
|
|
18917
|
+
const r = (0, import_node_child_process11.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
18918
|
+
cwd: root,
|
|
18919
|
+
encoding: "utf8",
|
|
18920
|
+
windowsHide: true,
|
|
18921
|
+
maxBuffer: 32 * 1024 * 1024
|
|
18922
|
+
});
|
|
18923
|
+
if (r.status !== 0) {
|
|
18924
|
+
throw new Error(`spawn policy: git ls-files failed: ${(r.stderr || "").trim() || `exit ${r.status}`}`);
|
|
18925
|
+
}
|
|
18926
|
+
return (r.stdout ?? "").split("\0").filter(Boolean).filter((f) => SOURCE_EXT.test(f)).filter((f) => !EXCLUDED.some((re) => re.test(f)));
|
|
18927
|
+
}
|
|
18928
|
+
function runSpawnPolicy(root) {
|
|
18929
|
+
const files = policedFiles(root);
|
|
18930
|
+
const findings = [];
|
|
18931
|
+
for (const file of files) {
|
|
18932
|
+
let raw;
|
|
18933
|
+
try {
|
|
18934
|
+
raw = (0, import_node_fs21.readFileSync)((0, import_node_path19.join)(root, file), "utf8");
|
|
18935
|
+
} catch {
|
|
18936
|
+
continue;
|
|
18937
|
+
}
|
|
18938
|
+
for (const v of findViolationsInSource(raw)) {
|
|
18939
|
+
findings.push({
|
|
18940
|
+
file,
|
|
18941
|
+
line: v.line,
|
|
18942
|
+
callee: v.callee,
|
|
18943
|
+
detail: `${file}:${v.line} ${v.callee}(...) has no windowsHide`
|
|
18944
|
+
});
|
|
18945
|
+
}
|
|
18946
|
+
}
|
|
18947
|
+
return { ok: findings.length === 0, scannedCount: files.length, findings };
|
|
18948
|
+
}
|
|
18949
|
+
|
|
18950
|
+
// src/test-policy-core.ts
|
|
18951
|
+
var import_node_child_process12 = require("node:child_process");
|
|
18952
|
+
var import_node_fs22 = require("node:fs");
|
|
18953
|
+
var import_node_path20 = require("node:path");
|
|
18905
18954
|
var POLICY_FILE = "test-policy.json";
|
|
18906
18955
|
var TEST_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
|
18907
18956
|
var PY_TEST_RE = /(?:^|\/)test_[^/]*\.py$|_test\.py$/;
|
|
@@ -18954,7 +19003,7 @@ function isTestPath(path2) {
|
|
|
18954
19003
|
return TEST_RE.test(path2) || PY_TEST_RE.test(path2);
|
|
18955
19004
|
}
|
|
18956
19005
|
function loadPolicy(root, readFile9 = readFileOrNull2) {
|
|
18957
|
-
const raw = readFile9((0,
|
|
19006
|
+
const raw = readFile9((0, import_node_path20.join)(root, POLICY_FILE));
|
|
18958
19007
|
if (raw == null) return { mandatory: [], declared: false };
|
|
18959
19008
|
try {
|
|
18960
19009
|
return { ...JSON.parse(raw), declared: true };
|
|
@@ -18964,7 +19013,7 @@ function loadPolicy(root, readFile9 = readFileOrNull2) {
|
|
|
18964
19013
|
}
|
|
18965
19014
|
function readFileOrNull2(path2) {
|
|
18966
19015
|
try {
|
|
18967
|
-
return (0,
|
|
19016
|
+
return (0, import_node_fs22.readFileSync)(path2, "utf8");
|
|
18968
19017
|
} catch {
|
|
18969
19018
|
return null;
|
|
18970
19019
|
}
|
|
@@ -18991,12 +19040,12 @@ function classify(changed, policy, present = () => false) {
|
|
|
18991
19040
|
const removedProtected = [...removed].filter((p) => protectedBy.has(p)).map((p) => ({ path: p, why: protectedBy.get(p) ?? "" }));
|
|
18992
19041
|
return { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected };
|
|
18993
19042
|
}
|
|
18994
|
-
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0,
|
|
18995
|
-
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0,
|
|
19043
|
+
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs22.existsSync)(path2)) {
|
|
19044
|
+
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path20.join)(root, p)));
|
|
18996
19045
|
}
|
|
18997
|
-
function unresolvedSatisfiers(policy, root, exists = (path2) => (0,
|
|
19046
|
+
function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs22.existsSync)(path2)) {
|
|
18998
19047
|
const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
|
|
18999
|
-
return [...new Set(declared)].filter((p) => !exists((0,
|
|
19048
|
+
return [...new Set(declared)].filter((p) => !exists((0, import_node_path20.join)(root, p)));
|
|
19000
19049
|
}
|
|
19001
19050
|
function evaluate(changed, policy, present = () => false) {
|
|
19002
19051
|
const { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected } = classify(changed, policy, present);
|
|
@@ -19029,14 +19078,15 @@ function evaluate(changed, policy, present = () => false) {
|
|
|
19029
19078
|
return findings;
|
|
19030
19079
|
}
|
|
19031
19080
|
function git(args, cwd) {
|
|
19032
|
-
return (0,
|
|
19081
|
+
return (0, import_node_child_process12.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
|
|
19033
19082
|
}
|
|
19034
19083
|
var COAUTHOR_KEY = "Co-authored-by";
|
|
19035
19084
|
var GH_MESSAGE_SEPARATOR = /^-{5,}$/;
|
|
19036
19085
|
var LIFTED_KEYS = new RegExp(`^(?:${TRAILER_KEY}|${COAUTHOR_KEY}):`, "i");
|
|
19037
19086
|
function parseTrailers(message, cwd) {
|
|
19038
19087
|
try {
|
|
19039
|
-
return (0,
|
|
19088
|
+
return (0, import_node_child_process12.execFileSync)("git", ["interpret-trailers", "--parse", "--unfold"], {
|
|
19089
|
+
windowsHide: true,
|
|
19040
19090
|
cwd,
|
|
19041
19091
|
input: message,
|
|
19042
19092
|
encoding: "utf8",
|
|
@@ -19177,13 +19227,13 @@ function changedFilesSince(base, cwd) {
|
|
|
19177
19227
|
}
|
|
19178
19228
|
function runTestPolicy(root, deps = {}) {
|
|
19179
19229
|
const policy = deps.policy ?? loadPolicy(root);
|
|
19180
|
-
const exists = deps.exists ?? ((path2) => (0,
|
|
19230
|
+
const exists = deps.exists ?? ((path2) => (0, import_node_fs22.existsSync)(path2));
|
|
19181
19231
|
const counts = { mandatoryCount: (policy.mandatory ?? []).length, protectedCount: (policy.protected ?? []).length };
|
|
19182
19232
|
const base = deps.changed ? "(injected)" : resolveBase(root, deps.base);
|
|
19183
19233
|
const refusal = deps.changed ? null : untrustworthyRange(root, base);
|
|
19184
19234
|
const changed = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
|
|
19185
19235
|
const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride(base, root) : { override: null, refusals: [] };
|
|
19186
|
-
const present = (path2) => exists((0,
|
|
19236
|
+
const present = (path2) => exists((0, import_node_path20.join)(root, path2));
|
|
19187
19237
|
const removedByThisDiff = removedPaths(changed);
|
|
19188
19238
|
const staleFindings = [];
|
|
19189
19239
|
const unresolved = unresolvedProtectedEntries(policy, root, exists).filter((p) => !removedByThisDiff.has(p));
|
|
@@ -19339,8 +19389,8 @@ function docsAuditStatus(fetch2, opts) {
|
|
|
19339
19389
|
}
|
|
19340
19390
|
|
|
19341
19391
|
// src/project-info-sync.ts
|
|
19342
|
-
var
|
|
19343
|
-
var
|
|
19392
|
+
var import_node_fs23 = require("node:fs");
|
|
19393
|
+
var import_node_path21 = require("node:path");
|
|
19344
19394
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
19345
19395
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
19346
19396
|
projectV2 { id }
|
|
@@ -19385,14 +19435,14 @@ function sharedName(entries, fallback) {
|
|
|
19385
19435
|
}
|
|
19386
19436
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
19387
19437
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
19388
|
-
const readmePath = (0,
|
|
19389
|
-
if (!(0,
|
|
19438
|
+
const readmePath = (0, import_node_path21.join)(repoRoot2, "README.md");
|
|
19439
|
+
if (!(0, import_node_fs23.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
19390
19440
|
const entries = entriesFor(project2, projects);
|
|
19391
19441
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
19392
19442
|
const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
|
|
19393
19443
|
if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
|
|
19394
19444
|
const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
|
|
19395
|
-
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0,
|
|
19445
|
+
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs23.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
|
|
19396
19446
|
const lines = [
|
|
19397
19447
|
`# ${projectName}`,
|
|
19398
19448
|
"",
|
|
@@ -19411,8 +19461,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
19411
19461
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
19412
19462
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
19413
19463
|
const orgDocs = [
|
|
19414
|
-
(0,
|
|
19415
|
-
(0,
|
|
19464
|
+
(0, import_node_fs23.existsSync)((0, import_node_path21.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
|
|
19465
|
+
(0, import_node_fs23.existsSync)((0, import_node_path21.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
|
|
19416
19466
|
].filter(Boolean);
|
|
19417
19467
|
if (orgDocs.length) lines.push("", "## Organisation docs", "", ...orgDocs);
|
|
19418
19468
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines.join("\n")}
|
|
@@ -20271,8 +20321,8 @@ function writeError(res) {
|
|
|
20271
20321
|
}
|
|
20272
20322
|
|
|
20273
20323
|
// src/secrets-commands.ts
|
|
20274
|
-
var
|
|
20275
|
-
var
|
|
20324
|
+
var import_node_fs24 = require("node:fs");
|
|
20325
|
+
var import_node_path22 = require("node:path");
|
|
20276
20326
|
var import_node_os8 = require("node:os");
|
|
20277
20327
|
|
|
20278
20328
|
// src/project-runtime.ts
|
|
@@ -20396,18 +20446,18 @@ function collectMap(value, previous = []) {
|
|
|
20396
20446
|
return [...previous, value];
|
|
20397
20447
|
}
|
|
20398
20448
|
async function decryptRailsCredentials(input) {
|
|
20399
|
-
const appDir = (0,
|
|
20449
|
+
const appDir = (0, import_node_path22.resolve)(input.appDir ?? process.cwd());
|
|
20400
20450
|
const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
20401
20451
|
const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
20402
|
-
const credentialsPath = (0,
|
|
20403
|
-
const masterKeyPath = (0,
|
|
20452
|
+
const credentialsPath = (0, import_node_path22.resolve)(appDir, credentialsFile);
|
|
20453
|
+
const masterKeyPath = (0, import_node_path22.resolve)(appDir, masterKeyFile);
|
|
20404
20454
|
const env = {
|
|
20405
20455
|
...process.env,
|
|
20406
20456
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
20407
20457
|
MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
|
|
20408
20458
|
};
|
|
20409
|
-
if ((0,
|
|
20410
|
-
env.RAILS_MASTER_KEY = (0,
|
|
20459
|
+
if ((0, import_node_fs24.existsSync)(masterKeyPath)) {
|
|
20460
|
+
env.RAILS_MASTER_KEY = (0, import_node_fs24.readFileSync)(masterKeyPath, "utf8").trim();
|
|
20411
20461
|
}
|
|
20412
20462
|
const script = [
|
|
20413
20463
|
'require "json"',
|
|
@@ -20417,9 +20467,9 @@ async function decryptRailsCredentials(input) {
|
|
|
20417
20467
|
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
20418
20468
|
"puts JSON.generate(config.config)"
|
|
20419
20469
|
].join("\n");
|
|
20420
|
-
const scriptDir = (0,
|
|
20421
|
-
const scriptPath = (0,
|
|
20422
|
-
(0,
|
|
20470
|
+
const scriptDir = (0, import_node_fs24.mkdtempSync)((0, import_node_path22.join)((0, import_node_os8.tmpdir)(), "mmi-rails-decrypt-"));
|
|
20471
|
+
const scriptPath = (0, import_node_path22.join)(scriptDir, "decrypt.rb");
|
|
20472
|
+
(0, import_node_fs24.writeFileSync)(scriptPath, script, "utf8");
|
|
20423
20473
|
try {
|
|
20424
20474
|
const args = ["exec", "ruby", scriptPath];
|
|
20425
20475
|
const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
|
|
@@ -20431,7 +20481,7 @@ async function decryptRailsCredentials(input) {
|
|
|
20431
20481
|
});
|
|
20432
20482
|
return JSON.parse(stdout);
|
|
20433
20483
|
} finally {
|
|
20434
|
-
(0,
|
|
20484
|
+
(0, import_node_fs24.rmSync)(scriptDir, { recursive: true, force: true });
|
|
20435
20485
|
}
|
|
20436
20486
|
}
|
|
20437
20487
|
async function readSecretStdin() {
|
|
@@ -20521,7 +20571,7 @@ function registerSecretsCommands(program3) {
|
|
|
20521
20571
|
let body;
|
|
20522
20572
|
if (o.file) {
|
|
20523
20573
|
try {
|
|
20524
|
-
body = (0,
|
|
20574
|
+
body = (0, import_node_fs24.readFileSync)((0, import_node_path22.resolve)(o.file), "utf8");
|
|
20525
20575
|
} catch (e) {
|
|
20526
20576
|
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
20527
20577
|
}
|
|
@@ -20626,7 +20676,7 @@ function registerSecretsCommands(program3) {
|
|
|
20626
20676
|
{
|
|
20627
20677
|
...d,
|
|
20628
20678
|
decryptRailsCredentials,
|
|
20629
|
-
removeFile: (path2) => (0,
|
|
20679
|
+
removeFile: (path2) => (0, import_node_fs24.unlinkSync)((0, import_node_path22.resolve)(o.appDir ?? process.cwd(), path2))
|
|
20630
20680
|
},
|
|
20631
20681
|
{
|
|
20632
20682
|
repo: o.repo,
|
|
@@ -20766,6 +20816,13 @@ async function mintInstallationToken(deps) {
|
|
|
20766
20816
|
}
|
|
20767
20817
|
return { token: body.token, expiresAt: body.expires_at };
|
|
20768
20818
|
}
|
|
20819
|
+
async function pollTokenOrUndefined(deps) {
|
|
20820
|
+
try {
|
|
20821
|
+
return (await mintInstallationToken(deps)).token;
|
|
20822
|
+
} catch {
|
|
20823
|
+
return void 0;
|
|
20824
|
+
}
|
|
20825
|
+
}
|
|
20769
20826
|
async function activateAppActor(commandPath3, env, mint) {
|
|
20770
20827
|
const requested = (env[APP_ACTOR_ENV] ?? "").trim();
|
|
20771
20828
|
if (!requested) return "personal";
|
|
@@ -20781,46 +20838,9 @@ async function activateAppActor(commandPath3, env, mint) {
|
|
|
20781
20838
|
env.GH_TOKEN = minted.token;
|
|
20782
20839
|
return "app";
|
|
20783
20840
|
}
|
|
20784
|
-
async function probeRatePools(fetchLike, token) {
|
|
20785
|
-
if (!token) return null;
|
|
20786
|
-
try {
|
|
20787
|
-
const res = await fetchLike("https://api.github.com/rate_limit", {
|
|
20788
|
-
method: "GET",
|
|
20789
|
-
headers: {
|
|
20790
|
-
accept: "application/vnd.github+json",
|
|
20791
|
-
"user-agent": "mmi-cli-app-actor",
|
|
20792
|
-
authorization: `Bearer ${token}`
|
|
20793
|
-
}
|
|
20794
|
-
});
|
|
20795
|
-
if (!res.ok) return null;
|
|
20796
|
-
const body = await res.json();
|
|
20797
|
-
const pool = (p) => typeof p?.remaining === "number" && typeof p?.limit === "number" && typeof p?.reset === "number" ? { remaining: p.remaining, limit: p.limit, reset: p.reset } : null;
|
|
20798
|
-
const core = pool(body.resources?.core);
|
|
20799
|
-
const graphql = pool(body.resources?.graphql);
|
|
20800
|
-
if (!core || !graphql) return null;
|
|
20801
|
-
return { core, graphql };
|
|
20802
|
-
} catch {
|
|
20803
|
-
return null;
|
|
20804
|
-
}
|
|
20805
|
-
}
|
|
20806
|
-
function renderPoolDetail(probe) {
|
|
20807
|
-
const fmt = (p) => `${p.remaining}/${p.limit} (resets ${new Date(p.reset * 1e3).toISOString().slice(11, 16)}Z)`;
|
|
20808
|
-
return `core ${fmt(probe.core)}, graphql ${fmt(probe.graphql)}`;
|
|
20809
|
-
}
|
|
20810
|
-
function checkGithubPools(probe) {
|
|
20811
|
-
if (!probe) return [];
|
|
20812
|
-
const lines = [];
|
|
20813
|
-
lines.push(probe.personal ? { id: "github-pools-personal", ok: true, label: "github pools (personal)", detail: renderPoolDetail(probe.personal) } : { id: "github-pools-personal", ok: false, label: "github pools (personal)", detail: "rate_limit unreadable", fix: "check `gh auth status`" });
|
|
20814
|
-
if (typeof probe.app === "string") {
|
|
20815
|
-
lines.push({ id: "github-pools-app", ok: false, label: "github pools (app actor)", fix: probe.app });
|
|
20816
|
-
} else if (probe.app) {
|
|
20817
|
-
lines.push({ id: "github-pools-app", ok: true, label: "github pools (app actor)", detail: renderPoolDetail(probe.app) });
|
|
20818
|
-
}
|
|
20819
|
-
return lines;
|
|
20820
|
-
}
|
|
20821
20841
|
|
|
20822
20842
|
// src/box-commands.ts
|
|
20823
|
-
var
|
|
20843
|
+
var import_node_fs25 = require("node:fs");
|
|
20824
20844
|
|
|
20825
20845
|
// src/box.ts
|
|
20826
20846
|
var BOX_KEYS = {
|
|
@@ -21023,7 +21043,7 @@ function registerBoxCommands(program3) {
|
|
|
21023
21043
|
}
|
|
21024
21044
|
if (o.json) console.log(JSON.stringify({ box: found, incomplete }, null, 2));
|
|
21025
21045
|
else if (o.ssh && o.script) {
|
|
21026
|
-
(0,
|
|
21046
|
+
(0, import_node_fs25.writeFileSync)(o.script, sshRecipeScript(found), "utf8");
|
|
21027
21047
|
console.log(`wrote ${o.script} \u2014 run: bash "${o.script}"`);
|
|
21028
21048
|
} else if (o.ssh) console.log(`${formatSshRecipe(found)}
|
|
21029
21049
|
${SSH_RECIPE_AGENT_NOTE}`);
|
|
@@ -21038,7 +21058,7 @@ ${SSH_RECIPE_AGENT_NOTE}`);
|
|
|
21038
21058
|
|
|
21039
21059
|
// src/schedules-commands.ts
|
|
21040
21060
|
var import_promises3 = require("node:fs/promises");
|
|
21041
|
-
var
|
|
21061
|
+
var import_node_child_process13 = require("node:child_process");
|
|
21042
21062
|
var import_node_util7 = require("node:util");
|
|
21043
21063
|
|
|
21044
21064
|
// src/schedules.ts
|
|
@@ -21326,12 +21346,6 @@ function spliceDoc(docText, generatedSection) {
|
|
|
21326
21346
|
}
|
|
21327
21347
|
return docText.slice(0, start) + generatedSection + docText.slice(end + DOC_END_MARKER.length);
|
|
21328
21348
|
}
|
|
21329
|
-
function driftClearableFrom(drift, repoName) {
|
|
21330
|
-
if (drift.class !== "file-vs-registry-stale") return false;
|
|
21331
|
-
const slash = drift.name.indexOf("/");
|
|
21332
|
-
if (slash <= 0) return false;
|
|
21333
|
-
return drift.name.slice(0, slash).toLowerCase() === repoName.toLowerCase();
|
|
21334
|
-
}
|
|
21335
21349
|
var HARBOUR_LLM_LAUNCHERS = /* @__PURE__ */ new Set(["cursor-agent"]);
|
|
21336
21350
|
function isHarbourLlmLauncher(executor) {
|
|
21337
21351
|
return HARBOUR_LLM_LAUNCHERS.has(executor);
|
|
@@ -21478,7 +21492,7 @@ function assembleReconciliation(githubEntries2, readRepos, registry2, activeWork
|
|
|
21478
21492
|
}
|
|
21479
21493
|
|
|
21480
21494
|
// src/schedules-commands.ts
|
|
21481
|
-
var execFileP5 = (0, import_node_util7.promisify)(
|
|
21495
|
+
var execFileP5 = (0, import_node_util7.promisify)(import_node_child_process13.execFile);
|
|
21482
21496
|
var AWS_REGION = "eu-central-1";
|
|
21483
21497
|
var AWS_TIMEOUT_MS = 3e4;
|
|
21484
21498
|
var AWS_RETRY_DELAY_MS = 1500;
|
|
@@ -21715,7 +21729,7 @@ function registerSchedulesCommands(program3) {
|
|
|
21715
21729
|
|
|
21716
21730
|
// src/file-lock.ts
|
|
21717
21731
|
var import_promises4 = require("node:fs/promises");
|
|
21718
|
-
var
|
|
21732
|
+
var import_node_path23 = require("node:path");
|
|
21719
21733
|
var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
21720
21734
|
var IMMEDIATE_RETRY_BUDGET = 3;
|
|
21721
21735
|
var FileLockBusyError = class extends Error {
|
|
@@ -21800,7 +21814,7 @@ async function releaseFileLock(lockPath, guard) {
|
|
|
21800
21814
|
}
|
|
21801
21815
|
async function withFileLock(lockPath, opts, fn) {
|
|
21802
21816
|
const resolved = resolveFileLockOpts(opts);
|
|
21803
|
-
await (0, import_promises4.mkdir)((0,
|
|
21817
|
+
await (0, import_promises4.mkdir)((0, import_node_path23.dirname)(lockPath), { recursive: true }).catch(() => void 0);
|
|
21804
21818
|
const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
|
|
21805
21819
|
try {
|
|
21806
21820
|
return await fn();
|
|
@@ -21811,7 +21825,7 @@ async function withFileLock(lockPath, opts, fn) {
|
|
|
21811
21825
|
|
|
21812
21826
|
// src/schedules-lift-command.ts
|
|
21813
21827
|
var import_promises5 = require("node:fs/promises");
|
|
21814
|
-
var
|
|
21828
|
+
var import_node_path24 = require("node:path");
|
|
21815
21829
|
|
|
21816
21830
|
// src/schedules-lift.ts
|
|
21817
21831
|
var SCHEDULE_HEADER_FIELDS = ["schedule", "what", "owner", "cadence", "executor", "llm", "output", "breaks", "kill"];
|
|
@@ -21917,7 +21931,7 @@ async function readWorkflowFiles(dir) {
|
|
|
21917
21931
|
const files = [];
|
|
21918
21932
|
for (const name of names.sort()) {
|
|
21919
21933
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
21920
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises5.readFile)((0,
|
|
21934
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises5.readFile)((0, import_node_path24.join)(dir, name), "utf8") });
|
|
21921
21935
|
}
|
|
21922
21936
|
return files;
|
|
21923
21937
|
}
|
|
@@ -22459,9 +22473,9 @@ function registerQueryCommands(program3) {
|
|
|
22459
22473
|
}
|
|
22460
22474
|
|
|
22461
22475
|
// src/bootstrap-commands.ts
|
|
22462
|
-
var
|
|
22476
|
+
var import_node_fs26 = require("node:fs");
|
|
22463
22477
|
var import_node_os9 = require("node:os");
|
|
22464
|
-
var
|
|
22478
|
+
var import_node_path25 = require("node:path");
|
|
22465
22479
|
|
|
22466
22480
|
// src/bootstrap-drift.ts
|
|
22467
22481
|
function byteComparableSeeds(manifest, cls) {
|
|
@@ -23075,13 +23089,13 @@ function registerBootstrapCommands(program3) {
|
|
|
23075
23089
|
client: defaultGitHubClient(),
|
|
23076
23090
|
projectMeta: meta,
|
|
23077
23091
|
deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
|
|
23078
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
23092
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs26.existsSync)(path2) ? (0, import_node_fs26.readFileSync)(path2, "utf8") : null,
|
|
23079
23093
|
// requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
|
|
23080
23094
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
23081
23095
|
// #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
|
|
23082
23096
|
// permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
|
|
23083
23097
|
// sanction, which is the pre-#3664 behaviour.
|
|
23084
|
-
sanctionedAdmins: (0,
|
|
23098
|
+
sanctionedAdmins: (0, import_node_fs26.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs26.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
|
|
23085
23099
|
requiredGcpApis: (() => {
|
|
23086
23100
|
const v = meta?.requiredGcpApis;
|
|
23087
23101
|
if (Array.isArray(v)) return v;
|
|
@@ -23119,12 +23133,12 @@ function registerBootstrapCommands(program3) {
|
|
|
23119
23133
|
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 () => {
|
|
23120
23134
|
const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
|
|
23121
23135
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
23122
|
-
if (!(0,
|
|
23123
|
-
const manifest = loadBootstrapSeeds((0,
|
|
23136
|
+
if (!(0, import_node_fs26.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`);
|
|
23137
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
|
|
23124
23138
|
const hubContents = /* @__PURE__ */ new Map();
|
|
23125
23139
|
for (const s of manifest.seeds) {
|
|
23126
23140
|
if (s.ownership !== "org" || s.source !== "self") continue;
|
|
23127
|
-
hubContents.set(s.target, (0,
|
|
23141
|
+
hubContents.set(s.target, (0, import_node_fs26.existsSync)(s.target) ? (0, import_node_fs26.readFileSync)(s.target, "utf8") : null);
|
|
23128
23142
|
}
|
|
23129
23143
|
let targets;
|
|
23130
23144
|
let classOf = (_repo) => "deployable";
|
|
@@ -23201,8 +23215,8 @@ function registerBootstrapCommands(program3) {
|
|
|
23201
23215
|
return fail(`bootstrap apply: ${e.message}`);
|
|
23202
23216
|
}
|
|
23203
23217
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
23204
|
-
if (!(0,
|
|
23205
|
-
const manifest = loadBootstrapSeeds((0,
|
|
23218
|
+
if (!(0, import_node_fs26.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`);
|
|
23219
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
|
|
23206
23220
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
23207
23221
|
const slug = parsedRepo.slug;
|
|
23208
23222
|
const onlyTarget = o.only.trim();
|
|
@@ -23213,16 +23227,16 @@ function registerBootstrapCommands(program3) {
|
|
|
23213
23227
|
${known}`);
|
|
23214
23228
|
}
|
|
23215
23229
|
const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
|
|
23216
|
-
const readFile9 = (p) => (0,
|
|
23230
|
+
const readFile9 = (p) => (0, import_node_fs26.existsSync)(p) ? (0, import_node_fs26.readFileSync)(p, "utf8") : null;
|
|
23217
23231
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
23218
23232
|
const putSeed = async (target, content, ref, sha) => {
|
|
23219
|
-
const tmp = (0,
|
|
23220
|
-
(0,
|
|
23233
|
+
const tmp = (0, import_node_path25.join)((0, import_node_os9.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
23234
|
+
(0, import_node_fs26.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
23221
23235
|
try {
|
|
23222
23236
|
await gh(contentPutInputArgs(repo, target, tmp));
|
|
23223
23237
|
} finally {
|
|
23224
23238
|
try {
|
|
23225
|
-
(0,
|
|
23239
|
+
(0, import_node_fs26.unlinkSync)(tmp);
|
|
23226
23240
|
} catch {
|
|
23227
23241
|
}
|
|
23228
23242
|
}
|
|
@@ -23474,12 +23488,12 @@ LIVE apply to ${repo}:
|
|
|
23474
23488
|
}
|
|
23475
23489
|
|
|
23476
23490
|
// src/stage-commands.ts
|
|
23477
|
-
var
|
|
23478
|
-
var
|
|
23491
|
+
var import_node_fs28 = require("node:fs");
|
|
23492
|
+
var import_node_path27 = require("node:path");
|
|
23479
23493
|
|
|
23480
23494
|
// src/port-registry.ts
|
|
23481
|
-
var
|
|
23482
|
-
var
|
|
23495
|
+
var import_node_fs27 = require("node:fs");
|
|
23496
|
+
var import_node_path26 = require("node:path");
|
|
23483
23497
|
|
|
23484
23498
|
// ../infra/port-geometry.mjs
|
|
23485
23499
|
var PORT_BLOCK = 100;
|
|
@@ -23493,8 +23507,8 @@ function nextPortBlock(registry2) {
|
|
|
23493
23507
|
return [base, base + PORT_SPAN];
|
|
23494
23508
|
}
|
|
23495
23509
|
function loadPortRegistry(path2) {
|
|
23496
|
-
if (!(0,
|
|
23497
|
-
const raw = JSON.parse((0,
|
|
23510
|
+
if (!(0, import_node_fs27.existsSync)(path2)) return {};
|
|
23511
|
+
const raw = JSON.parse((0, import_node_fs27.readFileSync)(path2, "utf8"));
|
|
23498
23512
|
const out = {};
|
|
23499
23513
|
for (const [key, value] of Object.entries(raw)) {
|
|
23500
23514
|
if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
|
|
@@ -23508,9 +23522,9 @@ function ensurePortRange(repo, path2) {
|
|
|
23508
23522
|
const existing = registry2[repo];
|
|
23509
23523
|
if (existing) return existing;
|
|
23510
23524
|
const range = nextPortBlock(registry2);
|
|
23511
|
-
const raw = (0,
|
|
23525
|
+
const raw = (0, import_node_fs27.existsSync)(path2) ? JSON.parse((0, import_node_fs27.readFileSync)(path2, "utf8")) : {};
|
|
23512
23526
|
raw[repo] = range;
|
|
23513
|
-
(0,
|
|
23527
|
+
(0, import_node_fs27.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
|
|
23514
23528
|
return range;
|
|
23515
23529
|
}
|
|
23516
23530
|
function portCursorSeed(registry2) {
|
|
@@ -23532,22 +23546,22 @@ function existingPortRange(repo, registry2) {
|
|
|
23532
23546
|
return registry2[repo] ?? null;
|
|
23533
23547
|
}
|
|
23534
23548
|
function portRangeInfraAt(root, source) {
|
|
23535
|
-
const registryPath = (0,
|
|
23536
|
-
const ddbScriptPath = (0,
|
|
23537
|
-
if (!(0,
|
|
23549
|
+
const registryPath = (0, import_node_path26.join)(root, "infra", "port-ranges.json");
|
|
23550
|
+
const ddbScriptPath = (0, import_node_path26.join)(root, "infra", "port-ddb.mjs");
|
|
23551
|
+
if (!(0, import_node_fs27.existsSync)(registryPath) || !(0, import_node_fs27.existsSync)(ddbScriptPath)) return null;
|
|
23538
23552
|
return { root, source, registryPath, ddbScriptPath };
|
|
23539
23553
|
}
|
|
23540
23554
|
function resolvePortRangeInfra(cwd, packageDir) {
|
|
23541
23555
|
const direct = portRangeInfraAt(cwd, "cwd");
|
|
23542
23556
|
if (direct) return direct;
|
|
23543
|
-
for (let dir = cwd; ; dir = (0,
|
|
23544
|
-
const sibling = portRangeInfraAt((0,
|
|
23557
|
+
for (let dir = cwd; ; dir = (0, import_node_path26.dirname)(dir)) {
|
|
23558
|
+
const sibling = portRangeInfraAt((0, import_node_path26.join)(dir, "MMI-Hub"), "sibling-hub");
|
|
23545
23559
|
if (sibling) return sibling;
|
|
23546
|
-
const parent = (0,
|
|
23560
|
+
const parent = (0, import_node_path26.dirname)(dir);
|
|
23547
23561
|
if (parent === dir) break;
|
|
23548
23562
|
}
|
|
23549
23563
|
if (packageDir) {
|
|
23550
|
-
const pkgRoot = (0,
|
|
23564
|
+
const pkgRoot = (0, import_node_path26.join)(packageDir, "..", "..");
|
|
23551
23565
|
const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
|
|
23552
23566
|
if (pkgFrom) return pkgFrom;
|
|
23553
23567
|
}
|
|
@@ -23723,8 +23737,8 @@ function registerStageCommands(program3) {
|
|
|
23723
23737
|
const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
|
|
23724
23738
|
return decideStage({
|
|
23725
23739
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
23726
|
-
hasCompose: (0,
|
|
23727
|
-
hasEnvExample: (0,
|
|
23740
|
+
hasCompose: (0, import_node_fs28.existsSync)((0, import_node_path27.join)(process.cwd(), "docker-compose.yml")),
|
|
23741
|
+
hasEnvExample: (0, import_node_fs28.existsSync)((0, import_node_path27.join)(process.cwd(), ".env.example"))
|
|
23728
23742
|
});
|
|
23729
23743
|
}
|
|
23730
23744
|
async function fetchStageVaultEnvMerge() {
|
|
@@ -24162,11 +24176,11 @@ function registerBoardCommands(program3) {
|
|
|
24162
24176
|
}
|
|
24163
24177
|
|
|
24164
24178
|
// src/merge-cleanup.ts
|
|
24165
|
-
var
|
|
24179
|
+
var import_node_fs29 = require("node:fs");
|
|
24166
24180
|
var import_promises7 = require("node:fs/promises");
|
|
24167
|
-
var
|
|
24181
|
+
var import_node_path29 = require("node:path");
|
|
24168
24182
|
var import_node_os10 = require("node:os");
|
|
24169
|
-
var
|
|
24183
|
+
var import_node_child_process14 = require("node:child_process");
|
|
24170
24184
|
|
|
24171
24185
|
// src/board-advance.ts
|
|
24172
24186
|
function repoOf2(ref) {
|
|
@@ -24252,7 +24266,7 @@ function boardAdvanceFailureMessage(result) {
|
|
|
24252
24266
|
|
|
24253
24267
|
// src/deferred-registry-store.ts
|
|
24254
24268
|
var import_promises6 = require("node:fs/promises");
|
|
24255
|
-
var
|
|
24269
|
+
var import_node_path28 = require("node:path");
|
|
24256
24270
|
var sleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
24257
24271
|
async function atomicWrite(target, contents) {
|
|
24258
24272
|
const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
@@ -24303,12 +24317,12 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
|
|
|
24303
24317
|
},
|
|
24304
24318
|
// Standalone atomic write — THROWS on failure (no best-effort swallow, #2846).
|
|
24305
24319
|
write: async (entries) => {
|
|
24306
|
-
await (0, import_promises6.mkdir)((0,
|
|
24320
|
+
await (0, import_promises6.mkdir)((0, import_node_path28.dirname)(registryPath), { recursive: true });
|
|
24307
24321
|
await atomicWrite(registryPath, serializeDeferredWorktrees(entries));
|
|
24308
24322
|
},
|
|
24309
24323
|
// Serialized read-modify-write under the repo-wide lock (#2846).
|
|
24310
24324
|
update: async (mutate) => {
|
|
24311
|
-
await (0, import_promises6.mkdir)((0,
|
|
24325
|
+
await (0, import_promises6.mkdir)((0, import_node_path28.dirname)(registryPath), { recursive: true });
|
|
24312
24326
|
const deadline = Date.now() + opts.maxWaitMs;
|
|
24313
24327
|
for (; ; ) {
|
|
24314
24328
|
const guard = await acquireLock(lockPath, opts, deadline);
|
|
@@ -24469,7 +24483,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
24469
24483
|
);
|
|
24470
24484
|
const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
24471
24485
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
24472
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
24486
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path29.dirname)((0, import_node_path29.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
24473
24487
|
const gcActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
|
|
24474
24488
|
const owners = readWorktreeOwners(primaryRepoRoot);
|
|
24475
24489
|
const removalNow = Date.now();
|
|
@@ -24500,7 +24514,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
24500
24514
|
const cleanup = await cleanupPrMergeLocalBranch(branch.branch, {
|
|
24501
24515
|
beforeWorktrees,
|
|
24502
24516
|
startingPath: branch.worktreePath,
|
|
24503
|
-
pathExists: (p) => (0,
|
|
24517
|
+
pathExists: (p) => (0, import_node_fs29.existsSync)(p),
|
|
24504
24518
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
24505
24519
|
teardownWorktreeStage,
|
|
24506
24520
|
deferredStore,
|
|
@@ -24528,7 +24542,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
24528
24542
|
for (const wt of worktreeDirsToRemove) {
|
|
24529
24543
|
try {
|
|
24530
24544
|
const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, siblingRoot, {
|
|
24531
|
-
realpath: (path2) => (0,
|
|
24545
|
+
realpath: (path2) => (0, import_node_fs29.realpathSync)(path2)
|
|
24532
24546
|
});
|
|
24533
24547
|
if (!cleanupTarget.ok) {
|
|
24534
24548
|
result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
|
|
@@ -24593,13 +24607,13 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
|
|
|
24593
24607
|
const commits = JSON.parse(raw).commits ?? [];
|
|
24594
24608
|
const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
|
|
24595
24609
|
if (!body) return void 0;
|
|
24596
|
-
const dir = (0,
|
|
24597
|
-
const path2 = (0,
|
|
24598
|
-
(0,
|
|
24610
|
+
const dir = (0, import_node_fs29.mkdtempSync)((0, import_node_path29.join)((0, import_node_os10.tmpdir)(), "mmi-squash-body-"));
|
|
24611
|
+
const path2 = (0, import_node_path29.join)(dir, "body.txt");
|
|
24612
|
+
(0, import_node_fs29.writeFileSync)(path2, `${body}
|
|
24599
24613
|
`, "utf8");
|
|
24600
24614
|
return { path: path2, cleanup: () => {
|
|
24601
24615
|
try {
|
|
24602
|
-
(0,
|
|
24616
|
+
(0, import_node_fs29.rmSync)(dir, { recursive: true, force: true });
|
|
24603
24617
|
} catch {
|
|
24604
24618
|
}
|
|
24605
24619
|
} };
|
|
@@ -24707,7 +24721,7 @@ async function remoteBranchExists2(branch, options = {}) {
|
|
|
24707
24721
|
}
|
|
24708
24722
|
var COMPOSE_TIMEOUT_MS = 12e4;
|
|
24709
24723
|
function spawnDeferredGcSweep() {
|
|
24710
|
-
spawnDetachedSelf(["worktree", "gc", "sweep-deferred", "--quiet"], { spawn:
|
|
24724
|
+
spawnDetachedSelf(["worktree", "gc", "sweep-deferred", "--quiet"], { spawn: import_node_child_process14.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
24711
24725
|
}
|
|
24712
24726
|
async function createDeferredWorktreeStore() {
|
|
24713
24727
|
try {
|
|
@@ -24721,13 +24735,13 @@ var realWorktreeDirRemover = {
|
|
|
24721
24735
|
probe: (p) => {
|
|
24722
24736
|
let st;
|
|
24723
24737
|
try {
|
|
24724
|
-
st = (0,
|
|
24738
|
+
st = (0, import_node_fs29.lstatSync)(p);
|
|
24725
24739
|
} catch {
|
|
24726
24740
|
return null;
|
|
24727
24741
|
}
|
|
24728
24742
|
if (st.isSymbolicLink()) return "link";
|
|
24729
24743
|
try {
|
|
24730
|
-
(0,
|
|
24744
|
+
(0, import_node_fs29.readlinkSync)(p);
|
|
24731
24745
|
return "link";
|
|
24732
24746
|
} catch {
|
|
24733
24747
|
}
|
|
@@ -24735,7 +24749,7 @@ var realWorktreeDirRemover = {
|
|
|
24735
24749
|
},
|
|
24736
24750
|
readdir: (p) => {
|
|
24737
24751
|
try {
|
|
24738
|
-
return (0,
|
|
24752
|
+
return (0, import_node_fs29.readdirSync)(p);
|
|
24739
24753
|
} catch {
|
|
24740
24754
|
return [];
|
|
24741
24755
|
}
|
|
@@ -24744,9 +24758,9 @@ var realWorktreeDirRemover = {
|
|
|
24744
24758
|
// leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
|
|
24745
24759
|
detachLink: (p) => {
|
|
24746
24760
|
try {
|
|
24747
|
-
(0,
|
|
24761
|
+
(0, import_node_fs29.rmdirSync)(p);
|
|
24748
24762
|
} catch {
|
|
24749
|
-
(0,
|
|
24763
|
+
(0, import_node_fs29.unlinkSync)(p);
|
|
24750
24764
|
}
|
|
24751
24765
|
},
|
|
24752
24766
|
removeTree: (p) => (0, import_promises7.rm)(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
|
|
@@ -24779,9 +24793,9 @@ async function worktreeHasStageState(worktreePath) {
|
|
|
24779
24793
|
}
|
|
24780
24794
|
}
|
|
24781
24795
|
function stageStateFileBelongsToWorktree(statePath, worktreePath) {
|
|
24782
|
-
if (!(0,
|
|
24796
|
+
if (!(0, import_node_fs29.existsSync)(statePath)) return false;
|
|
24783
24797
|
try {
|
|
24784
|
-
const state = JSON.parse((0,
|
|
24798
|
+
const state = JSON.parse((0, import_node_fs29.readFileSync)(statePath, "utf8"));
|
|
24785
24799
|
const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
|
|
24786
24800
|
return Boolean(recordedCwd && isPathUnderDirectory(recordedCwd, worktreePath));
|
|
24787
24801
|
} catch {
|
|
@@ -24804,8 +24818,25 @@ function teardownWorktreeStage(worktreePath) {
|
|
|
24804
24818
|
|
|
24805
24819
|
// src/pr-checks-rest.ts
|
|
24806
24820
|
var REST_GH_TIMEOUT_MS = 2e4;
|
|
24821
|
+
var pollTokenProvider;
|
|
24822
|
+
var pollTokenOnce;
|
|
24823
|
+
function setPollTokenProvider(provider) {
|
|
24824
|
+
pollTokenProvider = provider;
|
|
24825
|
+
pollTokenOnce = void 0;
|
|
24826
|
+
}
|
|
24827
|
+
async function pollToken() {
|
|
24828
|
+
if (!pollTokenProvider) return void 0;
|
|
24829
|
+
pollTokenOnce ??= pollTokenProvider().catch(() => void 0);
|
|
24830
|
+
return pollTokenOnce;
|
|
24831
|
+
}
|
|
24807
24832
|
async function defaultGhApi(args) {
|
|
24808
|
-
const
|
|
24833
|
+
const token = await pollToken();
|
|
24834
|
+
const { stdout } = await execFileP2("gh", ["api", ...args], {
|
|
24835
|
+
timeout: REST_GH_TIMEOUT_MS,
|
|
24836
|
+
// Only when a token was actually minted: passing `env` at all would otherwise replace the
|
|
24837
|
+
// inherited environment wholesale, and `gh` needs the rest of it (PATH, GH_HOST, config home).
|
|
24838
|
+
...token ? { env: { ...process.env, GH_TOKEN: token } } : {}
|
|
24839
|
+
});
|
|
24809
24840
|
return stdout;
|
|
24810
24841
|
}
|
|
24811
24842
|
function classifyCheckRun(run) {
|
|
@@ -24988,9 +25019,9 @@ async function fetchRestCorePool(gh = defaultGhApi) {
|
|
|
24988
25019
|
}
|
|
24989
25020
|
|
|
24990
25021
|
// src/worktree-lifecycle-commands.ts
|
|
24991
|
-
var
|
|
25022
|
+
var import_node_fs30 = require("node:fs");
|
|
24992
25023
|
var import_promises8 = require("node:fs/promises");
|
|
24993
|
-
var
|
|
25024
|
+
var import_node_path30 = require("node:path");
|
|
24994
25025
|
var GH_TIMEOUT_MS = 2e4;
|
|
24995
25026
|
var DEFAULT_BASE = "origin/development";
|
|
24996
25027
|
var DEFAULT_REMOTE = "origin";
|
|
@@ -25126,7 +25157,7 @@ function classifyStaleLeaks(input) {
|
|
|
25126
25157
|
var defaultOrphanDirScanDeps = {
|
|
25127
25158
|
listDirs: (root) => {
|
|
25128
25159
|
try {
|
|
25129
|
-
return (0,
|
|
25160
|
+
return (0, import_node_fs30.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path30.join)(root, e.name));
|
|
25130
25161
|
} catch {
|
|
25131
25162
|
return [];
|
|
25132
25163
|
}
|
|
@@ -25273,13 +25304,13 @@ function registerWorktreeCommands(program3) {
|
|
|
25273
25304
|
const headBorn = await execFileP2("git", ["-C", wtPath || ".", "rev-parse", "--verify", "--quiet", "HEAD"], { timeout: GIT_TIMEOUT_MS }).then(() => true).catch(() => false);
|
|
25274
25305
|
const branch = headBorn ? (await execFileP2("git", ["rev-parse", "--abbrev-ref", "HEAD"], { timeout: GIT_TIMEOUT_MS })).stdout.trim() : (await execFileP2("git", ["symbolic-ref", "--quiet", "--short", "HEAD"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
25275
25306
|
if (!wtPath || !branch) return fail("worktree land: not inside a git worktree");
|
|
25276
|
-
const gitFile = (0,
|
|
25277
|
-
const isLinked = (0,
|
|
25307
|
+
const gitFile = (0, import_node_path30.join)(wtPath, ".git");
|
|
25308
|
+
const isLinked = (0, import_node_fs30.existsSync)(gitFile) && (0, import_node_fs30.statSync)(gitFile).isFile();
|
|
25278
25309
|
if (apply && !isLinked) {
|
|
25279
25310
|
return fail("worktree land: run from inside the linked worktree you want to land (this is the primary checkout)");
|
|
25280
25311
|
}
|
|
25281
25312
|
const commonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
25282
|
-
const primaryCheckout = commonDir ? (0,
|
|
25313
|
+
const primaryCheckout = commonDir ? (0, import_node_path30.dirname)(commonDir) : wtPath;
|
|
25283
25314
|
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);
|
|
25284
25315
|
const orphan = classifyOrphanedWorktree({
|
|
25285
25316
|
branch,
|
|
@@ -25466,10 +25497,10 @@ async function gatherWorktreeContext() {
|
|
|
25466
25497
|
if (s) stages.push({ path: wt.path, port: s.port });
|
|
25467
25498
|
}
|
|
25468
25499
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
25469
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
25500
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path30.dirname)((0, import_node_path30.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
25470
25501
|
const wtRoot = siblingMmiWorktreesRoot(primaryRepoRoot);
|
|
25471
25502
|
let orphanDirs = [];
|
|
25472
|
-
if ((0,
|
|
25503
|
+
if ((0, import_node_fs30.existsSync)(wtRoot)) {
|
|
25473
25504
|
orphanDirs = scanOrphanDirs(wtRoot, worktreeGitRoot, {
|
|
25474
25505
|
...defaultOrphanDirScanDeps,
|
|
25475
25506
|
listDirs: (root) => worktreeScanDirs(root, primaryRepoRoot, defaultOrphanDirScanDeps.listDirs, isRepoCheckoutDir)
|
|
@@ -25492,7 +25523,7 @@ async function bestEffortGit(args, cwd, step, timeoutMs = GIT_TIMEOUT_MS) {
|
|
|
25492
25523
|
}
|
|
25493
25524
|
|
|
25494
25525
|
// src/issue-commands.ts
|
|
25495
|
-
var
|
|
25526
|
+
var import_node_fs31 = require("node:fs");
|
|
25496
25527
|
var import_node_crypto5 = require("node:crypto");
|
|
25497
25528
|
var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
|
|
25498
25529
|
var ReparentConflictError = class extends Error {
|
|
@@ -25510,7 +25541,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
25510
25541
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
25511
25542
|
const patch = {};
|
|
25512
25543
|
let bodyChanged = false;
|
|
25513
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
25544
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs31.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
25514
25545
|
if (options.titleFile !== void 0) {
|
|
25515
25546
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
25516
25547
|
} else if (options.title !== void 0) {
|
|
@@ -26073,7 +26104,7 @@ function extendCreateCommand(issue2, batchAttach) {
|
|
|
26073
26104
|
if (opts.batch) {
|
|
26074
26105
|
let specs;
|
|
26075
26106
|
try {
|
|
26076
|
-
const raw = (0,
|
|
26107
|
+
const raw = (0, import_node_fs31.readFileSync)(opts.batch, "utf8");
|
|
26077
26108
|
specs = JSON.parse(raw);
|
|
26078
26109
|
if (!Array.isArray(specs)) throw new Error("batch file must contain a JSON array");
|
|
26079
26110
|
} catch (e) {
|
|
@@ -26147,8 +26178,8 @@ ${lines}`, {
|
|
|
26147
26178
|
}
|
|
26148
26179
|
|
|
26149
26180
|
// src/train-commands.ts
|
|
26150
|
-
var
|
|
26151
|
-
var
|
|
26181
|
+
var import_node_fs32 = require("node:fs");
|
|
26182
|
+
var import_node_path31 = require("node:path");
|
|
26152
26183
|
|
|
26153
26184
|
// src/train-status.ts
|
|
26154
26185
|
function buildTrainStatusReport(input) {
|
|
@@ -26188,7 +26219,7 @@ function formatTrainStatus(r) {
|
|
|
26188
26219
|
// src/train-commands.ts
|
|
26189
26220
|
function readRepoVersion() {
|
|
26190
26221
|
try {
|
|
26191
|
-
return JSON.parse((0,
|
|
26222
|
+
return JSON.parse((0, import_node_fs32.readFileSync)((0, import_node_path31.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
26192
26223
|
} catch {
|
|
26193
26224
|
return void 0;
|
|
26194
26225
|
}
|
|
@@ -26334,9 +26365,9 @@ function registerDeployCommands(program3) {
|
|
|
26334
26365
|
}
|
|
26335
26366
|
|
|
26336
26367
|
// src/discovery-commands.ts
|
|
26337
|
-
var
|
|
26368
|
+
var import_node_fs33 = require("node:fs");
|
|
26338
26369
|
var import_node_os11 = require("node:os");
|
|
26339
|
-
var
|
|
26370
|
+
var import_node_path32 = require("node:path");
|
|
26340
26371
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
26341
26372
|
async function collectStatus() {
|
|
26342
26373
|
let branch = "";
|
|
@@ -26513,8 +26544,8 @@ async function collectOnboardStatus() {
|
|
|
26513
26544
|
}
|
|
26514
26545
|
const home = (0, import_node_os11.homedir)();
|
|
26515
26546
|
const plugin = onboardPluginGate({
|
|
26516
|
-
readKnown: () => readFileSyncSafe((0,
|
|
26517
|
-
readSettings: () => readFileSyncSafe((0,
|
|
26547
|
+
readKnown: () => readFileSyncSafe((0, import_node_path32.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs33.readFileSync),
|
|
26548
|
+
readSettings: () => readFileSyncSafe((0, import_node_path32.join)(home, ".claude", "settings.json"), import_node_fs33.readFileSync)
|
|
26518
26549
|
});
|
|
26519
26550
|
return { track, board, registry: registry2, secrets, plugin, nextCommand };
|
|
26520
26551
|
}
|
|
@@ -28100,7 +28131,7 @@ function planSurfaceRepair(diagnosis) {
|
|
|
28100
28131
|
owner: "mmi-cli",
|
|
28101
28132
|
command: "mmi-cli plugin heal",
|
|
28102
28133
|
changes: ["replace the active MMI plugin installation", `reload via ${descriptor.reload}`],
|
|
28103
|
-
instruction: `run \`mmi-cli doctor
|
|
28134
|
+
instruction: `run \`mmi-cli doctor\` (or \`mmi-cli plugin heal\`), then ${reloadInstruction(descriptor)}`
|
|
28104
28135
|
};
|
|
28105
28136
|
}
|
|
28106
28137
|
function buildSurfaceDoctorCheck(diagnosis) {
|
|
@@ -28206,34 +28237,6 @@ function checkRepoWorktrees(probe) {
|
|
|
28206
28237
|
]
|
|
28207
28238
|
};
|
|
28208
28239
|
}
|
|
28209
|
-
function formatBytes(bytes, partial) {
|
|
28210
|
-
const prefix = partial ? "\u2265" : "";
|
|
28211
|
-
return bytes >= 1e9 ? `${prefix}${(bytes / 1e9).toFixed(2)} GB` : `${prefix}${(bytes / 1e6).toFixed(1)} MB`;
|
|
28212
|
-
}
|
|
28213
|
-
function checkWorktreeRoots(probe) {
|
|
28214
|
-
if (!probe) return null;
|
|
28215
|
-
const evidence = [
|
|
28216
|
-
`scanned by worktree gc/list: ${probe.authoritative}`,
|
|
28217
|
-
...probe.stray.length ? probe.stray.map((s) => `unreachable by any gc: ${s.path} \u2014 ${s.dirs} dir(s), ${formatBytes(s.bytes, s.partial)}`) : ["no other *worktrees* directory beside this repo"]
|
|
28218
|
-
];
|
|
28219
|
-
if (!probe.stray.length) return { ok: true, id: "worktree-roots", label: "worktree roots", verbose: evidence };
|
|
28220
|
-
const named = probe.stray.map((s) => `${s.path} (${s.dirs} dir(s), ${formatBytes(s.bytes, s.partial)})`).join("; ");
|
|
28221
|
-
const cutShort = probe.stray.some((s) => s.partial) ? " (a \u2265 figure is a floor \u2014 the size walk hit its time budget, so the real total is larger)" : "";
|
|
28222
|
-
return {
|
|
28223
|
-
ok: false,
|
|
28224
|
-
id: "worktree-roots",
|
|
28225
|
-
label: "worktree roots",
|
|
28226
|
-
// #3485: report-only in EVERY mode means report-only in the exit code too. `worktree gc --root` does
|
|
28227
|
-
// exist and is the eventual fix, but this row is deliberately not a gate: the remedy is a human
|
|
28228
|
-
// review of directories that may hold unlanded work, and a size-based auto-sweep once nearly deleted
|
|
28229
|
-
// an unlanded correctness fix. Counting it made `mmi-cli doctor` exit 1 until someone swept by hand.
|
|
28230
|
-
reportOnly: true,
|
|
28231
|
-
// The fix is a review, never a delete: `gc --root` still refuses anything it cannot classify as dead,
|
|
28232
|
-
// and doctor itself — with or without --apply — never touches these directories.
|
|
28233
|
-
fix: `${probe.stray.length} worktrees root(s) outside ${probe.authoritative} \u2014 ${named}; no gc scans them. Review, then sweep deliberately from the owning repo with \`mmi-cli worktree gc --root <path> --apply\` (report-only here: doctor never deletes these)${cutShort}`,
|
|
28234
|
-
verbose: evidence
|
|
28235
|
-
};
|
|
28236
|
-
}
|
|
28237
28240
|
function checkCodexHookTrust(probe, displayName = "Codex") {
|
|
28238
28241
|
if (!probe?.applicable) return null;
|
|
28239
28242
|
const evidence = [
|
|
@@ -28344,81 +28347,6 @@ function checkTrainSync(result) {
|
|
|
28344
28347
|
verbose: evidence
|
|
28345
28348
|
};
|
|
28346
28349
|
}
|
|
28347
|
-
function checkSchedules(probe) {
|
|
28348
|
-
if (!probe) return null;
|
|
28349
|
-
const evidence = [
|
|
28350
|
-
`${probe.armed} armed (${probe.live} live, ${probe.declared} declared)`,
|
|
28351
|
-
...probe.incomplete.map((i) => `incomplete: ${i}`),
|
|
28352
|
-
...probe.drift.map((d) => `drift: ${d}`)
|
|
28353
|
-
];
|
|
28354
|
-
const clearableHere = probe.clearableHere ?? 0;
|
|
28355
|
-
if (probe.incomplete.length) {
|
|
28356
|
-
return {
|
|
28357
|
-
ok: false,
|
|
28358
|
-
id: "schedules",
|
|
28359
|
-
label: "schedules",
|
|
28360
|
-
// #3485: ruled report-only — an unreadable source is a read failure somewhere in the org, and
|
|
28361
|
-
// nothing in this checkout clears it.
|
|
28362
|
-
reportOnly: true,
|
|
28363
|
-
detail: `${probe.incomplete.length} source(s) unreadable`,
|
|
28364
|
-
fix: "run `mmi-cli org schedules` for the full report \u2014 the notebook may be missing armed entries",
|
|
28365
|
-
verbose: evidence
|
|
28366
|
-
};
|
|
28367
|
-
}
|
|
28368
|
-
if (probe.drift.length) {
|
|
28369
|
-
return {
|
|
28370
|
-
ok: false,
|
|
28371
|
-
id: "schedules",
|
|
28372
|
-
label: "schedules",
|
|
28373
|
-
// #3485 ruled this row report-only because the drift classes are owned by other repos. #3492 makes
|
|
28374
|
-
// that conditional rather than blanket: when a finding IS clearable from this checkout — a
|
|
28375
|
-
// `file-vs-registry-stale` row for this repo, one `org schedules register` away — the row gates
|
|
28376
|
-
// like any other actionable red. Exempting a fault the operator can fix in one command is the
|
|
28377
|
-
// tolerated-red failure the tier exists to prevent, not an instance of it.
|
|
28378
|
-
...clearableHere > 0 ? {} : { reportOnly: true },
|
|
28379
|
-
detail: `${probe.drift.length} drift finding(s)`,
|
|
28380
|
-
fix: clearableHere > 0 ? `${clearableHere} of ${probe.drift.length} clearable from this repo \u2014 from an up-to-date checkout run \`mmi-cli org schedules register\` here, then \`mmi-cli org schedules\` for the rest (each line names its own per-class remedy, applied in the owning repo)` : "run `mmi-cli org schedules` \u2014 each drift line names its per-class remedy (file/registry/live mismatch or harbour enforcement), applied in the owning repo",
|
|
28381
|
-
verbose: evidence
|
|
28382
|
-
};
|
|
28383
|
-
}
|
|
28384
|
-
return { ok: true, id: "schedules", label: "schedules", detail: `${probe.armed} armed`, verbose: evidence };
|
|
28385
|
-
}
|
|
28386
|
-
function checkDocsAudit(probe) {
|
|
28387
|
-
if (!probe) return null;
|
|
28388
|
-
if (!probe.armed) {
|
|
28389
|
-
return { ok: true, id: "docs-audit", label: "docs-audit", detail: "janitor not armed", verbose: [probe.detail] };
|
|
28390
|
-
}
|
|
28391
|
-
if (!probe.ok) {
|
|
28392
|
-
return {
|
|
28393
|
-
ok: false,
|
|
28394
|
-
id: "docs-audit",
|
|
28395
|
-
label: "docs-audit",
|
|
28396
|
-
// #3485: ruled report-only — the remedy is re-running or re-arming the janitor in the repo that
|
|
28397
|
-
// owns it. `mmi-cli docs audit record` can write a verdict from here, but hand-writing one to clear
|
|
28398
|
-
// a dead-man check defeats the check (#3067 pillar 4: silence is an alarm, never a success).
|
|
28399
|
-
reportOnly: true,
|
|
28400
|
-
fix: `${probe.detail} \u2014 re-run the janitor in the owning repo or re-arm its schedule (the remedy never lives in this doctor)`,
|
|
28401
|
-
verbose: [probe.detail]
|
|
28402
|
-
};
|
|
28403
|
-
}
|
|
28404
|
-
return { ok: true, id: "docs-audit", label: "docs-audit", detail: probe.detail, verbose: [probe.detail] };
|
|
28405
|
-
}
|
|
28406
|
-
function checkRedactorLiveness(probe) {
|
|
28407
|
-
if (!probe) return null;
|
|
28408
|
-
const evidence = [`secret-redact failed rows in the last 48h: ${probe.failed}${probe.lastTs ? ` (last ${probe.lastTs})` : ""}`];
|
|
28409
|
-
if (probe.failed === 0) {
|
|
28410
|
-
return { ok: true, id: "redactor-liveness", label: "secret-redact liveness", verbose: evidence };
|
|
28411
|
-
}
|
|
28412
|
-
return {
|
|
28413
|
-
ok: false,
|
|
28414
|
-
id: "redactor-liveness",
|
|
28415
|
-
label: "secret-redact liveness",
|
|
28416
|
-
reportOnly: true,
|
|
28417
|
-
detail: `${probe.failed} crash marker${probe.failed === 1 ? "" : "s"} in 48h`,
|
|
28418
|
-
fix: "the redactor is crashing during scans \u2014 read `.git/mmi-runtime/hooks/activity.jsonl` for the error and fix it; every crash is a scan that never ran",
|
|
28419
|
-
verbose: evidence
|
|
28420
|
-
};
|
|
28421
|
-
}
|
|
28422
28350
|
function checkSessionPayload(probe) {
|
|
28423
28351
|
if (!probe) return null;
|
|
28424
28352
|
const { chars } = probe;
|
|
@@ -28473,13 +28401,14 @@ function gcReapable(plan) {
|
|
|
28473
28401
|
return plan.branches.length + plan.trackingRefs.length + plan.worktreeDirs.length;
|
|
28474
28402
|
}
|
|
28475
28403
|
async function runDoctorClean(opts, io, deps) {
|
|
28476
|
-
const
|
|
28477
|
-
const
|
|
28404
|
+
const full = !opts.fast && !opts.banner && !opts.preflight;
|
|
28405
|
+
const applyEnv = full || Boolean(opts.preflight);
|
|
28406
|
+
const applyRepo = full && opts.repoWrites !== false;
|
|
28478
28407
|
const lane = {
|
|
28479
28408
|
banner: Boolean(opts.banner),
|
|
28480
28409
|
fast: Boolean(opts.fast),
|
|
28481
28410
|
preflight: Boolean(opts.preflight),
|
|
28482
|
-
full
|
|
28411
|
+
full
|
|
28483
28412
|
};
|
|
28484
28413
|
const probeReleased = !opts.fast || Boolean(opts.self);
|
|
28485
28414
|
const probeAws = !opts.fast && !opts.banner;
|
|
@@ -28577,9 +28506,6 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
28577
28506
|
async function runGithubAuthRow() {
|
|
28578
28507
|
emitNow(checkGithubAuth({ login, ghInstalled: ghInstalled2, reach }));
|
|
28579
28508
|
}
|
|
28580
|
-
async function runGithubPoolsRows() {
|
|
28581
|
-
for (const pool of checkGithubPools(await deps.githubPools())) emitNow(pool);
|
|
28582
|
-
}
|
|
28583
28509
|
async function runAwsRow() {
|
|
28584
28510
|
const aws = checkAwsIdentity({ isOrgRepo, probed: probeAws, callerArn });
|
|
28585
28511
|
if (aws) emitNow(aws);
|
|
@@ -28600,7 +28526,7 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
28600
28526
|
emitNow({ ok: true, id: "gitignore-block", label: "gitignore block", detail: "rewrote managed block", verbose: giEvidence });
|
|
28601
28527
|
restartPending = true;
|
|
28602
28528
|
} else {
|
|
28603
|
-
emitNow({ ok: false, id: "gitignore-block", label: "gitignore block", fix: "run `mmi-cli doctor --
|
|
28529
|
+
emitNow({ ok: false, id: "gitignore-block", label: "gitignore block", fix: "run `mmi-cli doctor` (without --no-repo-writes) to write the org-managed .gitignore block", verbose: giEvidence });
|
|
28604
28530
|
}
|
|
28605
28531
|
}
|
|
28606
28532
|
async function runPluginCacheRow() {
|
|
@@ -28611,37 +28537,15 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
28611
28537
|
if (payload) emitNow(payload);
|
|
28612
28538
|
}
|
|
28613
28539
|
async function runMarketplaceRows() {
|
|
28540
|
+
if (applyEnv) {
|
|
28541
|
+
const healed = deps.healMarketplacePins();
|
|
28542
|
+
if (healed) {
|
|
28543
|
+
healIntent(`marketplace pins \u2014 ${healed}`);
|
|
28544
|
+
restartPending = true;
|
|
28545
|
+
}
|
|
28546
|
+
}
|
|
28614
28547
|
for (const row of deps.marketplaceRows()) emitNow(row);
|
|
28615
28548
|
}
|
|
28616
|
-
async function runSchedulesRow() {
|
|
28617
|
-
const probe = await deps.schedulesNotebook().catch((e) => ({
|
|
28618
|
-
armed: 0,
|
|
28619
|
-
live: 0,
|
|
28620
|
-
declared: 0,
|
|
28621
|
-
incomplete: [`schedules probe failed \u2014 ${e.message}`],
|
|
28622
|
-
drift: []
|
|
28623
|
-
}));
|
|
28624
|
-
const sched = checkSchedules(probe);
|
|
28625
|
-
if (sched) emitNow(sched);
|
|
28626
|
-
}
|
|
28627
|
-
async function runRedactorRow() {
|
|
28628
|
-
const redactor = checkRedactorLiveness(deps.redactorLiveness());
|
|
28629
|
-
if (redactor) emitNow(redactor);
|
|
28630
|
-
}
|
|
28631
|
-
async function runDocsAuditRow() {
|
|
28632
|
-
const probe = await deps.docsAudit().catch((e) => ({
|
|
28633
|
-
armed: true,
|
|
28634
|
-
ok: false,
|
|
28635
|
-
detail: `docs-audit probe failed \u2014 ${e.message}`
|
|
28636
|
-
}));
|
|
28637
|
-
const docsAudit2 = checkDocsAudit(probe);
|
|
28638
|
-
if (docsAudit2) emitNow(docsAudit2);
|
|
28639
|
-
}
|
|
28640
|
-
async function runWorktreeRootsRow() {
|
|
28641
|
-
const probe = await deps.worktreeRoots().catch(() => void 0);
|
|
28642
|
-
const roots = checkWorktreeRoots(probe);
|
|
28643
|
-
if (roots) emitNow(roots);
|
|
28644
|
-
}
|
|
28645
28549
|
async function runTrainSyncRow() {
|
|
28646
28550
|
try {
|
|
28647
28551
|
emitNow(checkTrainSync(await deps.syncTrain()));
|
|
@@ -28697,7 +28601,7 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
28697
28601
|
// #3574: `${n} stale` was prepended to `fix` because the ✗ branch would not render `detail` —
|
|
28698
28602
|
// the same fact this row already puts in `detail` on its ✓ path four lines up. One convention now.
|
|
28699
28603
|
detail: `${n} stale`,
|
|
28700
|
-
fix: "run `mmi-cli doctor --
|
|
28604
|
+
fix: "run `mmi-cli doctor` (without --no-repo-writes) to reap merged branches, stale refs, and dead worktrees",
|
|
28701
28605
|
verbose: gcEvidence
|
|
28702
28606
|
});
|
|
28703
28607
|
}
|
|
@@ -28711,7 +28615,7 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
28711
28615
|
emitNow({ id: "scratch", ok: true, label: "scratch", detail: pruned ? `removed ${pruned} aged item(s)` : "nothing stale", verbose: scratchEvidence });
|
|
28712
28616
|
if (pruned) restartPending = true;
|
|
28713
28617
|
} else {
|
|
28714
|
-
emitNow(scratch.plan.safeAuto.length === 0 ? { id: "scratch", ok: true, label: "scratch", verbose: scratchEvidence } : { id: "scratch", ok: false, label: "scratch", detail: `${scratch.plan.safeAuto.length} aged item(s)`, fix: "run `mmi-cli doctor --
|
|
28618
|
+
emitNow(scratch.plan.safeAuto.length === 0 ? { id: "scratch", ok: true, label: "scratch", verbose: scratchEvidence } : { id: "scratch", ok: false, label: "scratch", detail: `${scratch.plan.safeAuto.length} aged item(s)`, fix: "run `mmi-cli doctor` (without --no-repo-writes)", verbose: scratchEvidence });
|
|
28715
28619
|
}
|
|
28716
28620
|
}
|
|
28717
28621
|
const table = [
|
|
@@ -28720,17 +28624,12 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
28720
28624
|
{ id: "plugin", when: true, run: runPluginRow },
|
|
28721
28625
|
{ id: "cli-version", when: true, run: runCliRow },
|
|
28722
28626
|
{ id: "github-auth", when: true, run: runGithubAuthRow },
|
|
28723
|
-
{ id: "github-pools", when: lane.full, run: runGithubPoolsRows },
|
|
28724
28627
|
{ id: "aws-identity", when: true, run: runAwsRow },
|
|
28725
28628
|
{ id: "repo-worktrees", when: true, run: runRepoWorktreesRow },
|
|
28726
28629
|
{ id: "gitignore-block", when: isOrgRepo, run: runGitignoreRow },
|
|
28727
28630
|
{ id: "plugin-cache", when: true, run: runPluginCacheRow },
|
|
28728
28631
|
{ id: "sessionstart-payload", when: true, run: runSessionPayloadRow },
|
|
28729
28632
|
{ id: "marketplace", when: true, run: runMarketplaceRows },
|
|
28730
|
-
{ id: "schedules", when: lane.full, run: runSchedulesRow },
|
|
28731
|
-
{ id: "redactor-liveness", when: lane.full, run: runRedactorRow },
|
|
28732
|
-
{ id: "docs-audit", when: lane.full, run: runDocsAuditRow },
|
|
28733
|
-
{ id: "worktree-roots", when: lane.full, run: runWorktreeRootsRow },
|
|
28734
28633
|
// The two most expensive things in this file — a real `git fetch` plus train-branch fast-forward,
|
|
28735
28634
|
// and a `gh`-backed gc sweep with a 20s timeout — so org repos on the full lane only (#3485).
|
|
28736
28635
|
{ id: "train-branches", when: isOrgRepo && lane.full, run: runTrainSyncRow },
|
|
@@ -28789,17 +28688,17 @@ function parseOriginRepo(remoteUrl) {
|
|
|
28789
28688
|
}
|
|
28790
28689
|
function ghHostsConfigPath(env, platform2) {
|
|
28791
28690
|
const sep2 = platform2 === "win32" ? "\\" : "/";
|
|
28792
|
-
const
|
|
28691
|
+
const join29 = (...parts) => parts.join(sep2);
|
|
28793
28692
|
const explicit = env.GH_CONFIG_DIR?.trim();
|
|
28794
|
-
if (explicit) return
|
|
28693
|
+
if (explicit) return join29(explicit, "hosts.yml");
|
|
28795
28694
|
if (platform2 === "win32") {
|
|
28796
28695
|
const appData = (env.AppData ?? env.APPDATA)?.trim();
|
|
28797
|
-
return appData ?
|
|
28696
|
+
return appData ? join29(appData, "GitHub CLI", "hosts.yml") : void 0;
|
|
28798
28697
|
}
|
|
28799
28698
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
28800
|
-
if (xdg) return
|
|
28699
|
+
if (xdg) return join29(xdg, "gh", "hosts.yml");
|
|
28801
28700
|
const home = env.HOME?.trim();
|
|
28802
|
-
return home ?
|
|
28701
|
+
return home ? join29(home, ".config", "gh", "hosts.yml") : void 0;
|
|
28803
28702
|
}
|
|
28804
28703
|
function parseGhHostsAccounts(yaml, host = "github.com") {
|
|
28805
28704
|
let hostIndent = null;
|
|
@@ -28849,17 +28748,17 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
28849
28748
|
}
|
|
28850
28749
|
|
|
28851
28750
|
// src/doctor-io.ts
|
|
28852
|
-
var
|
|
28751
|
+
var import_node_fs34 = require("node:fs");
|
|
28853
28752
|
var import_node_os12 = require("node:os");
|
|
28854
|
-
var
|
|
28855
|
-
var
|
|
28753
|
+
var import_node_path33 = require("node:path");
|
|
28754
|
+
var import_node_child_process15 = require("node:child_process");
|
|
28856
28755
|
var import_node_util8 = require("node:util");
|
|
28857
|
-
var execFileP6 = (0, import_node_util8.promisify)(
|
|
28756
|
+
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process15.execFile);
|
|
28858
28757
|
var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
28859
28758
|
function installedClaudePluginVersion() {
|
|
28860
28759
|
try {
|
|
28861
28760
|
const file = JSON.parse(
|
|
28862
|
-
(0,
|
|
28761
|
+
(0, import_node_fs34.readFileSync)((0, import_node_path33.join)((0, import_node_os12.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
28863
28762
|
);
|
|
28864
28763
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
28865
28764
|
if (versions.length === 0) return void 0;
|
|
@@ -28870,7 +28769,7 @@ function installedClaudePluginVersion() {
|
|
|
28870
28769
|
}
|
|
28871
28770
|
function manifestVersion(path2) {
|
|
28872
28771
|
try {
|
|
28873
|
-
const manifest = JSON.parse((0,
|
|
28772
|
+
const manifest = JSON.parse((0, import_node_fs34.readFileSync)(path2, "utf8"));
|
|
28874
28773
|
return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : void 0;
|
|
28875
28774
|
} catch {
|
|
28876
28775
|
return void 0;
|
|
@@ -28880,27 +28779,27 @@ function installedSurfacePluginVersion(surface) {
|
|
|
28880
28779
|
const token = surfaceToken(surface);
|
|
28881
28780
|
if (token === "kilo") {
|
|
28882
28781
|
try {
|
|
28883
|
-
const stamp = (0,
|
|
28782
|
+
const stamp = (0, import_node_fs34.readFileSync)((0, import_node_path33.join)((0, import_node_os12.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
|
|
28884
28783
|
return stamp || void 0;
|
|
28885
28784
|
} catch {
|
|
28886
28785
|
return void 0;
|
|
28887
28786
|
}
|
|
28888
28787
|
}
|
|
28889
28788
|
if (token === "cursor") {
|
|
28890
|
-
return manifestVersion((0,
|
|
28789
|
+
return manifestVersion((0, import_node_path33.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
|
|
28891
28790
|
}
|
|
28892
28791
|
if (token === "kimi") {
|
|
28893
|
-
return manifestVersion((0,
|
|
28792
|
+
return manifestVersion((0, import_node_path33.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
|
|
28894
28793
|
}
|
|
28895
28794
|
if (token === "claude") return installedClaudePluginVersion();
|
|
28896
28795
|
if (token !== "codex") return void 0;
|
|
28897
28796
|
try {
|
|
28898
|
-
const raw = process.platform === "win32" ? (0,
|
|
28797
|
+
const raw = process.platform === "win32" ? (0, import_node_child_process15.execFileSync)("cmd.exe", ["/c", "codex", "plugin", "list", "--json"], {
|
|
28899
28798
|
encoding: "utf8",
|
|
28900
28799
|
stdio: ["ignore", "pipe", "ignore"],
|
|
28901
28800
|
timeout: 15e3,
|
|
28902
28801
|
windowsHide: true
|
|
28903
|
-
}) : (0,
|
|
28802
|
+
}) : (0, import_node_child_process15.execFileSync)("codex", ["plugin", "list", "--json"], {
|
|
28904
28803
|
encoding: "utf8",
|
|
28905
28804
|
stdio: ["ignore", "pipe", "ignore"],
|
|
28906
28805
|
timeout: 15e3,
|
|
@@ -28918,7 +28817,7 @@ function installedActivePluginVersion(surface = detectSurface(process.env)) {
|
|
|
28918
28817
|
}
|
|
28919
28818
|
function worktreeRootSync() {
|
|
28920
28819
|
try {
|
|
28921
|
-
const out = (0,
|
|
28820
|
+
const out = (0, import_node_child_process15.execFileSync)("git", ["rev-parse", "--show-toplevel"], { windowsHide: true, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
28922
28821
|
let root = out.endsWith("\n") ? out.slice(0, -1) : out;
|
|
28923
28822
|
if (process.platform === "win32" && root.endsWith("\r")) root = root.slice(0, -1);
|
|
28924
28823
|
return root || null;
|
|
@@ -28928,13 +28827,13 @@ function worktreeRootSync() {
|
|
|
28928
28827
|
}
|
|
28929
28828
|
var gitignorePath = () => {
|
|
28930
28829
|
const root = worktreeRootSync();
|
|
28931
|
-
return root === null ? null : (0,
|
|
28830
|
+
return root === null ? null : (0, import_node_path33.join)(root, ".gitignore");
|
|
28932
28831
|
};
|
|
28933
28832
|
function readGitignore() {
|
|
28934
28833
|
const path2 = gitignorePath();
|
|
28935
28834
|
if (path2 === null) return null;
|
|
28936
28835
|
try {
|
|
28937
|
-
return (0,
|
|
28836
|
+
return (0, import_node_fs34.readFileSync)(path2, "utf8");
|
|
28938
28837
|
} catch {
|
|
28939
28838
|
return null;
|
|
28940
28839
|
}
|
|
@@ -28943,7 +28842,7 @@ function writeGitignore(content) {
|
|
|
28943
28842
|
const path2 = gitignorePath();
|
|
28944
28843
|
if (path2 === null) return false;
|
|
28945
28844
|
try {
|
|
28946
|
-
(0,
|
|
28845
|
+
(0, import_node_fs34.writeFileSync)(path2, content, "utf8");
|
|
28947
28846
|
return true;
|
|
28948
28847
|
} catch {
|
|
28949
28848
|
return false;
|
|
@@ -28967,7 +28866,7 @@ async function repoRoot() {
|
|
|
28967
28866
|
}
|
|
28968
28867
|
function hasRepoLocalWorktrees() {
|
|
28969
28868
|
const root = worktreeRootSync();
|
|
28970
|
-
return root !== null && (0,
|
|
28869
|
+
return root !== null && (0, import_node_fs34.existsSync)((0, import_node_path33.join)(root, ".worktrees"));
|
|
28971
28870
|
}
|
|
28972
28871
|
|
|
28973
28872
|
// src/index.ts
|
|
@@ -29003,8 +28902,8 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
29003
28902
|
function ghMultiAccountCaveat(announcedLogin) {
|
|
29004
28903
|
try {
|
|
29005
28904
|
const hostsPath = ghHostsConfigPath(process.env, process.platform);
|
|
29006
|
-
if (!hostsPath || !(0,
|
|
29007
|
-
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0,
|
|
28905
|
+
if (!hostsPath || !(0, import_node_fs35.existsSync)(hostsPath)) return void 0;
|
|
28906
|
+
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs35.readFileSync)(hostsPath, "utf8")));
|
|
29008
28907
|
} catch {
|
|
29009
28908
|
return void 0;
|
|
29010
28909
|
}
|
|
@@ -29012,7 +28911,7 @@ function ghMultiAccountCaveat(announcedLogin) {
|
|
|
29012
28911
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
29013
28912
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
29014
28913
|
function envHealLockPath(home) {
|
|
29015
|
-
return (0,
|
|
28914
|
+
return (0, import_node_path34.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
29016
28915
|
}
|
|
29017
28916
|
async function withEnvHealLock(what, run) {
|
|
29018
28917
|
try {
|
|
@@ -29028,9 +28927,6 @@ async function withEnvHealLock(what, run) {
|
|
|
29028
28927
|
return { ok: false, detail: `${what} could not take the env-heal lock \u2014 ${e.message}` };
|
|
29029
28928
|
}
|
|
29030
28929
|
}
|
|
29031
|
-
function docsJanitorScheduleId(repo) {
|
|
29032
|
-
return `${repo.split("/").pop()}/docs-janitor`;
|
|
29033
|
-
}
|
|
29034
28930
|
function throttledReleasedVersion() {
|
|
29035
28931
|
let note;
|
|
29036
28932
|
return {
|
|
@@ -29051,8 +28947,6 @@ function throttledReleasedVersion() {
|
|
|
29051
28947
|
}
|
|
29052
28948
|
function mmiDoctorDeps(opts = {}) {
|
|
29053
28949
|
const throttled = opts.throttleReleasedRead ? throttledReleasedVersion() : void 0;
|
|
29054
|
-
let notebook;
|
|
29055
|
-
const notebookOnce = () => notebook ??= fetchNotebook();
|
|
29056
28950
|
let surfaceEvidence;
|
|
29057
28951
|
let surfaceEvidenceRead = false;
|
|
29058
28952
|
const surfaceEvidenceOnce = (isOrgRepo) => {
|
|
@@ -29076,11 +28970,6 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
29076
28970
|
};
|
|
29077
28971
|
return surfaceEvidence;
|
|
29078
28972
|
};
|
|
29079
|
-
const docsJanitorArmedAt = async (repo) => {
|
|
29080
|
-
const wanted = docsJanitorScheduleId(repo);
|
|
29081
|
-
const { entries } = await notebookOnce();
|
|
29082
|
-
return entries.find((e) => e.scheduleId === wanted)?.armedAt;
|
|
29083
|
-
};
|
|
29084
28973
|
return {
|
|
29085
28974
|
githubLogin,
|
|
29086
28975
|
ghInstalled,
|
|
@@ -29119,24 +29008,6 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
29119
29008
|
// writes to the harness-owned cache itself. Same plan builder the verb uses, so the two never disagree.
|
|
29120
29009
|
// No `withBytes` here: doctor runs on EVERY SessionStart, and sizing means recursively stat'ing every
|
|
29121
29010
|
// stale version tree. The count comes from one cheap readdir; `plugin-prune` reports the MB.
|
|
29122
|
-
// #3025: both identities' rate pools. Personal probes with the ambient token; the App line only exists
|
|
29123
|
-
// when this machine can mint — a vault-denied read means "not an automation host", not a failure.
|
|
29124
|
-
githubPools: async () => {
|
|
29125
|
-
const deps = appActorDeps();
|
|
29126
|
-
const personal = await probeRatePools(deps.fetch, await githubToken());
|
|
29127
|
-
let app = null;
|
|
29128
|
-
try {
|
|
29129
|
-
const minted = await mintInstallationToken(deps);
|
|
29130
|
-
app = await probeRatePools(deps.fetch, minted.token) ?? "token minted but rate_limit probe failed \u2014 check network/App permissions";
|
|
29131
|
-
} catch (e) {
|
|
29132
|
-
if (e instanceof AppActorError) {
|
|
29133
|
-
if (e.code !== "vault-denied") app = e.message;
|
|
29134
|
-
} else {
|
|
29135
|
-
app = "app actor probe failed unexpectedly \u2014 run `MMI_ACTOR=app mmi-cli pr checks-wait --help` to reproduce";
|
|
29136
|
-
}
|
|
29137
|
-
}
|
|
29138
|
-
return { personal, app };
|
|
29139
|
-
},
|
|
29140
29011
|
pluginCache: () => {
|
|
29141
29012
|
const surface = detectSurface(process.env);
|
|
29142
29013
|
const configRoot = surfaceConfigRoot(surface);
|
|
@@ -29155,41 +29026,9 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
29155
29026
|
stagingBytes: plan.stagingBytes
|
|
29156
29027
|
};
|
|
29157
29028
|
},
|
|
29158
|
-
// #3008: the schedules-notebook drift probe, compacted for the check. Full doctor only — the gather in
|
|
29159
|
-
// runDoctorClean skips this dep entirely on fast/banner/preflight runs.
|
|
29160
|
-
schedulesNotebook: async () => {
|
|
29161
|
-
const { entries, incomplete, drift, reconciliation } = await notebookOnce();
|
|
29162
|
-
const repoName = await repoSlug().catch(() => "");
|
|
29163
|
-
const clearableHere = repoName ? reconciliation.filter((d) => driftClearableFrom(d, repoName)).length : 0;
|
|
29164
|
-
return {
|
|
29165
|
-
armed: entries.length,
|
|
29166
|
-
live: entries.filter((e) => e.resolved === "live").length,
|
|
29167
|
-
declared: entries.filter((e) => e.resolved === "declared").length,
|
|
29168
|
-
incomplete,
|
|
29169
|
-
drift,
|
|
29170
|
-
clearableHere
|
|
29171
|
-
};
|
|
29172
|
-
},
|
|
29173
|
-
// #3075: the docs-audit dead-man verdict probe, compacted for the check. Full doctor only, exactly like
|
|
29174
|
-
// schedulesNotebook — a registry read the SessionStart banner / --preflight / --fast lanes never pay for.
|
|
29175
|
-
// The 404/absent route maps to `armed:false` (an informational "not armed" line), never a false RED.
|
|
29176
|
-
docsAudit: async () => {
|
|
29177
|
-
const repo = await currentRepoFullName();
|
|
29178
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
29179
|
-
const armedAt = await docsJanitorArmedAt(repo).catch(() => void 0);
|
|
29180
|
-
const status = docsAuditStatus(await readDocsAuditFetch(repo), { repo, today, armedAt });
|
|
29181
|
-
return { armed: status.state !== "not-armed", ok: status.ok, detail: status.line };
|
|
29182
|
-
},
|
|
29183
|
-
// #3471: the competing-worktrees-roots probe. Full doctor only (the gather skips it on
|
|
29184
|
-
// fast/banner/preflight), and READ-ONLY by construction — it stats, it never removes. Nothing here is
|
|
29185
|
-
// wired to a reaper, in either mode.
|
|
29186
|
-
worktreeRoots: async () => worktreeRootsProbe(await repoRoot()),
|
|
29187
29029
|
// #3470: what the SessionStart hook LAST emitted, measured at emission by the session-start verb below.
|
|
29188
29030
|
// A local record read — cheap enough for every lane, including the banner.
|
|
29189
29031
|
sessionPayload: () => readSessionPayload(process.cwd()),
|
|
29190
|
-
// #3630: recent secret-redact crash markers from the shared hook-activity trace — with the Stop
|
|
29191
|
-
// hook retired, the full/scheduled doctor is the trace's only reader. Local tail-scan, fail-soft.
|
|
29192
|
-
redactorLiveness: () => redactorLivenessProbe(process.cwd()),
|
|
29193
29032
|
// #3485 items 9 and 8: why the MMI plugin has never printed an "updated — please restart" notice, and
|
|
29194
29033
|
// which branch it would pick one up from. Two local file reads, no network, fail-soft to no rows.
|
|
29195
29034
|
marketplaceRows: () => {
|
|
@@ -29198,12 +29037,25 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
29198
29037
|
const home = (0, import_node_os13.homedir)();
|
|
29199
29038
|
return marketplaceRows(
|
|
29200
29039
|
MMI_MARKETPLACE_NAME,
|
|
29201
|
-
readFileSyncSafe((0,
|
|
29202
|
-
readFileSyncSafe((0,
|
|
29040
|
+
readFileSyncSafe((0, import_node_path34.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs35.readFileSync),
|
|
29041
|
+
readFileSyncSafe((0, import_node_path34.join)(home, ".claude", "settings.json"), import_node_fs35.readFileSync),
|
|
29042
|
+
// #3974: this CLI heals these rows, so report mode names the doctor run rather than the hand
|
|
29043
|
+
// edit. Unconditional — it is a fact about mmi-cli, not about the lane this run is on.
|
|
29044
|
+
true
|
|
29203
29045
|
);
|
|
29204
29046
|
} catch {
|
|
29205
29047
|
return [];
|
|
29206
29048
|
}
|
|
29049
|
+
},
|
|
29050
|
+
// #3974: the heal behind those rows, under --apply only. Same Codex carve-out as the rows above —
|
|
29051
|
+
// Codex keeps its registration in config.toml, so there is no ~/.claude registration to pin.
|
|
29052
|
+
healMarketplacePins: () => {
|
|
29053
|
+
try {
|
|
29054
|
+
if (detectSurface(process.env) === "codex") return void 0;
|
|
29055
|
+
return applyOrgMarketplacePins((0, import_node_path34.join)((0, import_node_os13.homedir)(), ...KNOWN_MARKETPLACES_RELATIVE), [MMI_MARKETPLACE_NAME]);
|
|
29056
|
+
} catch {
|
|
29057
|
+
return void 0;
|
|
29058
|
+
}
|
|
29207
29059
|
}
|
|
29208
29060
|
};
|
|
29209
29061
|
}
|
|
@@ -29381,6 +29233,7 @@ function appActorDeps() {
|
|
|
29381
29233
|
fetch: (url, init) => fetch(url, init)
|
|
29382
29234
|
};
|
|
29383
29235
|
}
|
|
29236
|
+
setPollTokenProvider(() => pollTokenOrUndefined(appActorDeps()));
|
|
29384
29237
|
function shouldMarkWorktreeActivity(commandPath3) {
|
|
29385
29238
|
return commandPath3.split(" ")[0] !== "worktree";
|
|
29386
29239
|
}
|
|
@@ -29405,19 +29258,19 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
29405
29258
|
});
|
|
29406
29259
|
var rules = program2.command("rules").description("org-managed .gitignore delivery");
|
|
29407
29260
|
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) => {
|
|
29408
|
-
const path2 = (0,
|
|
29409
|
-
const current = (0,
|
|
29261
|
+
const path2 = (0, import_node_path34.join)(process.cwd(), ".gitignore");
|
|
29262
|
+
const current = (0, import_node_fs35.existsSync)(path2) ? (0, import_node_fs35.readFileSync)(path2, "utf8") : null;
|
|
29410
29263
|
const plan = planManagedGitignore(current);
|
|
29411
29264
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
29412
29265
|
if (opts.json) {
|
|
29413
|
-
if (opts.write && plan.changed) (0,
|
|
29266
|
+
if (opts.write && plan.changed) (0, import_node_fs35.writeFileSync)(path2, plan.content, "utf8");
|
|
29414
29267
|
console.log(JSON.stringify(plan, null, 2));
|
|
29415
29268
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
29416
29269
|
return;
|
|
29417
29270
|
}
|
|
29418
29271
|
if (opts.write) {
|
|
29419
29272
|
if (plan.changed) {
|
|
29420
|
-
(0,
|
|
29273
|
+
(0, import_node_fs35.writeFileSync)(path2, plan.content, "utf8");
|
|
29421
29274
|
console.log(`mmi-cli org rules gitignore: updated .gitignore (${drift})`);
|
|
29422
29275
|
} else {
|
|
29423
29276
|
console.log("mmi-cli org rules gitignore: up to date");
|
|
@@ -29572,8 +29425,8 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
|
|
|
29572
29425
|
if (!Number.isFinite(limit) || limit < 1) return fail("worktree gc: --limit must be a positive integer");
|
|
29573
29426
|
let root;
|
|
29574
29427
|
if (o.root !== void 0) {
|
|
29575
|
-
root = (0,
|
|
29576
|
-
if (!(0,
|
|
29428
|
+
root = (0, import_node_path34.resolve)(o.root);
|
|
29429
|
+
if (!(0, import_node_fs35.existsSync)(root) || !(0, import_node_fs35.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
|
|
29577
29430
|
const gcRepoRoot = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
29578
29431
|
if (isPathUnderDirectory(gcRepoRoot, root)) {
|
|
29579
29432
|
return fail(`worktree gc: --root ${root} contains this checkout \u2014 name a worktrees root, not the repo or an ancestor of it`);
|
|
@@ -29615,7 +29468,7 @@ function runWorktreeInstall(command, cwd, quiet) {
|
|
|
29615
29468
|
const file = isWin2 ? "cmd.exe" : bin;
|
|
29616
29469
|
const spawnArgs = isWin2 ? ["/c", bin, ...args] : args;
|
|
29617
29470
|
return new Promise((resolve5, reject) => {
|
|
29618
|
-
const child2 = (0,
|
|
29471
|
+
const child2 = (0, import_node_child_process16.spawn)(file, spawnArgs, { cwd, stdio: quiet ? "ignore" : "inherit", windowsHide: true });
|
|
29619
29472
|
const timer = setTimeout(() => {
|
|
29620
29473
|
try {
|
|
29621
29474
|
child2.kill();
|
|
@@ -29638,7 +29491,7 @@ async function primaryCheckoutRoot(from) {
|
|
|
29638
29491
|
return primaryCheckoutRootOf(async (args) => (await execFileP2("git", ["-C", from, ...args], { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
29639
29492
|
}
|
|
29640
29493
|
async function unprovenWorktreeReason(wtPath, repoRoot2) {
|
|
29641
|
-
if (!(0,
|
|
29494
|
+
if (!(0, import_node_fs35.existsSync)(wtPath)) return `${wtPath} does not exist on disk`;
|
|
29642
29495
|
const porcelain = (await execFileP2("git", ["-C", repoRoot2, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
|
|
29643
29496
|
const registered = parseWorktreePorcelainEntries(porcelain);
|
|
29644
29497
|
if (!registered.length) {
|
|
@@ -29659,26 +29512,26 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
|
|
|
29659
29512
|
function acquireWorktreeSetupLock(worktreeRoot) {
|
|
29660
29513
|
const lockPath = repoRuntimeStatePath(worktreeRoot, "worktree-setup.lock");
|
|
29661
29514
|
const take = () => {
|
|
29662
|
-
const fd = (0,
|
|
29515
|
+
const fd = (0, import_node_fs35.openSync)(lockPath, "wx");
|
|
29663
29516
|
try {
|
|
29664
|
-
(0,
|
|
29517
|
+
(0, import_node_fs35.writeSync)(fd, String(Date.now()));
|
|
29665
29518
|
} finally {
|
|
29666
|
-
(0,
|
|
29519
|
+
(0, import_node_fs35.closeSync)(fd);
|
|
29667
29520
|
}
|
|
29668
29521
|
return () => {
|
|
29669
29522
|
try {
|
|
29670
|
-
(0,
|
|
29523
|
+
(0, import_node_fs35.rmSync)(lockPath, { force: true });
|
|
29671
29524
|
} catch {
|
|
29672
29525
|
}
|
|
29673
29526
|
};
|
|
29674
29527
|
};
|
|
29675
29528
|
try {
|
|
29676
|
-
(0,
|
|
29529
|
+
(0, import_node_fs35.mkdirSync)((0, import_node_path34.dirname)(lockPath), { recursive: true });
|
|
29677
29530
|
return take();
|
|
29678
29531
|
} catch {
|
|
29679
29532
|
try {
|
|
29680
|
-
if (Date.now() - (0,
|
|
29681
|
-
(0,
|
|
29533
|
+
if (Date.now() - (0, import_node_fs35.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
|
|
29534
|
+
(0, import_node_fs35.rmSync)(lockPath, { force: true });
|
|
29682
29535
|
return take();
|
|
29683
29536
|
}
|
|
29684
29537
|
} catch {
|
|
@@ -29901,7 +29754,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
29901
29754
|
try {
|
|
29902
29755
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
|
|
29903
29756
|
if (o.repo) args.push("--repo", o.repo);
|
|
29904
|
-
spawnDetachedSelf(args, { spawn:
|
|
29757
|
+
spawnDetachedSelf(args, { spawn: import_node_child_process16.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
|
|
29905
29758
|
} catch {
|
|
29906
29759
|
}
|
|
29907
29760
|
}
|
|
@@ -29951,6 +29804,29 @@ docs.command("refs").description("deterministic doc reference gate: every backti
|
|
|
29951
29804
|
await failGraceful(e.message);
|
|
29952
29805
|
}
|
|
29953
29806
|
});
|
|
29807
|
+
var spawnCmd = program2.command("spawn").description("this repo's process-spawn contract \u2014 every child process must be unable to pop a console window");
|
|
29808
|
+
spawnCmd.command("policy").description("enforce the windowsHide contract across this repo's tracked source: a child_process call must set windowsHide, or take its options from a named constant, a type annotation, or a forwarded caller bag that does. Waive a call the scan cannot classify with a `// windows-hide-exempt: <reason>` comment on the line above it (#3979)").option("--json", "machine-readable result: { ok, scannedCount, findings[] }").action(async (o) => {
|
|
29809
|
+
try {
|
|
29810
|
+
const root = await repoRoot();
|
|
29811
|
+
const result = runSpawnPolicy(root);
|
|
29812
|
+
if (o.json) {
|
|
29813
|
+
consoleIo.log(JSON.stringify(result, null, 2));
|
|
29814
|
+
if (!result.ok) process.exitCode = 1;
|
|
29815
|
+
return;
|
|
29816
|
+
}
|
|
29817
|
+
if (result.ok) {
|
|
29818
|
+
console.log(`spawn policy: OK (${result.scannedCount} source file(s) scanned).`);
|
|
29819
|
+
return;
|
|
29820
|
+
}
|
|
29821
|
+
for (const f of result.findings) console.error(`spawn policy: ${f.detail}`);
|
|
29822
|
+
console.error(
|
|
29823
|
+
"\nA process spawned without windowsHide allocates a console when it has none to inherit.\nWhere Windows Terminal is the default terminal, that console becomes a desktop window the\nhost never reaps \u2014 it outlives the process as an empty frame. Set windowsHide: true, or\nroute the call through a helper that does."
|
|
29824
|
+
);
|
|
29825
|
+
process.exitCode = 1;
|
|
29826
|
+
} catch (e) {
|
|
29827
|
+
await failGraceful(e.message);
|
|
29828
|
+
}
|
|
29829
|
+
});
|
|
29954
29830
|
var tests = program2.command("tests").description("a repo's test-policy.json \u2014 the opt-in test contract and its enforcement");
|
|
29955
29831
|
tests.command("policy").description("enforce this repo's test-policy.json against the diff: a mandatory-zone change must carry a test, an unrequested new test file is refused, a `protected` test file may not be deleted or renamed away, and a `protected` entry naming a missing file is refused. Override any of them with a `Test-Policy-Override: <reason>` commit trailer (#3605)").option("--json", "machine-readable result: { ok, base, changedCount, findings[] }").option("--base <ref>", "comparison base (default: TEST_POLICY_BASE, then origin/development, then origin/main)").action(async (o) => {
|
|
29956
29832
|
try {
|
|
@@ -30257,7 +30133,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
30257
30133
|
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`);
|
|
30258
30134
|
if (o.secretsFile) {
|
|
30259
30135
|
try {
|
|
30260
|
-
vars.push(`secrets=${(0,
|
|
30136
|
+
vars.push(`secrets=${(0, import_node_fs35.readFileSync)(o.secretsFile, "utf8")}`);
|
|
30261
30137
|
} catch (e) {
|
|
30262
30138
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
30263
30139
|
}
|
|
@@ -31005,11 +30881,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
|
|
|
31005
30881
|
}
|
|
31006
30882
|
});
|
|
31007
30883
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
31008
|
-
const wfDir = (0,
|
|
31009
|
-
if (!(0,
|
|
31010
|
-
return (0,
|
|
30884
|
+
const wfDir = (0, import_node_path34.join)(cwd, ".github", "workflows");
|
|
30885
|
+
if (!(0, import_node_fs35.existsSync)(wfDir)) return [];
|
|
30886
|
+
return (0, import_node_fs35.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
31011
30887
|
try {
|
|
31012
|
-
return workflowReportsPrChecks((0,
|
|
30888
|
+
return workflowReportsPrChecks((0, import_node_fs35.readFileSync)((0, import_node_path34.join)(wfDir, name), "utf8"));
|
|
31013
30889
|
} catch {
|
|
31014
30890
|
return true;
|
|
31015
30891
|
}
|
|
@@ -31041,16 +30917,16 @@ function ciAuditDeps() {
|
|
|
31041
30917
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
31042
30918
|
readSeedFile: (path2) => {
|
|
31043
30919
|
if (!root) return null;
|
|
31044
|
-
const fullPath = (0,
|
|
31045
|
-
return (0,
|
|
30920
|
+
const fullPath = (0, import_node_path34.join)(root, path2);
|
|
30921
|
+
return (0, import_node_fs35.existsSync)(fullPath) ? (0, import_node_fs35.readFileSync)(fullPath, "utf8") : null;
|
|
31046
30922
|
}
|
|
31047
30923
|
};
|
|
31048
30924
|
}
|
|
31049
30925
|
function hubRoot() {
|
|
31050
|
-
const fromPkg = (0,
|
|
30926
|
+
const fromPkg = (0, import_node_path34.join)(__dirname, "..", "..");
|
|
31051
30927
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
31052
|
-
if ((0,
|
|
31053
|
-
if ((0,
|
|
30928
|
+
if ((0, import_node_fs35.existsSync)((0, import_node_path34.join)(fromPkg, marker))) return fromPkg;
|
|
30929
|
+
if ((0, import_node_fs35.existsSync)((0, import_node_path34.join)(process.cwd(), marker))) return process.cwd();
|
|
31054
30930
|
return null;
|
|
31055
30931
|
}
|
|
31056
30932
|
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) => {
|
|
@@ -31353,7 +31229,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
31353
31229
|
localCleanup = await cleanupPrMergeLocalBranch(headRef, {
|
|
31354
31230
|
beforeWorktrees,
|
|
31355
31231
|
startingPath,
|
|
31356
|
-
pathExists: (p) => (0,
|
|
31232
|
+
pathExists: (p) => (0, import_node_fs35.existsSync)(p),
|
|
31357
31233
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
31358
31234
|
teardownWorktreeStage,
|
|
31359
31235
|
deferredStore,
|
|
@@ -31828,26 +31704,25 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
31828
31704
|
targets = resolution.targets;
|
|
31829
31705
|
}
|
|
31830
31706
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
31831
|
-
const fileMatrix = (0,
|
|
31707
|
+
const fileMatrix = (0, import_node_fs35.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs35.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
31832
31708
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
31833
31709
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
31834
|
-
const fileContracts = (0,
|
|
31710
|
+
const fileContracts = (0, import_node_fs35.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs35.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
31835
31711
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
31836
|
-
const sanctioned = (0,
|
|
31712
|
+
const sanctioned = (0, import_node_fs35.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs35.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
31837
31713
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
31838
31714
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
31839
31715
|
if (!report.ok) process.exitCode = 1;
|
|
31840
31716
|
});
|
|
31841
31717
|
access.command("capabilities").description("enumerate your effective vault reach \u2014 every credential NAME + tier + scope you can read/use across project + org/master tiers (names only, no values) (#1615)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json", "machine-readable output").action((o) => withSecrets((d) => secretsCapabilities(d, o)));
|
|
31842
31718
|
var isWin2 = process.platform === "win32";
|
|
31843
|
-
program2.command("doctor").description("
|
|
31719
|
+
program2.command("doctor").description("heal CLI/plugin wiring and clean up repo cruft \u2014 repairs run by default (#3975); use --verbose for the full audit checklist").option("--banner", "one-line resume summary; silent when all gates pass").option("--fast", "offline-safe fast lane; read-only, skips slow checks and network probes").option("--preflight", "eager version/plugin-heal (env repairs only \u2014 never the repo working tree) with upfront notice when stale; silent when healthy (#1871)").option("--verbose", "print the evidence behind every check \u2014 probes, resolved paths, versions compared, and the names behind each count (#2977)").option("--guide", "print the MMI Agentic Onboarding guide URL").option("--json", "machine-readable output (an output format \u2014 repairs still run by lane)").option("--apply", "deprecated no-op: repairs run by default now (#3975); kept so older instructions still parse").option("--no-repo-writes", "env/plugin repairs only \u2014 never mutate the repo working tree; report pending managed .gitignore repairs with the follow-up command (for train preflights)").option("--self", "verify CLI/plugin version parity and gh auth reach; suggests plugin-heal on a hard gap (reads the published version, so not offline-safe) (#2689)").addHelpText("after", "\nExit codes:\n 0 no hard gaps remain; advisory gaps may exist\n 1 one or more included hard checks failed\n\nA plain run heals env drift (CLI, plugin, marketplace pins) and cleans repo cruft (gitignore block,\nmerged branches, dead worktrees, aged scratch) automatically (#3975). --no-repo-writes keeps the\nworking tree untouched for train preflights; --banner/--fast/--self are read-only lanes.\n\n--banner keeps the legacy SessionStart contract and returns 0 unless the process crashes.\n").action(async (opts) => {
|
|
31844
31720
|
if (opts.guide) {
|
|
31845
31721
|
consoleIo.log("MMI Agentic Onboarding: docs/Architecture/agentic-dev-environment.md");
|
|
31846
31722
|
return;
|
|
31847
31723
|
}
|
|
31848
31724
|
process.exitCode = await runDoctorClean(
|
|
31849
31725
|
{
|
|
31850
|
-
apply: Boolean(opts.apply),
|
|
31851
31726
|
repoWrites: opts.repoWrites,
|
|
31852
31727
|
banner: opts.banner,
|
|
31853
31728
|
preflight: opts.preflight,
|
|
@@ -31866,16 +31741,16 @@ function directoryBytes(path2) {
|
|
|
31866
31741
|
let total = 0;
|
|
31867
31742
|
let entries;
|
|
31868
31743
|
try {
|
|
31869
|
-
entries = (0,
|
|
31744
|
+
entries = (0, import_node_fs35.readdirSync)(path2, { withFileTypes: true });
|
|
31870
31745
|
} catch {
|
|
31871
31746
|
return 0;
|
|
31872
31747
|
}
|
|
31873
31748
|
for (const entry of entries) {
|
|
31874
|
-
const child2 = (0,
|
|
31749
|
+
const child2 = (0, import_node_path34.join)(path2, entry.name);
|
|
31875
31750
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
31876
31751
|
else {
|
|
31877
31752
|
try {
|
|
31878
|
-
total += (0,
|
|
31753
|
+
total += (0, import_node_fs35.statSync)(child2).size;
|
|
31879
31754
|
} catch {
|
|
31880
31755
|
}
|
|
31881
31756
|
}
|
|
@@ -31883,25 +31758,25 @@ function directoryBytes(path2) {
|
|
|
31883
31758
|
return total;
|
|
31884
31759
|
}
|
|
31885
31760
|
function listDirEntries(dir) {
|
|
31886
|
-
return (0,
|
|
31761
|
+
return (0, import_node_fs35.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
31887
31762
|
}
|
|
31888
31763
|
function readInstalledPluginRefs(configRoot) {
|
|
31889
31764
|
const p = installedPluginsPathForConfig(configRoot);
|
|
31890
|
-
if (!(0,
|
|
31765
|
+
if (!(0, import_node_fs35.existsSync)(p)) return [];
|
|
31891
31766
|
try {
|
|
31892
|
-
return installedPluginPaths((0,
|
|
31767
|
+
return installedPluginPaths((0, import_node_fs35.readFileSync)(p, "utf8"));
|
|
31893
31768
|
} catch {
|
|
31894
31769
|
return null;
|
|
31895
31770
|
}
|
|
31896
31771
|
}
|
|
31897
31772
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
31898
31773
|
return {
|
|
31899
|
-
exists: (p) => (0,
|
|
31900
|
-
listVersionDirs: (root) => (0,
|
|
31774
|
+
exists: (p) => (0, import_node_fs35.existsSync)(p),
|
|
31775
|
+
listVersionDirs: (root) => (0, import_node_fs35.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
31901
31776
|
dirBytes,
|
|
31902
|
-
listStagingDirs: (root) => (0,
|
|
31777
|
+
listStagingDirs: (root) => (0, import_node_fs35.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
31903
31778
|
try {
|
|
31904
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
31779
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path34.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs35.statSync)(p).mtimeMs) };
|
|
31905
31780
|
} catch {
|
|
31906
31781
|
return { name: d.name, mtimeMs: Date.now() };
|
|
31907
31782
|
}
|
|
@@ -31915,10 +31790,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
31915
31790
|
return {
|
|
31916
31791
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
31917
31792
|
mtimeMs: (name) => {
|
|
31918
|
-
const p = (0,
|
|
31919
|
-
if (!(0,
|
|
31793
|
+
const p = (0, import_node_path34.join)(stagingRoot, name);
|
|
31794
|
+
if (!(0, import_node_fs35.existsSync)(p)) return null;
|
|
31920
31795
|
try {
|
|
31921
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
31796
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs35.statSync)(q).mtimeMs);
|
|
31922
31797
|
} catch {
|
|
31923
31798
|
return null;
|
|
31924
31799
|
}
|
|
@@ -31944,7 +31819,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
31944
31819
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
31945
31820
|
);
|
|
31946
31821
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
31947
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
31822
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs35.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
31948
31823
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
31949
31824
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
31950
31825
|
else console.log(renderPluginCachePlan(plan, result));
|
|
@@ -32007,7 +31882,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
|
|
|
32007
31882
|
for (const line of scratchGcLines(process.cwd())) bannerIo.log(line);
|
|
32008
31883
|
const worktreeBanner = worktreeAutoProvisionBanner(process.cwd());
|
|
32009
31884
|
if (worktreeBanner) {
|
|
32010
|
-
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn:
|
|
31885
|
+
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process16.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
32011
31886
|
bannerIo.log(worktreeBanner);
|
|
32012
31887
|
}
|
|
32013
31888
|
if (isLinkedWorktree(process.cwd())) {
|