@mutmutco/cli 3.80.0 → 3.81.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 +374 -196
- 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
|
|
@@ -6259,6 +6261,8 @@ function readSettingsAutoUpdate(raw, name) {
|
|
|
6259
6261
|
var AUTO_UPDATE_TOGGLE_STEPS = "`/plugin` \u2192 Marketplaces tab \u2192 select the marketplace \u2192 Enable auto-update";
|
|
6260
6262
|
var CATALOG_CONTENT_REF = "main";
|
|
6261
6263
|
var CATALOG_REF_PIN_STEPS = `add \`"ref": "${CATALOG_CONTENT_REF}"\` to this marketplace's \`source\` in ~/.claude/plugins/known_marketplaces.json, then restart Claude`;
|
|
6264
|
+
var PIN_HEAL_COMMAND = "run `mmi-cli doctor --apply`";
|
|
6265
|
+
var ORG_MARKETPLACE_PINS = { autoUpdate: true, ref: CATALOG_CONTENT_REF };
|
|
6262
6266
|
function resolveCatalogRef(probe) {
|
|
6263
6267
|
const { name, registered, ref, autoUpdate } = probe;
|
|
6264
6268
|
if (!registered) return void 0;
|
|
@@ -6281,7 +6285,7 @@ function resolveCatalogRef(probe) {
|
|
|
6281
6285
|
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
6286
|
};
|
|
6283
6287
|
}
|
|
6284
|
-
function checkMarketplaceAutoUpdate(probe) {
|
|
6288
|
+
function checkMarketplaceAutoUpdate(probe, healable = false) {
|
|
6285
6289
|
if (!probe) return null;
|
|
6286
6290
|
const evidence = [`${probe.name}: auto-update ${probe.effective ? "on" : "off"} \u2014 ${probe.reason}`];
|
|
6287
6291
|
if (probe.effective) {
|
|
@@ -6292,11 +6296,11 @@ function checkMarketplaceAutoUpdate(probe) {
|
|
|
6292
6296
|
ok: true,
|
|
6293
6297
|
warn: true,
|
|
6294
6298
|
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}`,
|
|
6299
|
+
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
6300
|
verbose: evidence
|
|
6297
6301
|
};
|
|
6298
6302
|
}
|
|
6299
|
-
function checkMarketplaceCatalogRef(probe) {
|
|
6303
|
+
function checkMarketplaceCatalogRef(probe, healable = false) {
|
|
6300
6304
|
if (!probe) return null;
|
|
6301
6305
|
const label = `marketplace catalog ref (${probe.name})`;
|
|
6302
6306
|
const evidence = [`${probe.name}: ${probe.reason}`, `auto-update: ${probe.autoUpdate ? "on" : "off"}`];
|
|
@@ -6313,7 +6317,7 @@ function checkMarketplaceCatalogRef(probe) {
|
|
|
6313
6317
|
// ✗ branch carried it in `fix` only because `renderCheckLine` discarded `detail` on failure — one
|
|
6314
6318
|
// row, two conventions, decided by which field would actually print.
|
|
6315
6319
|
detail: `${drift} while auto-update is ON`,
|
|
6316
|
-
fix: `this machine installs plugin releases advertised by a branch nobody released; ${CATALOG_REF_PIN_STEPS}`,
|
|
6320
|
+
fix: `this machine installs plugin releases advertised by a branch nobody released; ${healable ? `${PIN_HEAL_COMMAND} to pin it` : CATALOG_REF_PIN_STEPS}`,
|
|
6317
6321
|
verbose: evidence
|
|
6318
6322
|
};
|
|
6319
6323
|
}
|
|
@@ -6322,11 +6326,11 @@ function checkMarketplaceCatalogRef(probe) {
|
|
|
6322
6326
|
ok: true,
|
|
6323
6327
|
warn: true,
|
|
6324
6328
|
label,
|
|
6325
|
-
detail: `${drift} \u2014 harmless while auto-update is off, and the thing to fix before turning it on; ${CATALOG_REF_PIN_STEPS}`,
|
|
6329
|
+
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
6330
|
verbose: evidence
|
|
6327
6331
|
};
|
|
6328
6332
|
}
|
|
6329
|
-
function marketplaceRows(name, known, settings) {
|
|
6333
|
+
function marketplaceRows(name, known, settings, healable = false) {
|
|
6330
6334
|
const { registered, declared, ref } = readKnownMarketplace(known, name);
|
|
6331
6335
|
const autoUpdate = resolveAutoUpdate({
|
|
6332
6336
|
name,
|
|
@@ -6334,8 +6338,9 @@ function marketplaceRows(name, known, settings) {
|
|
|
6334
6338
|
declared,
|
|
6335
6339
|
settingsDeclared: readSettingsAutoUpdate(settings, name)
|
|
6336
6340
|
});
|
|
6337
|
-
const rows = [checkMarketplaceAutoUpdate(autoUpdate), checkMarketplaceCatalogRef(
|
|
6338
|
-
resolveCatalogRef({ name, registered, ref, autoUpdate: autoUpdate.effective })
|
|
6341
|
+
const rows = [checkMarketplaceAutoUpdate(autoUpdate, healable), checkMarketplaceCatalogRef(
|
|
6342
|
+
resolveCatalogRef({ name, registered, ref, autoUpdate: autoUpdate.effective }),
|
|
6343
|
+
healable
|
|
6339
6344
|
)];
|
|
6340
6345
|
return rows.filter((r) => r !== null);
|
|
6341
6346
|
}
|
|
@@ -6619,7 +6624,7 @@ function commandLadderHint() {
|
|
|
6619
6624
|
}
|
|
6620
6625
|
|
|
6621
6626
|
// src/index.ts
|
|
6622
|
-
var
|
|
6627
|
+
var import_node_path34 = require("node:path");
|
|
6623
6628
|
|
|
6624
6629
|
// src/merge-ci-policy.ts
|
|
6625
6630
|
function resolveMergeCiPolicy(input) {
|
|
@@ -13440,6 +13445,7 @@ function spawnExitCode(status, signal) {
|
|
|
13440
13445
|
function defaultSpawn(command, args, env) {
|
|
13441
13446
|
const target = resolveSpawnTarget(command, args);
|
|
13442
13447
|
const r = (0, import_node_child_process6.spawnSync)(target.cmd, target.args, {
|
|
13448
|
+
windowsHide: true,
|
|
13443
13449
|
stdio: "inherit",
|
|
13444
13450
|
env,
|
|
13445
13451
|
windowsVerbatimArguments: target.windowsVerbatimArguments
|
|
@@ -14892,7 +14898,7 @@ function readKnownMarketplacesFile(path2) {
|
|
|
14892
14898
|
return void 0;
|
|
14893
14899
|
}
|
|
14894
14900
|
}
|
|
14895
|
-
function
|
|
14901
|
+
function writeMarketplacePinsOnDisk(path2, pins, succeeded, failedVerb) {
|
|
14896
14902
|
if (pins.size === 0) return void 0;
|
|
14897
14903
|
const after = readKnownMarketplacesFile(path2);
|
|
14898
14904
|
const next = restoreMarketplacePins(after, pins);
|
|
@@ -14900,14 +14906,35 @@ function restoreMarketplacePinsOnDisk(path2, pins) {
|
|
|
14900
14906
|
try {
|
|
14901
14907
|
(0, import_node_fs18.writeFileSync)(path2, next, "utf8");
|
|
14902
14908
|
} catch {
|
|
14903
|
-
return `could NOT
|
|
14909
|
+
return `could NOT ${failedVerb} ${[...pins.keys()].join(", ")} \u2014 set it by hand`;
|
|
14904
14910
|
}
|
|
14905
14911
|
const verify = readKnownMarketplacesFile(path2);
|
|
14906
14912
|
const failed = [...pins].filter(([name, want]) => {
|
|
14907
14913
|
const got = readKnownMarketplace(verify, name);
|
|
14908
14914
|
return typeof want.autoUpdate === "boolean" && got.declared !== want.autoUpdate || want.ref !== void 0 && got.ref !== want.ref;
|
|
14909
14915
|
});
|
|
14910
|
-
return failed.length ?
|
|
14916
|
+
return failed.length ? `${failedVerb} did NOT take for ${failed.map(([n]) => n).join(", ")} \u2014 set it by hand` : succeeded(pins);
|
|
14917
|
+
}
|
|
14918
|
+
function restoreMarketplacePinsOnDisk(path2, pins) {
|
|
14919
|
+
return writeMarketplacePinsOnDisk(path2, pins, restoredPinsLine, "restore");
|
|
14920
|
+
}
|
|
14921
|
+
function applyOrgMarketplacePins(path2, names) {
|
|
14922
|
+
return writeMarketplacePinsOnDisk(
|
|
14923
|
+
path2,
|
|
14924
|
+
new Map(names.map((name) => [name, ORG_MARKETPLACE_PINS])),
|
|
14925
|
+
(pins) => `pinned ${[...pins.keys()].join(", ")} to ${ORG_MARKETPLACE_PINS.ref} with auto-update on`,
|
|
14926
|
+
"pin"
|
|
14927
|
+
);
|
|
14928
|
+
}
|
|
14929
|
+
function restoredPinsLine(pins) {
|
|
14930
|
+
const named = [...pins].map(([name, want]) => {
|
|
14931
|
+
const fields = [
|
|
14932
|
+
...typeof want.autoUpdate === "boolean" ? ["auto-update"] : [],
|
|
14933
|
+
...want.ref ? ["catalog ref"] : []
|
|
14934
|
+
];
|
|
14935
|
+
return `${name} (${fields.join(" + ")})`;
|
|
14936
|
+
});
|
|
14937
|
+
return `re-pinned ${named.join(", ")} \u2014 restored across the reinstall`;
|
|
14911
14938
|
}
|
|
14912
14939
|
async function runGuard(readOrigin) {
|
|
14913
14940
|
const surface = detectSurface(process.env);
|
|
@@ -16562,12 +16589,13 @@ var PRIMARY_GROUPS = [
|
|
|
16562
16589
|
["Plan and work", ["board", "issue", "worktree", "stage"]],
|
|
16563
16590
|
["Review and ship", ["pr", "ci", "rcand", "release", "hotfix", "train"]],
|
|
16564
16591
|
// `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
|
-
|
|
16592
|
+
// step invokes (`docs refs`, `tests policy`), not org-plane operations (#3605). `spawn policy`
|
|
16593
|
+
// joins them on the same footing (#3979).
|
|
16594
|
+
["Setup and support", ["bootstrap", "secrets", "docs", "tests", "spawn"]],
|
|
16567
16595
|
["Coordinate and improve", ["wave", "report", "skill-lesson"]]
|
|
16568
16596
|
];
|
|
16569
16597
|
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"]);
|
|
16598
|
+
var SUPPORT_PRIMARY = /* @__PURE__ */ new Set(["doctor", "whoami", "commands", "explain", "docs", "tests", "spawn", "wave", "report", "skill-lesson"]);
|
|
16571
16599
|
var TOP_LEVEL_ORDER = /* @__PURE__ */ new Map();
|
|
16572
16600
|
var HELP_GROUP_ORDER = /* @__PURE__ */ new Map();
|
|
16573
16601
|
var topLevelPosition = 0;
|
|
@@ -16607,6 +16635,7 @@ var COMMAND_OWNERSHIP = {
|
|
|
16607
16635
|
secrets: { module_owner: "cli/src/secrets-commands.ts", consumer: "authenticated-operator" },
|
|
16608
16636
|
docs: { module_owner: "cli/src/docs-index-command.ts", consumer: "repo-gates" },
|
|
16609
16637
|
tests: { module_owner: "cli/src/test-policy-core.ts", consumer: "repo-gates" },
|
|
16638
|
+
spawn: { module_owner: "cli/src/spawn-policy-core.ts", consumer: "repo-gates" },
|
|
16610
16639
|
wave: { module_owner: "cli/src/wave-land.ts", consumer: "campaign-orchestrator" },
|
|
16611
16640
|
report: { module_owner: "cli/src/report.ts", consumer: "campaign-orchestrator" },
|
|
16612
16641
|
"skill-lesson": { module_owner: "cli/src/skill-lesson.ts", consumer: "campaign-orchestrator" },
|
|
@@ -18898,10 +18927,115 @@ function runDocRefs(root, deps = {}) {
|
|
|
18898
18927
|
};
|
|
18899
18928
|
}
|
|
18900
18929
|
|
|
18901
|
-
// src/
|
|
18930
|
+
// src/spawn-policy-core.ts
|
|
18902
18931
|
var import_node_child_process11 = require("node:child_process");
|
|
18903
18932
|
var import_node_fs21 = require("node:fs");
|
|
18904
18933
|
var import_node_path19 = require("node:path");
|
|
18934
|
+
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
18935
|
+
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
18936
|
+
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
18937
|
+
var EXCLUDED = [
|
|
18938
|
+
/(^|\/)node_modules\//,
|
|
18939
|
+
/(^|\/)(dist|build|coverage)\//,
|
|
18940
|
+
/(^|\/)(test|tests|__tests__|fixtures|__fixtures__)\//,
|
|
18941
|
+
/\.(test|spec)\.[cm]?[jt]s$/,
|
|
18942
|
+
/\.d\.[cm]?ts$/
|
|
18943
|
+
];
|
|
18944
|
+
function stripComments(src) {
|
|
18945
|
+
return src.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, " ")).replace(/(^|[^:])\/\/[^\n]*/g, (m, p) => p + " ".repeat(m.length - p.length));
|
|
18946
|
+
}
|
|
18947
|
+
function callArgs(src, open2) {
|
|
18948
|
+
let depth = 0;
|
|
18949
|
+
for (let i = open2; i < src.length; i++) {
|
|
18950
|
+
const c = src[i];
|
|
18951
|
+
if (c === "(") depth++;
|
|
18952
|
+
else if (c === ")") {
|
|
18953
|
+
depth--;
|
|
18954
|
+
if (depth === 0) return src.slice(open2, i + 1);
|
|
18955
|
+
}
|
|
18956
|
+
}
|
|
18957
|
+
return "";
|
|
18958
|
+
}
|
|
18959
|
+
function findViolationsInSource(raw) {
|
|
18960
|
+
if (!/from\s+["']node:child_process["']/.test(raw)) return [];
|
|
18961
|
+
const importBlock = /import\s*(?:type\s*)?\{([^}]*)\}\s*from\s+["']node:child_process["']/.exec(raw);
|
|
18962
|
+
const imported = new Set(
|
|
18963
|
+
(importBlock?.[1] ?? "").split(",").map((s) => s.trim().split(/\s+as\s+/)[0].trim()).filter(Boolean)
|
|
18964
|
+
);
|
|
18965
|
+
if (imported.size === 0) return [];
|
|
18966
|
+
const rawLines = raw.split("\n");
|
|
18967
|
+
const src = stripComments(raw);
|
|
18968
|
+
const call = new RegExp(CALL_SOURCE, "g");
|
|
18969
|
+
const found = [];
|
|
18970
|
+
let m;
|
|
18971
|
+
while (m = call.exec(src)) {
|
|
18972
|
+
const callee = m[2];
|
|
18973
|
+
if (!imported.has(callee)) continue;
|
|
18974
|
+
const open2 = m.index + m[0].length - 1;
|
|
18975
|
+
const args = callArgs(src, open2);
|
|
18976
|
+
if (!args) continue;
|
|
18977
|
+
if (args.includes("windowsHide")) continue;
|
|
18978
|
+
const line = src.slice(0, m.index + m[1].length).split("\n").length;
|
|
18979
|
+
if (/windows-hide-exempt:/.test(rawLines[line - 2] ?? "")) continue;
|
|
18980
|
+
if (/\.{3}\s*\w+/.test(args)) continue;
|
|
18981
|
+
if (/\b(opts|options|spawnOptions)\b/.test(args)) continue;
|
|
18982
|
+
const identifiers = new Set(args.match(/\b[A-Za-z_$][\w$]*\b/g) ?? []);
|
|
18983
|
+
let satisfied = false;
|
|
18984
|
+
for (const id of identifiers) {
|
|
18985
|
+
if (SPAWNERS.includes(id)) continue;
|
|
18986
|
+
const decl = new RegExp(String.raw`\b(?:const|let|var)\s+${id}\b[^;]*`, "s").exec(src);
|
|
18987
|
+
if (decl && decl[0].includes("windowsHide")) {
|
|
18988
|
+
satisfied = true;
|
|
18989
|
+
break;
|
|
18990
|
+
}
|
|
18991
|
+
if (new RegExp(String.raw`\b${id}\s*:\s*\{[^}]*windowsHide`, "s").test(src)) {
|
|
18992
|
+
satisfied = true;
|
|
18993
|
+
break;
|
|
18994
|
+
}
|
|
18995
|
+
}
|
|
18996
|
+
if (satisfied) continue;
|
|
18997
|
+
found.push({ line, callee });
|
|
18998
|
+
}
|
|
18999
|
+
return found;
|
|
19000
|
+
}
|
|
19001
|
+
function policedFiles(root) {
|
|
19002
|
+
const r = (0, import_node_child_process11.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
19003
|
+
cwd: root,
|
|
19004
|
+
encoding: "utf8",
|
|
19005
|
+
windowsHide: true,
|
|
19006
|
+
maxBuffer: 32 * 1024 * 1024
|
|
19007
|
+
});
|
|
19008
|
+
if (r.status !== 0) {
|
|
19009
|
+
throw new Error(`spawn policy: git ls-files failed: ${(r.stderr || "").trim() || `exit ${r.status}`}`);
|
|
19010
|
+
}
|
|
19011
|
+
return (r.stdout ?? "").split("\0").filter(Boolean).filter((f) => SOURCE_EXT.test(f)).filter((f) => !EXCLUDED.some((re) => re.test(f)));
|
|
19012
|
+
}
|
|
19013
|
+
function runSpawnPolicy(root) {
|
|
19014
|
+
const files = policedFiles(root);
|
|
19015
|
+
const findings = [];
|
|
19016
|
+
for (const file of files) {
|
|
19017
|
+
let raw;
|
|
19018
|
+
try {
|
|
19019
|
+
raw = (0, import_node_fs21.readFileSync)((0, import_node_path19.join)(root, file), "utf8");
|
|
19020
|
+
} catch {
|
|
19021
|
+
continue;
|
|
19022
|
+
}
|
|
19023
|
+
for (const v of findViolationsInSource(raw)) {
|
|
19024
|
+
findings.push({
|
|
19025
|
+
file,
|
|
19026
|
+
line: v.line,
|
|
19027
|
+
callee: v.callee,
|
|
19028
|
+
detail: `${file}:${v.line} ${v.callee}(...) has no windowsHide`
|
|
19029
|
+
});
|
|
19030
|
+
}
|
|
19031
|
+
}
|
|
19032
|
+
return { ok: findings.length === 0, scannedCount: files.length, findings };
|
|
19033
|
+
}
|
|
19034
|
+
|
|
19035
|
+
// src/test-policy-core.ts
|
|
19036
|
+
var import_node_child_process12 = require("node:child_process");
|
|
19037
|
+
var import_node_fs22 = require("node:fs");
|
|
19038
|
+
var import_node_path20 = require("node:path");
|
|
18905
19039
|
var POLICY_FILE = "test-policy.json";
|
|
18906
19040
|
var TEST_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
|
18907
19041
|
var PY_TEST_RE = /(?:^|\/)test_[^/]*\.py$|_test\.py$/;
|
|
@@ -18954,7 +19088,7 @@ function isTestPath(path2) {
|
|
|
18954
19088
|
return TEST_RE.test(path2) || PY_TEST_RE.test(path2);
|
|
18955
19089
|
}
|
|
18956
19090
|
function loadPolicy(root, readFile9 = readFileOrNull2) {
|
|
18957
|
-
const raw = readFile9((0,
|
|
19091
|
+
const raw = readFile9((0, import_node_path20.join)(root, POLICY_FILE));
|
|
18958
19092
|
if (raw == null) return { mandatory: [], declared: false };
|
|
18959
19093
|
try {
|
|
18960
19094
|
return { ...JSON.parse(raw), declared: true };
|
|
@@ -18964,7 +19098,7 @@ function loadPolicy(root, readFile9 = readFileOrNull2) {
|
|
|
18964
19098
|
}
|
|
18965
19099
|
function readFileOrNull2(path2) {
|
|
18966
19100
|
try {
|
|
18967
|
-
return (0,
|
|
19101
|
+
return (0, import_node_fs22.readFileSync)(path2, "utf8");
|
|
18968
19102
|
} catch {
|
|
18969
19103
|
return null;
|
|
18970
19104
|
}
|
|
@@ -18991,12 +19125,12 @@ function classify(changed, policy, present = () => false) {
|
|
|
18991
19125
|
const removedProtected = [...removed].filter((p) => protectedBy.has(p)).map((p) => ({ path: p, why: protectedBy.get(p) ?? "" }));
|
|
18992
19126
|
return { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected };
|
|
18993
19127
|
}
|
|
18994
|
-
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0,
|
|
18995
|
-
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0,
|
|
19128
|
+
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs22.existsSync)(path2)) {
|
|
19129
|
+
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path20.join)(root, p)));
|
|
18996
19130
|
}
|
|
18997
|
-
function unresolvedSatisfiers(policy, root, exists = (path2) => (0,
|
|
19131
|
+
function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs22.existsSync)(path2)) {
|
|
18998
19132
|
const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
|
|
18999
|
-
return [...new Set(declared)].filter((p) => !exists((0,
|
|
19133
|
+
return [...new Set(declared)].filter((p) => !exists((0, import_node_path20.join)(root, p)));
|
|
19000
19134
|
}
|
|
19001
19135
|
function evaluate(changed, policy, present = () => false) {
|
|
19002
19136
|
const { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected } = classify(changed, policy, present);
|
|
@@ -19029,14 +19163,15 @@ function evaluate(changed, policy, present = () => false) {
|
|
|
19029
19163
|
return findings;
|
|
19030
19164
|
}
|
|
19031
19165
|
function git(args, cwd) {
|
|
19032
|
-
return (0,
|
|
19166
|
+
return (0, import_node_child_process12.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
|
|
19033
19167
|
}
|
|
19034
19168
|
var COAUTHOR_KEY = "Co-authored-by";
|
|
19035
19169
|
var GH_MESSAGE_SEPARATOR = /^-{5,}$/;
|
|
19036
19170
|
var LIFTED_KEYS = new RegExp(`^(?:${TRAILER_KEY}|${COAUTHOR_KEY}):`, "i");
|
|
19037
19171
|
function parseTrailers(message, cwd) {
|
|
19038
19172
|
try {
|
|
19039
|
-
return (0,
|
|
19173
|
+
return (0, import_node_child_process12.execFileSync)("git", ["interpret-trailers", "--parse", "--unfold"], {
|
|
19174
|
+
windowsHide: true,
|
|
19040
19175
|
cwd,
|
|
19041
19176
|
input: message,
|
|
19042
19177
|
encoding: "utf8",
|
|
@@ -19177,13 +19312,13 @@ function changedFilesSince(base, cwd) {
|
|
|
19177
19312
|
}
|
|
19178
19313
|
function runTestPolicy(root, deps = {}) {
|
|
19179
19314
|
const policy = deps.policy ?? loadPolicy(root);
|
|
19180
|
-
const exists = deps.exists ?? ((path2) => (0,
|
|
19315
|
+
const exists = deps.exists ?? ((path2) => (0, import_node_fs22.existsSync)(path2));
|
|
19181
19316
|
const counts = { mandatoryCount: (policy.mandatory ?? []).length, protectedCount: (policy.protected ?? []).length };
|
|
19182
19317
|
const base = deps.changed ? "(injected)" : resolveBase(root, deps.base);
|
|
19183
19318
|
const refusal = deps.changed ? null : untrustworthyRange(root, base);
|
|
19184
19319
|
const changed = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
|
|
19185
19320
|
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,
|
|
19321
|
+
const present = (path2) => exists((0, import_node_path20.join)(root, path2));
|
|
19187
19322
|
const removedByThisDiff = removedPaths(changed);
|
|
19188
19323
|
const staleFindings = [];
|
|
19189
19324
|
const unresolved = unresolvedProtectedEntries(policy, root, exists).filter((p) => !removedByThisDiff.has(p));
|
|
@@ -19339,8 +19474,8 @@ function docsAuditStatus(fetch2, opts) {
|
|
|
19339
19474
|
}
|
|
19340
19475
|
|
|
19341
19476
|
// src/project-info-sync.ts
|
|
19342
|
-
var
|
|
19343
|
-
var
|
|
19477
|
+
var import_node_fs23 = require("node:fs");
|
|
19478
|
+
var import_node_path21 = require("node:path");
|
|
19344
19479
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
19345
19480
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
19346
19481
|
projectV2 { id }
|
|
@@ -19385,14 +19520,14 @@ function sharedName(entries, fallback) {
|
|
|
19385
19520
|
}
|
|
19386
19521
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
19387
19522
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
19388
|
-
const readmePath = (0,
|
|
19389
|
-
if (!(0,
|
|
19523
|
+
const readmePath = (0, import_node_path21.join)(repoRoot2, "README.md");
|
|
19524
|
+
if (!(0, import_node_fs23.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
19390
19525
|
const entries = entriesFor(project2, projects);
|
|
19391
19526
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
19392
19527
|
const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
|
|
19393
19528
|
if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
|
|
19394
19529
|
const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
|
|
19395
|
-
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0,
|
|
19530
|
+
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
19531
|
const lines = [
|
|
19397
19532
|
`# ${projectName}`,
|
|
19398
19533
|
"",
|
|
@@ -19411,8 +19546,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
19411
19546
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
19412
19547
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
19413
19548
|
const orgDocs = [
|
|
19414
|
-
(0,
|
|
19415
|
-
(0,
|
|
19549
|
+
(0, import_node_fs23.existsSync)((0, import_node_path21.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
|
|
19550
|
+
(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
19551
|
].filter(Boolean);
|
|
19417
19552
|
if (orgDocs.length) lines.push("", "## Organisation docs", "", ...orgDocs);
|
|
19418
19553
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines.join("\n")}
|
|
@@ -20271,8 +20406,8 @@ function writeError(res) {
|
|
|
20271
20406
|
}
|
|
20272
20407
|
|
|
20273
20408
|
// src/secrets-commands.ts
|
|
20274
|
-
var
|
|
20275
|
-
var
|
|
20409
|
+
var import_node_fs24 = require("node:fs");
|
|
20410
|
+
var import_node_path22 = require("node:path");
|
|
20276
20411
|
var import_node_os8 = require("node:os");
|
|
20277
20412
|
|
|
20278
20413
|
// src/project-runtime.ts
|
|
@@ -20396,18 +20531,18 @@ function collectMap(value, previous = []) {
|
|
|
20396
20531
|
return [...previous, value];
|
|
20397
20532
|
}
|
|
20398
20533
|
async function decryptRailsCredentials(input) {
|
|
20399
|
-
const appDir = (0,
|
|
20534
|
+
const appDir = (0, import_node_path22.resolve)(input.appDir ?? process.cwd());
|
|
20400
20535
|
const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
20401
20536
|
const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
20402
|
-
const credentialsPath = (0,
|
|
20403
|
-
const masterKeyPath = (0,
|
|
20537
|
+
const credentialsPath = (0, import_node_path22.resolve)(appDir, credentialsFile);
|
|
20538
|
+
const masterKeyPath = (0, import_node_path22.resolve)(appDir, masterKeyFile);
|
|
20404
20539
|
const env = {
|
|
20405
20540
|
...process.env,
|
|
20406
20541
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
20407
20542
|
MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
|
|
20408
20543
|
};
|
|
20409
|
-
if ((0,
|
|
20410
|
-
env.RAILS_MASTER_KEY = (0,
|
|
20544
|
+
if ((0, import_node_fs24.existsSync)(masterKeyPath)) {
|
|
20545
|
+
env.RAILS_MASTER_KEY = (0, import_node_fs24.readFileSync)(masterKeyPath, "utf8").trim();
|
|
20411
20546
|
}
|
|
20412
20547
|
const script = [
|
|
20413
20548
|
'require "json"',
|
|
@@ -20417,9 +20552,9 @@ async function decryptRailsCredentials(input) {
|
|
|
20417
20552
|
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
20418
20553
|
"puts JSON.generate(config.config)"
|
|
20419
20554
|
].join("\n");
|
|
20420
|
-
const scriptDir = (0,
|
|
20421
|
-
const scriptPath = (0,
|
|
20422
|
-
(0,
|
|
20555
|
+
const scriptDir = (0, import_node_fs24.mkdtempSync)((0, import_node_path22.join)((0, import_node_os8.tmpdir)(), "mmi-rails-decrypt-"));
|
|
20556
|
+
const scriptPath = (0, import_node_path22.join)(scriptDir, "decrypt.rb");
|
|
20557
|
+
(0, import_node_fs24.writeFileSync)(scriptPath, script, "utf8");
|
|
20423
20558
|
try {
|
|
20424
20559
|
const args = ["exec", "ruby", scriptPath];
|
|
20425
20560
|
const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
|
|
@@ -20431,7 +20566,7 @@ async function decryptRailsCredentials(input) {
|
|
|
20431
20566
|
});
|
|
20432
20567
|
return JSON.parse(stdout);
|
|
20433
20568
|
} finally {
|
|
20434
|
-
(0,
|
|
20569
|
+
(0, import_node_fs24.rmSync)(scriptDir, { recursive: true, force: true });
|
|
20435
20570
|
}
|
|
20436
20571
|
}
|
|
20437
20572
|
async function readSecretStdin() {
|
|
@@ -20521,7 +20656,7 @@ function registerSecretsCommands(program3) {
|
|
|
20521
20656
|
let body;
|
|
20522
20657
|
if (o.file) {
|
|
20523
20658
|
try {
|
|
20524
|
-
body = (0,
|
|
20659
|
+
body = (0, import_node_fs24.readFileSync)((0, import_node_path22.resolve)(o.file), "utf8");
|
|
20525
20660
|
} catch (e) {
|
|
20526
20661
|
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
20527
20662
|
}
|
|
@@ -20626,7 +20761,7 @@ function registerSecretsCommands(program3) {
|
|
|
20626
20761
|
{
|
|
20627
20762
|
...d,
|
|
20628
20763
|
decryptRailsCredentials,
|
|
20629
|
-
removeFile: (path2) => (0,
|
|
20764
|
+
removeFile: (path2) => (0, import_node_fs24.unlinkSync)((0, import_node_path22.resolve)(o.appDir ?? process.cwd(), path2))
|
|
20630
20765
|
},
|
|
20631
20766
|
{
|
|
20632
20767
|
repo: o.repo,
|
|
@@ -20820,7 +20955,7 @@ function checkGithubPools(probe) {
|
|
|
20820
20955
|
}
|
|
20821
20956
|
|
|
20822
20957
|
// src/box-commands.ts
|
|
20823
|
-
var
|
|
20958
|
+
var import_node_fs25 = require("node:fs");
|
|
20824
20959
|
|
|
20825
20960
|
// src/box.ts
|
|
20826
20961
|
var BOX_KEYS = {
|
|
@@ -21023,7 +21158,7 @@ function registerBoxCommands(program3) {
|
|
|
21023
21158
|
}
|
|
21024
21159
|
if (o.json) console.log(JSON.stringify({ box: found, incomplete }, null, 2));
|
|
21025
21160
|
else if (o.ssh && o.script) {
|
|
21026
|
-
(0,
|
|
21161
|
+
(0, import_node_fs25.writeFileSync)(o.script, sshRecipeScript(found), "utf8");
|
|
21027
21162
|
console.log(`wrote ${o.script} \u2014 run: bash "${o.script}"`);
|
|
21028
21163
|
} else if (o.ssh) console.log(`${formatSshRecipe(found)}
|
|
21029
21164
|
${SSH_RECIPE_AGENT_NOTE}`);
|
|
@@ -21038,7 +21173,7 @@ ${SSH_RECIPE_AGENT_NOTE}`);
|
|
|
21038
21173
|
|
|
21039
21174
|
// src/schedules-commands.ts
|
|
21040
21175
|
var import_promises3 = require("node:fs/promises");
|
|
21041
|
-
var
|
|
21176
|
+
var import_node_child_process13 = require("node:child_process");
|
|
21042
21177
|
var import_node_util7 = require("node:util");
|
|
21043
21178
|
|
|
21044
21179
|
// src/schedules.ts
|
|
@@ -21478,7 +21613,7 @@ function assembleReconciliation(githubEntries2, readRepos, registry2, activeWork
|
|
|
21478
21613
|
}
|
|
21479
21614
|
|
|
21480
21615
|
// src/schedules-commands.ts
|
|
21481
|
-
var execFileP5 = (0, import_node_util7.promisify)(
|
|
21616
|
+
var execFileP5 = (0, import_node_util7.promisify)(import_node_child_process13.execFile);
|
|
21482
21617
|
var AWS_REGION = "eu-central-1";
|
|
21483
21618
|
var AWS_TIMEOUT_MS = 3e4;
|
|
21484
21619
|
var AWS_RETRY_DELAY_MS = 1500;
|
|
@@ -21715,7 +21850,7 @@ function registerSchedulesCommands(program3) {
|
|
|
21715
21850
|
|
|
21716
21851
|
// src/file-lock.ts
|
|
21717
21852
|
var import_promises4 = require("node:fs/promises");
|
|
21718
|
-
var
|
|
21853
|
+
var import_node_path23 = require("node:path");
|
|
21719
21854
|
var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
21720
21855
|
var IMMEDIATE_RETRY_BUDGET = 3;
|
|
21721
21856
|
var FileLockBusyError = class extends Error {
|
|
@@ -21800,7 +21935,7 @@ async function releaseFileLock(lockPath, guard) {
|
|
|
21800
21935
|
}
|
|
21801
21936
|
async function withFileLock(lockPath, opts, fn) {
|
|
21802
21937
|
const resolved = resolveFileLockOpts(opts);
|
|
21803
|
-
await (0, import_promises4.mkdir)((0,
|
|
21938
|
+
await (0, import_promises4.mkdir)((0, import_node_path23.dirname)(lockPath), { recursive: true }).catch(() => void 0);
|
|
21804
21939
|
const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
|
|
21805
21940
|
try {
|
|
21806
21941
|
return await fn();
|
|
@@ -21811,7 +21946,7 @@ async function withFileLock(lockPath, opts, fn) {
|
|
|
21811
21946
|
|
|
21812
21947
|
// src/schedules-lift-command.ts
|
|
21813
21948
|
var import_promises5 = require("node:fs/promises");
|
|
21814
|
-
var
|
|
21949
|
+
var import_node_path24 = require("node:path");
|
|
21815
21950
|
|
|
21816
21951
|
// src/schedules-lift.ts
|
|
21817
21952
|
var SCHEDULE_HEADER_FIELDS = ["schedule", "what", "owner", "cadence", "executor", "llm", "output", "breaks", "kill"];
|
|
@@ -21917,7 +22052,7 @@ async function readWorkflowFiles(dir) {
|
|
|
21917
22052
|
const files = [];
|
|
21918
22053
|
for (const name of names.sort()) {
|
|
21919
22054
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
21920
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises5.readFile)((0,
|
|
22055
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises5.readFile)((0, import_node_path24.join)(dir, name), "utf8") });
|
|
21921
22056
|
}
|
|
21922
22057
|
return files;
|
|
21923
22058
|
}
|
|
@@ -22459,9 +22594,9 @@ function registerQueryCommands(program3) {
|
|
|
22459
22594
|
}
|
|
22460
22595
|
|
|
22461
22596
|
// src/bootstrap-commands.ts
|
|
22462
|
-
var
|
|
22597
|
+
var import_node_fs26 = require("node:fs");
|
|
22463
22598
|
var import_node_os9 = require("node:os");
|
|
22464
|
-
var
|
|
22599
|
+
var import_node_path25 = require("node:path");
|
|
22465
22600
|
|
|
22466
22601
|
// src/bootstrap-drift.ts
|
|
22467
22602
|
function byteComparableSeeds(manifest, cls) {
|
|
@@ -23075,13 +23210,13 @@ function registerBootstrapCommands(program3) {
|
|
|
23075
23210
|
client: defaultGitHubClient(),
|
|
23076
23211
|
projectMeta: meta,
|
|
23077
23212
|
deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
|
|
23078
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
23213
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs26.existsSync)(path2) ? (0, import_node_fs26.readFileSync)(path2, "utf8") : null,
|
|
23079
23214
|
// requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
|
|
23080
23215
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
23081
23216
|
// #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
|
|
23082
23217
|
// permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
|
|
23083
23218
|
// sanction, which is the pre-#3664 behaviour.
|
|
23084
|
-
sanctionedAdmins: (0,
|
|
23219
|
+
sanctionedAdmins: (0, import_node_fs26.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs26.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
|
|
23085
23220
|
requiredGcpApis: (() => {
|
|
23086
23221
|
const v = meta?.requiredGcpApis;
|
|
23087
23222
|
if (Array.isArray(v)) return v;
|
|
@@ -23119,12 +23254,12 @@ function registerBootstrapCommands(program3) {
|
|
|
23119
23254
|
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
23255
|
const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
|
|
23121
23256
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
23122
|
-
if (!(0,
|
|
23123
|
-
const manifest = loadBootstrapSeeds((0,
|
|
23257
|
+
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`);
|
|
23258
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
|
|
23124
23259
|
const hubContents = /* @__PURE__ */ new Map();
|
|
23125
23260
|
for (const s of manifest.seeds) {
|
|
23126
23261
|
if (s.ownership !== "org" || s.source !== "self") continue;
|
|
23127
|
-
hubContents.set(s.target, (0,
|
|
23262
|
+
hubContents.set(s.target, (0, import_node_fs26.existsSync)(s.target) ? (0, import_node_fs26.readFileSync)(s.target, "utf8") : null);
|
|
23128
23263
|
}
|
|
23129
23264
|
let targets;
|
|
23130
23265
|
let classOf = (_repo) => "deployable";
|
|
@@ -23201,8 +23336,8 @@ function registerBootstrapCommands(program3) {
|
|
|
23201
23336
|
return fail(`bootstrap apply: ${e.message}`);
|
|
23202
23337
|
}
|
|
23203
23338
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
23204
|
-
if (!(0,
|
|
23205
|
-
const manifest = loadBootstrapSeeds((0,
|
|
23339
|
+
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`);
|
|
23340
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
|
|
23206
23341
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
23207
23342
|
const slug = parsedRepo.slug;
|
|
23208
23343
|
const onlyTarget = o.only.trim();
|
|
@@ -23213,16 +23348,16 @@ function registerBootstrapCommands(program3) {
|
|
|
23213
23348
|
${known}`);
|
|
23214
23349
|
}
|
|
23215
23350
|
const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
|
|
23216
|
-
const readFile9 = (p) => (0,
|
|
23351
|
+
const readFile9 = (p) => (0, import_node_fs26.existsSync)(p) ? (0, import_node_fs26.readFileSync)(p, "utf8") : null;
|
|
23217
23352
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
23218
23353
|
const putSeed = async (target, content, ref, sha) => {
|
|
23219
|
-
const tmp = (0,
|
|
23220
|
-
(0,
|
|
23354
|
+
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`);
|
|
23355
|
+
(0, import_node_fs26.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
23221
23356
|
try {
|
|
23222
23357
|
await gh(contentPutInputArgs(repo, target, tmp));
|
|
23223
23358
|
} finally {
|
|
23224
23359
|
try {
|
|
23225
|
-
(0,
|
|
23360
|
+
(0, import_node_fs26.unlinkSync)(tmp);
|
|
23226
23361
|
} catch {
|
|
23227
23362
|
}
|
|
23228
23363
|
}
|
|
@@ -23474,12 +23609,12 @@ LIVE apply to ${repo}:
|
|
|
23474
23609
|
}
|
|
23475
23610
|
|
|
23476
23611
|
// src/stage-commands.ts
|
|
23477
|
-
var
|
|
23478
|
-
var
|
|
23612
|
+
var import_node_fs28 = require("node:fs");
|
|
23613
|
+
var import_node_path27 = require("node:path");
|
|
23479
23614
|
|
|
23480
23615
|
// src/port-registry.ts
|
|
23481
|
-
var
|
|
23482
|
-
var
|
|
23616
|
+
var import_node_fs27 = require("node:fs");
|
|
23617
|
+
var import_node_path26 = require("node:path");
|
|
23483
23618
|
|
|
23484
23619
|
// ../infra/port-geometry.mjs
|
|
23485
23620
|
var PORT_BLOCK = 100;
|
|
@@ -23493,8 +23628,8 @@ function nextPortBlock(registry2) {
|
|
|
23493
23628
|
return [base, base + PORT_SPAN];
|
|
23494
23629
|
}
|
|
23495
23630
|
function loadPortRegistry(path2) {
|
|
23496
|
-
if (!(0,
|
|
23497
|
-
const raw = JSON.parse((0,
|
|
23631
|
+
if (!(0, import_node_fs27.existsSync)(path2)) return {};
|
|
23632
|
+
const raw = JSON.parse((0, import_node_fs27.readFileSync)(path2, "utf8"));
|
|
23498
23633
|
const out = {};
|
|
23499
23634
|
for (const [key, value] of Object.entries(raw)) {
|
|
23500
23635
|
if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
|
|
@@ -23508,9 +23643,9 @@ function ensurePortRange(repo, path2) {
|
|
|
23508
23643
|
const existing = registry2[repo];
|
|
23509
23644
|
if (existing) return existing;
|
|
23510
23645
|
const range = nextPortBlock(registry2);
|
|
23511
|
-
const raw = (0,
|
|
23646
|
+
const raw = (0, import_node_fs27.existsSync)(path2) ? JSON.parse((0, import_node_fs27.readFileSync)(path2, "utf8")) : {};
|
|
23512
23647
|
raw[repo] = range;
|
|
23513
|
-
(0,
|
|
23648
|
+
(0, import_node_fs27.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
|
|
23514
23649
|
return range;
|
|
23515
23650
|
}
|
|
23516
23651
|
function portCursorSeed(registry2) {
|
|
@@ -23532,22 +23667,22 @@ function existingPortRange(repo, registry2) {
|
|
|
23532
23667
|
return registry2[repo] ?? null;
|
|
23533
23668
|
}
|
|
23534
23669
|
function portRangeInfraAt(root, source) {
|
|
23535
|
-
const registryPath = (0,
|
|
23536
|
-
const ddbScriptPath = (0,
|
|
23537
|
-
if (!(0,
|
|
23670
|
+
const registryPath = (0, import_node_path26.join)(root, "infra", "port-ranges.json");
|
|
23671
|
+
const ddbScriptPath = (0, import_node_path26.join)(root, "infra", "port-ddb.mjs");
|
|
23672
|
+
if (!(0, import_node_fs27.existsSync)(registryPath) || !(0, import_node_fs27.existsSync)(ddbScriptPath)) return null;
|
|
23538
23673
|
return { root, source, registryPath, ddbScriptPath };
|
|
23539
23674
|
}
|
|
23540
23675
|
function resolvePortRangeInfra(cwd, packageDir) {
|
|
23541
23676
|
const direct = portRangeInfraAt(cwd, "cwd");
|
|
23542
23677
|
if (direct) return direct;
|
|
23543
|
-
for (let dir = cwd; ; dir = (0,
|
|
23544
|
-
const sibling = portRangeInfraAt((0,
|
|
23678
|
+
for (let dir = cwd; ; dir = (0, import_node_path26.dirname)(dir)) {
|
|
23679
|
+
const sibling = portRangeInfraAt((0, import_node_path26.join)(dir, "MMI-Hub"), "sibling-hub");
|
|
23545
23680
|
if (sibling) return sibling;
|
|
23546
|
-
const parent = (0,
|
|
23681
|
+
const parent = (0, import_node_path26.dirname)(dir);
|
|
23547
23682
|
if (parent === dir) break;
|
|
23548
23683
|
}
|
|
23549
23684
|
if (packageDir) {
|
|
23550
|
-
const pkgRoot = (0,
|
|
23685
|
+
const pkgRoot = (0, import_node_path26.join)(packageDir, "..", "..");
|
|
23551
23686
|
const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
|
|
23552
23687
|
if (pkgFrom) return pkgFrom;
|
|
23553
23688
|
}
|
|
@@ -23723,8 +23858,8 @@ function registerStageCommands(program3) {
|
|
|
23723
23858
|
const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
|
|
23724
23859
|
return decideStage({
|
|
23725
23860
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
23726
|
-
hasCompose: (0,
|
|
23727
|
-
hasEnvExample: (0,
|
|
23861
|
+
hasCompose: (0, import_node_fs28.existsSync)((0, import_node_path27.join)(process.cwd(), "docker-compose.yml")),
|
|
23862
|
+
hasEnvExample: (0, import_node_fs28.existsSync)((0, import_node_path27.join)(process.cwd(), ".env.example"))
|
|
23728
23863
|
});
|
|
23729
23864
|
}
|
|
23730
23865
|
async function fetchStageVaultEnvMerge() {
|
|
@@ -24162,11 +24297,11 @@ function registerBoardCommands(program3) {
|
|
|
24162
24297
|
}
|
|
24163
24298
|
|
|
24164
24299
|
// src/merge-cleanup.ts
|
|
24165
|
-
var
|
|
24300
|
+
var import_node_fs29 = require("node:fs");
|
|
24166
24301
|
var import_promises7 = require("node:fs/promises");
|
|
24167
|
-
var
|
|
24302
|
+
var import_node_path29 = require("node:path");
|
|
24168
24303
|
var import_node_os10 = require("node:os");
|
|
24169
|
-
var
|
|
24304
|
+
var import_node_child_process14 = require("node:child_process");
|
|
24170
24305
|
|
|
24171
24306
|
// src/board-advance.ts
|
|
24172
24307
|
function repoOf2(ref) {
|
|
@@ -24252,7 +24387,7 @@ function boardAdvanceFailureMessage(result) {
|
|
|
24252
24387
|
|
|
24253
24388
|
// src/deferred-registry-store.ts
|
|
24254
24389
|
var import_promises6 = require("node:fs/promises");
|
|
24255
|
-
var
|
|
24390
|
+
var import_node_path28 = require("node:path");
|
|
24256
24391
|
var sleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
24257
24392
|
async function atomicWrite(target, contents) {
|
|
24258
24393
|
const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
@@ -24303,12 +24438,12 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
|
|
|
24303
24438
|
},
|
|
24304
24439
|
// Standalone atomic write — THROWS on failure (no best-effort swallow, #2846).
|
|
24305
24440
|
write: async (entries) => {
|
|
24306
|
-
await (0, import_promises6.mkdir)((0,
|
|
24441
|
+
await (0, import_promises6.mkdir)((0, import_node_path28.dirname)(registryPath), { recursive: true });
|
|
24307
24442
|
await atomicWrite(registryPath, serializeDeferredWorktrees(entries));
|
|
24308
24443
|
},
|
|
24309
24444
|
// Serialized read-modify-write under the repo-wide lock (#2846).
|
|
24310
24445
|
update: async (mutate) => {
|
|
24311
|
-
await (0, import_promises6.mkdir)((0,
|
|
24446
|
+
await (0, import_promises6.mkdir)((0, import_node_path28.dirname)(registryPath), { recursive: true });
|
|
24312
24447
|
const deadline = Date.now() + opts.maxWaitMs;
|
|
24313
24448
|
for (; ; ) {
|
|
24314
24449
|
const guard = await acquireLock(lockPath, opts, deadline);
|
|
@@ -24469,7 +24604,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
24469
24604
|
);
|
|
24470
24605
|
const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
24471
24606
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
24472
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
24607
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path29.dirname)((0, import_node_path29.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
24473
24608
|
const gcActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
|
|
24474
24609
|
const owners = readWorktreeOwners(primaryRepoRoot);
|
|
24475
24610
|
const removalNow = Date.now();
|
|
@@ -24500,7 +24635,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
24500
24635
|
const cleanup = await cleanupPrMergeLocalBranch(branch.branch, {
|
|
24501
24636
|
beforeWorktrees,
|
|
24502
24637
|
startingPath: branch.worktreePath,
|
|
24503
|
-
pathExists: (p) => (0,
|
|
24638
|
+
pathExists: (p) => (0, import_node_fs29.existsSync)(p),
|
|
24504
24639
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
24505
24640
|
teardownWorktreeStage,
|
|
24506
24641
|
deferredStore,
|
|
@@ -24528,7 +24663,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
24528
24663
|
for (const wt of worktreeDirsToRemove) {
|
|
24529
24664
|
try {
|
|
24530
24665
|
const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, siblingRoot, {
|
|
24531
|
-
realpath: (path2) => (0,
|
|
24666
|
+
realpath: (path2) => (0, import_node_fs29.realpathSync)(path2)
|
|
24532
24667
|
});
|
|
24533
24668
|
if (!cleanupTarget.ok) {
|
|
24534
24669
|
result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
|
|
@@ -24593,13 +24728,13 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
|
|
|
24593
24728
|
const commits = JSON.parse(raw).commits ?? [];
|
|
24594
24729
|
const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
|
|
24595
24730
|
if (!body) return void 0;
|
|
24596
|
-
const dir = (0,
|
|
24597
|
-
const path2 = (0,
|
|
24598
|
-
(0,
|
|
24731
|
+
const dir = (0, import_node_fs29.mkdtempSync)((0, import_node_path29.join)((0, import_node_os10.tmpdir)(), "mmi-squash-body-"));
|
|
24732
|
+
const path2 = (0, import_node_path29.join)(dir, "body.txt");
|
|
24733
|
+
(0, import_node_fs29.writeFileSync)(path2, `${body}
|
|
24599
24734
|
`, "utf8");
|
|
24600
24735
|
return { path: path2, cleanup: () => {
|
|
24601
24736
|
try {
|
|
24602
|
-
(0,
|
|
24737
|
+
(0, import_node_fs29.rmSync)(dir, { recursive: true, force: true });
|
|
24603
24738
|
} catch {
|
|
24604
24739
|
}
|
|
24605
24740
|
} };
|
|
@@ -24707,7 +24842,7 @@ async function remoteBranchExists2(branch, options = {}) {
|
|
|
24707
24842
|
}
|
|
24708
24843
|
var COMPOSE_TIMEOUT_MS = 12e4;
|
|
24709
24844
|
function spawnDeferredGcSweep() {
|
|
24710
|
-
spawnDetachedSelf(["worktree", "gc", "sweep-deferred", "--quiet"], { spawn:
|
|
24845
|
+
spawnDetachedSelf(["worktree", "gc", "sweep-deferred", "--quiet"], { spawn: import_node_child_process14.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
24711
24846
|
}
|
|
24712
24847
|
async function createDeferredWorktreeStore() {
|
|
24713
24848
|
try {
|
|
@@ -24721,13 +24856,13 @@ var realWorktreeDirRemover = {
|
|
|
24721
24856
|
probe: (p) => {
|
|
24722
24857
|
let st;
|
|
24723
24858
|
try {
|
|
24724
|
-
st = (0,
|
|
24859
|
+
st = (0, import_node_fs29.lstatSync)(p);
|
|
24725
24860
|
} catch {
|
|
24726
24861
|
return null;
|
|
24727
24862
|
}
|
|
24728
24863
|
if (st.isSymbolicLink()) return "link";
|
|
24729
24864
|
try {
|
|
24730
|
-
(0,
|
|
24865
|
+
(0, import_node_fs29.readlinkSync)(p);
|
|
24731
24866
|
return "link";
|
|
24732
24867
|
} catch {
|
|
24733
24868
|
}
|
|
@@ -24735,7 +24870,7 @@ var realWorktreeDirRemover = {
|
|
|
24735
24870
|
},
|
|
24736
24871
|
readdir: (p) => {
|
|
24737
24872
|
try {
|
|
24738
|
-
return (0,
|
|
24873
|
+
return (0, import_node_fs29.readdirSync)(p);
|
|
24739
24874
|
} catch {
|
|
24740
24875
|
return [];
|
|
24741
24876
|
}
|
|
@@ -24744,9 +24879,9 @@ var realWorktreeDirRemover = {
|
|
|
24744
24879
|
// leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
|
|
24745
24880
|
detachLink: (p) => {
|
|
24746
24881
|
try {
|
|
24747
|
-
(0,
|
|
24882
|
+
(0, import_node_fs29.rmdirSync)(p);
|
|
24748
24883
|
} catch {
|
|
24749
|
-
(0,
|
|
24884
|
+
(0, import_node_fs29.unlinkSync)(p);
|
|
24750
24885
|
}
|
|
24751
24886
|
},
|
|
24752
24887
|
removeTree: (p) => (0, import_promises7.rm)(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
|
|
@@ -24779,9 +24914,9 @@ async function worktreeHasStageState(worktreePath) {
|
|
|
24779
24914
|
}
|
|
24780
24915
|
}
|
|
24781
24916
|
function stageStateFileBelongsToWorktree(statePath, worktreePath) {
|
|
24782
|
-
if (!(0,
|
|
24917
|
+
if (!(0, import_node_fs29.existsSync)(statePath)) return false;
|
|
24783
24918
|
try {
|
|
24784
|
-
const state = JSON.parse((0,
|
|
24919
|
+
const state = JSON.parse((0, import_node_fs29.readFileSync)(statePath, "utf8"));
|
|
24785
24920
|
const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
|
|
24786
24921
|
return Boolean(recordedCwd && isPathUnderDirectory(recordedCwd, worktreePath));
|
|
24787
24922
|
} catch {
|
|
@@ -24988,9 +25123,9 @@ async function fetchRestCorePool(gh = defaultGhApi) {
|
|
|
24988
25123
|
}
|
|
24989
25124
|
|
|
24990
25125
|
// src/worktree-lifecycle-commands.ts
|
|
24991
|
-
var
|
|
25126
|
+
var import_node_fs30 = require("node:fs");
|
|
24992
25127
|
var import_promises8 = require("node:fs/promises");
|
|
24993
|
-
var
|
|
25128
|
+
var import_node_path30 = require("node:path");
|
|
24994
25129
|
var GH_TIMEOUT_MS = 2e4;
|
|
24995
25130
|
var DEFAULT_BASE = "origin/development";
|
|
24996
25131
|
var DEFAULT_REMOTE = "origin";
|
|
@@ -25126,7 +25261,7 @@ function classifyStaleLeaks(input) {
|
|
|
25126
25261
|
var defaultOrphanDirScanDeps = {
|
|
25127
25262
|
listDirs: (root) => {
|
|
25128
25263
|
try {
|
|
25129
|
-
return (0,
|
|
25264
|
+
return (0, import_node_fs30.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path30.join)(root, e.name));
|
|
25130
25265
|
} catch {
|
|
25131
25266
|
return [];
|
|
25132
25267
|
}
|
|
@@ -25273,13 +25408,13 @@ function registerWorktreeCommands(program3) {
|
|
|
25273
25408
|
const headBorn = await execFileP2("git", ["-C", wtPath || ".", "rev-parse", "--verify", "--quiet", "HEAD"], { timeout: GIT_TIMEOUT_MS }).then(() => true).catch(() => false);
|
|
25274
25409
|
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
25410
|
if (!wtPath || !branch) return fail("worktree land: not inside a git worktree");
|
|
25276
|
-
const gitFile = (0,
|
|
25277
|
-
const isLinked = (0,
|
|
25411
|
+
const gitFile = (0, import_node_path30.join)(wtPath, ".git");
|
|
25412
|
+
const isLinked = (0, import_node_fs30.existsSync)(gitFile) && (0, import_node_fs30.statSync)(gitFile).isFile();
|
|
25278
25413
|
if (apply && !isLinked) {
|
|
25279
25414
|
return fail("worktree land: run from inside the linked worktree you want to land (this is the primary checkout)");
|
|
25280
25415
|
}
|
|
25281
25416
|
const commonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
25282
|
-
const primaryCheckout = commonDir ? (0,
|
|
25417
|
+
const primaryCheckout = commonDir ? (0, import_node_path30.dirname)(commonDir) : wtPath;
|
|
25283
25418
|
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
25419
|
const orphan = classifyOrphanedWorktree({
|
|
25285
25420
|
branch,
|
|
@@ -25466,10 +25601,10 @@ async function gatherWorktreeContext() {
|
|
|
25466
25601
|
if (s) stages.push({ path: wt.path, port: s.port });
|
|
25467
25602
|
}
|
|
25468
25603
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
25469
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
25604
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path30.dirname)((0, import_node_path30.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
25470
25605
|
const wtRoot = siblingMmiWorktreesRoot(primaryRepoRoot);
|
|
25471
25606
|
let orphanDirs = [];
|
|
25472
|
-
if ((0,
|
|
25607
|
+
if ((0, import_node_fs30.existsSync)(wtRoot)) {
|
|
25473
25608
|
orphanDirs = scanOrphanDirs(wtRoot, worktreeGitRoot, {
|
|
25474
25609
|
...defaultOrphanDirScanDeps,
|
|
25475
25610
|
listDirs: (root) => worktreeScanDirs(root, primaryRepoRoot, defaultOrphanDirScanDeps.listDirs, isRepoCheckoutDir)
|
|
@@ -25492,7 +25627,7 @@ async function bestEffortGit(args, cwd, step, timeoutMs = GIT_TIMEOUT_MS) {
|
|
|
25492
25627
|
}
|
|
25493
25628
|
|
|
25494
25629
|
// src/issue-commands.ts
|
|
25495
|
-
var
|
|
25630
|
+
var import_node_fs31 = require("node:fs");
|
|
25496
25631
|
var import_node_crypto5 = require("node:crypto");
|
|
25497
25632
|
var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
|
|
25498
25633
|
var ReparentConflictError = class extends Error {
|
|
@@ -25510,7 +25645,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
25510
25645
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
25511
25646
|
const patch = {};
|
|
25512
25647
|
let bodyChanged = false;
|
|
25513
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
25648
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs31.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
25514
25649
|
if (options.titleFile !== void 0) {
|
|
25515
25650
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
25516
25651
|
} else if (options.title !== void 0) {
|
|
@@ -26073,7 +26208,7 @@ function extendCreateCommand(issue2, batchAttach) {
|
|
|
26073
26208
|
if (opts.batch) {
|
|
26074
26209
|
let specs;
|
|
26075
26210
|
try {
|
|
26076
|
-
const raw = (0,
|
|
26211
|
+
const raw = (0, import_node_fs31.readFileSync)(opts.batch, "utf8");
|
|
26077
26212
|
specs = JSON.parse(raw);
|
|
26078
26213
|
if (!Array.isArray(specs)) throw new Error("batch file must contain a JSON array");
|
|
26079
26214
|
} catch (e) {
|
|
@@ -26147,8 +26282,8 @@ ${lines}`, {
|
|
|
26147
26282
|
}
|
|
26148
26283
|
|
|
26149
26284
|
// src/train-commands.ts
|
|
26150
|
-
var
|
|
26151
|
-
var
|
|
26285
|
+
var import_node_fs32 = require("node:fs");
|
|
26286
|
+
var import_node_path31 = require("node:path");
|
|
26152
26287
|
|
|
26153
26288
|
// src/train-status.ts
|
|
26154
26289
|
function buildTrainStatusReport(input) {
|
|
@@ -26188,7 +26323,7 @@ function formatTrainStatus(r) {
|
|
|
26188
26323
|
// src/train-commands.ts
|
|
26189
26324
|
function readRepoVersion() {
|
|
26190
26325
|
try {
|
|
26191
|
-
return JSON.parse((0,
|
|
26326
|
+
return JSON.parse((0, import_node_fs32.readFileSync)((0, import_node_path31.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
26192
26327
|
} catch {
|
|
26193
26328
|
return void 0;
|
|
26194
26329
|
}
|
|
@@ -26334,9 +26469,9 @@ function registerDeployCommands(program3) {
|
|
|
26334
26469
|
}
|
|
26335
26470
|
|
|
26336
26471
|
// src/discovery-commands.ts
|
|
26337
|
-
var
|
|
26472
|
+
var import_node_fs33 = require("node:fs");
|
|
26338
26473
|
var import_node_os11 = require("node:os");
|
|
26339
|
-
var
|
|
26474
|
+
var import_node_path32 = require("node:path");
|
|
26340
26475
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
26341
26476
|
async function collectStatus() {
|
|
26342
26477
|
let branch = "";
|
|
@@ -26513,8 +26648,8 @@ async function collectOnboardStatus() {
|
|
|
26513
26648
|
}
|
|
26514
26649
|
const home = (0, import_node_os11.homedir)();
|
|
26515
26650
|
const plugin = onboardPluginGate({
|
|
26516
|
-
readKnown: () => readFileSyncSafe((0,
|
|
26517
|
-
readSettings: () => readFileSyncSafe((0,
|
|
26651
|
+
readKnown: () => readFileSyncSafe((0, import_node_path32.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs33.readFileSync),
|
|
26652
|
+
readSettings: () => readFileSyncSafe((0, import_node_path32.join)(home, ".claude", "settings.json"), import_node_fs33.readFileSync)
|
|
26518
26653
|
});
|
|
26519
26654
|
return { track, board, registry: registry2, secrets, plugin, nextCommand };
|
|
26520
26655
|
}
|
|
@@ -28611,6 +28746,13 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
28611
28746
|
if (payload) emitNow(payload);
|
|
28612
28747
|
}
|
|
28613
28748
|
async function runMarketplaceRows() {
|
|
28749
|
+
if (applyEnv) {
|
|
28750
|
+
const healed = deps.healMarketplacePins();
|
|
28751
|
+
if (healed) {
|
|
28752
|
+
healIntent(`marketplace pins \u2014 ${healed}`);
|
|
28753
|
+
restartPending = true;
|
|
28754
|
+
}
|
|
28755
|
+
}
|
|
28614
28756
|
for (const row of deps.marketplaceRows()) emitNow(row);
|
|
28615
28757
|
}
|
|
28616
28758
|
async function runSchedulesRow() {
|
|
@@ -28789,17 +28931,17 @@ function parseOriginRepo(remoteUrl) {
|
|
|
28789
28931
|
}
|
|
28790
28932
|
function ghHostsConfigPath(env, platform2) {
|
|
28791
28933
|
const sep2 = platform2 === "win32" ? "\\" : "/";
|
|
28792
|
-
const
|
|
28934
|
+
const join29 = (...parts) => parts.join(sep2);
|
|
28793
28935
|
const explicit = env.GH_CONFIG_DIR?.trim();
|
|
28794
|
-
if (explicit) return
|
|
28936
|
+
if (explicit) return join29(explicit, "hosts.yml");
|
|
28795
28937
|
if (platform2 === "win32") {
|
|
28796
28938
|
const appData = (env.AppData ?? env.APPDATA)?.trim();
|
|
28797
|
-
return appData ?
|
|
28939
|
+
return appData ? join29(appData, "GitHub CLI", "hosts.yml") : void 0;
|
|
28798
28940
|
}
|
|
28799
28941
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
28800
|
-
if (xdg) return
|
|
28942
|
+
if (xdg) return join29(xdg, "gh", "hosts.yml");
|
|
28801
28943
|
const home = env.HOME?.trim();
|
|
28802
|
-
return home ?
|
|
28944
|
+
return home ? join29(home, ".config", "gh", "hosts.yml") : void 0;
|
|
28803
28945
|
}
|
|
28804
28946
|
function parseGhHostsAccounts(yaml, host = "github.com") {
|
|
28805
28947
|
let hostIndent = null;
|
|
@@ -28849,17 +28991,17 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
28849
28991
|
}
|
|
28850
28992
|
|
|
28851
28993
|
// src/doctor-io.ts
|
|
28852
|
-
var
|
|
28994
|
+
var import_node_fs34 = require("node:fs");
|
|
28853
28995
|
var import_node_os12 = require("node:os");
|
|
28854
|
-
var
|
|
28855
|
-
var
|
|
28996
|
+
var import_node_path33 = require("node:path");
|
|
28997
|
+
var import_node_child_process15 = require("node:child_process");
|
|
28856
28998
|
var import_node_util8 = require("node:util");
|
|
28857
|
-
var execFileP6 = (0, import_node_util8.promisify)(
|
|
28999
|
+
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process15.execFile);
|
|
28858
29000
|
var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
28859
29001
|
function installedClaudePluginVersion() {
|
|
28860
29002
|
try {
|
|
28861
29003
|
const file = JSON.parse(
|
|
28862
|
-
(0,
|
|
29004
|
+
(0, import_node_fs34.readFileSync)((0, import_node_path33.join)((0, import_node_os12.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
28863
29005
|
);
|
|
28864
29006
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
28865
29007
|
if (versions.length === 0) return void 0;
|
|
@@ -28870,7 +29012,7 @@ function installedClaudePluginVersion() {
|
|
|
28870
29012
|
}
|
|
28871
29013
|
function manifestVersion(path2) {
|
|
28872
29014
|
try {
|
|
28873
|
-
const manifest = JSON.parse((0,
|
|
29015
|
+
const manifest = JSON.parse((0, import_node_fs34.readFileSync)(path2, "utf8"));
|
|
28874
29016
|
return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : void 0;
|
|
28875
29017
|
} catch {
|
|
28876
29018
|
return void 0;
|
|
@@ -28880,27 +29022,27 @@ function installedSurfacePluginVersion(surface) {
|
|
|
28880
29022
|
const token = surfaceToken(surface);
|
|
28881
29023
|
if (token === "kilo") {
|
|
28882
29024
|
try {
|
|
28883
|
-
const stamp = (0,
|
|
29025
|
+
const stamp = (0, import_node_fs34.readFileSync)((0, import_node_path33.join)((0, import_node_os12.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
|
|
28884
29026
|
return stamp || void 0;
|
|
28885
29027
|
} catch {
|
|
28886
29028
|
return void 0;
|
|
28887
29029
|
}
|
|
28888
29030
|
}
|
|
28889
29031
|
if (token === "cursor") {
|
|
28890
|
-
return manifestVersion((0,
|
|
29032
|
+
return manifestVersion((0, import_node_path33.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
|
|
28891
29033
|
}
|
|
28892
29034
|
if (token === "kimi") {
|
|
28893
|
-
return manifestVersion((0,
|
|
29035
|
+
return manifestVersion((0, import_node_path33.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
|
|
28894
29036
|
}
|
|
28895
29037
|
if (token === "claude") return installedClaudePluginVersion();
|
|
28896
29038
|
if (token !== "codex") return void 0;
|
|
28897
29039
|
try {
|
|
28898
|
-
const raw = process.platform === "win32" ? (0,
|
|
29040
|
+
const raw = process.platform === "win32" ? (0, import_node_child_process15.execFileSync)("cmd.exe", ["/c", "codex", "plugin", "list", "--json"], {
|
|
28899
29041
|
encoding: "utf8",
|
|
28900
29042
|
stdio: ["ignore", "pipe", "ignore"],
|
|
28901
29043
|
timeout: 15e3,
|
|
28902
29044
|
windowsHide: true
|
|
28903
|
-
}) : (0,
|
|
29045
|
+
}) : (0, import_node_child_process15.execFileSync)("codex", ["plugin", "list", "--json"], {
|
|
28904
29046
|
encoding: "utf8",
|
|
28905
29047
|
stdio: ["ignore", "pipe", "ignore"],
|
|
28906
29048
|
timeout: 15e3,
|
|
@@ -28918,7 +29060,7 @@ function installedActivePluginVersion(surface = detectSurface(process.env)) {
|
|
|
28918
29060
|
}
|
|
28919
29061
|
function worktreeRootSync() {
|
|
28920
29062
|
try {
|
|
28921
|
-
const out = (0,
|
|
29063
|
+
const out = (0, import_node_child_process15.execFileSync)("git", ["rev-parse", "--show-toplevel"], { windowsHide: true, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
28922
29064
|
let root = out.endsWith("\n") ? out.slice(0, -1) : out;
|
|
28923
29065
|
if (process.platform === "win32" && root.endsWith("\r")) root = root.slice(0, -1);
|
|
28924
29066
|
return root || null;
|
|
@@ -28928,13 +29070,13 @@ function worktreeRootSync() {
|
|
|
28928
29070
|
}
|
|
28929
29071
|
var gitignorePath = () => {
|
|
28930
29072
|
const root = worktreeRootSync();
|
|
28931
|
-
return root === null ? null : (0,
|
|
29073
|
+
return root === null ? null : (0, import_node_path33.join)(root, ".gitignore");
|
|
28932
29074
|
};
|
|
28933
29075
|
function readGitignore() {
|
|
28934
29076
|
const path2 = gitignorePath();
|
|
28935
29077
|
if (path2 === null) return null;
|
|
28936
29078
|
try {
|
|
28937
|
-
return (0,
|
|
29079
|
+
return (0, import_node_fs34.readFileSync)(path2, "utf8");
|
|
28938
29080
|
} catch {
|
|
28939
29081
|
return null;
|
|
28940
29082
|
}
|
|
@@ -28943,7 +29085,7 @@ function writeGitignore(content) {
|
|
|
28943
29085
|
const path2 = gitignorePath();
|
|
28944
29086
|
if (path2 === null) return false;
|
|
28945
29087
|
try {
|
|
28946
|
-
(0,
|
|
29088
|
+
(0, import_node_fs34.writeFileSync)(path2, content, "utf8");
|
|
28947
29089
|
return true;
|
|
28948
29090
|
} catch {
|
|
28949
29091
|
return false;
|
|
@@ -28967,7 +29109,7 @@ async function repoRoot() {
|
|
|
28967
29109
|
}
|
|
28968
29110
|
function hasRepoLocalWorktrees() {
|
|
28969
29111
|
const root = worktreeRootSync();
|
|
28970
|
-
return root !== null && (0,
|
|
29112
|
+
return root !== null && (0, import_node_fs34.existsSync)((0, import_node_path33.join)(root, ".worktrees"));
|
|
28971
29113
|
}
|
|
28972
29114
|
|
|
28973
29115
|
// src/index.ts
|
|
@@ -29003,8 +29145,8 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
29003
29145
|
function ghMultiAccountCaveat(announcedLogin) {
|
|
29004
29146
|
try {
|
|
29005
29147
|
const hostsPath = ghHostsConfigPath(process.env, process.platform);
|
|
29006
|
-
if (!hostsPath || !(0,
|
|
29007
|
-
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0,
|
|
29148
|
+
if (!hostsPath || !(0, import_node_fs35.existsSync)(hostsPath)) return void 0;
|
|
29149
|
+
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs35.readFileSync)(hostsPath, "utf8")));
|
|
29008
29150
|
} catch {
|
|
29009
29151
|
return void 0;
|
|
29010
29152
|
}
|
|
@@ -29012,7 +29154,7 @@ function ghMultiAccountCaveat(announcedLogin) {
|
|
|
29012
29154
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
29013
29155
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
29014
29156
|
function envHealLockPath(home) {
|
|
29015
|
-
return (0,
|
|
29157
|
+
return (0, import_node_path34.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
29016
29158
|
}
|
|
29017
29159
|
async function withEnvHealLock(what, run) {
|
|
29018
29160
|
try {
|
|
@@ -29198,12 +29340,25 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
29198
29340
|
const home = (0, import_node_os13.homedir)();
|
|
29199
29341
|
return marketplaceRows(
|
|
29200
29342
|
MMI_MARKETPLACE_NAME,
|
|
29201
|
-
readFileSyncSafe((0,
|
|
29202
|
-
readFileSyncSafe((0,
|
|
29343
|
+
readFileSyncSafe((0, import_node_path34.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs35.readFileSync),
|
|
29344
|
+
readFileSyncSafe((0, import_node_path34.join)(home, ".claude", "settings.json"), import_node_fs35.readFileSync),
|
|
29345
|
+
// #3974: this CLI heals these rows, so report mode names `doctor --apply` rather than the hand
|
|
29346
|
+
// edit. Unconditional — it is a fact about mmi-cli, not about whether --apply was passed.
|
|
29347
|
+
true
|
|
29203
29348
|
);
|
|
29204
29349
|
} catch {
|
|
29205
29350
|
return [];
|
|
29206
29351
|
}
|
|
29352
|
+
},
|
|
29353
|
+
// #3974: the heal behind those rows, under --apply only. Same Codex carve-out as the rows above —
|
|
29354
|
+
// Codex keeps its registration in config.toml, so there is no ~/.claude registration to pin.
|
|
29355
|
+
healMarketplacePins: () => {
|
|
29356
|
+
try {
|
|
29357
|
+
if (detectSurface(process.env) === "codex") return void 0;
|
|
29358
|
+
return applyOrgMarketplacePins((0, import_node_path34.join)((0, import_node_os13.homedir)(), ...KNOWN_MARKETPLACES_RELATIVE), [MMI_MARKETPLACE_NAME]);
|
|
29359
|
+
} catch {
|
|
29360
|
+
return void 0;
|
|
29361
|
+
}
|
|
29207
29362
|
}
|
|
29208
29363
|
};
|
|
29209
29364
|
}
|
|
@@ -29405,19 +29560,19 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
29405
29560
|
});
|
|
29406
29561
|
var rules = program2.command("rules").description("org-managed .gitignore delivery");
|
|
29407
29562
|
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,
|
|
29563
|
+
const path2 = (0, import_node_path34.join)(process.cwd(), ".gitignore");
|
|
29564
|
+
const current = (0, import_node_fs35.existsSync)(path2) ? (0, import_node_fs35.readFileSync)(path2, "utf8") : null;
|
|
29410
29565
|
const plan = planManagedGitignore(current);
|
|
29411
29566
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
29412
29567
|
if (opts.json) {
|
|
29413
|
-
if (opts.write && plan.changed) (0,
|
|
29568
|
+
if (opts.write && plan.changed) (0, import_node_fs35.writeFileSync)(path2, plan.content, "utf8");
|
|
29414
29569
|
console.log(JSON.stringify(plan, null, 2));
|
|
29415
29570
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
29416
29571
|
return;
|
|
29417
29572
|
}
|
|
29418
29573
|
if (opts.write) {
|
|
29419
29574
|
if (plan.changed) {
|
|
29420
|
-
(0,
|
|
29575
|
+
(0, import_node_fs35.writeFileSync)(path2, plan.content, "utf8");
|
|
29421
29576
|
console.log(`mmi-cli org rules gitignore: updated .gitignore (${drift})`);
|
|
29422
29577
|
} else {
|
|
29423
29578
|
console.log("mmi-cli org rules gitignore: up to date");
|
|
@@ -29572,8 +29727,8 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
|
|
|
29572
29727
|
if (!Number.isFinite(limit) || limit < 1) return fail("worktree gc: --limit must be a positive integer");
|
|
29573
29728
|
let root;
|
|
29574
29729
|
if (o.root !== void 0) {
|
|
29575
|
-
root = (0,
|
|
29576
|
-
if (!(0,
|
|
29730
|
+
root = (0, import_node_path34.resolve)(o.root);
|
|
29731
|
+
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
29732
|
const gcRepoRoot = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
29578
29733
|
if (isPathUnderDirectory(gcRepoRoot, root)) {
|
|
29579
29734
|
return fail(`worktree gc: --root ${root} contains this checkout \u2014 name a worktrees root, not the repo or an ancestor of it`);
|
|
@@ -29615,7 +29770,7 @@ function runWorktreeInstall(command, cwd, quiet) {
|
|
|
29615
29770
|
const file = isWin2 ? "cmd.exe" : bin;
|
|
29616
29771
|
const spawnArgs = isWin2 ? ["/c", bin, ...args] : args;
|
|
29617
29772
|
return new Promise((resolve5, reject) => {
|
|
29618
|
-
const child2 = (0,
|
|
29773
|
+
const child2 = (0, import_node_child_process16.spawn)(file, spawnArgs, { cwd, stdio: quiet ? "ignore" : "inherit", windowsHide: true });
|
|
29619
29774
|
const timer = setTimeout(() => {
|
|
29620
29775
|
try {
|
|
29621
29776
|
child2.kill();
|
|
@@ -29638,7 +29793,7 @@ async function primaryCheckoutRoot(from) {
|
|
|
29638
29793
|
return primaryCheckoutRootOf(async (args) => (await execFileP2("git", ["-C", from, ...args], { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
29639
29794
|
}
|
|
29640
29795
|
async function unprovenWorktreeReason(wtPath, repoRoot2) {
|
|
29641
|
-
if (!(0,
|
|
29796
|
+
if (!(0, import_node_fs35.existsSync)(wtPath)) return `${wtPath} does not exist on disk`;
|
|
29642
29797
|
const porcelain = (await execFileP2("git", ["-C", repoRoot2, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
|
|
29643
29798
|
const registered = parseWorktreePorcelainEntries(porcelain);
|
|
29644
29799
|
if (!registered.length) {
|
|
@@ -29659,26 +29814,26 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
|
|
|
29659
29814
|
function acquireWorktreeSetupLock(worktreeRoot) {
|
|
29660
29815
|
const lockPath = repoRuntimeStatePath(worktreeRoot, "worktree-setup.lock");
|
|
29661
29816
|
const take = () => {
|
|
29662
|
-
const fd = (0,
|
|
29817
|
+
const fd = (0, import_node_fs35.openSync)(lockPath, "wx");
|
|
29663
29818
|
try {
|
|
29664
|
-
(0,
|
|
29819
|
+
(0, import_node_fs35.writeSync)(fd, String(Date.now()));
|
|
29665
29820
|
} finally {
|
|
29666
|
-
(0,
|
|
29821
|
+
(0, import_node_fs35.closeSync)(fd);
|
|
29667
29822
|
}
|
|
29668
29823
|
return () => {
|
|
29669
29824
|
try {
|
|
29670
|
-
(0,
|
|
29825
|
+
(0, import_node_fs35.rmSync)(lockPath, { force: true });
|
|
29671
29826
|
} catch {
|
|
29672
29827
|
}
|
|
29673
29828
|
};
|
|
29674
29829
|
};
|
|
29675
29830
|
try {
|
|
29676
|
-
(0,
|
|
29831
|
+
(0, import_node_fs35.mkdirSync)((0, import_node_path34.dirname)(lockPath), { recursive: true });
|
|
29677
29832
|
return take();
|
|
29678
29833
|
} catch {
|
|
29679
29834
|
try {
|
|
29680
|
-
if (Date.now() - (0,
|
|
29681
|
-
(0,
|
|
29835
|
+
if (Date.now() - (0, import_node_fs35.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
|
|
29836
|
+
(0, import_node_fs35.rmSync)(lockPath, { force: true });
|
|
29682
29837
|
return take();
|
|
29683
29838
|
}
|
|
29684
29839
|
} catch {
|
|
@@ -29901,7 +30056,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
29901
30056
|
try {
|
|
29902
30057
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
|
|
29903
30058
|
if (o.repo) args.push("--repo", o.repo);
|
|
29904
|
-
spawnDetachedSelf(args, { spawn:
|
|
30059
|
+
spawnDetachedSelf(args, { spawn: import_node_child_process16.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
|
|
29905
30060
|
} catch {
|
|
29906
30061
|
}
|
|
29907
30062
|
}
|
|
@@ -29951,6 +30106,29 @@ docs.command("refs").description("deterministic doc reference gate: every backti
|
|
|
29951
30106
|
await failGraceful(e.message);
|
|
29952
30107
|
}
|
|
29953
30108
|
});
|
|
30109
|
+
var spawnCmd = program2.command("spawn").description("this repo's process-spawn contract \u2014 every child process must be unable to pop a console window");
|
|
30110
|
+
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) => {
|
|
30111
|
+
try {
|
|
30112
|
+
const root = await repoRoot();
|
|
30113
|
+
const result = runSpawnPolicy(root);
|
|
30114
|
+
if (o.json) {
|
|
30115
|
+
consoleIo.log(JSON.stringify(result, null, 2));
|
|
30116
|
+
if (!result.ok) process.exitCode = 1;
|
|
30117
|
+
return;
|
|
30118
|
+
}
|
|
30119
|
+
if (result.ok) {
|
|
30120
|
+
console.log(`spawn policy: OK (${result.scannedCount} source file(s) scanned).`);
|
|
30121
|
+
return;
|
|
30122
|
+
}
|
|
30123
|
+
for (const f of result.findings) console.error(`spawn policy: ${f.detail}`);
|
|
30124
|
+
console.error(
|
|
30125
|
+
"\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."
|
|
30126
|
+
);
|
|
30127
|
+
process.exitCode = 1;
|
|
30128
|
+
} catch (e) {
|
|
30129
|
+
await failGraceful(e.message);
|
|
30130
|
+
}
|
|
30131
|
+
});
|
|
29954
30132
|
var tests = program2.command("tests").description("a repo's test-policy.json \u2014 the opt-in test contract and its enforcement");
|
|
29955
30133
|
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
30134
|
try {
|
|
@@ -30257,7 +30435,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
30257
30435
|
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
30436
|
if (o.secretsFile) {
|
|
30259
30437
|
try {
|
|
30260
|
-
vars.push(`secrets=${(0,
|
|
30438
|
+
vars.push(`secrets=${(0, import_node_fs35.readFileSync)(o.secretsFile, "utf8")}`);
|
|
30261
30439
|
} catch (e) {
|
|
30262
30440
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
30263
30441
|
}
|
|
@@ -31005,11 +31183,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
|
|
|
31005
31183
|
}
|
|
31006
31184
|
});
|
|
31007
31185
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
31008
|
-
const wfDir = (0,
|
|
31009
|
-
if (!(0,
|
|
31010
|
-
return (0,
|
|
31186
|
+
const wfDir = (0, import_node_path34.join)(cwd, ".github", "workflows");
|
|
31187
|
+
if (!(0, import_node_fs35.existsSync)(wfDir)) return [];
|
|
31188
|
+
return (0, import_node_fs35.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
31011
31189
|
try {
|
|
31012
|
-
return workflowReportsPrChecks((0,
|
|
31190
|
+
return workflowReportsPrChecks((0, import_node_fs35.readFileSync)((0, import_node_path34.join)(wfDir, name), "utf8"));
|
|
31013
31191
|
} catch {
|
|
31014
31192
|
return true;
|
|
31015
31193
|
}
|
|
@@ -31041,16 +31219,16 @@ function ciAuditDeps() {
|
|
|
31041
31219
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
31042
31220
|
readSeedFile: (path2) => {
|
|
31043
31221
|
if (!root) return null;
|
|
31044
|
-
const fullPath = (0,
|
|
31045
|
-
return (0,
|
|
31222
|
+
const fullPath = (0, import_node_path34.join)(root, path2);
|
|
31223
|
+
return (0, import_node_fs35.existsSync)(fullPath) ? (0, import_node_fs35.readFileSync)(fullPath, "utf8") : null;
|
|
31046
31224
|
}
|
|
31047
31225
|
};
|
|
31048
31226
|
}
|
|
31049
31227
|
function hubRoot() {
|
|
31050
|
-
const fromPkg = (0,
|
|
31228
|
+
const fromPkg = (0, import_node_path34.join)(__dirname, "..", "..");
|
|
31051
31229
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
31052
|
-
if ((0,
|
|
31053
|
-
if ((0,
|
|
31230
|
+
if ((0, import_node_fs35.existsSync)((0, import_node_path34.join)(fromPkg, marker))) return fromPkg;
|
|
31231
|
+
if ((0, import_node_fs35.existsSync)((0, import_node_path34.join)(process.cwd(), marker))) return process.cwd();
|
|
31054
31232
|
return null;
|
|
31055
31233
|
}
|
|
31056
31234
|
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 +31531,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
31353
31531
|
localCleanup = await cleanupPrMergeLocalBranch(headRef, {
|
|
31354
31532
|
beforeWorktrees,
|
|
31355
31533
|
startingPath,
|
|
31356
|
-
pathExists: (p) => (0,
|
|
31534
|
+
pathExists: (p) => (0, import_node_fs35.existsSync)(p),
|
|
31357
31535
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
31358
31536
|
teardownWorktreeStage,
|
|
31359
31537
|
deferredStore,
|
|
@@ -31828,12 +32006,12 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
31828
32006
|
targets = resolution.targets;
|
|
31829
32007
|
}
|
|
31830
32008
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
31831
|
-
const fileMatrix = (0,
|
|
32009
|
+
const fileMatrix = (0, import_node_fs35.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs35.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
31832
32010
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
31833
32011
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
31834
|
-
const fileContracts = (0,
|
|
32012
|
+
const fileContracts = (0, import_node_fs35.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs35.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
31835
32013
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
31836
|
-
const sanctioned = (0,
|
|
32014
|
+
const sanctioned = (0, import_node_fs35.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs35.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
31837
32015
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
31838
32016
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
31839
32017
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -31866,16 +32044,16 @@ function directoryBytes(path2) {
|
|
|
31866
32044
|
let total = 0;
|
|
31867
32045
|
let entries;
|
|
31868
32046
|
try {
|
|
31869
|
-
entries = (0,
|
|
32047
|
+
entries = (0, import_node_fs35.readdirSync)(path2, { withFileTypes: true });
|
|
31870
32048
|
} catch {
|
|
31871
32049
|
return 0;
|
|
31872
32050
|
}
|
|
31873
32051
|
for (const entry of entries) {
|
|
31874
|
-
const child2 = (0,
|
|
32052
|
+
const child2 = (0, import_node_path34.join)(path2, entry.name);
|
|
31875
32053
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
31876
32054
|
else {
|
|
31877
32055
|
try {
|
|
31878
|
-
total += (0,
|
|
32056
|
+
total += (0, import_node_fs35.statSync)(child2).size;
|
|
31879
32057
|
} catch {
|
|
31880
32058
|
}
|
|
31881
32059
|
}
|
|
@@ -31883,25 +32061,25 @@ function directoryBytes(path2) {
|
|
|
31883
32061
|
return total;
|
|
31884
32062
|
}
|
|
31885
32063
|
function listDirEntries(dir) {
|
|
31886
|
-
return (0,
|
|
32064
|
+
return (0, import_node_fs35.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
31887
32065
|
}
|
|
31888
32066
|
function readInstalledPluginRefs(configRoot) {
|
|
31889
32067
|
const p = installedPluginsPathForConfig(configRoot);
|
|
31890
|
-
if (!(0,
|
|
32068
|
+
if (!(0, import_node_fs35.existsSync)(p)) return [];
|
|
31891
32069
|
try {
|
|
31892
|
-
return installedPluginPaths((0,
|
|
32070
|
+
return installedPluginPaths((0, import_node_fs35.readFileSync)(p, "utf8"));
|
|
31893
32071
|
} catch {
|
|
31894
32072
|
return null;
|
|
31895
32073
|
}
|
|
31896
32074
|
}
|
|
31897
32075
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
31898
32076
|
return {
|
|
31899
|
-
exists: (p) => (0,
|
|
31900
|
-
listVersionDirs: (root) => (0,
|
|
32077
|
+
exists: (p) => (0, import_node_fs35.existsSync)(p),
|
|
32078
|
+
listVersionDirs: (root) => (0, import_node_fs35.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
31901
32079
|
dirBytes,
|
|
31902
|
-
listStagingDirs: (root) => (0,
|
|
32080
|
+
listStagingDirs: (root) => (0, import_node_fs35.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
31903
32081
|
try {
|
|
31904
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
32082
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path34.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs35.statSync)(p).mtimeMs) };
|
|
31905
32083
|
} catch {
|
|
31906
32084
|
return { name: d.name, mtimeMs: Date.now() };
|
|
31907
32085
|
}
|
|
@@ -31915,10 +32093,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
31915
32093
|
return {
|
|
31916
32094
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
31917
32095
|
mtimeMs: (name) => {
|
|
31918
|
-
const p = (0,
|
|
31919
|
-
if (!(0,
|
|
32096
|
+
const p = (0, import_node_path34.join)(stagingRoot, name);
|
|
32097
|
+
if (!(0, import_node_fs35.existsSync)(p)) return null;
|
|
31920
32098
|
try {
|
|
31921
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
32099
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs35.statSync)(q).mtimeMs);
|
|
31922
32100
|
} catch {
|
|
31923
32101
|
return null;
|
|
31924
32102
|
}
|
|
@@ -31944,7 +32122,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
31944
32122
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
31945
32123
|
);
|
|
31946
32124
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
31947
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
32125
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs35.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
31948
32126
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
31949
32127
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
31950
32128
|
else console.log(renderPluginCachePlan(plan, result));
|
|
@@ -32007,7 +32185,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
|
|
|
32007
32185
|
for (const line of scratchGcLines(process.cwd())) bannerIo.log(line);
|
|
32008
32186
|
const worktreeBanner = worktreeAutoProvisionBanner(process.cwd());
|
|
32009
32187
|
if (worktreeBanner) {
|
|
32010
|
-
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn:
|
|
32188
|
+
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process16.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
32011
32189
|
bannerIo.log(worktreeBanner);
|
|
32012
32190
|
}
|
|
32013
32191
|
if (isLinkedWorktree(process.cwd())) {
|