@mutmutco/cli 2.61.1 → 2.62.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/README.md +0 -1
- package/dist/main.cjs +371 -637
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -3408,7 +3408,7 @@ var program = new Command();
|
|
|
3408
3408
|
|
|
3409
3409
|
// src/index.ts
|
|
3410
3410
|
var import_promises8 = require("node:fs/promises");
|
|
3411
|
-
var
|
|
3411
|
+
var import_node_fs25 = require("node:fs");
|
|
3412
3412
|
|
|
3413
3413
|
// src/rules-sync.ts
|
|
3414
3414
|
function normalizeEol(s) {
|
|
@@ -3471,7 +3471,7 @@ function purgeSpine(deps, files = SPINE_FILES) {
|
|
|
3471
3471
|
}
|
|
3472
3472
|
|
|
3473
3473
|
// src/index.ts
|
|
3474
|
-
var
|
|
3474
|
+
var import_node_child_process13 = require("node:child_process");
|
|
3475
3475
|
|
|
3476
3476
|
// src/cli-shared.ts
|
|
3477
3477
|
var import_promises = require("node:fs/promises");
|
|
@@ -6919,7 +6919,6 @@ function formatHandoffLine(item) {
|
|
|
6919
6919
|
|
|
6920
6920
|
// src/handoff-commands.ts
|
|
6921
6921
|
var FOREGROUND_FETCH = { attempts: 2, timeoutMs: 8e3 };
|
|
6922
|
-
var SESSION_START_FETCH = { attempts: 1, timeoutMs: 3e3 };
|
|
6923
6922
|
function timestampRank2(value) {
|
|
6924
6923
|
const ms = Date.parse(value ?? "");
|
|
6925
6924
|
return Number.isFinite(ms) ? ms : Number.NEGATIVE_INFINITY;
|
|
@@ -7082,20 +7081,6 @@ async function runHandoffList(opts, io = consoleIo) {
|
|
|
7082
7081
|
}
|
|
7083
7082
|
return handoffs;
|
|
7084
7083
|
}
|
|
7085
|
-
async function runHandoffOffer(io = consoleIo, opts = {}) {
|
|
7086
|
-
if (!await requireContinuityAccess("handoff", { quiet: opts.fast }, io)) return;
|
|
7087
|
-
const retry = opts.fast ? SESSION_START_FETCH : FOREGROUND_FETCH;
|
|
7088
|
-
let handoffs;
|
|
7089
|
-
try {
|
|
7090
|
-
({ handoffs } = await collectScopedHandoffs({ retry }));
|
|
7091
|
-
} catch {
|
|
7092
|
-
return;
|
|
7093
|
-
}
|
|
7094
|
-
if (!handoffs.length) return;
|
|
7095
|
-
io.log("Open handoffs:");
|
|
7096
|
-
for (const h of handoffs) io.log(`- ${formatHandoffLine(h)}`);
|
|
7097
|
-
io.log("Use `mmi-cli handoff accept <key>` to claim, `mmi-cli handoff cancel <key>` to close, or decline in chat to leave it open.");
|
|
7098
|
-
}
|
|
7099
7084
|
async function closeSourceHandoff(key, state, opts = {}, io = consoleIo) {
|
|
7100
7085
|
if (!await requireContinuityAccess("handoff", {}, io)) return;
|
|
7101
7086
|
const current = await sagaKey(await loadConfig(), resolveSessionId());
|
|
@@ -8631,21 +8616,10 @@ async function runSessionStart(parallel, sequential, io) {
|
|
|
8631
8616
|
for (const lines of buffered) flush(lines, io);
|
|
8632
8617
|
for (const step of sequential) flush(await runBufferedStep(step), io);
|
|
8633
8618
|
}
|
|
8634
|
-
function buildSessionStartPlan(verbs
|
|
8635
|
-
const continuityEnabled = opts.continuityEnabled ?? true;
|
|
8636
|
-
const continuitySteps = continuityEnabled ? [
|
|
8637
|
-
{ name: "saga show", run: verbs.sagaShow },
|
|
8638
|
-
{ name: "handoff offer", run: verbs.handoffOffer },
|
|
8639
|
-
// northstar context (#1228): curated plan cards when relevance is high-confidence; silent otherwise.
|
|
8640
|
-
{ name: "northstar context", run: verbs.northstarContext },
|
|
8641
|
-
{ name: "saga health", run: verbs.sagaHealth }
|
|
8642
|
-
] : [];
|
|
8619
|
+
function buildSessionStartPlan(verbs) {
|
|
8643
8620
|
return {
|
|
8644
8621
|
parallel: [
|
|
8645
8622
|
{ name: "rules purge", run: verbs.rulesPurge },
|
|
8646
|
-
...continuitySteps.slice(0, 2),
|
|
8647
|
-
{ name: "coop pending", run: verbs.coopPending },
|
|
8648
|
-
...continuitySteps.slice(2),
|
|
8649
8623
|
// whoami (#879): the resolved human lands in the banner so agents act --for them without asking.
|
|
8650
8624
|
// Identity reads are memoized process-wide, so this adds no extra gh/Hub round-trip.
|
|
8651
8625
|
{ name: "whoami", run: verbs.whoami },
|
|
@@ -8675,31 +8649,6 @@ function isInsideRepoSubdir(cwd, exists = import_node_fs13.existsSync) {
|
|
|
8675
8649
|
dir = parent;
|
|
8676
8650
|
}
|
|
8677
8651
|
}
|
|
8678
|
-
function planStoreLines(cwd) {
|
|
8679
|
-
const mdFiles = (dir, minSize = 0) => {
|
|
8680
|
-
const p = (0, import_node_path12.join)(cwd, dir);
|
|
8681
|
-
if (!(0, import_node_fs13.existsSync)(p)) return [];
|
|
8682
|
-
try {
|
|
8683
|
-
return (0, import_node_fs13.readdirSync)(p).filter((f) => f.toLowerCase().endsWith(".md")).filter((f) => {
|
|
8684
|
-
try {
|
|
8685
|
-
return (0, import_node_fs13.statSync)((0, import_node_path12.join)(p, f)).size >= minSize;
|
|
8686
|
-
} catch {
|
|
8687
|
-
return false;
|
|
8688
|
-
}
|
|
8689
|
-
}).map((f) => `${dir}/${f}`);
|
|
8690
|
-
} catch {
|
|
8691
|
-
return [];
|
|
8692
|
-
}
|
|
8693
|
-
};
|
|
8694
|
-
const tmpPlans = mdFiles("tmp", 1024);
|
|
8695
|
-
const localPlans = mdFiles("plans");
|
|
8696
|
-
const out = [];
|
|
8697
|
-
if (tmpPlans.length)
|
|
8698
|
-
out.push(`[plan-store] WARNING: SSOT-shaped file(s) in tmp/ \u2014 move to plans/<slug>.md (auto-saves to the store; never commit): ${tmpPlans.join(", ")}`);
|
|
8699
|
-
if (localPlans.length)
|
|
8700
|
-
out.push(`[plan-store] plans/ is gitignored on purpose \u2014 ${localPlans.length} local plan(s) auto-save to the North Star store on write; never git add or commit plans/ (use northstar push --wait only when you need immediate server confirmation).`);
|
|
8701
|
-
return out;
|
|
8702
|
-
}
|
|
8703
8652
|
function scratchGcLines(cwd, env = process.env, now = Date.now()) {
|
|
8704
8653
|
if (env.MMI_NO_AUTO_GC) return [];
|
|
8705
8654
|
try {
|
|
@@ -8713,20 +8662,10 @@ function scratchGcLines(cwd, env = process.env, now = Date.now()) {
|
|
|
8713
8662
|
return [];
|
|
8714
8663
|
}
|
|
8715
8664
|
}
|
|
8716
|
-
function northstarPointer(injected = false) {
|
|
8717
|
-
if (injected) {
|
|
8718
|
-
return "North Stars: `mmi-cli northstar relevant` for more matches; `northstar pull <slug>` for the full SSOT.";
|
|
8719
|
-
}
|
|
8720
|
-
return "North Stars: run `mmi-cli northstar relevant` to load plans relevant to your task (`northstar list` for all).";
|
|
8721
|
-
}
|
|
8722
|
-
function sessionStartContinuityLines(opts) {
|
|
8723
|
-
if (!opts.continuityEnabled) return [];
|
|
8724
|
-
return [northstarPointer(opts.northstarInjected), ...planStoreLines(opts.cwd)];
|
|
8725
|
-
}
|
|
8726
8665
|
|
|
8727
8666
|
// src/coop-commands.ts
|
|
8728
8667
|
var FOREGROUND_FETCH2 = { attempts: 2, timeoutMs: 15e3 };
|
|
8729
|
-
var
|
|
8668
|
+
var SESSION_START_FETCH = { attempts: 1, timeoutMs: 3e3 };
|
|
8730
8669
|
var COOP_WAIT_DELAYS_MS = [6e4, 12e4, 18e4, 3e5, 6e5, 18e5];
|
|
8731
8670
|
async function hubBase() {
|
|
8732
8671
|
const cfg = await loadConfig();
|
|
@@ -8880,7 +8819,7 @@ async function runCoopWait(coopId, io, opts = {}) {
|
|
|
8880
8819
|
console.log(JSON.stringify({ coopId, status: "timeout-open" }));
|
|
8881
8820
|
}
|
|
8882
8821
|
}
|
|
8883
|
-
async function fetchCoopPending(retry =
|
|
8822
|
+
async function fetchCoopPending(retry = SESSION_START_FETCH) {
|
|
8884
8823
|
const body = await getJson("/coop/pending", retry);
|
|
8885
8824
|
return body.pending ?? [];
|
|
8886
8825
|
}
|
|
@@ -10438,140 +10377,6 @@ State: ${payload2.statePath}
|
|
|
10438
10377
|
return overlord;
|
|
10439
10378
|
}
|
|
10440
10379
|
|
|
10441
|
-
// src/throttle-commands.ts
|
|
10442
|
-
var import_node_child_process8 = require("node:child_process");
|
|
10443
|
-
var import_node_fs16 = require("node:fs");
|
|
10444
|
-
var import_node_path14 = require("node:path");
|
|
10445
|
-
var THROTTLE_TRACE_REL = (0, import_node_path14.join)(".mmi", "throttle", "trace.jsonl");
|
|
10446
|
-
function resolveRepoGitRoot(cwd = process.cwd()) {
|
|
10447
|
-
try {
|
|
10448
|
-
const root = (0, import_node_child_process8.execFileSync)("git", ["-C", cwd, "rev-parse", "--show-toplevel"], {
|
|
10449
|
-
encoding: "utf8",
|
|
10450
|
-
timeout: 5e3
|
|
10451
|
-
}).trim();
|
|
10452
|
-
return root || cwd;
|
|
10453
|
-
} catch {
|
|
10454
|
-
return cwd;
|
|
10455
|
-
}
|
|
10456
|
-
}
|
|
10457
|
-
function resolveThrottleTracePath(cwd = process.cwd()) {
|
|
10458
|
-
return (0, import_node_path14.join)(resolveRepoGitRoot(cwd), THROTTLE_TRACE_REL);
|
|
10459
|
-
}
|
|
10460
|
-
function resolveModeFromEnv() {
|
|
10461
|
-
const v = String(process.env.MMI_THROTTLE_MODE ?? "block").trim().toLowerCase();
|
|
10462
|
-
if (v === "observe") return "observe";
|
|
10463
|
-
return "block";
|
|
10464
|
-
}
|
|
10465
|
-
function parseTraceLines(raw) {
|
|
10466
|
-
const out = [];
|
|
10467
|
-
for (const line of raw.split(/\r?\n/)) {
|
|
10468
|
-
const t = line.trim();
|
|
10469
|
-
if (!t) continue;
|
|
10470
|
-
try {
|
|
10471
|
-
const o = JSON.parse(t);
|
|
10472
|
-
if (o && typeof o === "object") out.push(o);
|
|
10473
|
-
} catch {
|
|
10474
|
-
}
|
|
10475
|
-
}
|
|
10476
|
-
return out;
|
|
10477
|
-
}
|
|
10478
|
-
function commandSafetyClass(reasonId) {
|
|
10479
|
-
if (reasonId === "shell_dialect_redirect") return "dialect/redirect mismatch";
|
|
10480
|
-
if (reasonId === "shell_log_dump") return "unbounded log/text dump";
|
|
10481
|
-
if (reasonId.startsWith("shell_")) return "shell command safety";
|
|
10482
|
-
return void 0;
|
|
10483
|
-
}
|
|
10484
|
-
function summarizeTrace(entries) {
|
|
10485
|
-
let denials = 0;
|
|
10486
|
-
let readBytesWouldBlock = 0;
|
|
10487
|
-
const byTool = {};
|
|
10488
|
-
const byReason = {};
|
|
10489
|
-
const bySurface = {};
|
|
10490
|
-
const byMode = {};
|
|
10491
|
-
const commandSafetyBySurface = {};
|
|
10492
|
-
for (const e of entries) {
|
|
10493
|
-
denials += 1;
|
|
10494
|
-
const tool = e.tool ?? "unknown";
|
|
10495
|
-
byTool[tool] = (byTool[tool] ?? 0) + 1;
|
|
10496
|
-
const reason = e.reasonId ?? "unknown";
|
|
10497
|
-
byReason[reason] = (byReason[reason] ?? 0) + 1;
|
|
10498
|
-
const surface = e.surface ?? "unknown";
|
|
10499
|
-
bySurface[surface] = (bySurface[surface] ?? 0) + 1;
|
|
10500
|
-
const mode = e.mode ?? "unknown";
|
|
10501
|
-
byMode[mode] = (byMode[mode] ?? 0) + 1;
|
|
10502
|
-
const safetyClass = commandSafetyClass(reason);
|
|
10503
|
-
if (safetyClass) {
|
|
10504
|
-
commandSafetyBySurface[surface] = commandSafetyBySurface[surface] ?? {};
|
|
10505
|
-
commandSafetyBySurface[surface][safetyClass] = (commandSafetyBySurface[surface][safetyClass] ?? 0) + 1;
|
|
10506
|
-
}
|
|
10507
|
-
if (reason === "read_unbounded_large") {
|
|
10508
|
-
readBytesWouldBlock += Number(e.fileBytes) || 0;
|
|
10509
|
-
}
|
|
10510
|
-
}
|
|
10511
|
-
return { denials, readBytesWouldBlock, byTool, byReason, bySurface, byMode, commandSafetyBySurface };
|
|
10512
|
-
}
|
|
10513
|
-
function runThrottleReport(io, tracePath = resolveThrottleTracePath()) {
|
|
10514
|
-
const mode = resolveModeFromEnv();
|
|
10515
|
-
if (!(0, import_node_fs16.existsSync)(tracePath)) {
|
|
10516
|
-
io.log(`Throttle: no trace at ${tracePath} (gates have not denied anything yet).`);
|
|
10517
|
-
io.log(`Active mode: ${mode} (MMI_THROTTLE_MODE env; default block).`);
|
|
10518
|
-
return 0;
|
|
10519
|
-
}
|
|
10520
|
-
let raw = "";
|
|
10521
|
-
try {
|
|
10522
|
-
raw = (0, import_node_fs16.readFileSync)(tracePath, "utf8");
|
|
10523
|
-
} catch (e) {
|
|
10524
|
-
io.err(`Throttle: could not read trace: ${e.message}`);
|
|
10525
|
-
return 1;
|
|
10526
|
-
}
|
|
10527
|
-
const entries = parseTraceLines(raw);
|
|
10528
|
-
const s = summarizeTrace(entries);
|
|
10529
|
-
io.log("Throttle gate summary");
|
|
10530
|
-
io.log(` mode (current env): ${mode}`);
|
|
10531
|
-
io.log(` trace entries: ${s.denials}`);
|
|
10532
|
-
io.log(` read bytes would-have-read (fs.stat only): ${s.readBytesWouldBlock}`);
|
|
10533
|
-
io.log(" (Shell denials are not converted to a tokens-saved figure \u2014 counterfactual.)");
|
|
10534
|
-
const modes = Object.entries(s.byMode).sort((a, b) => b[1] - a[1]);
|
|
10535
|
-
if (modes.length) {
|
|
10536
|
-
io.log(" by trace mode (at denial time):");
|
|
10537
|
-
for (const [name, count] of modes) {
|
|
10538
|
-
const label = name === "observe" ? "would-block (observe)" : name === "block" ? "denied (block)" : name;
|
|
10539
|
-
io.log(` ${label}: ${count}`);
|
|
10540
|
-
}
|
|
10541
|
-
}
|
|
10542
|
-
const tools = Object.entries(s.byTool).sort((a, b) => b[1] - a[1]);
|
|
10543
|
-
if (tools.length) {
|
|
10544
|
-
io.log(" by tool:");
|
|
10545
|
-
for (const [name, count] of tools) io.log(` ${name}: ${count}`);
|
|
10546
|
-
}
|
|
10547
|
-
const reasons = Object.entries(s.byReason).sort((a, b) => b[1] - a[1]);
|
|
10548
|
-
if (reasons.length) {
|
|
10549
|
-
io.log(" by reason:");
|
|
10550
|
-
for (const [name, count] of reasons) io.log(` ${name}: ${count}`);
|
|
10551
|
-
}
|
|
10552
|
-
const surfaces = Object.entries(s.bySurface).sort((a, b) => b[1] - a[1]);
|
|
10553
|
-
if (surfaces.length) {
|
|
10554
|
-
io.log(" by surface:");
|
|
10555
|
-
for (const [name, count] of surfaces) io.log(` ${name}: ${count}`);
|
|
10556
|
-
}
|
|
10557
|
-
const safetySurfaces = Object.entries(s.commandSafetyBySurface).sort((a, b) => a[0].localeCompare(b[0]));
|
|
10558
|
-
if (safetySurfaces.length) {
|
|
10559
|
-
io.log(" command safety (quoting/dialect/shell) by surface:");
|
|
10560
|
-
for (const [surface, classes] of safetySurfaces) {
|
|
10561
|
-
const detail = Object.entries(classes).sort((a, b) => b[1] - a[1]).map(([cls, count]) => `${cls}: ${count}`).join(", ");
|
|
10562
|
-
io.log(` ${surface}: ${detail}`);
|
|
10563
|
-
}
|
|
10564
|
-
}
|
|
10565
|
-
return 0;
|
|
10566
|
-
}
|
|
10567
|
-
function registerThrottleCommands(program3) {
|
|
10568
|
-
const throttle = program3.command("throttle").description("Scrooge v2 \u2014 tool-economy gate trace + report");
|
|
10569
|
-
throttle.command("report").description("print gate denial stats from .mmi/throttle/trace.jsonl").action(() => {
|
|
10570
|
-
const code = runThrottleReport({ log: (s) => console.log(s), err: (s) => console.error(s) });
|
|
10571
|
-
process.exit(code);
|
|
10572
|
-
});
|
|
10573
|
-
}
|
|
10574
|
-
|
|
10575
10380
|
// src/docs-sync.ts
|
|
10576
10381
|
var SYNCED_DOCS = ["README.md", "architecture.md"];
|
|
10577
10382
|
async function syncDocs(deps, docs2 = SYNCED_DOCS) {
|
|
@@ -10594,7 +10399,7 @@ async function syncDocs(deps, docs2 = SYNCED_DOCS) {
|
|
|
10594
10399
|
}
|
|
10595
10400
|
|
|
10596
10401
|
// src/board.ts
|
|
10597
|
-
var
|
|
10402
|
+
var import_node_child_process8 = require("node:child_process");
|
|
10598
10403
|
var import_node_util6 = require("node:util");
|
|
10599
10404
|
|
|
10600
10405
|
// src/board-priority.ts
|
|
@@ -10702,7 +10507,7 @@ async function filterDependencyBlockedClaimables(items, client, opts = {}) {
|
|
|
10702
10507
|
var BOARD_STATUSES = ["Todo", "In Progress", "In Review", "Done"];
|
|
10703
10508
|
|
|
10704
10509
|
// src/board.ts
|
|
10705
|
-
var rawExecFileP3 = (0, import_node_util6.promisify)(
|
|
10510
|
+
var rawExecFileP3 = (0, import_node_util6.promisify)(import_node_child_process8.execFile);
|
|
10706
10511
|
var BOARD_GIT_TIMEOUT_MS = 1e4;
|
|
10707
10512
|
var WRITE_PROBE_CONCURRENCY = 8;
|
|
10708
10513
|
var CLAIM_CONCURRENCY = 5;
|
|
@@ -11531,16 +11336,16 @@ function ghError(e) {
|
|
|
11531
11336
|
}
|
|
11532
11337
|
|
|
11533
11338
|
// src/board-slice-cache.ts
|
|
11534
|
-
var
|
|
11535
|
-
var
|
|
11536
|
-
var BOARD_SLICE_CACHE_FILE = (0,
|
|
11339
|
+
var import_node_fs16 = require("node:fs");
|
|
11340
|
+
var import_node_path14 = require("node:path");
|
|
11341
|
+
var BOARD_SLICE_CACHE_FILE = (0, import_node_path14.join)(".mmi", "board-slice.json");
|
|
11537
11342
|
var BOARD_SLICE_CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
11538
11343
|
function boardSliceCachePath(cwd) {
|
|
11539
|
-
return (0,
|
|
11344
|
+
return (0, import_node_path14.join)(cwd, BOARD_SLICE_CACHE_FILE);
|
|
11540
11345
|
}
|
|
11541
11346
|
function readCachedBoardSlice(cwd) {
|
|
11542
11347
|
try {
|
|
11543
|
-
const parsed = JSON.parse((0,
|
|
11348
|
+
const parsed = JSON.parse((0, import_node_fs16.readFileSync)(boardSliceCachePath(cwd), "utf8"));
|
|
11544
11349
|
if (typeof parsed.ts !== "number") return null;
|
|
11545
11350
|
return { block: typeof parsed.block === "string" ? parsed.block : null, ts: parsed.ts };
|
|
11546
11351
|
} catch {
|
|
@@ -11551,12 +11356,12 @@ function writeCachedBoardSlice(cwd, slice) {
|
|
|
11551
11356
|
const path2 = boardSliceCachePath(cwd);
|
|
11552
11357
|
const tmp = `${path2}.${process.pid}.tmp`;
|
|
11553
11358
|
try {
|
|
11554
|
-
(0,
|
|
11555
|
-
(0,
|
|
11556
|
-
(0,
|
|
11359
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path14.dirname)(path2), { recursive: true });
|
|
11360
|
+
(0, import_node_fs16.writeFileSync)(tmp, JSON.stringify(slice));
|
|
11361
|
+
(0, import_node_fs16.renameSync)(tmp, path2);
|
|
11557
11362
|
} catch {
|
|
11558
11363
|
try {
|
|
11559
|
-
(0,
|
|
11364
|
+
(0, import_node_fs16.rmSync)(tmp, { force: true });
|
|
11560
11365
|
} catch {
|
|
11561
11366
|
}
|
|
11562
11367
|
}
|
|
@@ -11684,8 +11489,8 @@ async function refreshBoardSliceCache(deps) {
|
|
|
11684
11489
|
}
|
|
11685
11490
|
|
|
11686
11491
|
// src/worktree.ts
|
|
11687
|
-
var
|
|
11688
|
-
var
|
|
11492
|
+
var import_node_fs17 = require("node:fs");
|
|
11493
|
+
var import_node_path15 = require("node:path");
|
|
11689
11494
|
var LOCAL_ONLY_FILES = [".claude/settings.local.json"];
|
|
11690
11495
|
var PKG = "package.json";
|
|
11691
11496
|
var LOCKFILE = "package-lock.json";
|
|
@@ -11693,21 +11498,21 @@ var NODE_MODULES = "node_modules";
|
|
|
11693
11498
|
var realFsProbe = {
|
|
11694
11499
|
isDir: (p) => {
|
|
11695
11500
|
try {
|
|
11696
|
-
return (0,
|
|
11501
|
+
return (0, import_node_fs17.statSync)(p).isDirectory();
|
|
11697
11502
|
} catch {
|
|
11698
11503
|
return false;
|
|
11699
11504
|
}
|
|
11700
11505
|
},
|
|
11701
11506
|
isFile: (p) => {
|
|
11702
11507
|
try {
|
|
11703
|
-
return (0,
|
|
11508
|
+
return (0, import_node_fs17.statSync)(p).isFile();
|
|
11704
11509
|
} catch {
|
|
11705
11510
|
return false;
|
|
11706
11511
|
}
|
|
11707
11512
|
},
|
|
11708
11513
|
listDirs: (p) => {
|
|
11709
11514
|
try {
|
|
11710
|
-
return (0,
|
|
11515
|
+
return (0, import_node_fs17.readdirSync)(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
11711
11516
|
} catch {
|
|
11712
11517
|
return [];
|
|
11713
11518
|
}
|
|
@@ -11715,12 +11520,12 @@ var realFsProbe = {
|
|
|
11715
11520
|
};
|
|
11716
11521
|
function scanInstallDirs(root, fs2 = realFsProbe) {
|
|
11717
11522
|
const factsFor = (dir) => {
|
|
11718
|
-
const abs = dir ? (0,
|
|
11523
|
+
const abs = dir ? (0, import_node_path15.join)(root, dir) : root;
|
|
11719
11524
|
return {
|
|
11720
11525
|
dir,
|
|
11721
|
-
hasPackageJson: fs2.isFile((0,
|
|
11722
|
-
hasLockfile: fs2.isFile((0,
|
|
11723
|
-
hasNodeModules: fs2.isDir((0,
|
|
11526
|
+
hasPackageJson: fs2.isFile((0, import_node_path15.join)(abs, PKG)),
|
|
11527
|
+
hasLockfile: fs2.isFile((0, import_node_path15.join)(abs, LOCKFILE)),
|
|
11528
|
+
hasNodeModules: fs2.isDir((0, import_node_path15.join)(abs, NODE_MODULES))
|
|
11724
11529
|
};
|
|
11725
11530
|
};
|
|
11726
11531
|
const children = fs2.listDirs(root).filter((name) => name !== NODE_MODULES && name !== ".git");
|
|
@@ -11730,7 +11535,7 @@ function npmInstallTargets(dirs) {
|
|
|
11730
11535
|
return dirs.filter((d) => d.hasPackageJson && !d.hasNodeModules).map((d) => ({ dir: d.dir, command: d.hasLockfile ? "npm ci" : "npm install" }));
|
|
11731
11536
|
}
|
|
11732
11537
|
function isLinkedWorktree(root, fs2 = realFsProbe) {
|
|
11733
|
-
return fs2.isFile((0,
|
|
11538
|
+
return fs2.isFile((0, import_node_path15.join)(root, ".git"));
|
|
11734
11539
|
}
|
|
11735
11540
|
function worktreeAutoProvisionBanner(root, fs2 = realFsProbe) {
|
|
11736
11541
|
if (!isLinkedWorktree(root, fs2)) return null;
|
|
@@ -11740,8 +11545,8 @@ function worktreeAutoProvisionBanner(root, fs2 = realFsProbe) {
|
|
|
11740
11545
|
return `[worktree] provisioning tooling in the background (deps in ${where} + local config) \u2014 \`mmi-cli worktree setup\` to redo`;
|
|
11741
11546
|
}
|
|
11742
11547
|
function defaultCopyFile(from, to) {
|
|
11743
|
-
(0,
|
|
11744
|
-
(0,
|
|
11548
|
+
(0, import_node_fs17.mkdirSync)((0, import_node_path15.dirname)(to), { recursive: true });
|
|
11549
|
+
(0, import_node_fs17.copyFileSync)(from, to);
|
|
11745
11550
|
}
|
|
11746
11551
|
async function provisionWorktree(worktreeRoot, deps) {
|
|
11747
11552
|
const fs2 = deps.fs ?? realFsProbe;
|
|
@@ -11753,7 +11558,7 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
11753
11558
|
const skippedInstall = allDirs.filter((d) => d.hasPackageJson && d.hasNodeModules).map((d) => d.dir);
|
|
11754
11559
|
const installed = [];
|
|
11755
11560
|
for (const target of targets) {
|
|
11756
|
-
const cwd = target.dir ? (0,
|
|
11561
|
+
const cwd = target.dir ? (0, import_node_path15.join)(worktreeRoot, target.dir) : worktreeRoot;
|
|
11757
11562
|
log(`installing deps: ${target.command} in ${target.dir || "."}`);
|
|
11758
11563
|
await deps.runInstall(target.command, cwd);
|
|
11759
11564
|
installed.push(target);
|
|
@@ -11762,7 +11567,7 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
11762
11567
|
const copySkipped = [];
|
|
11763
11568
|
const primary = await deps.primaryCheckout();
|
|
11764
11569
|
for (const rel of LOCAL_ONLY_FILES) {
|
|
11765
|
-
const dest = (0,
|
|
11570
|
+
const dest = (0, import_node_path15.join)(worktreeRoot, rel);
|
|
11766
11571
|
if (fs2.isFile(dest)) {
|
|
11767
11572
|
copySkipped.push({ file: rel, reason: "already-present" });
|
|
11768
11573
|
continue;
|
|
@@ -11771,11 +11576,11 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
11771
11576
|
copySkipped.push({ file: rel, reason: "no-primary" });
|
|
11772
11577
|
continue;
|
|
11773
11578
|
}
|
|
11774
|
-
if (!fs2.isFile((0,
|
|
11579
|
+
if (!fs2.isFile((0, import_node_path15.join)(primary, rel))) {
|
|
11775
11580
|
copySkipped.push({ file: rel, reason: "absent-in-primary" });
|
|
11776
11581
|
continue;
|
|
11777
11582
|
}
|
|
11778
|
-
copyFile((0,
|
|
11583
|
+
copyFile((0, import_node_path15.join)(primary, rel), dest);
|
|
11779
11584
|
copied.push(rel);
|
|
11780
11585
|
log(`copied local config: ${rel}`);
|
|
11781
11586
|
}
|
|
@@ -11783,7 +11588,7 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
11783
11588
|
}
|
|
11784
11589
|
function defaultWorktreePath(repoRoot, branch) {
|
|
11785
11590
|
const safe = branch.replace(/[/\\]+/g, "-");
|
|
11786
|
-
return (0,
|
|
11591
|
+
return (0, import_node_path15.join)((0, import_node_path15.dirname)(repoRoot), "mmi-worktrees", safe);
|
|
11787
11592
|
}
|
|
11788
11593
|
function resolveWorktreeBase(from, remote) {
|
|
11789
11594
|
const remotePrefix = `${remote}/`;
|
|
@@ -11791,121 +11596,6 @@ function resolveWorktreeBase(from, remote) {
|
|
|
11791
11596
|
return { base: from, fetchBranch };
|
|
11792
11597
|
}
|
|
11793
11598
|
|
|
11794
|
-
// src/northstar-context.ts
|
|
11795
|
-
var SESSION_START_NORTHSTAR_TIMEOUT_MS = 3e3;
|
|
11796
|
-
var NORTHSTAR_CONTEXT_MIN_SCORE = 3;
|
|
11797
|
-
var NORTHSTAR_CONTEXT_MAX_CARDS = 2;
|
|
11798
|
-
var NORTHSTAR_CONTEXT_CHAR_BUDGET = 900;
|
|
11799
|
-
var NORTHSTAR_CONTEXT_FRAMING = [
|
|
11800
|
-
"> NORTH STAR CONTEXT \u2014 SSOT plans likely relevant to this task/session. Use them to",
|
|
11801
|
-
" orient direction and constraints. This is a PRIOR, not an instruction: the live",
|
|
11802
|
-
" user/master instruction always wins. Do not narrate or cite this block; pull the",
|
|
11803
|
-
" full plan with `mmi-cli northstar pull <slug>` when you need detail."
|
|
11804
|
-
].join("\n");
|
|
11805
|
-
function compactText(text, max) {
|
|
11806
|
-
const t = text.replace(/\s+/g, " ").trim();
|
|
11807
|
-
if (t.length <= max) return t;
|
|
11808
|
-
return `${t.slice(0, max - 1)}\u2026`;
|
|
11809
|
-
}
|
|
11810
|
-
function derivePlanIntent(content) {
|
|
11811
|
-
const fromFm = frontmatterValue(content, "intent") ?? frontmatterValue(content, "summary");
|
|
11812
|
-
if (fromFm?.trim()) return compactText(fromFm, 240);
|
|
11813
|
-
const { body } = splitFrontmatter(content);
|
|
11814
|
-
const lines = body.split(/\r?\n/);
|
|
11815
|
-
const paragraph = [];
|
|
11816
|
-
for (const raw of lines) {
|
|
11817
|
-
const line = raw.trim();
|
|
11818
|
-
if (!line) {
|
|
11819
|
-
if (paragraph.length) break;
|
|
11820
|
-
continue;
|
|
11821
|
-
}
|
|
11822
|
-
if (/^#+\s/.test(line)) continue;
|
|
11823
|
-
if (/^[-*]\s/.test(line) || /^```/.test(line)) break;
|
|
11824
|
-
paragraph.push(line.replace(/^#+\s*/, ""));
|
|
11825
|
-
if (paragraph.length >= 3) break;
|
|
11826
|
-
}
|
|
11827
|
-
if (!paragraph.length) return void 0;
|
|
11828
|
-
return compactText(paragraph.join(" "), 240);
|
|
11829
|
-
}
|
|
11830
|
-
function formatPlanContextCard(slug, title, intent) {
|
|
11831
|
-
const head = title?.trim() ? ` \u2022 ${slug} \u2014 ${title.trim()}` : ` \u2022 ${slug}`;
|
|
11832
|
-
if (!intent?.trim()) return head;
|
|
11833
|
-
return `${head}
|
|
11834
|
-
${intent.trim()}`;
|
|
11835
|
-
}
|
|
11836
|
-
function isHighConfidenceMatch(r, signals) {
|
|
11837
|
-
if (r.score >= NORTHSTAR_CONTEXT_MIN_SCORE) return true;
|
|
11838
|
-
const branch = (signals.branch ?? "").toLowerCase();
|
|
11839
|
-
const slug = r.plan.slug.toLowerCase();
|
|
11840
|
-
return slug.length >= 5 && branch.includes(slug);
|
|
11841
|
-
}
|
|
11842
|
-
function selectHighConfidencePlans(ranked, signals, opts = {}) {
|
|
11843
|
-
const max = opts.maxCards ?? NORTHSTAR_CONTEXT_MAX_CARDS;
|
|
11844
|
-
const anchor = opts.anchorSlug?.trim().toLowerCase();
|
|
11845
|
-
const picked = [];
|
|
11846
|
-
if (anchor) {
|
|
11847
|
-
const direct = ranked.find((r) => r.plan.slug.toLowerCase() === anchor);
|
|
11848
|
-
if (direct) picked.push(direct);
|
|
11849
|
-
}
|
|
11850
|
-
for (const r of ranked) {
|
|
11851
|
-
if (picked.length >= max) break;
|
|
11852
|
-
if (picked.some((p) => p.plan.slug === r.plan.slug && p.plan.project === r.plan.project)) continue;
|
|
11853
|
-
if (isHighConfidenceMatch(r, signals)) picked.push(r);
|
|
11854
|
-
}
|
|
11855
|
-
return picked.slice(0, max);
|
|
11856
|
-
}
|
|
11857
|
-
function buildNorthstarContextBlock(ranked, signals, readLocal, opts = {}) {
|
|
11858
|
-
const selected = selectHighConfidencePlans(ranked, signals, opts);
|
|
11859
|
-
if (!selected.length) return null;
|
|
11860
|
-
const cards = [];
|
|
11861
|
-
let used = 0;
|
|
11862
|
-
for (const r of selected) {
|
|
11863
|
-
const local = readLocal(r.plan.slug);
|
|
11864
|
-
const intent = local ? derivePlanIntent(local) : void 0;
|
|
11865
|
-
const card = formatPlanContextCard(r.plan.slug, r.plan.title, intent);
|
|
11866
|
-
if (used + card.length > NORTHSTAR_CONTEXT_CHAR_BUDGET) break;
|
|
11867
|
-
cards.push(card);
|
|
11868
|
-
used += card.length;
|
|
11869
|
-
}
|
|
11870
|
-
if (!cards.length) return null;
|
|
11871
|
-
return [NORTHSTAR_CONTEXT_FRAMING, ...cards].join("\n");
|
|
11872
|
-
}
|
|
11873
|
-
async function withTimeout2(promise, ms) {
|
|
11874
|
-
let timer;
|
|
11875
|
-
try {
|
|
11876
|
-
return await Promise.race([
|
|
11877
|
-
promise,
|
|
11878
|
-
new Promise((_, reject) => {
|
|
11879
|
-
timer = setTimeout(() => reject(new Error("northstar context timeout")), ms);
|
|
11880
|
-
})
|
|
11881
|
-
]);
|
|
11882
|
-
} finally {
|
|
11883
|
-
if (timer) clearTimeout(timer);
|
|
11884
|
-
}
|
|
11885
|
-
}
|
|
11886
|
-
async function runNorthstarContext(io, deps) {
|
|
11887
|
-
try {
|
|
11888
|
-
const injected = await withTimeout2(
|
|
11889
|
-
(async () => {
|
|
11890
|
-
const plans = await deps.loadPlans();
|
|
11891
|
-
if (!plans.length) return false;
|
|
11892
|
-
const signals = await deps.gatherSignals();
|
|
11893
|
-
const ranked = rankPlansByRelevance(plans, signals);
|
|
11894
|
-
const block = buildNorthstarContextBlock(ranked, signals, deps.readLocal, {
|
|
11895
|
-
anchorSlug: signals.anchorSlug
|
|
11896
|
-
});
|
|
11897
|
-
if (!block) return false;
|
|
11898
|
-
io.log(block);
|
|
11899
|
-
return true;
|
|
11900
|
-
})(),
|
|
11901
|
-
deps.timeoutMs ?? SESSION_START_NORTHSTAR_TIMEOUT_MS
|
|
11902
|
-
);
|
|
11903
|
-
return injected;
|
|
11904
|
-
} catch {
|
|
11905
|
-
return false;
|
|
11906
|
-
}
|
|
11907
|
-
}
|
|
11908
|
-
|
|
11909
11599
|
// src/whoami.ts
|
|
11910
11600
|
async function resolveWhoami(deps) {
|
|
11911
11601
|
let session;
|
|
@@ -11930,7 +11620,7 @@ function whoamiLine(report) {
|
|
|
11930
11620
|
}
|
|
11931
11621
|
|
|
11932
11622
|
// src/index.ts
|
|
11933
|
-
var
|
|
11623
|
+
var import_node_path22 = require("node:path");
|
|
11934
11624
|
|
|
11935
11625
|
// src/merge-ci-policy.ts
|
|
11936
11626
|
function resolveMergeCiPolicy(input) {
|
|
@@ -13182,7 +12872,7 @@ async function resolveAutoAddBoardAttach(client, cfg, selector, priority, warn =
|
|
|
13182
12872
|
// src/gh-create.ts
|
|
13183
12873
|
var import_promises5 = require("node:fs/promises");
|
|
13184
12874
|
var import_node_os3 = require("node:os");
|
|
13185
|
-
var
|
|
12875
|
+
var import_node_path16 = require("node:path");
|
|
13186
12876
|
var import_node_crypto6 = require("node:crypto");
|
|
13187
12877
|
var ISSUE_TYPES = ["bug", "feature", "task"];
|
|
13188
12878
|
var GH_MUTATION_TIMEOUT_MS = 12e4;
|
|
@@ -13223,7 +12913,7 @@ async function bodyArgsViaFile(args, deps = {}) {
|
|
|
13223
12913
|
} };
|
|
13224
12914
|
const write = deps.write ?? import_promises5.writeFile;
|
|
13225
12915
|
const remove = deps.remove ?? import_promises5.unlink;
|
|
13226
|
-
const file = (0,
|
|
12916
|
+
const file = (0, import_node_path16.join)(deps.dir ?? (0, import_node_os3.tmpdir)(), `mmi-gh-body-${process.pid}-${(0, import_node_crypto6.randomBytes)(4).toString("hex")}.md`);
|
|
13227
12917
|
await write(file, args[i + 1], "utf8");
|
|
13228
12918
|
return {
|
|
13229
12919
|
args: [...args.slice(0, i), "--body-file", file, ...args.slice(i + 2)],
|
|
@@ -13672,7 +13362,7 @@ ${buildReportBody(body, sourceRepo)}`;
|
|
|
13672
13362
|
|
|
13673
13363
|
// src/skill-lesson.ts
|
|
13674
13364
|
var SKILL_LESSON_LABEL = "skill-lesson";
|
|
13675
|
-
var SKILL_NAMES = ["bootstrap", "browser-automation", "
|
|
13365
|
+
var SKILL_NAMES = ["bootstrap", "browser-automation", "hotfix", "mmi", "rcand", "release", "secrets", "stage"];
|
|
13676
13366
|
function assertSkillName(name) {
|
|
13677
13367
|
const match = SKILL_NAMES.find((skill) => skill === name);
|
|
13678
13368
|
if (!match) throw new Error(`unknown skill "${name}" \u2014 expected one of: ${SKILL_NAMES.join(", ")}`);
|
|
@@ -15470,12 +15160,12 @@ function renderVerifySecrets(body) {
|
|
|
15470
15160
|
}
|
|
15471
15161
|
|
|
15472
15162
|
// src/hotfix-coverage.ts
|
|
15473
|
-
var
|
|
15163
|
+
var import_node_child_process9 = require("node:child_process");
|
|
15474
15164
|
var CHERRY_TRAILER = /\(cherry picked from commit ([0-9a-f]{7,40})\)/g;
|
|
15475
15165
|
function checkHotfixCoverage(options = {}) {
|
|
15476
15166
|
const { cwd = process.cwd(), mainRef = "origin/main", rcRef = "origin/rc", manifestPaths = [] } = options;
|
|
15477
15167
|
const ack = (options.ack ?? []).filter(Boolean);
|
|
15478
|
-
const git = options.git ?? ((args, opts) => (0,
|
|
15168
|
+
const git = options.git ?? ((args, opts) => (0, import_node_child_process9.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
|
|
15479
15169
|
const revList = (range) => {
|
|
15480
15170
|
const out = git(["rev-list", "--no-merges", range]).trim();
|
|
15481
15171
|
return out ? out.split("\n") : [];
|
|
@@ -16196,8 +15886,8 @@ async function announceRelease(deps, args) {
|
|
|
16196
15886
|
}
|
|
16197
15887
|
|
|
16198
15888
|
// src/port-registry.ts
|
|
16199
|
-
var
|
|
16200
|
-
var
|
|
15889
|
+
var import_node_fs18 = require("node:fs");
|
|
15890
|
+
var import_node_path17 = require("node:path");
|
|
16201
15891
|
|
|
16202
15892
|
// ../infra/port-geometry.mjs
|
|
16203
15893
|
var PORT_BLOCK = 100;
|
|
@@ -16211,8 +15901,8 @@ function nextPortBlock(registry2) {
|
|
|
16211
15901
|
return [base, base + PORT_SPAN];
|
|
16212
15902
|
}
|
|
16213
15903
|
function loadPortRegistry(path2) {
|
|
16214
|
-
if (!(0,
|
|
16215
|
-
const raw = JSON.parse((0,
|
|
15904
|
+
if (!(0, import_node_fs18.existsSync)(path2)) return {};
|
|
15905
|
+
const raw = JSON.parse((0, import_node_fs18.readFileSync)(path2, "utf8"));
|
|
16216
15906
|
const out = {};
|
|
16217
15907
|
for (const [key, value] of Object.entries(raw)) {
|
|
16218
15908
|
if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
|
|
@@ -16226,9 +15916,9 @@ function ensurePortRange(repo, path2) {
|
|
|
16226
15916
|
const existing = registry2[repo];
|
|
16227
15917
|
if (existing) return existing;
|
|
16228
15918
|
const range = nextPortBlock(registry2);
|
|
16229
|
-
const raw = (0,
|
|
15919
|
+
const raw = (0, import_node_fs18.existsSync)(path2) ? JSON.parse((0, import_node_fs18.readFileSync)(path2, "utf8")) : {};
|
|
16230
15920
|
raw[repo] = range;
|
|
16231
|
-
(0,
|
|
15921
|
+
(0, import_node_fs18.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
|
|
16232
15922
|
return range;
|
|
16233
15923
|
}
|
|
16234
15924
|
function portCursorSeed(registry2) {
|
|
@@ -16250,18 +15940,18 @@ function existingPortRange(repo, registry2) {
|
|
|
16250
15940
|
return registry2[repo] ?? null;
|
|
16251
15941
|
}
|
|
16252
15942
|
function portRangeInfraAt(root, source) {
|
|
16253
|
-
const registryPath = (0,
|
|
16254
|
-
const ddbScriptPath = (0,
|
|
16255
|
-
if (!(0,
|
|
15943
|
+
const registryPath = (0, import_node_path17.join)(root, "infra", "port-ranges.json");
|
|
15944
|
+
const ddbScriptPath = (0, import_node_path17.join)(root, "infra", "port-ddb.mjs");
|
|
15945
|
+
if (!(0, import_node_fs18.existsSync)(registryPath) || !(0, import_node_fs18.existsSync)(ddbScriptPath)) return null;
|
|
16256
15946
|
return { root, source, registryPath, ddbScriptPath };
|
|
16257
15947
|
}
|
|
16258
15948
|
function resolvePortRangeInfra(cwd) {
|
|
16259
15949
|
const direct = portRangeInfraAt(cwd, "cwd");
|
|
16260
15950
|
if (direct) return direct;
|
|
16261
|
-
for (let dir = cwd; ; dir = (0,
|
|
16262
|
-
const sibling = portRangeInfraAt((0,
|
|
15951
|
+
for (let dir = cwd; ; dir = (0, import_node_path17.dirname)(dir)) {
|
|
15952
|
+
const sibling = portRangeInfraAt((0, import_node_path17.join)(dir, "MMI-Hub"), "sibling-hub");
|
|
16263
15953
|
if (sibling) return sibling;
|
|
16264
|
-
const parent = (0,
|
|
15954
|
+
const parent = (0, import_node_path17.dirname)(dir);
|
|
16265
15955
|
if (parent === dir) return null;
|
|
16266
15956
|
}
|
|
16267
15957
|
}
|
|
@@ -17699,6 +17389,39 @@ function authorizeBodyHasMismatch(body) {
|
|
|
17699
17389
|
var UNSET_KEYS = ["oauth", "requiredRuntimeSecrets", "edgeDomains", "requiredGcpApis", "publishRequired", "publishDir", "dsManifestPath", "fofuEnabled", "consumesDesignSystem", "ci", "requiredChecks", "gate"];
|
|
17700
17390
|
var UNSET_KEY_SET = new Set(UNSET_KEYS);
|
|
17701
17391
|
var RUNTIME_SECRET_STAGES = ["dev", "rc", "main"];
|
|
17392
|
+
var SECRET_CONSUMERS = ["runtime", "lambda", "actions", "agent", "box"];
|
|
17393
|
+
var SECRET_ENV_NAME_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
17394
|
+
function parseSecretsCatalogVar(raw) {
|
|
17395
|
+
let parsed;
|
|
17396
|
+
try {
|
|
17397
|
+
parsed = JSON.parse(raw);
|
|
17398
|
+
} catch {
|
|
17399
|
+
throw new Error('project set: secrets must be JSON keyed by KEY, e.g. {"SCRAPER_API_KEY":{"key":"SCRAPER_API_KEY","purpose":"scrape provider key","group":"scraper","owner":"oguz","stages":[],"consumers":["box"]}}');
|
|
17400
|
+
}
|
|
17401
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
17402
|
+
throw new Error("project set: secrets must be a map keyed by canonical KEY");
|
|
17403
|
+
}
|
|
17404
|
+
const nonEmpty = (v) => typeof v === "string" && v.trim().length > 0;
|
|
17405
|
+
for (const [mapKey, value] of Object.entries(parsed)) {
|
|
17406
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`project set: secrets["${mapKey}"] must be an object`);
|
|
17407
|
+
const e = value;
|
|
17408
|
+
if (typeof e.key !== "string" || !SECRET_ENV_NAME_RE.test(e.key) || e.key !== mapKey) {
|
|
17409
|
+
throw new Error(`project set: secrets["${mapKey}"].key must equal the map key and be an env-name (UPPER_SNAKE)`);
|
|
17410
|
+
}
|
|
17411
|
+
if (!nonEmpty(e.purpose) || !nonEmpty(e.group) || !nonEmpty(e.owner)) throw new Error(`project set: secrets["${mapKey}"] needs non-empty purpose, group, owner`);
|
|
17412
|
+
if (e.provider !== void 0 && !nonEmpty(e.provider)) throw new Error(`project set: secrets["${mapKey}"].provider must be a non-empty string`);
|
|
17413
|
+
if (!Array.isArray(e.stages) || e.stages.some((s) => !RUNTIME_SECRET_STAGES.includes(s))) {
|
|
17414
|
+
throw new Error(`project set: secrets["${mapKey}"].stages must be a subset of dev/rc/main ([] = shared)`);
|
|
17415
|
+
}
|
|
17416
|
+
if (!Array.isArray(e.consumers) || e.consumers.some((c) => !SECRET_CONSUMERS.includes(c))) {
|
|
17417
|
+
throw new Error(`project set: secrets["${mapKey}"].consumers must be a subset of ${SECRET_CONSUMERS.join("/")}`);
|
|
17418
|
+
}
|
|
17419
|
+
if (e.aliases !== void 0 && (!Array.isArray(e.aliases) || e.aliases.some((a) => typeof a !== "string"))) {
|
|
17420
|
+
throw new Error(`project set: secrets["${mapKey}"].aliases must be a string array`);
|
|
17421
|
+
}
|
|
17422
|
+
}
|
|
17423
|
+
return parsed;
|
|
17424
|
+
}
|
|
17702
17425
|
function parseRuntimeSecretsVar(raw) {
|
|
17703
17426
|
let parsed;
|
|
17704
17427
|
try {
|
|
@@ -17954,7 +17677,8 @@ var SETTABLE_VAR_KEYS = [
|
|
|
17954
17677
|
"portRange",
|
|
17955
17678
|
"ci",
|
|
17956
17679
|
"requiredChecks",
|
|
17957
|
-
"gate"
|
|
17680
|
+
"gate",
|
|
17681
|
+
"secrets"
|
|
17958
17682
|
];
|
|
17959
17683
|
var SETTABLE_VAR_KEY_SET = new Set(SETTABLE_VAR_KEYS);
|
|
17960
17684
|
var SETTABLE_VAR_HINTS = {
|
|
@@ -17968,6 +17692,7 @@ var SETTABLE_VAR_HINTS = {
|
|
|
17968
17692
|
oauth: "JSON {subdomains,domains,callbackPath,fofuSubdomain}",
|
|
17969
17693
|
requiredGcpApis: "comma-string",
|
|
17970
17694
|
requiredRuntimeSecrets: 'JSON stage map, e.g. {"dev":["KEY"],"rc":["KEY"],"main":["KEY"]}',
|
|
17695
|
+
secrets: "JSON catalog map keyed by KEY {key,purpose,group,owner,stages[],consumers[]} \u2014 prefer --secrets-file",
|
|
17971
17696
|
edgeDomains: "JSON {dev,rc,main} domain map",
|
|
17972
17697
|
statusOptions: "JSON name\u2192id map",
|
|
17973
17698
|
priorityOptions: "JSON {Urgent,High,Medium,Low}\u2192id map",
|
|
@@ -18023,6 +17748,8 @@ function buildProjectSetPatch(input) {
|
|
|
18023
17748
|
patch[key] = n;
|
|
18024
17749
|
} else if (key === "requiredRuntimeSecrets") {
|
|
18025
17750
|
patch[key] = parseRuntimeSecretsVar(raw);
|
|
17751
|
+
} else if (key === "secrets") {
|
|
17752
|
+
patch[key] = parseSecretsCatalogVar(raw);
|
|
18026
17753
|
} else if (key === "edgeDomains") {
|
|
18027
17754
|
patch[key] = parseEdgeDomainsVar(raw);
|
|
18028
17755
|
} else if (key === "priorityOptions") {
|
|
@@ -18132,15 +17859,15 @@ function requireProjectTarget(commandName, explicitTarget, currentRepo) {
|
|
|
18132
17859
|
}
|
|
18133
17860
|
|
|
18134
17861
|
// src/northstar-commands.ts
|
|
18135
|
-
var
|
|
18136
|
-
var
|
|
17862
|
+
var import_node_fs19 = require("node:fs");
|
|
17863
|
+
var import_node_child_process10 = require("node:child_process");
|
|
18137
17864
|
var import_promises6 = require("node:fs/promises");
|
|
18138
17865
|
var planSyncDetached = false;
|
|
18139
17866
|
function detachPlanSync() {
|
|
18140
17867
|
if (planSyncDetached) return;
|
|
18141
17868
|
planSyncDetached = true;
|
|
18142
17869
|
try {
|
|
18143
|
-
(0,
|
|
17870
|
+
(0, import_node_child_process10.spawn)(process.execPath, [process.argv[1], "northstar", "sync", "--quiet"], {
|
|
18144
17871
|
detached: true,
|
|
18145
17872
|
stdio: "ignore",
|
|
18146
17873
|
windowsHide: true,
|
|
@@ -18150,7 +17877,7 @@ function detachPlanSync() {
|
|
|
18150
17877
|
}
|
|
18151
17878
|
}
|
|
18152
17879
|
function makePlanDeps(cfg, io = consoleIo) {
|
|
18153
|
-
const ensureDir = () => (0,
|
|
17880
|
+
const ensureDir = () => (0, import_node_fs19.mkdirSync)(PLANS_DIR, { recursive: true });
|
|
18154
17881
|
return {
|
|
18155
17882
|
apiUrl: cfg.sagaApiUrl,
|
|
18156
17883
|
fetch: (url, init = {}) => fetch(url, { ...init, signal: init.signal ?? AbortSignal.timeout(1e4) }),
|
|
@@ -18158,31 +17885,31 @@ function makePlanDeps(cfg, io = consoleIo) {
|
|
|
18158
17885
|
project: async () => (await sagaKey(cfg)).project,
|
|
18159
17886
|
readLocal: (slug) => {
|
|
18160
17887
|
try {
|
|
18161
|
-
return (0,
|
|
17888
|
+
return (0, import_node_fs19.readFileSync)(planPath(slug), "utf8");
|
|
18162
17889
|
} catch {
|
|
18163
17890
|
return null;
|
|
18164
17891
|
}
|
|
18165
17892
|
},
|
|
18166
17893
|
listLocalSlugs: () => {
|
|
18167
17894
|
try {
|
|
18168
|
-
return (0,
|
|
17895
|
+
return (0, import_node_fs19.readdirSync)(PLANS_DIR, { withFileTypes: true }).filter((entry) => entry.isFile() && /^[A-Za-z0-9][A-Za-z0-9_-]*\.md$/.test(entry.name)).map((entry) => entry.name.replace(/\.md$/, ""));
|
|
18169
17896
|
} catch {
|
|
18170
17897
|
return [];
|
|
18171
17898
|
}
|
|
18172
17899
|
},
|
|
18173
17900
|
writeLocal: (slug, content) => {
|
|
18174
17901
|
ensureDir();
|
|
18175
|
-
(0,
|
|
17902
|
+
(0, import_node_fs19.writeFileSync)(planPath(slug), content, "utf8");
|
|
18176
17903
|
},
|
|
18177
17904
|
removeLocal: (slug) => {
|
|
18178
17905
|
try {
|
|
18179
|
-
(0,
|
|
17906
|
+
(0, import_node_fs19.rmSync)(planPath(slug));
|
|
18180
17907
|
} catch {
|
|
18181
17908
|
}
|
|
18182
17909
|
},
|
|
18183
17910
|
readMetaRaw: () => {
|
|
18184
17911
|
try {
|
|
18185
|
-
return (0,
|
|
17912
|
+
return (0, import_node_fs19.readFileSync)(META_FILE, "utf8");
|
|
18186
17913
|
} catch {
|
|
18187
17914
|
return null;
|
|
18188
17915
|
}
|
|
@@ -18193,7 +17920,7 @@ function makePlanDeps(cfg, io = consoleIo) {
|
|
|
18193
17920
|
},
|
|
18194
17921
|
readIndexRaw: () => {
|
|
18195
17922
|
try {
|
|
18196
|
-
return (0,
|
|
17923
|
+
return (0, import_node_fs19.readFileSync)(INDEX_FILE, "utf8");
|
|
18197
17924
|
} catch {
|
|
18198
17925
|
return null;
|
|
18199
17926
|
}
|
|
@@ -18204,7 +17931,7 @@ function makePlanDeps(cfg, io = consoleIo) {
|
|
|
18204
17931
|
},
|
|
18205
17932
|
readQueueRaw: () => {
|
|
18206
17933
|
try {
|
|
18207
|
-
return (0,
|
|
17934
|
+
return (0, import_node_fs19.readFileSync)(QUEUE_FILE, "utf8");
|
|
18208
17935
|
} catch {
|
|
18209
17936
|
return null;
|
|
18210
17937
|
}
|
|
@@ -18226,7 +17953,7 @@ function openInEditor(path2) {
|
|
|
18226
17953
|
return;
|
|
18227
17954
|
}
|
|
18228
17955
|
try {
|
|
18229
|
-
(0,
|
|
17956
|
+
(0, import_node_child_process10.spawn)(editor, [path2], { stdio: "inherit" });
|
|
18230
17957
|
} catch {
|
|
18231
17958
|
console.log(`open ${path2} manually`);
|
|
18232
17959
|
}
|
|
@@ -18266,7 +17993,7 @@ function repoInfoFromRemote(remote) {
|
|
|
18266
17993
|
}
|
|
18267
17994
|
function readStageUrl() {
|
|
18268
17995
|
try {
|
|
18269
|
-
const state = JSON.parse((0,
|
|
17996
|
+
const state = JSON.parse((0, import_node_fs19.readFileSync)("tmp/stage/state.json", "utf8"));
|
|
18270
17997
|
if (typeof state.url === "string" && state.url.trim()) return state.url.trim();
|
|
18271
17998
|
if (typeof state.port === "number" && Number.isFinite(state.port)) return `http://127.0.0.1:${state.port}/`;
|
|
18272
17999
|
if (typeof state.healthUrl === "string" && state.healthUrl.trim()) {
|
|
@@ -18702,6 +18429,7 @@ function formatDrift(d) {
|
|
|
18702
18429
|
section("declared but MISSING in SSM", d.declaredMissing.map((m) => m.path));
|
|
18703
18430
|
section("ORPHAN in SSM, undeclared \u2014 declare or delete", d.orphan.map((o) => o.path));
|
|
18704
18431
|
section("OFF-SCHEME path", d.offScheme.map((o) => `${o.path} (${o.reason})`));
|
|
18432
|
+
section("STAGE-SPLIT \u2014 collapse to one shared key", (d.stageSplit ?? []).map((s) => `${s.slug}/${s.leaf} split across ${s.stages.join("/")}`));
|
|
18705
18433
|
section("DUPLICATE org-infra secret", d.duplicate.map((dup) => `${dup.logical}: ${dup.paths.join(" , ")}`));
|
|
18706
18434
|
return lines.join("\n").trimEnd();
|
|
18707
18435
|
}
|
|
@@ -18746,6 +18474,34 @@ async function secretsDoctor(deps, opts) {
|
|
|
18746
18474
|
deps.log(formatDrift(data.drift));
|
|
18747
18475
|
return data.drift.ok;
|
|
18748
18476
|
}
|
|
18477
|
+
async function secretsOrgCatalogSet(deps, fileBody) {
|
|
18478
|
+
let parsed;
|
|
18479
|
+
try {
|
|
18480
|
+
parsed = JSON.parse(fileBody);
|
|
18481
|
+
} catch {
|
|
18482
|
+
deps.err('secrets org-catalog: file is not valid JSON ({ entries: { "<provider>/<KEY>": {...} } })');
|
|
18483
|
+
return false;
|
|
18484
|
+
}
|
|
18485
|
+
let res;
|
|
18486
|
+
try {
|
|
18487
|
+
res = await deps.fetch(`${deps.apiUrl}/catalog/org-infra`, {
|
|
18488
|
+
method: "POST",
|
|
18489
|
+
headers: await deps.headers({ "content-type": "application/json" }),
|
|
18490
|
+
body: JSON.stringify(parsed),
|
|
18491
|
+
signal: AbortSignal.timeout(TIMEOUT_MS2)
|
|
18492
|
+
});
|
|
18493
|
+
} catch (e) {
|
|
18494
|
+
deps.err(`secrets org-catalog: ${e.message}`);
|
|
18495
|
+
return false;
|
|
18496
|
+
}
|
|
18497
|
+
if (!res.ok) {
|
|
18498
|
+
deps.err(await upgradeMessage(res) ?? `secrets org-catalog failed: HTTP ${res.status}${await readErr(res)}`);
|
|
18499
|
+
return false;
|
|
18500
|
+
}
|
|
18501
|
+
const out = await res.json();
|
|
18502
|
+
deps.log(`org-infra catalog updated: ${out.entries ?? "?"} entries`);
|
|
18503
|
+
return true;
|
|
18504
|
+
}
|
|
18749
18505
|
var DEFAULT_RUNTIME_SECRET_NAMES2 = ["GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET"];
|
|
18750
18506
|
function stringList(v) {
|
|
18751
18507
|
return Array.isArray(v) ? v.filter((x) => typeof x === "string" && isValidSecretKey(x)) : [];
|
|
@@ -19117,8 +18873,8 @@ async function secretsUse(deps, key, opts) {
|
|
|
19117
18873
|
}
|
|
19118
18874
|
|
|
19119
18875
|
// src/secrets-commands.ts
|
|
19120
|
-
var
|
|
19121
|
-
var
|
|
18876
|
+
var import_node_fs20 = require("node:fs");
|
|
18877
|
+
var import_node_path18 = require("node:path");
|
|
19122
18878
|
var RAILS_CREDENTIALS_DECRYPT_TIMEOUT_MS = 3e4;
|
|
19123
18879
|
var DEFAULT_RAILS_CREDENTIALS_FILE = "config/credentials.yml.enc";
|
|
19124
18880
|
var DEFAULT_RAILS_MASTER_KEY_FILE = "config/master.key";
|
|
@@ -19126,18 +18882,18 @@ function collectMap(value, previous = []) {
|
|
|
19126
18882
|
return [...previous, value];
|
|
19127
18883
|
}
|
|
19128
18884
|
async function decryptRailsCredentials(input) {
|
|
19129
|
-
const appDir = (0,
|
|
18885
|
+
const appDir = (0, import_node_path18.resolve)(input.appDir ?? process.cwd());
|
|
19130
18886
|
const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
19131
18887
|
const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
19132
|
-
const credentialsPath = (0,
|
|
19133
|
-
const masterKeyPath = (0,
|
|
18888
|
+
const credentialsPath = (0, import_node_path18.resolve)(appDir, credentialsFile);
|
|
18889
|
+
const masterKeyPath = (0, import_node_path18.resolve)(appDir, masterKeyFile);
|
|
19134
18890
|
const env = {
|
|
19135
18891
|
...process.env,
|
|
19136
18892
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
19137
18893
|
MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
|
|
19138
18894
|
};
|
|
19139
|
-
if ((0,
|
|
19140
|
-
env.RAILS_MASTER_KEY = (0,
|
|
18895
|
+
if ((0, import_node_fs20.existsSync)(masterKeyPath)) {
|
|
18896
|
+
env.RAILS_MASTER_KEY = (0, import_node_fs20.readFileSync)(masterKeyPath, "utf8").trim();
|
|
19141
18897
|
}
|
|
19142
18898
|
const script = [
|
|
19143
18899
|
'require "json"',
|
|
@@ -19200,6 +18956,15 @@ function registerSecretsCommands(program3) {
|
|
|
19200
18956
|
secrets.command("doctor").description("vault drift report \u2014 declared-missing / orphan / off-scheme / duplicate (master-only, #2244)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json", "machine-readable output").action((o) => withSecrets(async (d) => {
|
|
19201
18957
|
if (!await secretsDoctor(d, o)) process.exitCode = 1;
|
|
19202
18958
|
}));
|
|
18959
|
+
secrets.command("org-catalog").description("MASTER-ONLY: set the _org/<provider>/* catalog declarations from a JSON file (#2244)").requiredOption("--file <path>", 'JSON file: { "entries": { "<provider>/<KEY>": {key,purpose,group,owner,provider,stages[],consumers[]} } }').action((o) => withSecrets(async (d) => {
|
|
18960
|
+
let body;
|
|
18961
|
+
try {
|
|
18962
|
+
body = (0, import_node_fs20.readFileSync)((0, import_node_path18.resolve)(o.file), "utf8");
|
|
18963
|
+
} catch (e) {
|
|
18964
|
+
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
18965
|
+
}
|
|
18966
|
+
if (!await secretsOrgCatalogSet(d, body)) process.exitCode = 1;
|
|
18967
|
+
}));
|
|
19203
18968
|
secrets.command("preflight").description("check required stage secret names for a deploy/train without reading values").requiredOption("--stage <dev|rc|main>", "stage to check").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--required <KEY...>", "required keys; bare keys are scoped under --stage").action(async (o) => {
|
|
19204
18969
|
if (!["dev", "rc", "main"].includes(o.stage)) {
|
|
19205
18970
|
return fail("secrets preflight: --stage must be dev, rc, or main");
|
|
@@ -19269,7 +19034,7 @@ function registerSecretsCommands(program3) {
|
|
|
19269
19034
|
{
|
|
19270
19035
|
...d,
|
|
19271
19036
|
decryptRailsCredentials,
|
|
19272
|
-
removeFile: (path2) => (0,
|
|
19037
|
+
removeFile: (path2) => (0, import_node_fs20.unlinkSync)((0, import_node_path18.resolve)(o.appDir ?? process.cwd(), path2))
|
|
19273
19038
|
},
|
|
19274
19039
|
{
|
|
19275
19040
|
repo: o.repo,
|
|
@@ -19355,10 +19120,10 @@ function registerEdgeCommands(program3) {
|
|
|
19355
19120
|
}
|
|
19356
19121
|
|
|
19357
19122
|
// src/doctor-run.ts
|
|
19358
|
-
var
|
|
19359
|
-
var
|
|
19123
|
+
var import_node_fs24 = require("node:fs");
|
|
19124
|
+
var import_node_child_process12 = require("node:child_process");
|
|
19360
19125
|
var import_promises7 = require("node:fs/promises");
|
|
19361
|
-
var
|
|
19126
|
+
var import_node_path21 = require("node:path");
|
|
19362
19127
|
var import_node_os5 = require("node:os");
|
|
19363
19128
|
|
|
19364
19129
|
// src/plugin-guard.ts
|
|
@@ -19379,10 +19144,10 @@ function buildGuardSessionStartLine(state, opts = {}) {
|
|
|
19379
19144
|
}
|
|
19380
19145
|
|
|
19381
19146
|
// src/cursor-plugin-seed.ts
|
|
19382
|
-
var
|
|
19383
|
-
var
|
|
19147
|
+
var import_node_child_process11 = require("node:child_process");
|
|
19148
|
+
var import_node_fs21 = require("node:fs");
|
|
19384
19149
|
var import_node_os4 = require("node:os");
|
|
19385
|
-
var
|
|
19150
|
+
var import_node_path19 = require("node:path");
|
|
19386
19151
|
var import_node_util7 = require("node:util");
|
|
19387
19152
|
function isSemverVersion(v) {
|
|
19388
19153
|
return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
|
|
@@ -19390,7 +19155,7 @@ function isSemverVersion(v) {
|
|
|
19390
19155
|
var MMI_HUB_REPO = "mutmutco/MMI-Hub";
|
|
19391
19156
|
var CURSOR_THIRD_PARTY_STATE_KEY = "cursor/thirdPartyExtensibilityEnabled";
|
|
19392
19157
|
var PLUGIN_JSON_REL = ".cursor-plugin/plugin.json";
|
|
19393
|
-
var execFileBuffer = (0, import_node_util7.promisify)(
|
|
19158
|
+
var execFileBuffer = (0, import_node_util7.promisify)(import_node_child_process11.execFile);
|
|
19394
19159
|
function gitFetchReleaseTagArgs(hubCheckout, tag) {
|
|
19395
19160
|
return ["-C", hubCheckout, "fetch", "origin", "tag", tag, "--quiet"];
|
|
19396
19161
|
}
|
|
@@ -19399,17 +19164,17 @@ function ghReleaseTarballApiArgs(tag) {
|
|
|
19399
19164
|
}
|
|
19400
19165
|
function cursorUserGlobalStatePath() {
|
|
19401
19166
|
if (process.platform === "win32") {
|
|
19402
|
-
const base = process.env.APPDATA || (0,
|
|
19403
|
-
return (0,
|
|
19167
|
+
const base = process.env.APPDATA || (0, import_node_path19.join)((0, import_node_os4.homedir)(), "AppData", "Roaming");
|
|
19168
|
+
return (0, import_node_path19.join)(base, "Cursor", "User", "globalStorage", "state.vscdb");
|
|
19404
19169
|
}
|
|
19405
19170
|
if (process.platform === "darwin") {
|
|
19406
|
-
return (0,
|
|
19171
|
+
return (0, import_node_path19.join)((0, import_node_os4.homedir)(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
19407
19172
|
}
|
|
19408
|
-
return (0,
|
|
19173
|
+
return (0, import_node_path19.join)((0, import_node_os4.homedir)(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
19409
19174
|
}
|
|
19410
19175
|
async function readCursorThirdPartyExtensibilityEnabled(execFileP5) {
|
|
19411
19176
|
const dbPath = cursorUserGlobalStatePath();
|
|
19412
|
-
if (!(0,
|
|
19177
|
+
if (!(0, import_node_fs21.existsSync)(dbPath)) return void 0;
|
|
19413
19178
|
try {
|
|
19414
19179
|
const { stdout } = await execFileP5("sqlite3", [dbPath, `SELECT value FROM ItemTable WHERE key = '${CURSOR_THIRD_PARTY_STATE_KEY}';`], {
|
|
19415
19180
|
timeout: 5e3
|
|
@@ -19423,57 +19188,57 @@ async function readCursorThirdPartyExtensibilityEnabled(execFileP5) {
|
|
|
19423
19188
|
}
|
|
19424
19189
|
}
|
|
19425
19190
|
function syncDirContents(src, dest) {
|
|
19426
|
-
(0,
|
|
19427
|
-
for (const name of (0,
|
|
19428
|
-
(0,
|
|
19191
|
+
(0, import_node_fs21.mkdirSync)(dest, { recursive: true });
|
|
19192
|
+
for (const name of (0, import_node_fs21.readdirSync)(dest)) {
|
|
19193
|
+
(0, import_node_fs21.rmSync)((0, import_node_path19.join)(dest, name), { recursive: true, force: true });
|
|
19429
19194
|
}
|
|
19430
|
-
(0,
|
|
19195
|
+
(0, import_node_fs21.cpSync)(src, dest, { recursive: true });
|
|
19431
19196
|
}
|
|
19432
19197
|
function releaseTag(releasedVersion) {
|
|
19433
19198
|
return releasedVersion.startsWith("v") ? releasedVersion : `v${releasedVersion}`;
|
|
19434
19199
|
}
|
|
19435
19200
|
async function extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5) {
|
|
19436
|
-
const tarFile = (0,
|
|
19201
|
+
const tarFile = (0, import_node_path19.join)(tmpRoot, "archive.tar");
|
|
19437
19202
|
try {
|
|
19438
19203
|
await execFileP5("git", gitFetchReleaseTagArgs(hubCheckout, tag), { timeout: 6e4 });
|
|
19439
19204
|
await execFileP5("git", ["-C", hubCheckout, "archive", "--format=tar", `--output=${tarFile}`, tag, "plugins/mmi"], {
|
|
19440
19205
|
timeout: 6e4
|
|
19441
19206
|
});
|
|
19442
19207
|
await execFileP5("tar", ["-xf", tarFile, "-C", tmpRoot], { timeout: 6e4 });
|
|
19443
|
-
const pluginMmi = (0,
|
|
19444
|
-
return (0,
|
|
19208
|
+
const pluginMmi = (0, import_node_path19.join)(tmpRoot, "plugins", "mmi");
|
|
19209
|
+
return (0, import_node_fs21.existsSync)((0, import_node_path19.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
|
|
19445
19210
|
} catch {
|
|
19446
19211
|
return void 0;
|
|
19447
19212
|
}
|
|
19448
19213
|
}
|
|
19449
19214
|
async function downloadPluginMmiViaGh(tag, tmpRoot) {
|
|
19450
|
-
const tarPath = (0,
|
|
19215
|
+
const tarPath = (0, import_node_path19.join)(tmpRoot, "repo.tgz");
|
|
19451
19216
|
try {
|
|
19452
|
-
(0,
|
|
19217
|
+
(0, import_node_fs21.mkdirSync)(tmpRoot, { recursive: true });
|
|
19453
19218
|
const { stdout } = await execFileBuffer("gh", ghReleaseTarballApiArgs(tag), {
|
|
19454
19219
|
timeout: 12e4,
|
|
19455
19220
|
maxBuffer: 100 * 1024 * 1024,
|
|
19456
19221
|
encoding: "buffer",
|
|
19457
19222
|
windowsHide: true
|
|
19458
19223
|
});
|
|
19459
|
-
(0,
|
|
19224
|
+
(0, import_node_fs21.writeFileSync)(tarPath, stdout);
|
|
19460
19225
|
await execFileBuffer("tar", ["-xzf", tarPath, "-C", tmpRoot], { timeout: 12e4, windowsHide: true });
|
|
19461
|
-
const top = (0,
|
|
19226
|
+
const top = (0, import_node_fs21.readdirSync)(tmpRoot).find((entry) => entry !== "repo.tgz");
|
|
19462
19227
|
if (!top) return void 0;
|
|
19463
|
-
const pluginMmi = (0,
|
|
19464
|
-
return (0,
|
|
19228
|
+
const pluginMmi = (0, import_node_path19.join)(tmpRoot, top, "plugins", "mmi");
|
|
19229
|
+
return (0, import_node_fs21.existsSync)((0, import_node_path19.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
|
|
19465
19230
|
} catch {
|
|
19466
19231
|
return void 0;
|
|
19467
19232
|
}
|
|
19468
19233
|
}
|
|
19469
19234
|
async function resolvePluginMmiSource(releasedVersion, hubCheckout, tmpRoot, execFileP5) {
|
|
19470
|
-
(0,
|
|
19235
|
+
(0, import_node_fs21.mkdirSync)(tmpRoot, { recursive: true });
|
|
19471
19236
|
const tag = releaseTag(releasedVersion);
|
|
19472
19237
|
if (hubCheckout) {
|
|
19473
19238
|
const fromHub = await extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5);
|
|
19474
19239
|
if (fromHub) return fromHub;
|
|
19475
19240
|
}
|
|
19476
|
-
return downloadPluginMmiViaGh(tag, (0,
|
|
19241
|
+
return downloadPluginMmiViaGh(tag, (0, import_node_path19.join)(tmpRoot, "gh"));
|
|
19477
19242
|
}
|
|
19478
19243
|
function cursorPluginPinsNeedingSeed(pins, releasedVersion) {
|
|
19479
19244
|
if (!isSemverVersion(releasedVersion)) return pins.filter((pin) => !pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty);
|
|
@@ -19494,7 +19259,7 @@ async function applyCursorPluginCacheSeed(input) {
|
|
|
19494
19259
|
for (const pin of pinsToSeed) {
|
|
19495
19260
|
syncDirContents(source, pin.path);
|
|
19496
19261
|
}
|
|
19497
|
-
(0,
|
|
19262
|
+
(0, import_node_fs21.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
19498
19263
|
return true;
|
|
19499
19264
|
}
|
|
19500
19265
|
|
|
@@ -20343,7 +20108,7 @@ function cursorPluginInstallFix(input) {
|
|
|
20343
20108
|
const cacheDir = joinCachePath(input.cacheRoot, pin);
|
|
20344
20109
|
const autoSeed = "run `mmi-cli doctor --apply` to seed plugins/mmi from the latest release into the active pin";
|
|
20345
20110
|
const localSeed = input.hubCheckout ? `temporary fallback: copy ${joinCachePath(input.hubCheckout, "plugins", "mmi")} to ${cacheDir}, then restart Cursor` : `temporary fallback: copy plugins/mmi from a local MMI-Hub checkout to ${cacheDir}, then restart Cursor`;
|
|
20346
|
-
return `Cursor plugin cache at ${cacheDir} is empty or missing ${CURSOR_PLUGIN_JSON_REL}, ${CURSOR_HOOKS_JSON_REL}, or the
|
|
20111
|
+
return `Cursor plugin cache at ${cacheDir} is empty or missing ${CURSOR_PLUGIN_JSON_REL}, ${CURSOR_HOOKS_JSON_REL}, or the cursor-hook dispatcher script \u2014 ${autoSeed}; ${marketplaceRefresh}; ${authSteps}; ${localSeed}; ${logHint}; ${guide}`;
|
|
20347
20112
|
}
|
|
20348
20113
|
function buildCursorPluginInstallCheck(input) {
|
|
20349
20114
|
const base = {
|
|
@@ -20365,7 +20130,7 @@ function buildCursorPluginInstallCheck(input) {
|
|
|
20365
20130
|
};
|
|
20366
20131
|
}
|
|
20367
20132
|
for (const pin of input.pins) {
|
|
20368
|
-
if (!pin.hasPluginJson || !pin.hasHooksJson || pin.
|
|
20133
|
+
if (!pin.hasPluginJson || !pin.hasHooksJson || pin.hasCursorHookScript === false || pin.isEmpty) {
|
|
20369
20134
|
return {
|
|
20370
20135
|
...base,
|
|
20371
20136
|
ok: false,
|
|
@@ -20647,9 +20412,9 @@ function buildPluginResolvabilityCheck(input) {
|
|
|
20647
20412
|
}
|
|
20648
20413
|
|
|
20649
20414
|
// src/cli-doctor-shared.ts
|
|
20415
|
+
var import_node_fs22 = require("node:fs");
|
|
20416
|
+
var import_node_path20 = require("node:path");
|
|
20650
20417
|
var import_node_fs23 = require("node:fs");
|
|
20651
|
-
var import_node_path21 = require("node:path");
|
|
20652
|
-
var import_node_fs24 = require("node:fs");
|
|
20653
20418
|
var GC_GH_TIMEOUT_MS = 2e4;
|
|
20654
20419
|
async function awsCallerArn() {
|
|
20655
20420
|
try {
|
|
@@ -20695,7 +20460,7 @@ async function localBranchHeads() {
|
|
|
20695
20460
|
}
|
|
20696
20461
|
async function currentRepoWorktreeGitRoot(repoRoot) {
|
|
20697
20462
|
const gitCommonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
20698
|
-
return gitCommonDir ? (0,
|
|
20463
|
+
return gitCommonDir ? (0, import_node_path20.resolve)(repoRoot, gitCommonDir, "worktrees") : "";
|
|
20699
20464
|
}
|
|
20700
20465
|
async function worktreeBranches() {
|
|
20701
20466
|
const { stdout } = await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS });
|
|
@@ -20715,18 +20480,18 @@ function resolveGitdirForWorktreeFile(worktreePath, content) {
|
|
|
20715
20480
|
const match = /^gitdir:\s*(.+)\s*$/im.exec(content);
|
|
20716
20481
|
if (!match?.[1]) return void 0;
|
|
20717
20482
|
const raw = match[1].trim();
|
|
20718
|
-
return (0,
|
|
20483
|
+
return (0, import_node_path20.isAbsolute)(raw) ? raw : (0, import_node_path20.resolve)(worktreePath, raw);
|
|
20719
20484
|
}
|
|
20720
20485
|
function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
20721
20486
|
if (!worktreeGitRoot) return false;
|
|
20722
20487
|
try {
|
|
20723
|
-
const entries = (0,
|
|
20488
|
+
const entries = (0, import_node_fs23.readdirSync)(worktreeGitRoot, { withFileTypes: true });
|
|
20724
20489
|
for (const ent of entries) {
|
|
20725
20490
|
if (!ent.isDirectory()) continue;
|
|
20726
20491
|
try {
|
|
20727
|
-
const gitdirPath = (0,
|
|
20728
|
-
const resolvedGitdir = (0,
|
|
20729
|
-
if (sameWorktreeMetadataPath((0,
|
|
20492
|
+
const gitdirPath = (0, import_node_fs22.readFileSync)((0, import_node_path20.join)(worktreeGitRoot, ent.name, "gitdir"), "utf8").trim();
|
|
20493
|
+
const resolvedGitdir = (0, import_node_path20.isAbsolute)(gitdirPath) ? gitdirPath : (0, import_node_path20.resolve)(worktreeGitRoot, ent.name, gitdirPath);
|
|
20494
|
+
if (sameWorktreeMetadataPath((0, import_node_path20.dirname)(resolvedGitdir), worktreePath)) return true;
|
|
20730
20495
|
} catch {
|
|
20731
20496
|
}
|
|
20732
20497
|
}
|
|
@@ -20736,7 +20501,7 @@ function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
|
20736
20501
|
}
|
|
20737
20502
|
function pathExistsKnown(path2) {
|
|
20738
20503
|
try {
|
|
20739
|
-
(0,
|
|
20504
|
+
(0, import_node_fs23.statSync)(path2);
|
|
20740
20505
|
return true;
|
|
20741
20506
|
} catch (e) {
|
|
20742
20507
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
@@ -20745,10 +20510,10 @@ function pathExistsKnown(path2) {
|
|
|
20745
20510
|
}
|
|
20746
20511
|
}
|
|
20747
20512
|
function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
20748
|
-
const gitPath = (0,
|
|
20513
|
+
const gitPath = (0, import_node_path20.join)(path2, ".git");
|
|
20749
20514
|
let st;
|
|
20750
20515
|
try {
|
|
20751
|
-
st = (0,
|
|
20516
|
+
st = (0, import_node_fs23.lstatSync)(gitPath);
|
|
20752
20517
|
} catch (e) {
|
|
20753
20518
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
20754
20519
|
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
@@ -20765,7 +20530,7 @@ function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
|
20765
20530
|
if (st.isDirectory()) return { path: path2, gitType: "dir" };
|
|
20766
20531
|
if (!st.isFile()) return { path: path2, gitType: "other" };
|
|
20767
20532
|
try {
|
|
20768
|
-
const gitFileContent = (0,
|
|
20533
|
+
const gitFileContent = (0, import_node_fs22.readFileSync)(gitPath, "utf8");
|
|
20769
20534
|
const gitdir = resolveGitdirForWorktreeFile(path2, gitFileContent);
|
|
20770
20535
|
const gitDirExists = gitdir ? pathExistsKnown(gitdir) : false;
|
|
20771
20536
|
return {
|
|
@@ -20782,7 +20547,7 @@ function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
|
20782
20547
|
}
|
|
20783
20548
|
function inspectDeadWorktreeDirContent(path2) {
|
|
20784
20549
|
try {
|
|
20785
|
-
return { entries: (0,
|
|
20550
|
+
return { entries: (0, import_node_fs23.readdirSync)(path2) };
|
|
20786
20551
|
} catch (e) {
|
|
20787
20552
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
20788
20553
|
return { error: code ? `unable to inspect directory contents (${code})` : "unable to inspect directory contents" };
|
|
@@ -20801,8 +20566,8 @@ async function siblingWorktreeDirs() {
|
|
|
20801
20566
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot);
|
|
20802
20567
|
const siblingRoot = siblingMmiWorktreesRoot(repoRoot);
|
|
20803
20568
|
try {
|
|
20804
|
-
const entries = (0,
|
|
20805
|
-
return entries.filter((ent) => ent.isDirectory()).map((ent) => inspectSiblingWorktreeDir((0,
|
|
20569
|
+
const entries = (0, import_node_fs23.readdirSync)(siblingRoot, { withFileTypes: true });
|
|
20570
|
+
return entries.filter((ent) => ent.isDirectory()).map((ent) => inspectSiblingWorktreeDir((0, import_node_path20.join)(siblingRoot, ent.name), worktreeGitRoot)).filter((entry) => Boolean(entry));
|
|
20806
20571
|
} catch {
|
|
20807
20572
|
return [];
|
|
20808
20573
|
}
|
|
@@ -20852,7 +20617,7 @@ async function fetchHubVersionInfo(baseUrl) {
|
|
|
20852
20617
|
}
|
|
20853
20618
|
function readRepoVersion() {
|
|
20854
20619
|
try {
|
|
20855
|
-
return JSON.parse((0,
|
|
20620
|
+
return JSON.parse((0, import_node_fs24.readFileSync)((0, import_node_path21.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
20856
20621
|
} catch {
|
|
20857
20622
|
return void 0;
|
|
20858
20623
|
}
|
|
@@ -20912,7 +20677,7 @@ function reexecMmiCli(args) {
|
|
|
20912
20677
|
}
|
|
20913
20678
|
};
|
|
20914
20679
|
const env = { ...process.env, [DOCTOR_POST_SELF_UPDATE_ENV]: "1" };
|
|
20915
|
-
const child = isWin ? (0,
|
|
20680
|
+
const child = isWin ? (0, import_node_child_process12.spawn)("cmd.exe", ["/c", "mmi-cli", ...args], { stdio: "inherit", env }) : (0, import_node_child_process12.spawn)("mmi-cli", args, { stdio: "inherit", env });
|
|
20916
20681
|
child.on("error", () => done(-1));
|
|
20917
20682
|
child.on("exit", (code) => done(code ?? 0));
|
|
20918
20683
|
});
|
|
@@ -20969,11 +20734,11 @@ async function applyPluginHeal(token, surface, log, opts) {
|
|
|
20969
20734
|
}
|
|
20970
20735
|
var installedPluginsPath = (surface = detectSurface(process.env)) => {
|
|
20971
20736
|
const homeDir = surface === "codex" ? ".codex" : ".claude";
|
|
20972
|
-
return (0,
|
|
20737
|
+
return (0, import_node_path21.join)((0, import_node_os5.homedir)(), homeDir, "plugins", "installed_plugins.json");
|
|
20973
20738
|
};
|
|
20974
20739
|
function readInstalledPlugins(surface = detectSurface(process.env)) {
|
|
20975
20740
|
try {
|
|
20976
|
-
return JSON.parse((0,
|
|
20741
|
+
return JSON.parse((0, import_node_fs24.readFileSync)(installedPluginsPath(surface), "utf8"));
|
|
20977
20742
|
} catch {
|
|
20978
20743
|
return null;
|
|
20979
20744
|
}
|
|
@@ -20984,15 +20749,15 @@ function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRep
|
|
|
20984
20749
|
return {
|
|
20985
20750
|
isOrgRepo,
|
|
20986
20751
|
installRecordPresent: hasUserInstallRecord(installed, MMI_PLUGIN_ID) || hasProjectInstallRecord(installed, MMI_PLUGIN_ID, process.cwd()),
|
|
20987
|
-
marketplaceClonePresent: (0,
|
|
20988
|
-
pluginCachePresent: (0,
|
|
20752
|
+
marketplaceClonePresent: (0, import_node_fs24.existsSync)((0, import_node_path21.join)((0, import_node_os5.homedir)(), homeDir, "plugins", "marketplaces", "mutmutco")),
|
|
20753
|
+
pluginCachePresent: (0, import_node_fs24.existsSync)((0, import_node_path21.join)((0, import_node_os5.homedir)(), homeDir, "plugins", "cache", "mutmutco", "mmi"))
|
|
20989
20754
|
};
|
|
20990
20755
|
}
|
|
20991
20756
|
function installedPluginSources() {
|
|
20992
20757
|
return ["claude", "codex"].map((surface) => {
|
|
20993
|
-
const recordPath = (0,
|
|
20758
|
+
const recordPath = (0, import_node_path21.join)((0, import_node_os5.homedir)(), `.${surface}`, "plugins", "installed_plugins.json");
|
|
20994
20759
|
try {
|
|
20995
|
-
return { surface, installed: JSON.parse((0,
|
|
20760
|
+
return { surface, installed: JSON.parse((0, import_node_fs24.readFileSync)(recordPath, "utf8")), recordPath };
|
|
20996
20761
|
} catch {
|
|
20997
20762
|
return { surface, installed: null, recordPath };
|
|
20998
20763
|
}
|
|
@@ -21000,7 +20765,7 @@ function installedPluginSources() {
|
|
|
21000
20765
|
}
|
|
21001
20766
|
function readClaudeSettings() {
|
|
21002
20767
|
try {
|
|
21003
|
-
return JSON.parse((0,
|
|
20768
|
+
return JSON.parse((0, import_node_fs24.readFileSync)((0, import_node_path21.join)(process.cwd(), ".claude", "settings.json"), "utf8"));
|
|
21004
20769
|
} catch {
|
|
21005
20770
|
return null;
|
|
21006
20771
|
}
|
|
@@ -21022,7 +20787,7 @@ function writeProjectInstallRecord(record) {
|
|
|
21022
20787
|
const list = file.plugins[MMI_PLUGIN_ID] ?? [];
|
|
21023
20788
|
list.push(record);
|
|
21024
20789
|
file.plugins[MMI_PLUGIN_ID] = list;
|
|
21025
|
-
(0,
|
|
20790
|
+
(0, import_node_fs24.writeFileSync)(installedPluginsPath(), `${JSON.stringify(file, null, 2)}
|
|
21026
20791
|
`, "utf8");
|
|
21027
20792
|
return true;
|
|
21028
20793
|
} catch {
|
|
@@ -21035,9 +20800,9 @@ function backupAndWriteInstalledPlugins(records, pluginId) {
|
|
|
21035
20800
|
if (!file) return false;
|
|
21036
20801
|
if (!file.plugins) file.plugins = {};
|
|
21037
20802
|
const path2 = installedPluginsPath();
|
|
21038
|
-
(0,
|
|
20803
|
+
(0, import_node_fs24.copyFileSync)(path2, `${path2}.bak`);
|
|
21039
20804
|
file.plugins[pluginId] = records;
|
|
21040
|
-
(0,
|
|
20805
|
+
(0, import_node_fs24.writeFileSync)(path2, `${JSON.stringify(file, null, 2)}
|
|
21041
20806
|
`, "utf8");
|
|
21042
20807
|
return true;
|
|
21043
20808
|
} catch {
|
|
@@ -21045,22 +20810,22 @@ function backupAndWriteInstalledPlugins(records, pluginId) {
|
|
|
21045
20810
|
}
|
|
21046
20811
|
}
|
|
21047
20812
|
function opencodeConfigDir() {
|
|
21048
|
-
return (0,
|
|
20813
|
+
return (0, import_node_path21.join)((0, import_node_os5.homedir)(), ".config", "opencode");
|
|
21049
20814
|
}
|
|
21050
20815
|
function opencodeConfigPath() {
|
|
21051
|
-
return (0,
|
|
20816
|
+
return (0, import_node_path21.join)(opencodeConfigDir(), "opencode.jsonc");
|
|
21052
20817
|
}
|
|
21053
20818
|
function opencodeCommandsDir() {
|
|
21054
|
-
return (0,
|
|
20819
|
+
return (0, import_node_path21.join)(opencodeConfigDir(), "commands");
|
|
21055
20820
|
}
|
|
21056
20821
|
function opencodeSkillsPath() {
|
|
21057
|
-
return (0,
|
|
20822
|
+
return (0, import_node_path21.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "skills");
|
|
21058
20823
|
}
|
|
21059
20824
|
function opencodeConfigSnapshot() {
|
|
21060
20825
|
const path2 = opencodeConfigPath();
|
|
21061
|
-
if (!(0,
|
|
20826
|
+
if (!(0, import_node_fs24.existsSync)(path2)) return { path: path2, hasConfig: false, hasPluginField: false, parseOk: true };
|
|
21062
20827
|
try {
|
|
21063
|
-
const raw = (0,
|
|
20828
|
+
const raw = (0, import_node_fs24.readFileSync)(path2, "utf8");
|
|
21064
20829
|
const parsed = JSON.parse(stripJsonc(raw));
|
|
21065
20830
|
const hasPluginField = Object.prototype.hasOwnProperty.call(parsed, "plugin");
|
|
21066
20831
|
const skillsPaths = Array.isArray(parsed.skills?.paths) ? parsed.skills.paths.filter((p) => typeof p === "string") : void 0;
|
|
@@ -21083,9 +20848,9 @@ function writeOpencodeConfigPlugin(snapshot) {
|
|
|
21083
20848
|
const plan2 = planOpencodeConfigWrite(snapshot.hasConfig ? snapshot.raw : void 0);
|
|
21084
20849
|
if (plan2.action === "already") return true;
|
|
21085
20850
|
if (!plan2.text || plan2.action === "unsafe") return false;
|
|
21086
|
-
(0,
|
|
21087
|
-
if (snapshot.hasConfig) (0,
|
|
21088
|
-
(0,
|
|
20851
|
+
(0, import_node_fs24.mkdirSync)((0, import_node_path21.dirname)(path2), { recursive: true });
|
|
20852
|
+
if (snapshot.hasConfig) (0, import_node_fs24.copyFileSync)(path2, `${path2}.bak`);
|
|
20853
|
+
(0, import_node_fs24.writeFileSync)(path2, plan2.text, "utf8");
|
|
21089
20854
|
return true;
|
|
21090
20855
|
} catch {
|
|
21091
20856
|
return false;
|
|
@@ -21100,9 +20865,9 @@ function writeOpencodeSkillsPath(snapshot, skillsPath) {
|
|
|
21100
20865
|
const normalized = skillsPath.replace(/\\/g, "/");
|
|
21101
20866
|
if (!paths.some((p) => p.replace(/\\/g, "/") === normalized)) paths.push(skillsPath.replace(/\\/g, "/"));
|
|
21102
20867
|
parsed.skills = { ...skills, paths };
|
|
21103
|
-
(0,
|
|
21104
|
-
if (snapshot.hasConfig && (0,
|
|
21105
|
-
(0,
|
|
20868
|
+
(0, import_node_fs24.mkdirSync)((0, import_node_path21.dirname)(snapshot.path), { recursive: true });
|
|
20869
|
+
if (snapshot.hasConfig && (0, import_node_fs24.existsSync)(snapshot.path)) (0, import_node_fs24.copyFileSync)(snapshot.path, `${snapshot.path}.bak`);
|
|
20870
|
+
(0, import_node_fs24.writeFileSync)(snapshot.path, `${JSON.stringify(parsed, null, 2)}
|
|
21106
20871
|
`, "utf8");
|
|
21107
20872
|
return true;
|
|
21108
20873
|
} catch {
|
|
@@ -21111,7 +20876,7 @@ function writeOpencodeSkillsPath(snapshot, skillsPath) {
|
|
|
21111
20876
|
}
|
|
21112
20877
|
function opencodeExistingCommands() {
|
|
21113
20878
|
try {
|
|
21114
|
-
return (0,
|
|
20879
|
+
return (0, import_node_fs24.readdirSync)(opencodeCommandsDir(), { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name.slice(0, -3).toLowerCase());
|
|
21115
20880
|
} catch {
|
|
21116
20881
|
return [];
|
|
21117
20882
|
}
|
|
@@ -21119,9 +20884,9 @@ function opencodeExistingCommands() {
|
|
|
21119
20884
|
function writeOpencodeCommandFiles() {
|
|
21120
20885
|
try {
|
|
21121
20886
|
const dir = opencodeCommandsDir();
|
|
21122
|
-
(0,
|
|
20887
|
+
(0, import_node_fs24.mkdirSync)(dir, { recursive: true });
|
|
21123
20888
|
for (const command of OPENCODE_WORKFLOW_COMMANDS) {
|
|
21124
|
-
(0,
|
|
20889
|
+
(0, import_node_fs24.writeFileSync)((0, import_node_path21.join)(dir, `${command}.md`), opencodeCommandMarkdown(command), "utf8");
|
|
21125
20890
|
}
|
|
21126
20891
|
return true;
|
|
21127
20892
|
} catch {
|
|
@@ -21130,12 +20895,12 @@ function writeOpencodeCommandFiles() {
|
|
|
21130
20895
|
}
|
|
21131
20896
|
function readOpencodeAdapterDiskVersion() {
|
|
21132
20897
|
const candidates = [
|
|
21133
|
-
(0,
|
|
21134
|
-
(0,
|
|
20898
|
+
(0, import_node_path21.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "package.json"),
|
|
20899
|
+
(0, import_node_path21.join)((0, import_node_os5.homedir)(), ".cache", "opencode", "node_modules", "@mutmutco", "opencode-mmi", "package.json")
|
|
21135
20900
|
];
|
|
21136
20901
|
for (const path2 of candidates) {
|
|
21137
20902
|
try {
|
|
21138
|
-
const parsed = JSON.parse((0,
|
|
20903
|
+
const parsed = JSON.parse((0, import_node_fs24.readFileSync)(path2, "utf8"));
|
|
21139
20904
|
if (typeof parsed.version === "string" && parsed.version.trim()) return parsed.version.trim();
|
|
21140
20905
|
} catch {
|
|
21141
20906
|
continue;
|
|
@@ -21151,7 +20916,7 @@ async function forceInstallOpencodeMmiPlugins(snapshot, log) {
|
|
|
21151
20916
|
try {
|
|
21152
20917
|
const specs = opencodeMmiPluginSpecs(snapshot);
|
|
21153
20918
|
log(` \u21BB force-refreshing OpenCode MMI npm plugin(s): ${specs.join(", ")}\u2026`);
|
|
21154
|
-
(0,
|
|
20919
|
+
(0, import_node_fs24.mkdirSync)(opencodeConfigDir(), { recursive: true });
|
|
21155
20920
|
await runHostBin("npm", ["install", "--prefix", opencodeConfigDir(), "--force", ...specs], { timeout: NPM_UPDATE_TIMEOUT_MS });
|
|
21156
20921
|
return true;
|
|
21157
20922
|
} catch {
|
|
@@ -21166,30 +20931,30 @@ function opencodePluginVersionsForReport() {
|
|
|
21166
20931
|
}
|
|
21167
20932
|
function opencodeDesktopLogsRoot() {
|
|
21168
20933
|
if (process.platform === "win32") {
|
|
21169
|
-
const base = process.env.APPDATA || (0,
|
|
21170
|
-
return (0,
|
|
20934
|
+
const base = process.env.APPDATA || (0, import_node_path21.join)((0, import_node_os5.homedir)(), "AppData", "Roaming");
|
|
20935
|
+
return (0, import_node_path21.join)(base, "ai.opencode.desktop", "logs");
|
|
21171
20936
|
}
|
|
21172
20937
|
if (process.platform === "darwin") {
|
|
21173
|
-
return (0,
|
|
20938
|
+
return (0, import_node_path21.join)((0, import_node_os5.homedir)(), "Library", "Application Support", "ai.opencode.desktop", "logs");
|
|
21174
20939
|
}
|
|
21175
|
-
return (0,
|
|
20940
|
+
return (0, import_node_path21.join)((0, import_node_os5.homedir)(), ".config", "ai.opencode.desktop", "logs");
|
|
21176
20941
|
}
|
|
21177
20942
|
function opencodeDesktopBootstrapSnapshot() {
|
|
21178
20943
|
const root = opencodeDesktopLogsRoot();
|
|
21179
20944
|
try {
|
|
21180
|
-
const sessionDirs = (0,
|
|
20945
|
+
const sessionDirs = (0, import_node_fs24.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => (0, import_node_path21.join)(root, entry.name)).sort((a, b) => (0, import_node_fs24.statSync)(b).mtimeMs - (0, import_node_fs24.statSync)(a).mtimeMs);
|
|
21181
20946
|
const newest = sessionDirs[0];
|
|
21182
20947
|
if (!newest) return [];
|
|
21183
|
-
const logPath = (0,
|
|
21184
|
-
const text = (0,
|
|
21185
|
-
return opencodeAgentDirectoriesFromLog(text).filter((directory) => !(0,
|
|
20948
|
+
const logPath = (0, import_node_path21.join)(newest, "renderer.log");
|
|
20949
|
+
const text = (0, import_node_fs24.readFileSync)(logPath, "utf8");
|
|
20950
|
+
return opencodeAgentDirectoriesFromLog(text).filter((directory) => !(0, import_node_fs24.existsSync)(directory)).map((directory) => ({ directory, logPath }));
|
|
21186
20951
|
} catch {
|
|
21187
20952
|
return [];
|
|
21188
20953
|
}
|
|
21189
20954
|
}
|
|
21190
20955
|
function opencodeLegacyConfigSnapshot() {
|
|
21191
|
-
const legacyPath = (0,
|
|
21192
|
-
if (!(0,
|
|
20956
|
+
const legacyPath = (0, import_node_path21.join)((0, import_node_os5.homedir)(), ".opencode", "opencode.json");
|
|
20957
|
+
if (!(0, import_node_fs24.existsSync)(legacyPath)) return {};
|
|
21193
20958
|
const content = readTextFile(legacyPath);
|
|
21194
20959
|
if (content == null) return {};
|
|
21195
20960
|
const plugins = parseOpencodeLegacyConfigPlugins(content);
|
|
@@ -21201,46 +20966,45 @@ function opencodeLegacyConfigSnapshot() {
|
|
|
21201
20966
|
function quarantineOpencodeLegacyConfig(legacyPath) {
|
|
21202
20967
|
try {
|
|
21203
20968
|
const backupPath = `${legacyPath}.bak`;
|
|
21204
|
-
if ((0,
|
|
21205
|
-
(0,
|
|
20969
|
+
if ((0, import_node_fs24.existsSync)(backupPath)) return false;
|
|
20970
|
+
(0, import_node_fs24.renameSync)(legacyPath, backupPath);
|
|
21206
20971
|
return true;
|
|
21207
20972
|
} catch {
|
|
21208
20973
|
return false;
|
|
21209
20974
|
}
|
|
21210
20975
|
}
|
|
21211
20976
|
function cursorPluginCacheRoot() {
|
|
21212
|
-
return (0,
|
|
20977
|
+
return (0, import_node_path21.join)((0, import_node_os5.homedir)(), ".cursor", "plugins", "cache", "mutmutco", "mmi");
|
|
21213
20978
|
}
|
|
21214
20979
|
function cursorPluginCachePinSnapshots() {
|
|
21215
20980
|
const root = cursorPluginCacheRoot();
|
|
21216
20981
|
try {
|
|
21217
|
-
return (0,
|
|
21218
|
-
const path2 = (0,
|
|
21219
|
-
const pluginJson = (0,
|
|
21220
|
-
const hooksJson = (0,
|
|
21221
|
-
const cliBundle = (0,
|
|
21222
|
-
const
|
|
21223
|
-
const throttleCore = (0, import_node_path22.join)(path2, "scripts", "throttle-core.mjs");
|
|
20982
|
+
return (0, import_node_fs24.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => {
|
|
20983
|
+
const path2 = (0, import_node_path21.join)(root, entry.name);
|
|
20984
|
+
const pluginJson = (0, import_node_path21.join)(path2, ".cursor-plugin", "plugin.json");
|
|
20985
|
+
const hooksJson = (0, import_node_path21.join)(path2, "hooks", "hooks.json");
|
|
20986
|
+
const cliBundle = (0, import_node_path21.join)(path2, "cli", "dist", "index.cjs");
|
|
20987
|
+
const cursorHook = (0, import_node_path21.join)(path2, "scripts", "cursor-hook.mjs");
|
|
21224
20988
|
let version;
|
|
21225
20989
|
try {
|
|
21226
|
-
const raw = JSON.parse((0,
|
|
20990
|
+
const raw = JSON.parse((0, import_node_fs24.readFileSync)(pluginJson, "utf8"));
|
|
21227
20991
|
version = typeof raw.version === "string" ? raw.version : void 0;
|
|
21228
20992
|
} catch {
|
|
21229
20993
|
version = void 0;
|
|
21230
20994
|
}
|
|
21231
20995
|
let isEmpty = true;
|
|
21232
20996
|
try {
|
|
21233
|
-
isEmpty = (0,
|
|
20997
|
+
isEmpty = (0, import_node_fs24.readdirSync)(path2).length === 0;
|
|
21234
20998
|
} catch {
|
|
21235
20999
|
isEmpty = true;
|
|
21236
21000
|
}
|
|
21237
21001
|
return {
|
|
21238
21002
|
name: entry.name,
|
|
21239
21003
|
path: path2,
|
|
21240
|
-
hasPluginJson: (0,
|
|
21241
|
-
hasHooksJson: (0,
|
|
21242
|
-
hasCliBundle: (0,
|
|
21243
|
-
|
|
21004
|
+
hasPluginJson: (0, import_node_fs24.existsSync)(pluginJson),
|
|
21005
|
+
hasHooksJson: (0, import_node_fs24.existsSync)(hooksJson),
|
|
21006
|
+
hasCliBundle: (0, import_node_fs24.existsSync)(cliBundle),
|
|
21007
|
+
hasCursorHookScript: (0, import_node_fs24.existsSync)(cursorHook),
|
|
21244
21008
|
isEmpty,
|
|
21245
21009
|
version
|
|
21246
21010
|
};
|
|
@@ -21250,19 +21014,19 @@ function cursorPluginCachePinSnapshots() {
|
|
|
21250
21014
|
}
|
|
21251
21015
|
}
|
|
21252
21016
|
function hubCheckoutForCursorSeed() {
|
|
21253
|
-
const manifest = (0,
|
|
21254
|
-
return (0,
|
|
21017
|
+
const manifest = (0, import_node_path21.join)(process.cwd(), "plugins", "mmi", ".cursor-plugin", "plugin.json");
|
|
21018
|
+
return (0, import_node_fs24.existsSync)(manifest) ? process.cwd() : void 0;
|
|
21255
21019
|
}
|
|
21256
21020
|
function mmiPluginCacheRootSnapshots() {
|
|
21257
21021
|
const roots = [
|
|
21258
|
-
{ surface: "claude", root: (0,
|
|
21259
|
-
{ surface: "codex", root: (0,
|
|
21022
|
+
{ surface: "claude", root: (0, import_node_path21.join)((0, import_node_os5.homedir)(), ".claude", "plugins", "cache", "mutmutco", "mmi") },
|
|
21023
|
+
{ surface: "codex", root: (0, import_node_path21.join)((0, import_node_os5.homedir)(), ".codex", "plugins", "cache", "mutmutco", "mmi") }
|
|
21260
21024
|
];
|
|
21261
21025
|
return roots.flatMap(({ surface, root }) => {
|
|
21262
21026
|
try {
|
|
21263
|
-
const entries = (0,
|
|
21027
|
+
const entries = (0, import_node_fs24.readdirSync)(root, { withFileTypes: true }).map((entry) => ({
|
|
21264
21028
|
name: entry.name,
|
|
21265
|
-
path: (0,
|
|
21029
|
+
path: (0, import_node_path21.join)(root, entry.name),
|
|
21266
21030
|
isDirectory: entry.isDirectory()
|
|
21267
21031
|
}));
|
|
21268
21032
|
return [{ surface, root, entries }];
|
|
@@ -21273,7 +21037,7 @@ function mmiPluginCacheRootSnapshots() {
|
|
|
21273
21037
|
}
|
|
21274
21038
|
function hasNestedMmiChild(versionDir) {
|
|
21275
21039
|
try {
|
|
21276
|
-
return (0,
|
|
21040
|
+
return (0, import_node_fs24.statSync)((0, import_node_path21.join)(versionDir, "mmi")).isDirectory();
|
|
21277
21041
|
} catch {
|
|
21278
21042
|
return false;
|
|
21279
21043
|
}
|
|
@@ -21284,10 +21048,10 @@ function nestedPluginTreeSnapshot() {
|
|
|
21284
21048
|
);
|
|
21285
21049
|
}
|
|
21286
21050
|
function uniqueQuarantineTarget(path2) {
|
|
21287
|
-
if (!(0,
|
|
21051
|
+
if (!(0, import_node_fs24.existsSync)(path2)) return path2;
|
|
21288
21052
|
for (let i = 1; i < 100; i += 1) {
|
|
21289
21053
|
const candidate = `${path2}-${i}`;
|
|
21290
|
-
if (!(0,
|
|
21054
|
+
if (!(0, import_node_fs24.existsSync)(candidate)) return candidate;
|
|
21291
21055
|
}
|
|
21292
21056
|
return `${path2}-${Date.now()}`;
|
|
21293
21057
|
}
|
|
@@ -21296,10 +21060,10 @@ function quarantinePluginCacheDirs(plan2) {
|
|
|
21296
21060
|
const failed = [];
|
|
21297
21061
|
for (const move of plan2) {
|
|
21298
21062
|
try {
|
|
21299
|
-
if (!(0,
|
|
21063
|
+
if (!(0, import_node_fs24.existsSync)(move.from)) continue;
|
|
21300
21064
|
const target = uniqueQuarantineTarget(move.to);
|
|
21301
|
-
(0,
|
|
21302
|
-
(0,
|
|
21065
|
+
(0, import_node_fs24.mkdirSync)((0, import_node_path21.dirname)(target), { recursive: true });
|
|
21066
|
+
(0, import_node_fs24.renameSync)(move.from, target);
|
|
21303
21067
|
moved += 1;
|
|
21304
21068
|
} catch {
|
|
21305
21069
|
failed.push(move);
|
|
@@ -21318,23 +21082,23 @@ async function robocopyMirrorEmpty(emptyDir, target) {
|
|
|
21318
21082
|
}
|
|
21319
21083
|
async function clearNestedPluginTreeDir(targetPath) {
|
|
21320
21084
|
try {
|
|
21321
|
-
if (!(0,
|
|
21085
|
+
if (!(0, import_node_fs24.existsSync)(targetPath)) return true;
|
|
21322
21086
|
if (isWin) {
|
|
21323
|
-
const emptyDir = (0,
|
|
21324
|
-
(0,
|
|
21087
|
+
const emptyDir = (0, import_node_path21.join)((0, import_node_os5.tmpdir)(), `mmi-empty-${Date.now()}`);
|
|
21088
|
+
(0, import_node_fs24.mkdirSync)(emptyDir, { recursive: true });
|
|
21325
21089
|
try {
|
|
21326
21090
|
await robocopyMirrorEmpty(emptyDir, targetPath);
|
|
21327
|
-
(0,
|
|
21091
|
+
(0, import_node_fs24.rmSync)(targetPath, { recursive: true, force: true });
|
|
21328
21092
|
} finally {
|
|
21329
21093
|
try {
|
|
21330
|
-
(0,
|
|
21094
|
+
(0, import_node_fs24.rmSync)(emptyDir, { recursive: true, force: true });
|
|
21331
21095
|
} catch {
|
|
21332
21096
|
}
|
|
21333
21097
|
}
|
|
21334
|
-
return !(0,
|
|
21098
|
+
return !(0, import_node_fs24.existsSync)(targetPath);
|
|
21335
21099
|
}
|
|
21336
|
-
(0,
|
|
21337
|
-
return !(0,
|
|
21100
|
+
(0, import_node_fs24.rmSync)(targetPath, { recursive: true, force: true });
|
|
21101
|
+
return !(0, import_node_fs24.existsSync)(targetPath);
|
|
21338
21102
|
} catch {
|
|
21339
21103
|
return false;
|
|
21340
21104
|
}
|
|
@@ -21347,11 +21111,11 @@ async function applyNestedPluginTreeCleanup(paths, log) {
|
|
|
21347
21111
|
}
|
|
21348
21112
|
return true;
|
|
21349
21113
|
}
|
|
21350
|
-
var gitignorePath = () => (0,
|
|
21114
|
+
var gitignorePath = () => (0, import_node_path21.join)(process.cwd(), ".gitignore");
|
|
21351
21115
|
function readTextFile(path2) {
|
|
21352
21116
|
try {
|
|
21353
|
-
if (!(0,
|
|
21354
|
-
return (0,
|
|
21117
|
+
if (!(0, import_node_fs24.existsSync)(path2)) return null;
|
|
21118
|
+
return (0, import_node_fs24.readFileSync)(path2, "utf8");
|
|
21355
21119
|
} catch {
|
|
21356
21120
|
return null;
|
|
21357
21121
|
}
|
|
@@ -21360,10 +21124,10 @@ function playwrightMcpConfigSnapshots() {
|
|
|
21360
21124
|
const cwd = process.cwd();
|
|
21361
21125
|
const home = (0, import_node_os5.homedir)();
|
|
21362
21126
|
const candidates = [
|
|
21363
|
-
(0,
|
|
21364
|
-
(0,
|
|
21365
|
-
(0,
|
|
21366
|
-
(0,
|
|
21127
|
+
(0, import_node_path21.join)(cwd, ".mcp.json"),
|
|
21128
|
+
(0, import_node_path21.join)(cwd, ".cursor", "mcp.json"),
|
|
21129
|
+
(0, import_node_path21.join)(home, ".cursor", "mcp.json"),
|
|
21130
|
+
(0, import_node_path21.join)(home, ".codex", "config.toml")
|
|
21367
21131
|
];
|
|
21368
21132
|
const out = [];
|
|
21369
21133
|
for (const path2 of candidates) {
|
|
@@ -21376,7 +21140,7 @@ function strayBrowserArtifactPaths() {
|
|
|
21376
21140
|
const cwd = process.cwd();
|
|
21377
21141
|
return STRAY_BROWSER_ARTIFACT_DIRS.filter((rel) => {
|
|
21378
21142
|
try {
|
|
21379
|
-
return (0,
|
|
21143
|
+
return (0, import_node_fs24.existsSync)((0, import_node_path21.join)(cwd, rel));
|
|
21380
21144
|
} catch {
|
|
21381
21145
|
return false;
|
|
21382
21146
|
}
|
|
@@ -21395,8 +21159,8 @@ function latestIso(values) {
|
|
|
21395
21159
|
return best;
|
|
21396
21160
|
}
|
|
21397
21161
|
function latestNorthstarContinuityAt() {
|
|
21398
|
-
const meta = parseMeta(readTextFile((0,
|
|
21399
|
-
const queue = parseQueue(readTextFile((0,
|
|
21162
|
+
const meta = parseMeta(readTextFile((0, import_node_path21.join)(process.cwd(), META_FILE)));
|
|
21163
|
+
const queue = parseQueue(readTextFile((0, import_node_path21.join)(process.cwd(), QUEUE_FILE)));
|
|
21400
21164
|
return latestIso([
|
|
21401
21165
|
...Object.values(meta).map((entry) => entry.syncedAt),
|
|
21402
21166
|
...queue.map((entry) => entry.queuedAt)
|
|
@@ -21410,14 +21174,14 @@ async function latestBranchWorkAt() {
|
|
|
21410
21174
|
}
|
|
21411
21175
|
function readGitignore() {
|
|
21412
21176
|
try {
|
|
21413
|
-
return (0,
|
|
21177
|
+
return (0, import_node_fs24.readFileSync)(gitignorePath(), "utf8");
|
|
21414
21178
|
} catch {
|
|
21415
21179
|
return null;
|
|
21416
21180
|
}
|
|
21417
21181
|
}
|
|
21418
21182
|
function writeGitignore(content) {
|
|
21419
21183
|
try {
|
|
21420
|
-
(0,
|
|
21184
|
+
(0, import_node_fs24.writeFileSync)(gitignorePath(), content, "utf8");
|
|
21421
21185
|
return true;
|
|
21422
21186
|
} catch {
|
|
21423
21187
|
return false;
|
|
@@ -21456,7 +21220,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
21456
21220
|
const semverPrefix = /^\d+\.\d+\.\d+/;
|
|
21457
21221
|
const isBehind = (installed2, released) => Boolean(installed2 && released && semverPrefix.test(installed2) && semverPrefix.test(released) && compareVersions(installed2, released) < 0);
|
|
21458
21222
|
const opencodeAdapterStale = isBehind(opencodeInstalledVersionForDoctor(), releasedVersion);
|
|
21459
|
-
const cursorCacheStale = (0,
|
|
21223
|
+
const cursorCacheStale = (0, import_node_fs24.existsSync)(cursorPluginCacheRoot()) && (cursorPluginCachePinSnapshots() ?? []).some((p) => isBehind(p.version, releasedVersion));
|
|
21460
21224
|
const healPlan = doctorHealPlan({
|
|
21461
21225
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
21462
21226
|
surface,
|
|
@@ -21491,7 +21255,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
21491
21255
|
let onPath = pathProbe;
|
|
21492
21256
|
if (!onPath) {
|
|
21493
21257
|
const root = process.env.CLAUDE_PLUGIN_ROOT;
|
|
21494
|
-
if (root && (0,
|
|
21258
|
+
if (root && (0, import_node_fs24.existsSync)(`${root}/bin/mmi-cli${isWin ? ".cmd" : ""}`)) onPath = true;
|
|
21495
21259
|
}
|
|
21496
21260
|
checks.push({ ok: onPath, label: "mmi-cli on PATH", fix: "auto-provisioned at session start \u2014 reopen the session, or install the MMI plugin" });
|
|
21497
21261
|
const reloadHint = reloadAction(surface);
|
|
@@ -21860,7 +21624,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
21860
21624
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
21861
21625
|
surface,
|
|
21862
21626
|
cacheRoot: cursorCacheRoot,
|
|
21863
|
-
cacheRootExists: (0,
|
|
21627
|
+
cacheRootExists: (0, import_node_fs24.existsSync)(cursorCacheRoot),
|
|
21864
21628
|
pins: cursorPins,
|
|
21865
21629
|
hubCheckout: hubCheckoutForCursorSeed(),
|
|
21866
21630
|
releasedVersion
|
|
@@ -21871,7 +21635,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
21871
21635
|
releasedVersion,
|
|
21872
21636
|
hubCheckout: hubCheckoutForCursorSeed(),
|
|
21873
21637
|
execFileP: execFileP2,
|
|
21874
|
-
mkdtemp: (prefix) => (0, import_promises7.mkdtemp)((0,
|
|
21638
|
+
mkdtemp: (prefix) => (0, import_promises7.mkdtemp)((0, import_node_path21.join)((0, import_node_os5.tmpdir)(), prefix)),
|
|
21875
21639
|
log: (m) => io.err(m)
|
|
21876
21640
|
});
|
|
21877
21641
|
if (seeded) {
|
|
@@ -21880,7 +21644,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
21880
21644
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
21881
21645
|
surface,
|
|
21882
21646
|
cacheRoot: cursorCacheRoot,
|
|
21883
|
-
cacheRootExists: (0,
|
|
21647
|
+
cacheRootExists: (0, import_node_fs24.existsSync)(cursorCacheRoot),
|
|
21884
21648
|
pins: cursorPins,
|
|
21885
21649
|
hubCheckout: hubCheckoutForCursorSeed(),
|
|
21886
21650
|
releasedVersion
|
|
@@ -22034,23 +21798,23 @@ function mergeGuardHook(settings) {
|
|
|
22034
21798
|
next.hooks = hooks;
|
|
22035
21799
|
return next;
|
|
22036
21800
|
}
|
|
22037
|
-
var userScopeSettingsPath = (surface = detectSurface(process.env)) => (0,
|
|
21801
|
+
var userScopeSettingsPath = (surface = detectSurface(process.env)) => (0, import_node_path21.join)((0, import_node_os5.homedir)(), surface === "codex" ? ".codex" : ".claude", "settings.json");
|
|
22038
21802
|
function ensureUserScopeGuardHook(opts = {}) {
|
|
22039
21803
|
const path2 = opts.settingsPath ?? userScopeSettingsPath();
|
|
22040
21804
|
try {
|
|
22041
21805
|
let current = null;
|
|
22042
|
-
if ((0,
|
|
21806
|
+
if ((0, import_node_fs24.existsSync)(path2)) {
|
|
22043
21807
|
try {
|
|
22044
|
-
current = JSON.parse((0,
|
|
21808
|
+
current = JSON.parse((0, import_node_fs24.readFileSync)(path2, "utf8"));
|
|
22045
21809
|
} catch {
|
|
22046
21810
|
return "failed";
|
|
22047
21811
|
}
|
|
22048
21812
|
}
|
|
22049
21813
|
if (settingsHasGuardHook(current)) return "already";
|
|
22050
21814
|
const merged = mergeGuardHook(current);
|
|
22051
|
-
(0,
|
|
22052
|
-
if ((0,
|
|
22053
|
-
(0,
|
|
21815
|
+
(0, import_node_fs24.mkdirSync)((0, import_node_path21.dirname)(path2), { recursive: true });
|
|
21816
|
+
if ((0, import_node_fs24.existsSync)(path2)) (0, import_node_fs24.copyFileSync)(path2, `${path2}.bak`);
|
|
21817
|
+
(0, import_node_fs24.writeFileSync)(path2, `${JSON.stringify(merged, null, 2)}
|
|
22054
21818
|
`, "utf8");
|
|
22055
21819
|
return "written";
|
|
22056
21820
|
} catch {
|
|
@@ -22185,7 +21949,7 @@ async function applyGcPlan(plan2, remote) {
|
|
|
22185
21949
|
cleanupBranch: (branch, expectedHeadOid) => cleanupPrMergeLocalBranch(branch.branch, {
|
|
22186
21950
|
beforeWorktrees,
|
|
22187
21951
|
startingPath: branch.worktreePath,
|
|
22188
|
-
pathExists: (p) => (0,
|
|
21952
|
+
pathExists: (p) => (0, import_node_fs25.existsSync)(p),
|
|
22189
21953
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
22190
21954
|
teardownWorktreeStage,
|
|
22191
21955
|
deferredStore,
|
|
@@ -22208,7 +21972,7 @@ async function applyGcPlan(plan2, remote) {
|
|
|
22208
21972
|
for (const wt of plan2.worktreeDirs) {
|
|
22209
21973
|
try {
|
|
22210
21974
|
const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, siblingRoot, {
|
|
22211
|
-
realpath: (path2) => (0,
|
|
21975
|
+
realpath: (path2) => (0, import_node_fs25.realpathSync)(path2)
|
|
22212
21976
|
});
|
|
22213
21977
|
if (!cleanupTarget.ok) {
|
|
22214
21978
|
result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
|
|
@@ -22288,10 +22052,10 @@ async function runRulesSync(opts, io = consoleIo) {
|
|
|
22288
22052
|
for (const entry of fetched) {
|
|
22289
22053
|
if ("error" in entry) continue;
|
|
22290
22054
|
const { file, source } = entry;
|
|
22291
|
-
const current = (0,
|
|
22055
|
+
const current = (0, import_node_fs25.existsSync)(file) ? await (0, import_promises8.readFile)(file, "utf8") : null;
|
|
22292
22056
|
if (needsUpdate(source, current)) {
|
|
22293
22057
|
const slash = file.lastIndexOf("/");
|
|
22294
|
-
if (slash > 0) (0,
|
|
22058
|
+
if (slash > 0) (0, import_node_fs25.mkdirSync)(file.slice(0, slash), { recursive: true });
|
|
22295
22059
|
await (0, import_promises8.writeFile)(file, normalizeEol(source), "utf8");
|
|
22296
22060
|
changed++;
|
|
22297
22061
|
if (!opts.quiet) io.log(`mmi-cli rules: updated ${file}`);
|
|
@@ -22311,8 +22075,8 @@ async function runRulesPurge(opts, io = consoleIo) {
|
|
|
22311
22075
|
return true;
|
|
22312
22076
|
}
|
|
22313
22077
|
const report = purgeSpine({
|
|
22314
|
-
readFile: (p) => (0,
|
|
22315
|
-
remove: (p) => (0,
|
|
22078
|
+
readFile: (p) => (0, import_node_fs25.existsSync)(p) ? (0, import_node_fs25.readFileSync)(p, "utf8") : null,
|
|
22079
|
+
remove: (p) => (0, import_node_fs25.rmSync)(p, { force: true })
|
|
22316
22080
|
});
|
|
22317
22081
|
if (!opts.quiet) {
|
|
22318
22082
|
for (const f of report.removed) io.log(`mmi-cli rules: removed org-delivered ${f}`);
|
|
@@ -22329,13 +22093,13 @@ rules.command("purge").option("--quiet", "stay silent unless something changed o
|
|
|
22329
22093
|
if (!await runRulesPurge(opts)) process.exitCode = 1;
|
|
22330
22094
|
});
|
|
22331
22095
|
rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
|
|
22332
|
-
const path2 = (0,
|
|
22333
|
-
const current = (0,
|
|
22096
|
+
const path2 = (0, import_node_path22.join)(process.cwd(), ".gitignore");
|
|
22097
|
+
const current = (0, import_node_fs25.existsSync)(path2) ? (0, import_node_fs25.readFileSync)(path2, "utf8") : null;
|
|
22334
22098
|
const plan2 = planManagedGitignore(current);
|
|
22335
22099
|
const drift = [...plan2.added.map((l) => `+${l}`), ...plan2.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
22336
22100
|
if (opts.write) {
|
|
22337
22101
|
if (plan2.changed) {
|
|
22338
|
-
(0,
|
|
22102
|
+
(0, import_node_fs25.writeFileSync)(path2, plan2.content, "utf8");
|
|
22339
22103
|
console.log(`mmi-cli rules gitignore: updated .gitignore (${drift})`);
|
|
22340
22104
|
} else {
|
|
22341
22105
|
console.log("mmi-cli rules gitignore: up to date");
|
|
@@ -22362,7 +22126,7 @@ async function runDocsSync(opts, io = consoleIo) {
|
|
|
22362
22126
|
return null;
|
|
22363
22127
|
}
|
|
22364
22128
|
},
|
|
22365
|
-
localContent: async (f) => (0,
|
|
22129
|
+
localContent: async (f) => (0, import_node_fs25.existsSync)(f) ? await (0, import_promises8.readFile)(f, "utf8") : null,
|
|
22366
22130
|
writeDoc: async (f, c) => {
|
|
22367
22131
|
await (0, import_promises8.writeFile)(f, c, "utf8");
|
|
22368
22132
|
}
|
|
@@ -22377,7 +22141,6 @@ registerSagaCommands(program2);
|
|
|
22377
22141
|
registerHandoffCommands(program2);
|
|
22378
22142
|
registerCoopCommands(program2);
|
|
22379
22143
|
registerOverlordCommands(program2);
|
|
22380
|
-
registerThrottleCommands(program2);
|
|
22381
22144
|
program2.command("commands").description("print the command manifest \u2014 every subcommand + its flags (ground against this instead of guessing)").option("--json", "machine-readable JSON: { name, version, tree, index } \u2014 index is a flat list of every leaf command path").action((o) => {
|
|
22382
22145
|
const manifest = buildCommandManifest(program2);
|
|
22383
22146
|
consoleIo.log(o.json ? JSON.stringify(manifest, null, 2) : formatManifestHuman(manifest));
|
|
@@ -22515,7 +22278,7 @@ function runWorktreeInstall(command, cwd, quiet) {
|
|
|
22515
22278
|
const file = isWin2 ? "cmd.exe" : bin;
|
|
22516
22279
|
const spawnArgs = isWin2 ? ["/c", bin, ...args] : args;
|
|
22517
22280
|
return new Promise((resolve6, reject) => {
|
|
22518
|
-
const child = (0,
|
|
22281
|
+
const child = (0, import_node_child_process13.spawn)(file, spawnArgs, { cwd, stdio: quiet ? "ignore" : "inherit", windowsHide: true });
|
|
22519
22282
|
const timer = setTimeout(() => {
|
|
22520
22283
|
try {
|
|
22521
22284
|
child.kill();
|
|
@@ -22537,7 +22300,7 @@ function runWorktreeInstall(command, cwd, quiet) {
|
|
|
22537
22300
|
async function primaryCheckoutRoot(worktreeRoot) {
|
|
22538
22301
|
try {
|
|
22539
22302
|
const out = (await execFileP2("git", ["-C", worktreeRoot, "rev-parse", "--path-format=absolute", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
|
|
22540
|
-
return out ? (0,
|
|
22303
|
+
return out ? (0, import_node_path22.dirname)(out) : void 0;
|
|
22541
22304
|
} catch {
|
|
22542
22305
|
return void 0;
|
|
22543
22306
|
}
|
|
@@ -22550,28 +22313,28 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
|
|
|
22550
22313
|
};
|
|
22551
22314
|
}
|
|
22552
22315
|
function acquireWorktreeSetupLock(worktreeRoot) {
|
|
22553
|
-
const lockPath = (0,
|
|
22316
|
+
const lockPath = (0, import_node_path22.join)(worktreeRoot, ".mmi", "worktree-setup.lock");
|
|
22554
22317
|
const take = () => {
|
|
22555
|
-
const fd = (0,
|
|
22318
|
+
const fd = (0, import_node_fs25.openSync)(lockPath, "wx");
|
|
22556
22319
|
try {
|
|
22557
|
-
(0,
|
|
22320
|
+
(0, import_node_fs25.writeSync)(fd, String(Date.now()));
|
|
22558
22321
|
} finally {
|
|
22559
|
-
(0,
|
|
22322
|
+
(0, import_node_fs25.closeSync)(fd);
|
|
22560
22323
|
}
|
|
22561
22324
|
return () => {
|
|
22562
22325
|
try {
|
|
22563
|
-
(0,
|
|
22326
|
+
(0, import_node_fs25.rmSync)(lockPath, { force: true });
|
|
22564
22327
|
} catch {
|
|
22565
22328
|
}
|
|
22566
22329
|
};
|
|
22567
22330
|
};
|
|
22568
22331
|
try {
|
|
22569
|
-
(0,
|
|
22332
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path22.dirname)(lockPath), { recursive: true });
|
|
22570
22333
|
return take();
|
|
22571
22334
|
} catch {
|
|
22572
22335
|
try {
|
|
22573
|
-
if (Date.now() - (0,
|
|
22574
|
-
(0,
|
|
22336
|
+
if (Date.now() - (0, import_node_fs25.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
|
|
22337
|
+
(0, import_node_fs25.rmSync)(lockPath, { force: true });
|
|
22575
22338
|
return take();
|
|
22576
22339
|
}
|
|
22577
22340
|
} catch {
|
|
@@ -22710,7 +22473,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
22710
22473
|
try {
|
|
22711
22474
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body];
|
|
22712
22475
|
if (o.repo) args.push("--repo", o.repo);
|
|
22713
|
-
(0,
|
|
22476
|
+
(0, import_node_child_process13.spawn)(process.execPath, [process.argv[1], ...args], {
|
|
22714
22477
|
detached: true,
|
|
22715
22478
|
stdio: "ignore",
|
|
22716
22479
|
windowsHide: true,
|
|
@@ -23060,7 +22823,7 @@ project.command("attest [owner/repo]").description("attest this repo's app-owned
|
|
|
23060
22823
|
const res = await attestAppGaps(slugOf(target), repo, registryClientDeps(cfg));
|
|
23061
22824
|
return reportWrite("project attest", res);
|
|
23062
22825
|
});
|
|
23063
|
-
project.command("set [owner/repo]").description("upsert project META (idempotent merge; master-gated)").option("--class <class>", "deployable | content").option("--project-type <type>", `${PROJECT_TYPES.join(" | ")} (v2 capability shape)`).option("--deploy-model <model>", `${DEPLOY_MODELS.join(" | ")} (release/deploy path; none means no Hub deploy registration)`).option("--release-track <track>", `${RELEASE_TRACKS.join(" | ")} (branch topology; direct skips rc)`).option("--var <KEY=VALUE...>", settableVarHelp()).option("--unset <KEY...>", "META field to remove (repeatable): oauth, requiredRuntimeSecrets, edgeDomains, requiredGcpApis, publishRequired").option("--clear-web-profile", "remove web-only registry fields (oauth, edgeDomains) for non-web/content projects").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
|
|
22826
|
+
project.command("set [owner/repo]").description("upsert project META (idempotent merge; master-gated)").option("--class <class>", "deployable | content").option("--project-type <type>", `${PROJECT_TYPES.join(" | ")} (v2 capability shape)`).option("--deploy-model <model>", `${DEPLOY_MODELS.join(" | ")} (release/deploy path; none means no Hub deploy registration)`).option("--release-track <track>", `${RELEASE_TRACKS.join(" | ")} (branch topology; direct skips rc)`).option("--var <KEY=VALUE...>", settableVarHelp()).option("--secrets-file <path>", "read the #2244 secrets catalog map (JSON) from a file and set it as --var secrets (robust for large maps)").option("--unset <KEY...>", "META field to remove (repeatable): oauth, requiredRuntimeSecrets, edgeDomains, requiredGcpApis, publishRequired").option("--clear-web-profile", "remove web-only registry fields (oauth, edgeDomains) for non-web/content projects").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
|
|
23064
22827
|
const cfg = await loadConfig();
|
|
23065
22828
|
let target;
|
|
23066
22829
|
try {
|
|
@@ -23070,6 +22833,14 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
23070
22833
|
}
|
|
23071
22834
|
const slug = slugOf(target);
|
|
23072
22835
|
const repo = target.includes("/") ? target : `mutmutco/${slug}`;
|
|
22836
|
+
const vars = rawValues("--var");
|
|
22837
|
+
if (o.secretsFile) {
|
|
22838
|
+
try {
|
|
22839
|
+
vars.push(`secrets=${(0, import_node_fs25.readFileSync)(o.secretsFile, "utf8")}`);
|
|
22840
|
+
} catch (e) {
|
|
22841
|
+
return fail(`project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
22842
|
+
}
|
|
22843
|
+
}
|
|
23073
22844
|
let patch;
|
|
23074
22845
|
try {
|
|
23075
22846
|
patch = buildProjectSetPatch({
|
|
@@ -23077,7 +22848,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
23077
22848
|
projectType: o.projectType,
|
|
23078
22849
|
deployModel: o.deployModel,
|
|
23079
22850
|
releaseTrack: o.releaseTrack,
|
|
23080
|
-
vars
|
|
22851
|
+
vars,
|
|
23081
22852
|
unsets: rawValues("--unset"),
|
|
23082
22853
|
clearWebProfile: Boolean(o.clearWebProfile)
|
|
23083
22854
|
});
|
|
@@ -23688,9 +23459,9 @@ pr.command("create").description("create a PR and print {number,url} JSON").opti
|
|
|
23688
23459
|
console.log(JSON.stringify(created));
|
|
23689
23460
|
});
|
|
23690
23461
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
23691
|
-
const wfDir = (0,
|
|
23692
|
-
if (!(0,
|
|
23693
|
-
return (0,
|
|
23462
|
+
const wfDir = (0, import_node_path22.join)(cwd, ".github", "workflows");
|
|
23463
|
+
if (!(0, import_node_fs25.existsSync)(wfDir)) return [];
|
|
23464
|
+
return (0, import_node_fs25.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).map((name) => `.github/workflows/${name}`);
|
|
23694
23465
|
}
|
|
23695
23466
|
async function resolveMergeCiPolicyForCheckout(repoOpt) {
|
|
23696
23467
|
const repo = repoOpt ?? await resolveRepo();
|
|
@@ -23709,7 +23480,7 @@ function ciAuditDeps() {
|
|
|
23709
23480
|
// Continuous CI delivery (#1550): the gate re-seed renders from the Hub's on-disk seed templates. The
|
|
23710
23481
|
// reconcile runs IN the Hub checkout, so this is local-file I/O (no network fetch). Path is relative to
|
|
23711
23482
|
// the repo root (e.g. skills/bootstrap/seeds/gate.template.yml).
|
|
23712
|
-
readSeedFile: (path2) => (0,
|
|
23483
|
+
readSeedFile: (path2) => (0, import_node_fs25.existsSync)(path2) ? (0, import_node_fs25.readFileSync)(path2, "utf8") : null
|
|
23713
23484
|
};
|
|
23714
23485
|
}
|
|
23715
23486
|
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) => {
|
|
@@ -23846,7 +23617,7 @@ async function remoteBranchExists2(branch, options = {}) {
|
|
|
23846
23617
|
}
|
|
23847
23618
|
var COMPOSE_TIMEOUT_MS = 12e4;
|
|
23848
23619
|
function spawnDeferredGcSweep() {
|
|
23849
|
-
spawnDetachedSelf(["gc", "sweep-deferred", "--quiet"], { spawn:
|
|
23620
|
+
spawnDetachedSelf(["gc", "sweep-deferred", "--quiet"], { spawn: import_node_child_process13.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
23850
23621
|
}
|
|
23851
23622
|
async function createDeferredWorktreeStore() {
|
|
23852
23623
|
try {
|
|
@@ -23862,7 +23633,7 @@ async function createDeferredWorktreeStore() {
|
|
|
23862
23633
|
},
|
|
23863
23634
|
write: async (entries) => {
|
|
23864
23635
|
try {
|
|
23865
|
-
await (0, import_promises8.mkdir)((0,
|
|
23636
|
+
await (0, import_promises8.mkdir)((0, import_node_path22.dirname)(registryPath), { recursive: true });
|
|
23866
23637
|
await (0, import_promises8.writeFile)(registryPath, serializeDeferredWorktrees(entries), "utf8");
|
|
23867
23638
|
} catch {
|
|
23868
23639
|
}
|
|
@@ -23876,13 +23647,13 @@ var realWorktreeDirRemover = {
|
|
|
23876
23647
|
probe: (p) => {
|
|
23877
23648
|
let st;
|
|
23878
23649
|
try {
|
|
23879
|
-
st = (0,
|
|
23650
|
+
st = (0, import_node_fs25.lstatSync)(p);
|
|
23880
23651
|
} catch {
|
|
23881
23652
|
return null;
|
|
23882
23653
|
}
|
|
23883
23654
|
if (st.isSymbolicLink()) return "link";
|
|
23884
23655
|
try {
|
|
23885
|
-
(0,
|
|
23656
|
+
(0, import_node_fs25.readlinkSync)(p);
|
|
23886
23657
|
return "link";
|
|
23887
23658
|
} catch {
|
|
23888
23659
|
}
|
|
@@ -23890,7 +23661,7 @@ var realWorktreeDirRemover = {
|
|
|
23890
23661
|
},
|
|
23891
23662
|
readdir: (p) => {
|
|
23892
23663
|
try {
|
|
23893
|
-
return (0,
|
|
23664
|
+
return (0, import_node_fs25.readdirSync)(p);
|
|
23894
23665
|
} catch {
|
|
23895
23666
|
return [];
|
|
23896
23667
|
}
|
|
@@ -23899,9 +23670,9 @@ var realWorktreeDirRemover = {
|
|
|
23899
23670
|
// leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
|
|
23900
23671
|
detachLink: (p) => {
|
|
23901
23672
|
try {
|
|
23902
|
-
(0,
|
|
23673
|
+
(0, import_node_fs25.rmdirSync)(p);
|
|
23903
23674
|
} catch {
|
|
23904
|
-
(0,
|
|
23675
|
+
(0, import_node_fs25.unlinkSync)(p);
|
|
23905
23676
|
}
|
|
23906
23677
|
},
|
|
23907
23678
|
removeTree: (p) => (0, import_promises8.rm)(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
|
|
@@ -23931,9 +23702,9 @@ async function worktreeHasStageState(worktreePath) {
|
|
|
23931
23702
|
}
|
|
23932
23703
|
}
|
|
23933
23704
|
function stageStateFileBelongsToWorktree(statePath, worktreePath) {
|
|
23934
|
-
if (!(0,
|
|
23705
|
+
if (!(0, import_node_fs25.existsSync)(statePath)) return false;
|
|
23935
23706
|
try {
|
|
23936
|
-
const state = JSON.parse((0,
|
|
23707
|
+
const state = JSON.parse((0, import_node_fs25.readFileSync)(statePath, "utf8"));
|
|
23937
23708
|
const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
|
|
23938
23709
|
return Boolean(recordedCwd && isPathUnderDirectory(recordedCwd, worktreePath));
|
|
23939
23710
|
} catch {
|
|
@@ -24006,7 +23777,7 @@ pr.command("merge <number>").description("merge a PR (squash by default) and cle
|
|
|
24006
23777
|
} : await cleanupPrMergeLocalBranch(headRef, {
|
|
24007
23778
|
beforeWorktrees,
|
|
24008
23779
|
startingPath,
|
|
24009
|
-
pathExists: (p) => (0,
|
|
23780
|
+
pathExists: (p) => (0, import_node_fs25.existsSync)(p),
|
|
24010
23781
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
24011
23782
|
teardownWorktreeStage,
|
|
24012
23783
|
deferredStore,
|
|
@@ -24203,7 +23974,7 @@ function stageScopedRunOpts(o) {
|
|
|
24203
23974
|
};
|
|
24204
23975
|
}
|
|
24205
23976
|
function printLine(value) {
|
|
24206
|
-
(0,
|
|
23977
|
+
(0, import_node_fs25.writeSync)(1, `${value}
|
|
24207
23978
|
`);
|
|
24208
23979
|
}
|
|
24209
23980
|
function stageKeepAlive() {
|
|
@@ -24220,8 +23991,8 @@ async function resolveStage() {
|
|
|
24220
23991
|
local,
|
|
24221
23992
|
shell: shellFor(),
|
|
24222
23993
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
24223
|
-
hasCompose: (0,
|
|
24224
|
-
hasEnvExample: (0,
|
|
23994
|
+
hasCompose: (0, import_node_fs25.existsSync)((0, import_node_path22.join)(process.cwd(), "docker-compose.yml")),
|
|
23995
|
+
hasEnvExample: (0, import_node_fs25.existsSync)((0, import_node_path22.join)(process.cwd(), ".env.example"))
|
|
24225
23996
|
});
|
|
24226
23997
|
}
|
|
24227
23998
|
async function fetchStageVaultEnvMerge() {
|
|
@@ -24685,7 +24456,7 @@ bootstrap.command("verify <repo>").description("audit whether an existing repo i
|
|
|
24685
24456
|
client: defaultGitHubClient(),
|
|
24686
24457
|
projectMeta: meta,
|
|
24687
24458
|
deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
|
|
24688
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
24459
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs25.existsSync)(path2) ? (0, import_node_fs25.readFileSync)(path2, "utf8") : null,
|
|
24689
24460
|
// requiredGcpApis is stored as an array by a JSON write, but `project set --var KEY=VALUE` stores a raw
|
|
24690
24461
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
24691
24462
|
requiredGcpApis: (() => {
|
|
@@ -24728,12 +24499,12 @@ bootstrap.command("apply <repo>").description("idempotent seed apply from skills
|
|
|
24728
24499
|
return fail(`bootstrap apply: ${e.message}`);
|
|
24729
24500
|
}
|
|
24730
24501
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
24731
|
-
if (!(0,
|
|
24732
|
-
const manifest = loadBootstrapSeeds((0,
|
|
24502
|
+
if (!(0, import_node_fs25.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; run from the MMI-Hub repo root`);
|
|
24503
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs25.readFileSync)(manifestPath, "utf8"));
|
|
24733
24504
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
24734
24505
|
const slug = parsedRepo.slug;
|
|
24735
24506
|
const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
|
|
24736
|
-
const readFile7 = (p) => (0,
|
|
24507
|
+
const readFile7 = (p) => (0, import_node_fs25.existsSync)(p) ? (0, import_node_fs25.readFileSync)(p, "utf8") : null;
|
|
24737
24508
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
24738
24509
|
const rawVars = {};
|
|
24739
24510
|
for (const value of rawValues("--var")) {
|
|
@@ -24980,16 +24751,16 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
24980
24751
|
const repoClass = o.class;
|
|
24981
24752
|
targets = [{ repo: o.repo, class: repoClass, releaseTrack: repoClass === "content" ? "trunk" : resolveReleaseTrack(meta, void 0, o.repo) }];
|
|
24982
24753
|
} else {
|
|
24983
|
-
const projectsJson = registryProjects ? JSON.stringify({ projects: registryProjects }) : (0,
|
|
24754
|
+
const projectsJson = registryProjects ? JSON.stringify({ projects: registryProjects }) : (0, import_node_fs25.existsSync)("projects.json") ? (0, import_node_fs25.readFileSync)("projects.json", "utf8") : null;
|
|
24984
24755
|
if (!projectsJson) return failGraceful("access audit: no project registry \u2014 Hub API unreachable and projects.json not found; run from the MMI-Hub repo root or pass --repo <owner/repo>");
|
|
24985
|
-
const fanoutJson = (0,
|
|
24756
|
+
const fanoutJson = (0, import_node_fs25.existsSync)(".github/fanout-targets.json") ? (0, import_node_fs25.readFileSync)(".github/fanout-targets.json", "utf8") : null;
|
|
24986
24757
|
targets = loadAccessTargets(projectsJson, fanoutJson);
|
|
24987
24758
|
}
|
|
24988
24759
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
24989
|
-
const fileMatrix = (0,
|
|
24760
|
+
const fileMatrix = (0, import_node_fs25.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs25.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
24990
24761
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
24991
24762
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
24992
|
-
const fileContracts = (0,
|
|
24763
|
+
const fileContracts = (0, import_node_fs25.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs25.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
24993
24764
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
24994
24765
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess);
|
|
24995
24766
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
@@ -25003,77 +24774,40 @@ program2.command("doctor").description("check onboarding gates and auto-heal CLI
|
|
|
25003
24774
|
));
|
|
25004
24775
|
program2.command("guard").description("detect a pruned/unresolved MMI plugin on disk; loud one-line stderr at session start").option("--session-start", "run in user-scope SessionStart mode").action((opts) => runGuard({ sessionStart: opts.sessionStart }));
|
|
25005
24776
|
program2.command("plugin-heal").description("reinstall + re-enable the MMI plugin (recover from a marketplace prune)").action(() => runPluginHeal());
|
|
25006
|
-
program2.command("session-start").description("run the SessionStart verbs (rules
|
|
24777
|
+
program2.command("session-start").description("run the SessionStart verbs (rules purge, whoami, board slice, doctor) in one process; docs sync runs detached").action(async () => {
|
|
25007
24778
|
if (isInsideRepoSubdir(process.cwd())) {
|
|
25008
24779
|
console.error("[mmi-hook] session-start: cwd is a repository SUBDIRECTORY \u2014 skipping the SessionStart hook (spine/docs/plan/saga delivery); run it from the repo root.");
|
|
25009
24780
|
return;
|
|
25010
24781
|
}
|
|
25011
24782
|
if (!isOrgRepoRoot(process.cwd())) return;
|
|
25012
|
-
|
|
25013
|
-
if (continuityEnabled) {
|
|
25014
|
-
try {
|
|
25015
|
-
const hook = parseHookInput(await readStdin());
|
|
25016
|
-
if (hook.session_id) persistSession(hook.session_id);
|
|
25017
|
-
} catch (e) {
|
|
25018
|
-
console.error(`[mmi-hook] saga session failed: ${e.message}`);
|
|
25019
|
-
}
|
|
25020
|
-
}
|
|
25021
|
-
spawnDetachedSelf(["docs", "sync", "--quiet"], { spawn: import_node_child_process14.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
24783
|
+
spawnDetachedSelf(["docs", "sync", "--quiet"], { spawn: import_node_child_process13.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
25022
24784
|
spawnDeferredGcSweep();
|
|
25023
|
-
|
|
25024
|
-
|
|
25025
|
-
|
|
25026
|
-
|
|
25027
|
-
|
|
25028
|
-
|
|
25029
|
-
|
|
25030
|
-
|
|
25031
|
-
|
|
25032
|
-
|
|
25033
|
-
|
|
25034
|
-
northstarInjected = await runNorthstarContext(io, {
|
|
25035
|
-
loadPlans: () => scopedPlanList(planDeps),
|
|
25036
|
-
readLocal: (slug) => planDeps.readLocal(slug),
|
|
25037
|
-
// #1812: thread the saga HEAD's North Star anchor (its NEXT slug) into the relevance gate so
|
|
25038
|
-
// the plan the agent is actively on is force-injected even on a generic branch with no token
|
|
25039
|
-
// overlap. fetchSagaHead errors are swallowed via a silent io — a missing/failed HEAD just
|
|
25040
|
-
// falls back to token-overlap scoring, never noises or blocks the banner.
|
|
25041
|
-
gatherSignals: () => gatherRelevanceSignals({
|
|
25042
|
-
anchorSlug: () => fetchSagaHead({ log: () => {
|
|
25043
|
-
}, err: () => {
|
|
25044
|
-
} }).then((h) => h?.anchor?.slug ?? void 0)
|
|
25045
|
-
})
|
|
25046
|
-
});
|
|
25047
|
-
},
|
|
25048
|
-
sagaHealth: (io) => runSagaHealth({ banner: true, quiet: true }, io),
|
|
25049
|
-
// whoami (#879): surface the resolved human so agents act --for them without asking. Silent
|
|
25050
|
-
// when unknown — a missing gh login must not noise or fail the banner.
|
|
25051
|
-
whoami: async (io) => {
|
|
25052
|
-
const report = await resolveWhoami({
|
|
25053
|
-
hubSession: async () => hubAuthSession({ baseUrl: (await loadConfig()).sagaApiUrl ?? defaultHubUrl(), githubToken }),
|
|
25054
|
-
ghLogin: githubLogin
|
|
25055
|
-
});
|
|
25056
|
-
const line = whoamiLine(report);
|
|
25057
|
-
if (line) io.log(line);
|
|
25058
|
-
},
|
|
25059
|
-
boardSlice: (io) => runBoardSlice(io, {
|
|
25060
|
-
loadConfig: () => loadConfigForRepo(),
|
|
25061
|
-
readBoard,
|
|
25062
|
-
// #1813: warm the slice cache out-of-band (detached, like docs sync) so the ~20s live read
|
|
25063
|
-
// never costs banner time and next session's glance renders instantly within budget.
|
|
25064
|
-
scheduleRefresh: () => spawnDetachedSelf(["board", "slice-refresh", "--quiet"], { spawn: import_node_child_process14.spawn, execPath: process.execPath, scriptPath: process.argv[1] })
|
|
25065
|
-
}),
|
|
25066
|
-
doctor: (io) => runDoctor({ banner: true }, io)
|
|
24785
|
+
const { parallel, sequential } = buildSessionStartPlan({
|
|
24786
|
+
rulesPurge: (io) => runRulesPurge({ quiet: true }, io),
|
|
24787
|
+
// whoami (#879): surface the resolved human so agents act --for them without asking. Silent
|
|
24788
|
+
// when unknown — a missing gh login must not noise or fail the banner.
|
|
24789
|
+
whoami: async (io) => {
|
|
24790
|
+
const report = await resolveWhoami({
|
|
24791
|
+
hubSession: async () => hubAuthSession({ baseUrl: (await loadConfig()).sagaApiUrl ?? defaultHubUrl(), githubToken }),
|
|
24792
|
+
ghLogin: githubLogin
|
|
24793
|
+
});
|
|
24794
|
+
const line = whoamiLine(report);
|
|
24795
|
+
if (line) io.log(line);
|
|
25067
24796
|
},
|
|
25068
|
-
|
|
25069
|
-
|
|
24797
|
+
boardSlice: (io) => runBoardSlice(io, {
|
|
24798
|
+
loadConfig: () => loadConfigForRepo(),
|
|
24799
|
+
readBoard,
|
|
24800
|
+
// #1813: warm the slice cache out-of-band (detached, like docs sync) so the ~20s live read
|
|
24801
|
+
// never costs banner time and next session's glance renders instantly within budget.
|
|
24802
|
+
scheduleRefresh: () => spawnDetachedSelf(["board", "slice-refresh", "--quiet"], { spawn: import_node_child_process13.spawn, execPath: process.execPath, scriptPath: process.argv[1] })
|
|
24803
|
+
}),
|
|
24804
|
+
doctor: (io) => runDoctor({ banner: true }, io)
|
|
24805
|
+
});
|
|
25070
24806
|
await runSessionStart(parallel, sequential, consoleIo);
|
|
25071
|
-
for (const line of sessionStartContinuityLines({ continuityEnabled, northstarInjected, cwd: process.cwd() })) consoleIo.log(line);
|
|
25072
|
-
if (continuityEnabled) await runPlanAutosave(consoleIo, { quiet: true }).catch(() => void 0);
|
|
25073
24807
|
for (const line of scratchGcLines(process.cwd())) consoleIo.log(line);
|
|
25074
24808
|
const worktreeBanner = worktreeAutoProvisionBanner(process.cwd());
|
|
25075
24809
|
if (worktreeBanner) {
|
|
25076
|
-
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn:
|
|
24810
|
+
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process13.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
25077
24811
|
consoleIo.log(worktreeBanner);
|
|
25078
24812
|
}
|
|
25079
24813
|
});
|