@mutmutco/cli 3.41.0 → 3.43.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 +363 -136
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -3410,8 +3410,8 @@ var program = new Command();
|
|
|
3410
3410
|
|
|
3411
3411
|
// src/index.ts
|
|
3412
3412
|
var import_promises7 = require("node:fs/promises");
|
|
3413
|
-
var
|
|
3414
|
-
var
|
|
3413
|
+
var import_node_fs29 = require("node:fs");
|
|
3414
|
+
var import_node_child_process13 = require("node:child_process");
|
|
3415
3415
|
|
|
3416
3416
|
// src/cli-shared.ts
|
|
3417
3417
|
var import_node_child_process3 = require("node:child_process");
|
|
@@ -8949,7 +8949,7 @@ function commandLadderHint() {
|
|
|
8949
8949
|
}
|
|
8950
8950
|
|
|
8951
8951
|
// src/index.ts
|
|
8952
|
-
var
|
|
8952
|
+
var import_node_path26 = require("node:path");
|
|
8953
8953
|
|
|
8954
8954
|
// src/merge-ci-policy.ts
|
|
8955
8955
|
function resolveMergeCiPolicy(input) {
|
|
@@ -15375,6 +15375,207 @@ function createDocsIndexDeps(repoRoot2) {
|
|
|
15375
15375
|
};
|
|
15376
15376
|
}
|
|
15377
15377
|
|
|
15378
|
+
// src/doc-refs-core.ts
|
|
15379
|
+
var import_node_child_process9 = require("node:child_process");
|
|
15380
|
+
var import_node_fs18 = require("node:fs");
|
|
15381
|
+
var import_node_path16 = require("node:path");
|
|
15382
|
+
var PIN_RE = /<!--\s*pinned by\s+([^:]+?)\s*:\s*"([^"]+)"[^"]*-->/;
|
|
15383
|
+
var PIN_MENTION_RE = /<!--\s*pinned by\b/;
|
|
15384
|
+
var REF_RE = /`([A-Za-z0-9_./-]+\.[A-Za-z0-9]+)(?::\d+)?`/g;
|
|
15385
|
+
var LINK_RE = /\]\(([^)#\s]+?\.md)(?:#[^)]*)?\)/g;
|
|
15386
|
+
var CMD_RE = /`mmi-cli ((?:[a-z][a-z-]*)(?: [a-z][a-z-]*){0,3})/g;
|
|
15387
|
+
var RETIRED_RE = /\b(retired|historical|removed|deleted|gone|no longer|superseded|legacy)\b/i;
|
|
15388
|
+
var ROOT_DOCS = ["README.md", "architecture.md"];
|
|
15389
|
+
var SKIP_WALK = ["docs/Archive/", "docs/incidents/"];
|
|
15390
|
+
function stripFences(markdown) {
|
|
15391
|
+
let inFence = false;
|
|
15392
|
+
return markdown.split(/\r?\n/).map((line) => {
|
|
15393
|
+
if (/^\s*```/.test(line)) {
|
|
15394
|
+
inFence = !inFence;
|
|
15395
|
+
return "";
|
|
15396
|
+
}
|
|
15397
|
+
return inFence ? "" : line;
|
|
15398
|
+
});
|
|
15399
|
+
}
|
|
15400
|
+
function isRetiredLine(text) {
|
|
15401
|
+
return RETIRED_RE.test(text);
|
|
15402
|
+
}
|
|
15403
|
+
function extractPins(markdown) {
|
|
15404
|
+
const pins = [];
|
|
15405
|
+
markdown.split(/\r?\n/).forEach((text, index) => {
|
|
15406
|
+
const match = PIN_RE.exec(text);
|
|
15407
|
+
if (match) {
|
|
15408
|
+
pins.push({ file: match[1].trim(), phrase: match[2], line: index + 1 });
|
|
15409
|
+
} else if (PIN_MENTION_RE.test(text.replace(/`[^`]*`/g, ""))) {
|
|
15410
|
+
pins.push({ malformed: true, line: index + 1, text: text.trim() });
|
|
15411
|
+
}
|
|
15412
|
+
});
|
|
15413
|
+
return pins;
|
|
15414
|
+
}
|
|
15415
|
+
function checkPins(root, readFile6, docs2) {
|
|
15416
|
+
const findings = [];
|
|
15417
|
+
for (const [doc, markdown] of Object.entries(docs2)) {
|
|
15418
|
+
for (const pin of extractPins(markdown)) {
|
|
15419
|
+
if ("malformed" in pin) {
|
|
15420
|
+
findings.push({ kind: "malformed-pin", doc, line: pin.line, detail: pin.text });
|
|
15421
|
+
continue;
|
|
15422
|
+
}
|
|
15423
|
+
const source = readFile6((0, import_node_path16.join)(root, pin.file));
|
|
15424
|
+
if (source == null) {
|
|
15425
|
+
findings.push({ kind: "missing-test", doc, line: pin.line, detail: pin.file });
|
|
15426
|
+
continue;
|
|
15427
|
+
}
|
|
15428
|
+
if (!source.includes(pin.phrase)) {
|
|
15429
|
+
findings.push({ kind: "unasserted-phrase", doc, line: pin.line, detail: pin.phrase });
|
|
15430
|
+
}
|
|
15431
|
+
}
|
|
15432
|
+
}
|
|
15433
|
+
return { ok: findings.length === 0, findings };
|
|
15434
|
+
}
|
|
15435
|
+
function isDomainLike(ref) {
|
|
15436
|
+
const first = ref.split("/")[0];
|
|
15437
|
+
return first.includes(".") && !first.startsWith(".");
|
|
15438
|
+
}
|
|
15439
|
+
function extractRefs(markdown) {
|
|
15440
|
+
const lines = stripFences(markdown);
|
|
15441
|
+
const refs = [];
|
|
15442
|
+
lines.forEach((text, index) => {
|
|
15443
|
+
if (isRetiredLine(text)) return;
|
|
15444
|
+
for (const match of text.matchAll(REF_RE)) {
|
|
15445
|
+
const ref = match[1];
|
|
15446
|
+
if (!ref.includes("/")) continue;
|
|
15447
|
+
if (ref.startsWith("/") || ref.startsWith(".git/")) continue;
|
|
15448
|
+
if (isDomainLike(ref)) continue;
|
|
15449
|
+
refs.push({ ref, line: index + 1 });
|
|
15450
|
+
}
|
|
15451
|
+
});
|
|
15452
|
+
return refs;
|
|
15453
|
+
}
|
|
15454
|
+
function extractLinks(markdown) {
|
|
15455
|
+
const lines = stripFences(markdown);
|
|
15456
|
+
const links = [];
|
|
15457
|
+
lines.forEach((text, index) => {
|
|
15458
|
+
if (isRetiredLine(text)) return;
|
|
15459
|
+
for (const match of text.matchAll(LINK_RE)) {
|
|
15460
|
+
const target = match[1];
|
|
15461
|
+
if (/^[a-z]+:/i.test(target)) continue;
|
|
15462
|
+
links.push({ target, line: index + 1 });
|
|
15463
|
+
}
|
|
15464
|
+
});
|
|
15465
|
+
return links;
|
|
15466
|
+
}
|
|
15467
|
+
function extractCommands(markdown) {
|
|
15468
|
+
const lines = stripFences(markdown);
|
|
15469
|
+
const commands = [];
|
|
15470
|
+
lines.forEach((text, index) => {
|
|
15471
|
+
if (isRetiredLine(text)) return;
|
|
15472
|
+
for (const match of text.matchAll(CMD_RE)) {
|
|
15473
|
+
commands.push({ command: match[1], line: index + 1 });
|
|
15474
|
+
}
|
|
15475
|
+
});
|
|
15476
|
+
return commands;
|
|
15477
|
+
}
|
|
15478
|
+
function checkRefs(root, deps, docs2) {
|
|
15479
|
+
const { exists, isIgnored = () => /* @__PURE__ */ new Set() } = deps;
|
|
15480
|
+
const candidates = [];
|
|
15481
|
+
for (const [doc, markdown] of Object.entries(docs2)) {
|
|
15482
|
+
for (const { ref, line } of extractRefs(markdown)) {
|
|
15483
|
+
const first = ref.split("/")[0];
|
|
15484
|
+
if (!exists((0, import_node_path16.join)(root, first))) continue;
|
|
15485
|
+
if (!exists((0, import_node_path16.join)(root, ref))) candidates.push({ kind: "missing-path", doc, line, detail: ref });
|
|
15486
|
+
}
|
|
15487
|
+
const docDir = import_node_path16.posix.dirname(doc);
|
|
15488
|
+
for (const { target, line } of extractLinks(markdown)) {
|
|
15489
|
+
const resolved = import_node_path16.posix.normalize(import_node_path16.posix.join(docDir === "." ? "" : docDir, target));
|
|
15490
|
+
if (!exists((0, import_node_path16.join)(root, resolved))) {
|
|
15491
|
+
candidates.push({ kind: "missing-link", doc, line, detail: target, resolved });
|
|
15492
|
+
}
|
|
15493
|
+
}
|
|
15494
|
+
}
|
|
15495
|
+
const ignored = candidates.length ? isIgnored(candidates.map((c) => c.kind === "missing-link" ? c.resolved : c.detail)) : /* @__PURE__ */ new Set();
|
|
15496
|
+
const findings = candidates.filter((c) => !ignored.has(c.kind === "missing-link" ? c.resolved : c.detail)).map(({ resolved, ...finding }) => finding);
|
|
15497
|
+
return { ok: findings.length === 0, findings };
|
|
15498
|
+
}
|
|
15499
|
+
function checkCommands(docs2, commandPaths) {
|
|
15500
|
+
const findings = [];
|
|
15501
|
+
const prefixesSome = (prefix) => {
|
|
15502
|
+
for (const path2 of commandPaths) {
|
|
15503
|
+
if (path2 === prefix || path2.startsWith(`${prefix} `)) return true;
|
|
15504
|
+
}
|
|
15505
|
+
return false;
|
|
15506
|
+
};
|
|
15507
|
+
for (const [doc, markdown] of Object.entries(docs2)) {
|
|
15508
|
+
for (const { command, line } of extractCommands(markdown)) {
|
|
15509
|
+
const tokens2 = command.split(" ");
|
|
15510
|
+
for (let i = 1; i <= tokens2.length; i += 1) {
|
|
15511
|
+
const prefix = tokens2.slice(0, i).join(" ");
|
|
15512
|
+
if (prefixesSome(prefix)) continue;
|
|
15513
|
+
const parent = tokens2.slice(0, i - 1).join(" ");
|
|
15514
|
+
if (i > 1 && commandPaths.has(parent)) break;
|
|
15515
|
+
findings.push({ kind: "unknown-command", doc, line, detail: `mmi-cli ${prefix}` });
|
|
15516
|
+
break;
|
|
15517
|
+
}
|
|
15518
|
+
}
|
|
15519
|
+
}
|
|
15520
|
+
return { ok: findings.length === 0, findings };
|
|
15521
|
+
}
|
|
15522
|
+
function readFileOrNull(path2) {
|
|
15523
|
+
return (0, import_node_fs18.existsSync)(path2) ? (0, import_node_fs18.readFileSync)(path2, "utf8") : null;
|
|
15524
|
+
}
|
|
15525
|
+
function walk(dir, root, out) {
|
|
15526
|
+
for (const entry of (0, import_node_fs18.readdirSync)(dir)) {
|
|
15527
|
+
const full = (0, import_node_path16.join)(dir, entry);
|
|
15528
|
+
if ((0, import_node_fs18.statSync)(full).isDirectory()) walk(full, root, out);
|
|
15529
|
+
else if (entry.endsWith(".md")) out.push(full.slice(root.length + 1).replaceAll("\\", "/"));
|
|
15530
|
+
}
|
|
15531
|
+
return out;
|
|
15532
|
+
}
|
|
15533
|
+
function defaultListDocs(root) {
|
|
15534
|
+
const docsDir = (0, import_node_path16.join)(root, "docs");
|
|
15535
|
+
const docs2 = ((0, import_node_fs18.existsSync)(docsDir) ? walk(docsDir, root, []) : []).filter(
|
|
15536
|
+
(rel) => !SKIP_WALK.some((skip) => rel.startsWith(skip))
|
|
15537
|
+
);
|
|
15538
|
+
return [...ROOT_DOCS.filter((rel) => (0, import_node_fs18.existsSync)((0, import_node_path16.join)(root, rel))), ...docs2];
|
|
15539
|
+
}
|
|
15540
|
+
function defaultIsIgnored(root, relPaths, exec = import_node_child_process9.execFileSync) {
|
|
15541
|
+
try {
|
|
15542
|
+
const out = exec("git", ["check-ignore", "--stdin"], {
|
|
15543
|
+
cwd: root,
|
|
15544
|
+
input: relPaths.join("\n"),
|
|
15545
|
+
encoding: "utf8"
|
|
15546
|
+
});
|
|
15547
|
+
return new Set(out.split(/\r?\n/).filter(Boolean));
|
|
15548
|
+
} catch (error) {
|
|
15549
|
+
if (error?.status === 1) return /* @__PURE__ */ new Set();
|
|
15550
|
+
throw error;
|
|
15551
|
+
}
|
|
15552
|
+
}
|
|
15553
|
+
function runDocRefs(root, deps = {}) {
|
|
15554
|
+
const readFile6 = deps.readFile ?? readFileOrNull;
|
|
15555
|
+
const exists = deps.exists ?? import_node_fs18.existsSync;
|
|
15556
|
+
const listDocs = deps.listDocs ?? defaultListDocs;
|
|
15557
|
+
const isIgnored = deps.isIgnored ?? ((paths) => defaultIsIgnored(root, paths));
|
|
15558
|
+
const commandPaths = deps.commandPaths ?? null;
|
|
15559
|
+
const docs2 = Object.fromEntries(
|
|
15560
|
+
listDocs(root).map((rel) => [rel, readFile6((0, import_node_path16.join)(root, rel))]).filter(([, body]) => body != null)
|
|
15561
|
+
);
|
|
15562
|
+
const findings = [
|
|
15563
|
+
...checkPins(root, readFile6, docs2).findings,
|
|
15564
|
+
...checkRefs(root, { exists, isIgnored }, docs2).findings
|
|
15565
|
+
];
|
|
15566
|
+
if (commandPaths == null) {
|
|
15567
|
+
findings.push({
|
|
15568
|
+
kind: "command-manifest-unavailable",
|
|
15569
|
+
doc: "cli command manifest",
|
|
15570
|
+
line: 0,
|
|
15571
|
+
detail: "the CLI could not enumerate its own commands \u2014 command refs cannot be verified"
|
|
15572
|
+
});
|
|
15573
|
+
} else {
|
|
15574
|
+
findings.push(...checkCommands(docs2, commandPaths).findings);
|
|
15575
|
+
}
|
|
15576
|
+
return { ok: findings.length === 0, findings, docCount: Object.keys(docs2).length };
|
|
15577
|
+
}
|
|
15578
|
+
|
|
15378
15579
|
// src/docs-audit-command.ts
|
|
15379
15580
|
function serializeOutcome(outcome) {
|
|
15380
15581
|
switch (outcome.kind) {
|
|
@@ -16298,8 +16499,8 @@ function writeError(res) {
|
|
|
16298
16499
|
}
|
|
16299
16500
|
|
|
16300
16501
|
// src/secrets-commands.ts
|
|
16301
|
-
var
|
|
16302
|
-
var
|
|
16502
|
+
var import_node_fs19 = require("node:fs");
|
|
16503
|
+
var import_node_path17 = require("node:path");
|
|
16303
16504
|
var import_node_os5 = require("node:os");
|
|
16304
16505
|
|
|
16305
16506
|
// src/project-runtime.ts
|
|
@@ -16423,18 +16624,18 @@ function collectMap(value, previous = []) {
|
|
|
16423
16624
|
return [...previous, value];
|
|
16424
16625
|
}
|
|
16425
16626
|
async function decryptRailsCredentials(input) {
|
|
16426
|
-
const appDir = (0,
|
|
16627
|
+
const appDir = (0, import_node_path17.resolve)(input.appDir ?? process.cwd());
|
|
16427
16628
|
const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
16428
16629
|
const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
16429
|
-
const credentialsPath = (0,
|
|
16430
|
-
const masterKeyPath = (0,
|
|
16630
|
+
const credentialsPath = (0, import_node_path17.resolve)(appDir, credentialsFile);
|
|
16631
|
+
const masterKeyPath = (0, import_node_path17.resolve)(appDir, masterKeyFile);
|
|
16431
16632
|
const env = {
|
|
16432
16633
|
...process.env,
|
|
16433
16634
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
16434
16635
|
MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
|
|
16435
16636
|
};
|
|
16436
|
-
if ((0,
|
|
16437
|
-
env.RAILS_MASTER_KEY = (0,
|
|
16637
|
+
if ((0, import_node_fs19.existsSync)(masterKeyPath)) {
|
|
16638
|
+
env.RAILS_MASTER_KEY = (0, import_node_fs19.readFileSync)(masterKeyPath, "utf8").trim();
|
|
16438
16639
|
}
|
|
16439
16640
|
const script = [
|
|
16440
16641
|
'require "json"',
|
|
@@ -16444,9 +16645,9 @@ async function decryptRailsCredentials(input) {
|
|
|
16444
16645
|
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
16445
16646
|
"puts JSON.generate(config.config)"
|
|
16446
16647
|
].join("\n");
|
|
16447
|
-
const scriptDir = (0,
|
|
16448
|
-
const scriptPath = (0,
|
|
16449
|
-
(0,
|
|
16648
|
+
const scriptDir = (0, import_node_fs19.mkdtempSync)((0, import_node_path17.join)((0, import_node_os5.tmpdir)(), "mmi-rails-decrypt-"));
|
|
16649
|
+
const scriptPath = (0, import_node_path17.join)(scriptDir, "decrypt.rb");
|
|
16650
|
+
(0, import_node_fs19.writeFileSync)(scriptPath, script, "utf8");
|
|
16450
16651
|
try {
|
|
16451
16652
|
const args = ["exec", "ruby", scriptPath];
|
|
16452
16653
|
const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
|
|
@@ -16458,7 +16659,7 @@ async function decryptRailsCredentials(input) {
|
|
|
16458
16659
|
});
|
|
16459
16660
|
return JSON.parse(stdout);
|
|
16460
16661
|
} finally {
|
|
16461
|
-
(0,
|
|
16662
|
+
(0, import_node_fs19.rmSync)(scriptDir, { recursive: true, force: true });
|
|
16462
16663
|
}
|
|
16463
16664
|
}
|
|
16464
16665
|
async function readSecretStdin() {
|
|
@@ -16548,7 +16749,7 @@ function registerSecretsCommands(program3) {
|
|
|
16548
16749
|
let body;
|
|
16549
16750
|
if (o.file) {
|
|
16550
16751
|
try {
|
|
16551
|
-
body = (0,
|
|
16752
|
+
body = (0, import_node_fs19.readFileSync)((0, import_node_path17.resolve)(o.file), "utf8");
|
|
16552
16753
|
} catch (e) {
|
|
16553
16754
|
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
16554
16755
|
}
|
|
@@ -16653,7 +16854,7 @@ function registerSecretsCommands(program3) {
|
|
|
16653
16854
|
{
|
|
16654
16855
|
...d,
|
|
16655
16856
|
decryptRailsCredentials,
|
|
16656
|
-
removeFile: (path2) => (0,
|
|
16857
|
+
removeFile: (path2) => (0, import_node_fs19.unlinkSync)((0, import_node_path17.resolve)(o.appDir ?? process.cwd(), path2))
|
|
16657
16858
|
},
|
|
16658
16859
|
{
|
|
16659
16860
|
repo: o.repo,
|
|
@@ -17028,7 +17229,7 @@ function registerBoxCommands(program3) {
|
|
|
17028
17229
|
|
|
17029
17230
|
// src/schedules-commands.ts
|
|
17030
17231
|
var import_promises2 = require("node:fs/promises");
|
|
17031
|
-
var
|
|
17232
|
+
var import_node_child_process10 = require("node:child_process");
|
|
17032
17233
|
var import_node_util7 = require("node:util");
|
|
17033
17234
|
|
|
17034
17235
|
// src/schedules.ts
|
|
@@ -17334,7 +17535,7 @@ function assembleReconciliation(githubEntries2, readRepos, registry2, activeWork
|
|
|
17334
17535
|
}
|
|
17335
17536
|
|
|
17336
17537
|
// src/schedules-commands.ts
|
|
17337
|
-
var execFileP5 = (0, import_node_util7.promisify)(
|
|
17538
|
+
var execFileP5 = (0, import_node_util7.promisify)(import_node_child_process10.execFile);
|
|
17338
17539
|
var AWS_REGION = "eu-central-1";
|
|
17339
17540
|
var AWS_TIMEOUT_MS = 3e4;
|
|
17340
17541
|
var AWS_RETRY_DELAY_MS = 1500;
|
|
@@ -17570,14 +17771,14 @@ function registerSchedulesCommands(program3) {
|
|
|
17570
17771
|
|
|
17571
17772
|
// src/schedules-lift-command.ts
|
|
17572
17773
|
var import_promises3 = require("node:fs/promises");
|
|
17573
|
-
var
|
|
17774
|
+
var import_node_path18 = require("node:path");
|
|
17574
17775
|
|
|
17575
17776
|
// src/schedules-lift.ts
|
|
17576
17777
|
var SCHEDULE_HEADER_FIELDS = ["schedule", "what", "owner", "cadence", "executor", "llm", "output", "breaks", "kill"];
|
|
17577
17778
|
function parseScheduleHeader(yamlText) {
|
|
17578
17779
|
const out = {};
|
|
17579
17780
|
for (const rawLine of yamlText.split("\n")) {
|
|
17580
|
-
const m = /^#\s*(schedule|what|owner|cadence|executor|llm|output|breaks|kill|target):\s*(.+?)\s*$/.exec(rawLine);
|
|
17781
|
+
const m = /^#\s*(schedule|what|owner|cadence|executor|llm|output|breaks|kill|target|model):\s*(.+?)\s*$/.exec(rawLine);
|
|
17581
17782
|
if (m && out[m[1]] === void 0) out[m[1]] = m[2].trim();
|
|
17582
17783
|
}
|
|
17583
17784
|
return out;
|
|
@@ -17614,6 +17815,10 @@ function scheduleRecordFromWorkflow(repo, workflowPath, yamlText) {
|
|
|
17614
17815
|
if (target !== void 0 && !/^mmi-fleet-[A-Za-z0-9_-]{1,130}$/.test(target)) {
|
|
17615
17816
|
throw new Error(`schedules lift: ${expectedId} header \`target: ${target}\` must be a bare lambda function name in the reserved fleet namespace \`mmi-fleet-*\` (allowed after the prefix: A-Z a-z 0-9 _ -; total max 140). The dispatcher can only invoke mmi-fleet-* functions.`);
|
|
17616
17817
|
}
|
|
17818
|
+
const model = header.model;
|
|
17819
|
+
if (model !== void 0 && !/^[a-z][a-z0-9-]{0,30}$/.test(model)) {
|
|
17820
|
+
throw new Error(`schedules lift: ${expectedId} header \`model: ${model}\` must be a model ROLE KEY (lowercase kebab, max 31 chars \u2014 e.g. janitor, arbiter), never a model id \u2014 ids resolve from the vault at dispatch.`);
|
|
17821
|
+
}
|
|
17617
17822
|
return {
|
|
17618
17823
|
id: expectedId,
|
|
17619
17824
|
what: h.what,
|
|
@@ -17626,7 +17831,8 @@ function scheduleRecordFromWorkflow(repo, workflowPath, yamlText) {
|
|
|
17626
17831
|
kill: h.kill,
|
|
17627
17832
|
repo,
|
|
17628
17833
|
sourcePath: workflowPath,
|
|
17629
|
-
...target !== void 0 ? { target } : {}
|
|
17834
|
+
...target !== void 0 ? { target } : {},
|
|
17835
|
+
...model !== void 0 ? { model } : {}
|
|
17630
17836
|
};
|
|
17631
17837
|
}
|
|
17632
17838
|
function scheduleRecordsFromWorkflows(repo, files) {
|
|
@@ -17671,7 +17877,7 @@ async function readWorkflowFiles(dir) {
|
|
|
17671
17877
|
const files = [];
|
|
17672
17878
|
for (const name of names.sort()) {
|
|
17673
17879
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
17674
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises3.readFile)((0,
|
|
17880
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises3.readFile)((0, import_node_path18.join)(dir, name), "utf8") });
|
|
17675
17881
|
}
|
|
17676
17882
|
return files;
|
|
17677
17883
|
}
|
|
@@ -18211,7 +18417,7 @@ function registerQueryCommands(program3) {
|
|
|
18211
18417
|
}
|
|
18212
18418
|
|
|
18213
18419
|
// src/bootstrap-commands.ts
|
|
18214
|
-
var
|
|
18420
|
+
var import_node_fs20 = require("node:fs");
|
|
18215
18421
|
|
|
18216
18422
|
// src/bootstrap-verify.ts
|
|
18217
18423
|
var TRAIN_BRANCHES2 = ["development", "rc", "main"];
|
|
@@ -18759,7 +18965,7 @@ function registerBootstrapCommands(program3) {
|
|
|
18759
18965
|
client: defaultGitHubClient(),
|
|
18760
18966
|
projectMeta: meta,
|
|
18761
18967
|
deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
|
|
18762
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
18968
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs20.existsSync)(path2) ? (0, import_node_fs20.readFileSync)(path2, "utf8") : null,
|
|
18763
18969
|
// requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
|
|
18764
18970
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
18765
18971
|
requiredGcpApis: (() => {
|
|
@@ -18810,12 +19016,12 @@ function registerBootstrapCommands(program3) {
|
|
|
18810
19016
|
return fail(`bootstrap apply: ${e.message}`);
|
|
18811
19017
|
}
|
|
18812
19018
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
18813
|
-
if (!(0,
|
|
18814
|
-
const manifest = loadBootstrapSeeds((0,
|
|
19019
|
+
if (!(0, import_node_fs20.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`);
|
|
19020
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs20.readFileSync)(manifestPath, "utf8"));
|
|
18815
19021
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
18816
19022
|
const slug = parsedRepo.slug;
|
|
18817
19023
|
const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
|
|
18818
|
-
const readFile6 = (p) => (0,
|
|
19024
|
+
const readFile6 = (p) => (0, import_node_fs20.existsSync)(p) ? (0, import_node_fs20.readFileSync)(p, "utf8") : null;
|
|
18819
19025
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
18820
19026
|
const rawVars = {};
|
|
18821
19027
|
for (const value of cmdOpts.var ?? []) {
|
|
@@ -19011,12 +19217,12 @@ LIVE apply to ${repo}:
|
|
|
19011
19217
|
}
|
|
19012
19218
|
|
|
19013
19219
|
// src/stage-commands.ts
|
|
19014
|
-
var
|
|
19015
|
-
var
|
|
19220
|
+
var import_node_fs22 = require("node:fs");
|
|
19221
|
+
var import_node_path20 = require("node:path");
|
|
19016
19222
|
|
|
19017
19223
|
// src/port-registry.ts
|
|
19018
|
-
var
|
|
19019
|
-
var
|
|
19224
|
+
var import_node_fs21 = require("node:fs");
|
|
19225
|
+
var import_node_path19 = require("node:path");
|
|
19020
19226
|
|
|
19021
19227
|
// ../infra/port-geometry.mjs
|
|
19022
19228
|
var PORT_BLOCK = 100;
|
|
@@ -19030,8 +19236,8 @@ function nextPortBlock(registry2) {
|
|
|
19030
19236
|
return [base, base + PORT_SPAN];
|
|
19031
19237
|
}
|
|
19032
19238
|
function loadPortRegistry(path2) {
|
|
19033
|
-
if (!(0,
|
|
19034
|
-
const raw = JSON.parse((0,
|
|
19239
|
+
if (!(0, import_node_fs21.existsSync)(path2)) return {};
|
|
19240
|
+
const raw = JSON.parse((0, import_node_fs21.readFileSync)(path2, "utf8"));
|
|
19035
19241
|
const out = {};
|
|
19036
19242
|
for (const [key, value] of Object.entries(raw)) {
|
|
19037
19243
|
if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
|
|
@@ -19045,9 +19251,9 @@ function ensurePortRange(repo, path2) {
|
|
|
19045
19251
|
const existing = registry2[repo];
|
|
19046
19252
|
if (existing) return existing;
|
|
19047
19253
|
const range = nextPortBlock(registry2);
|
|
19048
|
-
const raw = (0,
|
|
19254
|
+
const raw = (0, import_node_fs21.existsSync)(path2) ? JSON.parse((0, import_node_fs21.readFileSync)(path2, "utf8")) : {};
|
|
19049
19255
|
raw[repo] = range;
|
|
19050
|
-
(0,
|
|
19256
|
+
(0, import_node_fs21.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
|
|
19051
19257
|
return range;
|
|
19052
19258
|
}
|
|
19053
19259
|
function portCursorSeed(registry2) {
|
|
@@ -19069,22 +19275,22 @@ function existingPortRange(repo, registry2) {
|
|
|
19069
19275
|
return registry2[repo] ?? null;
|
|
19070
19276
|
}
|
|
19071
19277
|
function portRangeInfraAt(root, source) {
|
|
19072
|
-
const registryPath = (0,
|
|
19073
|
-
const ddbScriptPath = (0,
|
|
19074
|
-
if (!(0,
|
|
19278
|
+
const registryPath = (0, import_node_path19.join)(root, "infra", "port-ranges.json");
|
|
19279
|
+
const ddbScriptPath = (0, import_node_path19.join)(root, "infra", "port-ddb.mjs");
|
|
19280
|
+
if (!(0, import_node_fs21.existsSync)(registryPath) || !(0, import_node_fs21.existsSync)(ddbScriptPath)) return null;
|
|
19075
19281
|
return { root, source, registryPath, ddbScriptPath };
|
|
19076
19282
|
}
|
|
19077
19283
|
function resolvePortRangeInfra(cwd, packageDir) {
|
|
19078
19284
|
const direct = portRangeInfraAt(cwd, "cwd");
|
|
19079
19285
|
if (direct) return direct;
|
|
19080
|
-
for (let dir = cwd; ; dir = (0,
|
|
19081
|
-
const sibling = portRangeInfraAt((0,
|
|
19286
|
+
for (let dir = cwd; ; dir = (0, import_node_path19.dirname)(dir)) {
|
|
19287
|
+
const sibling = portRangeInfraAt((0, import_node_path19.join)(dir, "MMI-Hub"), "sibling-hub");
|
|
19082
19288
|
if (sibling) return sibling;
|
|
19083
|
-
const parent = (0,
|
|
19289
|
+
const parent = (0, import_node_path19.dirname)(dir);
|
|
19084
19290
|
if (parent === dir) break;
|
|
19085
19291
|
}
|
|
19086
19292
|
if (packageDir) {
|
|
19087
|
-
const pkgRoot = (0,
|
|
19293
|
+
const pkgRoot = (0, import_node_path19.join)(packageDir, "..", "..");
|
|
19088
19294
|
const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
|
|
19089
19295
|
if (pkgFrom) return pkgFrom;
|
|
19090
19296
|
}
|
|
@@ -19260,8 +19466,8 @@ function registerStageCommands(program3) {
|
|
|
19260
19466
|
const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
|
|
19261
19467
|
return decideStage({
|
|
19262
19468
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
19263
|
-
hasCompose: (0,
|
|
19264
|
-
hasEnvExample: (0,
|
|
19469
|
+
hasCompose: (0, import_node_fs22.existsSync)((0, import_node_path20.join)(process.cwd(), "docker-compose.yml")),
|
|
19470
|
+
hasEnvExample: (0, import_node_fs22.existsSync)((0, import_node_path20.join)(process.cwd(), ".env.example"))
|
|
19265
19471
|
});
|
|
19266
19472
|
}
|
|
19267
19473
|
async function fetchStageVaultEnvMerge() {
|
|
@@ -19696,9 +19902,9 @@ function registerBoardCommands(program3) {
|
|
|
19696
19902
|
}
|
|
19697
19903
|
|
|
19698
19904
|
// src/merge-cleanup.ts
|
|
19699
|
-
var
|
|
19905
|
+
var import_node_fs23 = require("node:fs");
|
|
19700
19906
|
var import_promises5 = require("node:fs/promises");
|
|
19701
|
-
var
|
|
19907
|
+
var import_node_child_process11 = require("node:child_process");
|
|
19702
19908
|
|
|
19703
19909
|
// src/board-advance.ts
|
|
19704
19910
|
function repoOf2(ref) {
|
|
@@ -19784,7 +19990,7 @@ function boardAdvanceFailureMessage(result) {
|
|
|
19784
19990
|
|
|
19785
19991
|
// src/deferred-registry-store.ts
|
|
19786
19992
|
var import_promises4 = require("node:fs/promises");
|
|
19787
|
-
var
|
|
19993
|
+
var import_node_path21 = require("node:path");
|
|
19788
19994
|
var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
19789
19995
|
async function atomicWrite(target, contents) {
|
|
19790
19996
|
const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
@@ -19888,12 +20094,12 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
|
|
|
19888
20094
|
},
|
|
19889
20095
|
// Standalone atomic write — THROWS on failure (no best-effort swallow, #2846).
|
|
19890
20096
|
write: async (entries) => {
|
|
19891
|
-
await (0, import_promises4.mkdir)((0,
|
|
20097
|
+
await (0, import_promises4.mkdir)((0, import_node_path21.dirname)(registryPath), { recursive: true });
|
|
19892
20098
|
await atomicWrite(registryPath, serializeDeferredWorktrees(entries));
|
|
19893
20099
|
},
|
|
19894
20100
|
// Serialized read-modify-write under the repo-wide lock (#2846).
|
|
19895
20101
|
update: async (mutate) => {
|
|
19896
|
-
await (0, import_promises4.mkdir)((0,
|
|
20102
|
+
await (0, import_promises4.mkdir)((0, import_node_path21.dirname)(registryPath), { recursive: true });
|
|
19897
20103
|
const deadline = Date.now() + opts.maxWaitMs;
|
|
19898
20104
|
for (; ; ) {
|
|
19899
20105
|
const guard = await acquireLock(lockPath, opts, deadline);
|
|
@@ -20020,7 +20226,7 @@ async function applyGcPlan(plan, remote) {
|
|
|
20020
20226
|
return cleanupPrMergeLocalBranch(branch.branch, {
|
|
20021
20227
|
beforeWorktrees,
|
|
20022
20228
|
startingPath: branch.worktreePath,
|
|
20023
|
-
pathExists: (p) => (0,
|
|
20229
|
+
pathExists: (p) => (0, import_node_fs23.existsSync)(p),
|
|
20024
20230
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
20025
20231
|
teardownWorktreeStage,
|
|
20026
20232
|
deferredStore,
|
|
@@ -20046,7 +20252,7 @@ async function applyGcPlan(plan, remote) {
|
|
|
20046
20252
|
for (const wt of plan.worktreeDirs) {
|
|
20047
20253
|
try {
|
|
20048
20254
|
const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, siblingRoot, {
|
|
20049
|
-
realpath: (path2) => (0,
|
|
20255
|
+
realpath: (path2) => (0, import_node_fs23.realpathSync)(path2)
|
|
20050
20256
|
});
|
|
20051
20257
|
if (!cleanupTarget.ok) {
|
|
20052
20258
|
result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
|
|
@@ -20159,7 +20365,7 @@ async function remoteBranchExists2(branch, options = {}) {
|
|
|
20159
20365
|
}
|
|
20160
20366
|
var COMPOSE_TIMEOUT_MS = 12e4;
|
|
20161
20367
|
function spawnDeferredGcSweep() {
|
|
20162
|
-
spawnDetachedSelf(["worktree", "gc", "sweep-deferred", "--quiet"], { spawn:
|
|
20368
|
+
spawnDetachedSelf(["worktree", "gc", "sweep-deferred", "--quiet"], { spawn: import_node_child_process11.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
20163
20369
|
}
|
|
20164
20370
|
async function createDeferredWorktreeStore() {
|
|
20165
20371
|
try {
|
|
@@ -20173,13 +20379,13 @@ var realWorktreeDirRemover = {
|
|
|
20173
20379
|
probe: (p) => {
|
|
20174
20380
|
let st;
|
|
20175
20381
|
try {
|
|
20176
|
-
st = (0,
|
|
20382
|
+
st = (0, import_node_fs23.lstatSync)(p);
|
|
20177
20383
|
} catch {
|
|
20178
20384
|
return null;
|
|
20179
20385
|
}
|
|
20180
20386
|
if (st.isSymbolicLink()) return "link";
|
|
20181
20387
|
try {
|
|
20182
|
-
(0,
|
|
20388
|
+
(0, import_node_fs23.readlinkSync)(p);
|
|
20183
20389
|
return "link";
|
|
20184
20390
|
} catch {
|
|
20185
20391
|
}
|
|
@@ -20187,7 +20393,7 @@ var realWorktreeDirRemover = {
|
|
|
20187
20393
|
},
|
|
20188
20394
|
readdir: (p) => {
|
|
20189
20395
|
try {
|
|
20190
|
-
return (0,
|
|
20396
|
+
return (0, import_node_fs23.readdirSync)(p);
|
|
20191
20397
|
} catch {
|
|
20192
20398
|
return [];
|
|
20193
20399
|
}
|
|
@@ -20196,9 +20402,9 @@ var realWorktreeDirRemover = {
|
|
|
20196
20402
|
// leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
|
|
20197
20403
|
detachLink: (p) => {
|
|
20198
20404
|
try {
|
|
20199
|
-
(0,
|
|
20405
|
+
(0, import_node_fs23.rmdirSync)(p);
|
|
20200
20406
|
} catch {
|
|
20201
|
-
(0,
|
|
20407
|
+
(0, import_node_fs23.unlinkSync)(p);
|
|
20202
20408
|
}
|
|
20203
20409
|
},
|
|
20204
20410
|
removeTree: (p) => (0, import_promises5.rm)(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
|
|
@@ -20231,9 +20437,9 @@ async function worktreeHasStageState(worktreePath) {
|
|
|
20231
20437
|
}
|
|
20232
20438
|
}
|
|
20233
20439
|
function stageStateFileBelongsToWorktree(statePath, worktreePath) {
|
|
20234
|
-
if (!(0,
|
|
20440
|
+
if (!(0, import_node_fs23.existsSync)(statePath)) return false;
|
|
20235
20441
|
try {
|
|
20236
|
-
const state = JSON.parse((0,
|
|
20442
|
+
const state = JSON.parse((0, import_node_fs23.readFileSync)(statePath, "utf8"));
|
|
20237
20443
|
const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
|
|
20238
20444
|
return Boolean(recordedCwd && isPathUnderDirectory(recordedCwd, worktreePath));
|
|
20239
20445
|
} catch {
|
|
@@ -20361,8 +20567,8 @@ async function fetchRestCorePool(gh = defaultGhApi) {
|
|
|
20361
20567
|
}
|
|
20362
20568
|
|
|
20363
20569
|
// src/worktree-lifecycle-commands.ts
|
|
20364
|
-
var
|
|
20365
|
-
var
|
|
20570
|
+
var import_node_fs24 = require("node:fs");
|
|
20571
|
+
var import_node_path22 = require("node:path");
|
|
20366
20572
|
var GH_TIMEOUT_MS = 2e4;
|
|
20367
20573
|
var DEFAULT_BASE = "origin/development";
|
|
20368
20574
|
var DEFAULT_REMOTE = "origin";
|
|
@@ -20499,13 +20705,13 @@ function registerWorktreeCommands(program3) {
|
|
|
20499
20705
|
if (PROTECTED_BRANCHES2.has(branch)) {
|
|
20500
20706
|
return fail(`worktree land: refusing to land protected branch '${branch}'`, { code: ERROR_CODES.ERR_BAD_ENUM });
|
|
20501
20707
|
}
|
|
20502
|
-
const gitFile = (0,
|
|
20503
|
-
const isLinked = (0,
|
|
20708
|
+
const gitFile = (0, import_node_path22.join)(wtPath, ".git");
|
|
20709
|
+
const isLinked = (0, import_node_fs24.existsSync)(gitFile) && (0, import_node_fs24.statSync)(gitFile).isFile();
|
|
20504
20710
|
if (apply && !isLinked) {
|
|
20505
20711
|
return fail("worktree land: run from inside the linked worktree you want to land (this is the primary checkout)");
|
|
20506
20712
|
}
|
|
20507
20713
|
const commonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
20508
|
-
const primaryCheckout = commonDir ? (0,
|
|
20714
|
+
const primaryCheckout = commonDir ? (0, import_node_path22.dirname)(commonDir) : wtPath;
|
|
20509
20715
|
const stageSummary = readStageSummary(wtPath);
|
|
20510
20716
|
const hasStage = stageSummary != null;
|
|
20511
20717
|
const steps = landSteps(branch, true, hasStage);
|
|
@@ -20617,10 +20823,10 @@ async function gatherWorktreeContext() {
|
|
|
20617
20823
|
}
|
|
20618
20824
|
const orphanDirs = [];
|
|
20619
20825
|
const wtRoot = siblingMmiWorktreesRoot(repoRoot2);
|
|
20620
|
-
if ((0,
|
|
20826
|
+
if ((0, import_node_fs24.existsSync)(wtRoot)) {
|
|
20621
20827
|
let entries = [];
|
|
20622
20828
|
try {
|
|
20623
|
-
entries = (0,
|
|
20829
|
+
entries = (0, import_node_fs24.readdirSync)(wtRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path22.join)(wtRoot, e.name));
|
|
20624
20830
|
} catch {
|
|
20625
20831
|
}
|
|
20626
20832
|
const known = new Set(worktrees.map((w) => w.path));
|
|
@@ -20645,7 +20851,7 @@ async function bestEffortGit(args, cwd, step, timeoutMs = GIT_TIMEOUT_MS) {
|
|
|
20645
20851
|
}
|
|
20646
20852
|
|
|
20647
20853
|
// src/issue-commands.ts
|
|
20648
|
-
var
|
|
20854
|
+
var import_node_fs25 = require("node:fs");
|
|
20649
20855
|
var import_node_crypto5 = require("node:crypto");
|
|
20650
20856
|
var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
|
|
20651
20857
|
async function editIssue(client, options, deps = {}) {
|
|
@@ -20659,7 +20865,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
20659
20865
|
if (options.body !== void 0 || options.bodyFile !== void 0) {
|
|
20660
20866
|
let body;
|
|
20661
20867
|
if (options.bodyFile) {
|
|
20662
|
-
const td = deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
20868
|
+
const td = deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs25.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
20663
20869
|
body = await resolveIssueBody({ body: options.body, bodyFile: options.bodyFile }, td);
|
|
20664
20870
|
} else {
|
|
20665
20871
|
body = options.body ?? "";
|
|
@@ -21142,7 +21348,7 @@ function extendCreateCommand(issue2) {
|
|
|
21142
21348
|
if (opts.batch) {
|
|
21143
21349
|
let specs;
|
|
21144
21350
|
try {
|
|
21145
|
-
const raw = (0,
|
|
21351
|
+
const raw = (0, import_node_fs25.readFileSync)(opts.batch, "utf8");
|
|
21146
21352
|
specs = JSON.parse(raw);
|
|
21147
21353
|
if (!Array.isArray(specs)) throw new Error("batch file must contain a JSON array");
|
|
21148
21354
|
} catch (e) {
|
|
@@ -21186,12 +21392,12 @@ ${lines}`);
|
|
|
21186
21392
|
}
|
|
21187
21393
|
|
|
21188
21394
|
// src/train-commands.ts
|
|
21189
|
-
var
|
|
21190
|
-
var
|
|
21395
|
+
var import_node_fs27 = require("node:fs");
|
|
21396
|
+
var import_node_path24 = require("node:path");
|
|
21191
21397
|
|
|
21192
21398
|
// src/plugin-guard-io.ts
|
|
21193
|
-
var
|
|
21194
|
-
var
|
|
21399
|
+
var import_node_fs26 = require("node:fs");
|
|
21400
|
+
var import_node_path23 = require("node:path");
|
|
21195
21401
|
var import_node_os6 = require("node:os");
|
|
21196
21402
|
|
|
21197
21403
|
// src/plugin-guard.ts
|
|
@@ -21321,11 +21527,11 @@ function runHostBin(bin, args, opts) {
|
|
|
21321
21527
|
}
|
|
21322
21528
|
var installedPluginsPath2 = (surface = detectSurface(process.env)) => {
|
|
21323
21529
|
const homeDir = surface === "codex" ? ".codex" : ".claude";
|
|
21324
|
-
return (0,
|
|
21530
|
+
return (0, import_node_path23.join)((0, import_node_os6.homedir)(), homeDir, "plugins", "installed_plugins.json");
|
|
21325
21531
|
};
|
|
21326
21532
|
function readInstalledPlugins(surface = detectSurface(process.env)) {
|
|
21327
21533
|
try {
|
|
21328
|
-
return JSON.parse((0,
|
|
21534
|
+
return JSON.parse((0, import_node_fs26.readFileSync)(installedPluginsPath2(surface), "utf8"));
|
|
21329
21535
|
} catch {
|
|
21330
21536
|
return null;
|
|
21331
21537
|
}
|
|
@@ -21333,13 +21539,13 @@ function readInstalledPlugins(surface = detectSurface(process.env)) {
|
|
|
21333
21539
|
function marketplaceCloneCandidates(surface, home) {
|
|
21334
21540
|
if (surface === "codex") {
|
|
21335
21541
|
return [
|
|
21336
|
-
(0,
|
|
21337
|
-
(0,
|
|
21542
|
+
(0, import_node_path23.join)(home, ".codex", ".tmp", "marketplaces", "mutmutco"),
|
|
21543
|
+
(0, import_node_path23.join)(home, ".codex", "plugins", "marketplaces", "mutmutco")
|
|
21338
21544
|
];
|
|
21339
21545
|
}
|
|
21340
|
-
return [(0,
|
|
21546
|
+
return [(0, import_node_path23.join)(home, ".claude", "plugins", "marketplaces", "mutmutco")];
|
|
21341
21547
|
}
|
|
21342
|
-
function marketplaceClonePresent(surface, home, exists =
|
|
21548
|
+
function marketplaceClonePresent(surface, home, exists = import_node_fs26.existsSync) {
|
|
21343
21549
|
return marketplaceCloneCandidates(surface, home).some(exists);
|
|
21344
21550
|
}
|
|
21345
21551
|
async function fetchNpmReleasedVersion() {
|
|
@@ -21366,7 +21572,7 @@ function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRep
|
|
|
21366
21572
|
isOrgRepo,
|
|
21367
21573
|
installRecordPresent: hasUserInstallRecord(installed, MMI_PLUGIN_ID) || hasProjectInstallRecord(installed, MMI_PLUGIN_ID, process.cwd()),
|
|
21368
21574
|
marketplaceClonePresent: marketplaceClonePresent(surface, (0, import_node_os6.homedir)()),
|
|
21369
|
-
pluginCachePresent: (0,
|
|
21575
|
+
pluginCachePresent: (0, import_node_fs26.existsSync)((0, import_node_path23.join)((0, import_node_os6.homedir)(), homeDir, "plugins", "cache", "mutmutco", "mmi"))
|
|
21370
21576
|
};
|
|
21371
21577
|
}
|
|
21372
21578
|
function claudePluginGuardState(isOrgRepo) {
|
|
@@ -21501,7 +21707,7 @@ function formatTrainStatus(r) {
|
|
|
21501
21707
|
// src/train-commands.ts
|
|
21502
21708
|
function readRepoVersion() {
|
|
21503
21709
|
try {
|
|
21504
|
-
return JSON.parse((0,
|
|
21710
|
+
return JSON.parse((0, import_node_fs27.readFileSync)((0, import_node_path24.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
21505
21711
|
} catch {
|
|
21506
21712
|
return void 0;
|
|
21507
21713
|
}
|
|
@@ -22953,17 +23159,17 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
22953
23159
|
}
|
|
22954
23160
|
|
|
22955
23161
|
// src/doctor-io.ts
|
|
22956
|
-
var
|
|
23162
|
+
var import_node_fs28 = require("node:fs");
|
|
22957
23163
|
var import_node_os7 = require("node:os");
|
|
22958
|
-
var
|
|
22959
|
-
var
|
|
23164
|
+
var import_node_path25 = require("node:path");
|
|
23165
|
+
var import_node_child_process12 = require("node:child_process");
|
|
22960
23166
|
var import_node_util8 = require("node:util");
|
|
22961
|
-
var execFileP6 = (0, import_node_util8.promisify)(
|
|
23167
|
+
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process12.execFile);
|
|
22962
23168
|
var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
22963
23169
|
function installedClaudePluginVersion() {
|
|
22964
23170
|
try {
|
|
22965
23171
|
const file = JSON.parse(
|
|
22966
|
-
(0,
|
|
23172
|
+
(0, import_node_fs28.readFileSync)((0, import_node_path25.join)((0, import_node_os7.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
22967
23173
|
);
|
|
22968
23174
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
22969
23175
|
if (versions.length === 0) return void 0;
|
|
@@ -22974,7 +23180,7 @@ function installedClaudePluginVersion() {
|
|
|
22974
23180
|
}
|
|
22975
23181
|
function worktreeRootSync() {
|
|
22976
23182
|
try {
|
|
22977
|
-
const out = (0,
|
|
23183
|
+
const out = (0, import_node_child_process12.execFileSync)("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
22978
23184
|
let root = out.endsWith("\n") ? out.slice(0, -1) : out;
|
|
22979
23185
|
if (process.platform === "win32" && root.endsWith("\r")) root = root.slice(0, -1);
|
|
22980
23186
|
return root || null;
|
|
@@ -22984,13 +23190,13 @@ function worktreeRootSync() {
|
|
|
22984
23190
|
}
|
|
22985
23191
|
var gitignorePath = () => {
|
|
22986
23192
|
const root = worktreeRootSync();
|
|
22987
|
-
return root === null ? null : (0,
|
|
23193
|
+
return root === null ? null : (0, import_node_path25.join)(root, ".gitignore");
|
|
22988
23194
|
};
|
|
22989
23195
|
function readGitignore() {
|
|
22990
23196
|
const path2 = gitignorePath();
|
|
22991
23197
|
if (path2 === null) return null;
|
|
22992
23198
|
try {
|
|
22993
|
-
return (0,
|
|
23199
|
+
return (0, import_node_fs28.readFileSync)(path2, "utf8");
|
|
22994
23200
|
} catch {
|
|
22995
23201
|
return null;
|
|
22996
23202
|
}
|
|
@@ -22999,7 +23205,7 @@ function writeGitignore(content) {
|
|
|
22999
23205
|
const path2 = gitignorePath();
|
|
23000
23206
|
if (path2 === null) return false;
|
|
23001
23207
|
try {
|
|
23002
|
-
(0,
|
|
23208
|
+
(0, import_node_fs28.writeFileSync)(path2, content, "utf8");
|
|
23003
23209
|
return true;
|
|
23004
23210
|
} catch {
|
|
23005
23211
|
return false;
|
|
@@ -23023,7 +23229,7 @@ async function repoRoot() {
|
|
|
23023
23229
|
}
|
|
23024
23230
|
function hasRepoLocalWorktrees() {
|
|
23025
23231
|
const root = worktreeRootSync();
|
|
23026
|
-
return root !== null && (0,
|
|
23232
|
+
return root !== null && (0, import_node_fs28.existsSync)((0, import_node_path25.join)(root, ".worktrees"));
|
|
23027
23233
|
}
|
|
23028
23234
|
|
|
23029
23235
|
// src/index.ts
|
|
@@ -23141,16 +23347,16 @@ var cachedLongFlags;
|
|
|
23141
23347
|
function allLongFlags() {
|
|
23142
23348
|
if (cachedLongFlags) return cachedLongFlags;
|
|
23143
23349
|
const acc = /* @__PURE__ */ new Set();
|
|
23144
|
-
const
|
|
23350
|
+
const walk2 = (node) => {
|
|
23145
23351
|
if (!isCanonicalSuggestion(node)) return;
|
|
23146
23352
|
for (const opt of node.options) {
|
|
23147
23353
|
if (opt.discovery) continue;
|
|
23148
23354
|
const m = /--[\w-]+/.exec(opt.flags);
|
|
23149
23355
|
if (m) acc.add(m[0]);
|
|
23150
23356
|
}
|
|
23151
|
-
for (const child2 of node.subcommands)
|
|
23357
|
+
for (const child2 of node.subcommands) walk2(child2);
|
|
23152
23358
|
};
|
|
23153
|
-
|
|
23359
|
+
walk2(buildCommandManifest(program2).tree);
|
|
23154
23360
|
cachedLongFlags = [...acc];
|
|
23155
23361
|
return cachedLongFlags;
|
|
23156
23362
|
}
|
|
@@ -23250,19 +23456,19 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
23250
23456
|
});
|
|
23251
23457
|
var rules = program2.command("rules").description("org-managed .gitignore delivery");
|
|
23252
23458
|
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) => {
|
|
23253
|
-
const path2 = (0,
|
|
23254
|
-
const current = (0,
|
|
23459
|
+
const path2 = (0, import_node_path26.join)(process.cwd(), ".gitignore");
|
|
23460
|
+
const current = (0, import_node_fs29.existsSync)(path2) ? (0, import_node_fs29.readFileSync)(path2, "utf8") : null;
|
|
23255
23461
|
const plan = planManagedGitignore(current);
|
|
23256
23462
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
23257
23463
|
if (opts.json) {
|
|
23258
|
-
if (opts.write && plan.changed) (0,
|
|
23464
|
+
if (opts.write && plan.changed) (0, import_node_fs29.writeFileSync)(path2, plan.content, "utf8");
|
|
23259
23465
|
console.log(JSON.stringify(plan, null, 2));
|
|
23260
23466
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
23261
23467
|
return;
|
|
23262
23468
|
}
|
|
23263
23469
|
if (opts.write) {
|
|
23264
23470
|
if (plan.changed) {
|
|
23265
|
-
(0,
|
|
23471
|
+
(0, import_node_fs29.writeFileSync)(path2, plan.content, "utf8");
|
|
23266
23472
|
console.log(`mmi-cli org rules gitignore: updated .gitignore (${drift})`);
|
|
23267
23473
|
} else {
|
|
23268
23474
|
console.log("mmi-cli org rules gitignore: up to date");
|
|
@@ -23420,7 +23626,7 @@ function runWorktreeInstall(command, cwd, quiet) {
|
|
|
23420
23626
|
const file = isWin2 ? "cmd.exe" : bin;
|
|
23421
23627
|
const spawnArgs = isWin2 ? ["/c", bin, ...args] : args;
|
|
23422
23628
|
return new Promise((resolve5, reject) => {
|
|
23423
|
-
const child2 = (0,
|
|
23629
|
+
const child2 = (0, import_node_child_process13.spawn)(file, spawnArgs, { cwd, stdio: quiet ? "ignore" : "inherit", windowsHide: true });
|
|
23424
23630
|
const timer = setTimeout(() => {
|
|
23425
23631
|
try {
|
|
23426
23632
|
child2.kill();
|
|
@@ -23452,26 +23658,26 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
|
|
|
23452
23658
|
function acquireWorktreeSetupLock(worktreeRoot) {
|
|
23453
23659
|
const lockPath = repoRuntimeStatePath(worktreeRoot, "worktree-setup.lock");
|
|
23454
23660
|
const take = () => {
|
|
23455
|
-
const fd = (0,
|
|
23661
|
+
const fd = (0, import_node_fs29.openSync)(lockPath, "wx");
|
|
23456
23662
|
try {
|
|
23457
|
-
(0,
|
|
23663
|
+
(0, import_node_fs29.writeSync)(fd, String(Date.now()));
|
|
23458
23664
|
} finally {
|
|
23459
|
-
(0,
|
|
23665
|
+
(0, import_node_fs29.closeSync)(fd);
|
|
23460
23666
|
}
|
|
23461
23667
|
return () => {
|
|
23462
23668
|
try {
|
|
23463
|
-
(0,
|
|
23669
|
+
(0, import_node_fs29.rmSync)(lockPath, { force: true });
|
|
23464
23670
|
} catch {
|
|
23465
23671
|
}
|
|
23466
23672
|
};
|
|
23467
23673
|
};
|
|
23468
23674
|
try {
|
|
23469
|
-
(0,
|
|
23675
|
+
(0, import_node_fs29.mkdirSync)((0, import_node_path26.dirname)(lockPath), { recursive: true });
|
|
23470
23676
|
return take();
|
|
23471
23677
|
} catch {
|
|
23472
23678
|
try {
|
|
23473
|
-
if (Date.now() - (0,
|
|
23474
|
-
(0,
|
|
23679
|
+
if (Date.now() - (0, import_node_fs29.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
|
|
23680
|
+
(0, import_node_fs29.rmSync)(lockPath, { force: true });
|
|
23475
23681
|
return take();
|
|
23476
23682
|
}
|
|
23477
23683
|
} catch {
|
|
@@ -23651,7 +23857,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
23651
23857
|
try {
|
|
23652
23858
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
|
|
23653
23859
|
if (o.repo) args.push("--repo", o.repo);
|
|
23654
|
-
spawnDetachedSelf(args, { spawn:
|
|
23860
|
+
spawnDetachedSelf(args, { spawn: import_node_child_process13.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
|
|
23655
23861
|
} catch {
|
|
23656
23862
|
}
|
|
23657
23863
|
}
|
|
@@ -23677,6 +23883,27 @@ docs.command("index").description("regenerate docs/index.md from the docs/ tree
|
|
|
23677
23883
|
await failGraceful(e.message);
|
|
23678
23884
|
}
|
|
23679
23885
|
});
|
|
23886
|
+
docs.command("refs").description("deterministic doc reference gate: every backticked repo path, relative .md link, `mmi-cli` command ref, and `<!-- pinned by \u2026 -->` comment across docs/** + README.md + architecture.md must resolve, or exit 1 (fleet-portable port of the Hub gate scripts/check-doc-refs.mjs, #3339)").option("--json", "machine-readable findings list: { ok, docCount, findings[] }").action(async (o) => {
|
|
23887
|
+
try {
|
|
23888
|
+
const root = await repoRoot();
|
|
23889
|
+
const commandPaths = new Set(buildCommandManifest(program2).index.map((entry) => entry.path));
|
|
23890
|
+
const result = runDocRefs(root, { commandPaths });
|
|
23891
|
+
if (o.json) {
|
|
23892
|
+
consoleIo.log(JSON.stringify({ ok: result.ok, docCount: result.docCount, findings: result.findings }, null, 2));
|
|
23893
|
+
if (!result.ok) process.exitCode = 1;
|
|
23894
|
+
return;
|
|
23895
|
+
}
|
|
23896
|
+
if (result.ok) {
|
|
23897
|
+
console.log(`doc refs: ${result.docCount} docs, every reference resolves`);
|
|
23898
|
+
return;
|
|
23899
|
+
}
|
|
23900
|
+
console.error("doc refs: unresolved references");
|
|
23901
|
+
for (const f of result.findings) console.error(`- ${f.doc}:${f.line} [${f.kind}] ${f.detail}`);
|
|
23902
|
+
process.exitCode = 1;
|
|
23903
|
+
} catch (e) {
|
|
23904
|
+
await failGraceful(e.message);
|
|
23905
|
+
}
|
|
23906
|
+
});
|
|
23680
23907
|
var docsAudit = program2.command("docs-audit").description("the docs janitor verdict ledger \u2014 record a dated run verdict, or read the dead-man status back");
|
|
23681
23908
|
docsAudit.command("record").description("write a dated janitor verdict for one repo to the registry ledger (master-only server-side)").option("--repo <owner/name>", "the repo the verdict is for (default: the current repo)").option("--date <YYYY-MM-DD>", "the ISO day the run examined (default: today)").requiredOption("--sha-range <a..b>", "the git range the janitor read (e.g. <lastVerdictSha>..HEAD)").requiredOption("--outcome <kind>", "clean | refreshed | failed").option("--count <n>", "docs refreshed (required when --outcome refreshed)").option("--reason <text>", "why the run failed (required when --outcome failed)").requiredOption("--checker-vendor <vendor>", "which vendor's model actually ran the check").action(async (o) => {
|
|
23682
23909
|
try {
|
|
@@ -23892,7 +24119,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
23892
24119
|
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`);
|
|
23893
24120
|
if (o.secretsFile) {
|
|
23894
24121
|
try {
|
|
23895
|
-
vars.push(`secrets=${(0,
|
|
24122
|
+
vars.push(`secrets=${(0, import_node_fs29.readFileSync)(o.secretsFile, "utf8")}`);
|
|
23896
24123
|
} catch (e) {
|
|
23897
24124
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
23898
24125
|
}
|
|
@@ -24527,11 +24754,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
|
|
|
24527
24754
|
}
|
|
24528
24755
|
});
|
|
24529
24756
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
24530
|
-
const wfDir = (0,
|
|
24531
|
-
if (!(0,
|
|
24532
|
-
return (0,
|
|
24757
|
+
const wfDir = (0, import_node_path26.join)(cwd, ".github", "workflows");
|
|
24758
|
+
if (!(0, import_node_fs29.existsSync)(wfDir)) return [];
|
|
24759
|
+
return (0, import_node_fs29.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
24533
24760
|
try {
|
|
24534
|
-
return workflowReportsPrChecks((0,
|
|
24761
|
+
return workflowReportsPrChecks((0, import_node_fs29.readFileSync)((0, import_node_path26.join)(wfDir, name), "utf8"));
|
|
24535
24762
|
} catch {
|
|
24536
24763
|
return true;
|
|
24537
24764
|
}
|
|
@@ -24558,16 +24785,16 @@ function ciAuditDeps() {
|
|
|
24558
24785
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
24559
24786
|
readSeedFile: (path2) => {
|
|
24560
24787
|
if (!root) return null;
|
|
24561
|
-
const fullPath = (0,
|
|
24562
|
-
return (0,
|
|
24788
|
+
const fullPath = (0, import_node_path26.join)(root, path2);
|
|
24789
|
+
return (0, import_node_fs29.existsSync)(fullPath) ? (0, import_node_fs29.readFileSync)(fullPath, "utf8") : null;
|
|
24563
24790
|
}
|
|
24564
24791
|
};
|
|
24565
24792
|
}
|
|
24566
24793
|
function hubRoot() {
|
|
24567
|
-
const fromPkg = (0,
|
|
24794
|
+
const fromPkg = (0, import_node_path26.join)(__dirname, "..", "..");
|
|
24568
24795
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
24569
|
-
if ((0,
|
|
24570
|
-
if ((0,
|
|
24796
|
+
if ((0, import_node_fs29.existsSync)((0, import_node_path26.join)(fromPkg, marker))) return fromPkg;
|
|
24797
|
+
if ((0, import_node_fs29.existsSync)((0, import_node_path26.join)(process.cwd(), marker))) return process.cwd();
|
|
24571
24798
|
return null;
|
|
24572
24799
|
}
|
|
24573
24800
|
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) => {
|
|
@@ -24806,7 +25033,7 @@ pr.command("merge <number>").description("merge a PR (squash by default) and cle
|
|
|
24806
25033
|
localCleanup = await cleanupPrMergeLocalBranch(headRef, {
|
|
24807
25034
|
beforeWorktrees,
|
|
24808
25035
|
startingPath,
|
|
24809
|
-
pathExists: (p) => (0,
|
|
25036
|
+
pathExists: (p) => (0, import_node_fs29.existsSync)(p),
|
|
24810
25037
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
24811
25038
|
teardownWorktreeStage,
|
|
24812
25039
|
deferredStore,
|
|
@@ -25146,10 +25373,10 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
25146
25373
|
targets = resolution.targets;
|
|
25147
25374
|
}
|
|
25148
25375
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
25149
|
-
const fileMatrix = (0,
|
|
25376
|
+
const fileMatrix = (0, import_node_fs29.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs29.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
25150
25377
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
25151
25378
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
25152
|
-
const fileContracts = (0,
|
|
25379
|
+
const fileContracts = (0, import_node_fs29.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs29.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
25153
25380
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
25154
25381
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess);
|
|
25155
25382
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
@@ -25182,16 +25409,16 @@ function directoryBytes(path2) {
|
|
|
25182
25409
|
let total = 0;
|
|
25183
25410
|
let entries;
|
|
25184
25411
|
try {
|
|
25185
|
-
entries = (0,
|
|
25412
|
+
entries = (0, import_node_fs29.readdirSync)(path2, { withFileTypes: true });
|
|
25186
25413
|
} catch {
|
|
25187
25414
|
return 0;
|
|
25188
25415
|
}
|
|
25189
25416
|
for (const entry of entries) {
|
|
25190
|
-
const child2 = (0,
|
|
25417
|
+
const child2 = (0, import_node_path26.join)(path2, entry.name);
|
|
25191
25418
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
25192
25419
|
else {
|
|
25193
25420
|
try {
|
|
25194
|
-
total += (0,
|
|
25421
|
+
total += (0, import_node_fs29.statSync)(child2).size;
|
|
25195
25422
|
} catch {
|
|
25196
25423
|
}
|
|
25197
25424
|
}
|
|
@@ -25199,25 +25426,25 @@ function directoryBytes(path2) {
|
|
|
25199
25426
|
return total;
|
|
25200
25427
|
}
|
|
25201
25428
|
function listDirEntries(dir) {
|
|
25202
|
-
return (0,
|
|
25429
|
+
return (0, import_node_fs29.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
25203
25430
|
}
|
|
25204
25431
|
function readInstalledPluginRefs(home) {
|
|
25205
25432
|
const p = installedPluginsPath(home);
|
|
25206
|
-
if (!(0,
|
|
25433
|
+
if (!(0, import_node_fs29.existsSync)(p)) return [];
|
|
25207
25434
|
try {
|
|
25208
|
-
return installedPluginPaths((0,
|
|
25435
|
+
return installedPluginPaths((0, import_node_fs29.readFileSync)(p, "utf8"));
|
|
25209
25436
|
} catch {
|
|
25210
25437
|
return null;
|
|
25211
25438
|
}
|
|
25212
25439
|
}
|
|
25213
25440
|
function pluginCacheFsDeps(home, dirBytes) {
|
|
25214
25441
|
return {
|
|
25215
|
-
exists: (p) => (0,
|
|
25216
|
-
listVersionDirs: (root) => (0,
|
|
25442
|
+
exists: (p) => (0, import_node_fs29.existsSync)(p),
|
|
25443
|
+
listVersionDirs: (root) => (0, import_node_fs29.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
25217
25444
|
dirBytes,
|
|
25218
|
-
listStagingDirs: (root) => (0,
|
|
25445
|
+
listStagingDirs: (root) => (0, import_node_fs29.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
25219
25446
|
try {
|
|
25220
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
25447
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path26.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs29.statSync)(p).mtimeMs) };
|
|
25221
25448
|
} catch {
|
|
25222
25449
|
return { name: d.name, mtimeMs: Date.now() };
|
|
25223
25450
|
}
|
|
@@ -25231,10 +25458,10 @@ function stagingApplyFsGuard(home) {
|
|
|
25231
25458
|
return {
|
|
25232
25459
|
referencedPaths: () => readInstalledPluginRefs(home),
|
|
25233
25460
|
mtimeMs: (name) => {
|
|
25234
|
-
const p = (0,
|
|
25235
|
-
if (!(0,
|
|
25461
|
+
const p = (0, import_node_path26.join)(stagingRoot, name);
|
|
25462
|
+
if (!(0, import_node_fs29.existsSync)(p)) return null;
|
|
25236
25463
|
try {
|
|
25237
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
25464
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs29.statSync)(q).mtimeMs);
|
|
25238
25465
|
} catch {
|
|
25239
25466
|
return null;
|
|
25240
25467
|
}
|
|
@@ -25250,7 +25477,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
25250
25477
|
{ withBytes: true }
|
|
25251
25478
|
);
|
|
25252
25479
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
25253
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
25480
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs29.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard((0, import_node_os8.homedir)())) : void 0;
|
|
25254
25481
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
25255
25482
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
25256
25483
|
else console.log(renderPluginCachePlan(plan, result));
|
|
@@ -25283,7 +25510,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
|
|
|
25283
25510
|
readBoard,
|
|
25284
25511
|
// #1813: warm the slice cache out-of-band (detached) so the ~20s live read
|
|
25285
25512
|
// never costs banner time and next session's glance renders instantly within budget.
|
|
25286
|
-
scheduleRefresh: () => spawnDetachedSelf(["board", "slice-refresh", "--quiet"], { spawn:
|
|
25513
|
+
scheduleRefresh: () => spawnDetachedSelf(["board", "slice-refresh", "--quiet"], { spawn: import_node_child_process13.spawn, execPath: process.execPath, scriptPath: process.argv[1] })
|
|
25287
25514
|
}),
|
|
25288
25515
|
// command-ladder hint (#2609): compact gh→mmi-cli cheat sheet so agents reach for mmi-cli first,
|
|
25289
25516
|
// not after a denied gh call. <1KB, pure in-memory — no network, no fs, fail-soft.
|
|
@@ -25314,7 +25541,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
|
|
|
25314
25541
|
for (const line of scratchGcLines(process.cwd())) consoleIo.log(line);
|
|
25315
25542
|
const worktreeBanner = worktreeAutoProvisionBanner(process.cwd());
|
|
25316
25543
|
if (worktreeBanner) {
|
|
25317
|
-
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn:
|
|
25544
|
+
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process13.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
25318
25545
|
consoleIo.log(worktreeBanner);
|
|
25319
25546
|
}
|
|
25320
25547
|
appendHookActivity(process.cwd(), { event: "SessionStart", script: "session-start", outcome: "ran", action: "session-start hook completed" });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutmutco/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.43.0",
|
|
4
4
|
"description": "MMI Future CLI — the org dev toolbox (board, registry, keyless secrets, release train, bootstrap, doctor) and the cross-IDE engine the plugin's session-start hook drives.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|