@mutmutco/cli 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.cjs +641 -440
- 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_promises3 = require("node:fs/promises");
|
|
3411
|
-
var
|
|
3411
|
+
var import_node_fs19 = require("node:fs");
|
|
3412
3412
|
|
|
3413
3413
|
// src/bootstrap-org-ruleset.ts
|
|
3414
3414
|
var ORG_NO_AGENT_FILES_RULESET_NAME = "mmi-no-agent-files-org";
|
|
@@ -3502,7 +3502,7 @@ async function fetchOrgNoAgentFilesRuleset(client, org = ORG_LOGIN) {
|
|
|
3502
3502
|
}
|
|
3503
3503
|
|
|
3504
3504
|
// src/index.ts
|
|
3505
|
-
var
|
|
3505
|
+
var import_node_child_process12 = require("node:child_process");
|
|
3506
3506
|
|
|
3507
3507
|
// src/cli-shared.ts
|
|
3508
3508
|
var import_node_child_process3 = require("node:child_process");
|
|
@@ -3605,7 +3605,7 @@ function hardExit(code) {
|
|
|
3605
3605
|
async function cleanExit(code) {
|
|
3606
3606
|
process.exitCode = code;
|
|
3607
3607
|
await closeHttpPool();
|
|
3608
|
-
await new Promise((
|
|
3608
|
+
await new Promise((resolve5) => setImmediate(resolve5));
|
|
3609
3609
|
process.exit(code);
|
|
3610
3610
|
}
|
|
3611
3611
|
async function failGraceful(msg) {
|
|
@@ -3770,7 +3770,7 @@ async function fetchWithRetry(fetchImpl, url, init, opts = {}) {
|
|
|
3770
3770
|
const attempts = opts.attempts ?? 3;
|
|
3771
3771
|
const baseDelayMs = opts.baseDelayMs ?? 250;
|
|
3772
3772
|
const retryOn = opts.retryOn ?? ((res) => res.status >= 500);
|
|
3773
|
-
const sleep = opts.sleep ?? ((ms) => new Promise((
|
|
3773
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
|
|
3774
3774
|
let lastErr;
|
|
3775
3775
|
for (let i = 0; i < attempts; i++) {
|
|
3776
3776
|
const isLast = i === attempts - 1;
|
|
@@ -3914,8 +3914,8 @@ async function readStdin(opts = {}) {
|
|
|
3914
3914
|
})().catch(() => {
|
|
3915
3915
|
});
|
|
3916
3916
|
let timer;
|
|
3917
|
-
const timeout = new Promise((
|
|
3918
|
-
timer = setTimeout(
|
|
3917
|
+
const timeout = new Promise((resolve5) => {
|
|
3918
|
+
timer = setTimeout(resolve5, timeoutMs);
|
|
3919
3919
|
});
|
|
3920
3920
|
try {
|
|
3921
3921
|
await Promise.race([drain, timeout]);
|
|
@@ -5257,7 +5257,7 @@ async function setBoardItemPriority(client, cfg, itemId, priority) {
|
|
|
5257
5257
|
await updateItemSingleSelect(client, cfg.projectId, itemId, cfg.priorityFieldId, optionId);
|
|
5258
5258
|
return cliPriorityToFieldName(priority);
|
|
5259
5259
|
}
|
|
5260
|
-
var defaultRetrySleep = (ms) => new Promise((
|
|
5260
|
+
var defaultRetrySleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
5261
5261
|
async function resolveProjectItemIdWithRetry(client, cfg, selector, opts = {}) {
|
|
5262
5262
|
const attempts = Math.max(1, opts.attempts ?? 5);
|
|
5263
5263
|
const delayMs = opts.delayMs ?? 300;
|
|
@@ -5861,7 +5861,7 @@ function whoamiLine(report) {
|
|
|
5861
5861
|
}
|
|
5862
5862
|
|
|
5863
5863
|
// src/index.ts
|
|
5864
|
-
var
|
|
5864
|
+
var import_node_path19 = require("node:path");
|
|
5865
5865
|
|
|
5866
5866
|
// src/merge-ci-policy.ts
|
|
5867
5867
|
function resolveMergeCiPolicy(input) {
|
|
@@ -6265,11 +6265,10 @@ function parseOwnerRepo(repo) {
|
|
|
6265
6265
|
if (owner.includes("\\") || name.includes("\\")) throw new Error("repo must be owner/repo");
|
|
6266
6266
|
return { owner, name, slug: name.toLowerCase(), fullName: `${owner}/${name}` };
|
|
6267
6267
|
}
|
|
6268
|
-
var DEFAULT_INSTALL_CMD = "npm ci";
|
|
6269
6268
|
var DEFAULT_GATE_CMD = "npm run check";
|
|
6270
6269
|
var DEFAULT_GATE_PY_VERSION = "3.11";
|
|
6271
6270
|
var GATE_RUNTIME_DEFAULTS = {
|
|
6272
|
-
node: { cmd: DEFAULT_GATE_CMD, install:
|
|
6271
|
+
node: { cmd: DEFAULT_GATE_CMD, install: "npm ci" },
|
|
6273
6272
|
python: { cmd: "pytest", install: 'pip install -e ".[dev]"' }
|
|
6274
6273
|
};
|
|
6275
6274
|
function gateSeedVars(cls, releaseTrack, runtime = "node") {
|
|
@@ -6311,7 +6310,6 @@ function withDerivedRepoVars(vars, parsed, cls, releaseTrack) {
|
|
|
6311
6310
|
out.REPO_NAME ??= parsed.name;
|
|
6312
6311
|
out.REPO_SLUG ??= parsed.slug;
|
|
6313
6312
|
out.CLASS ??= cls;
|
|
6314
|
-
out.INSTALL_CMD ??= DEFAULT_INSTALL_CMD;
|
|
6315
6313
|
const track = releaseTrack ?? resolveBootstrapReleaseTrack(cls);
|
|
6316
6314
|
const runtime = out.GATE_RUNTIME === "python" ? "python" : "node";
|
|
6317
6315
|
for (const [key, value] of Object.entries(gateSeedVars(cls, track, runtime))) {
|
|
@@ -7022,7 +7020,7 @@ async function mergeAutoWithTransientRetry(prNumber, repo, deps) {
|
|
|
7022
7020
|
if (first.mergeStatus !== "failed") return first;
|
|
7023
7021
|
const ready = await deps.probeMergeReady(prNumber, repo).catch(() => ({ open: false, mergeable: false, checksPassing: false }));
|
|
7024
7022
|
if (!ready.open || !ready.mergeable || !ready.checksPassing) return first;
|
|
7025
|
-
const sleep = deps.sleep ?? ((ms) => new Promise((
|
|
7023
|
+
const sleep = deps.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
|
|
7026
7024
|
await sleep(PR_LAND_MERGE_RETRY_DELAY_MS);
|
|
7027
7025
|
const retried = await deps.mergeAuto(prNumber, repo);
|
|
7028
7026
|
if (retried.mergeStatus !== "failed") return retried;
|
|
@@ -7031,7 +7029,7 @@ async function mergeAutoWithTransientRetry(prNumber, repo, deps) {
|
|
|
7031
7029
|
async function readGhPrStateWithRetry(fetchState, options) {
|
|
7032
7030
|
const retries = options?.retries ?? PR_LAND_STATE_READ_RETRIES;
|
|
7033
7031
|
const delayMs = options?.delayMs ?? PR_LAND_STATE_READ_DELAY_MS;
|
|
7034
|
-
const sleep = options?.sleep ?? ((ms) => new Promise((
|
|
7032
|
+
const sleep = options?.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
|
|
7035
7033
|
let lastError = "empty state";
|
|
7036
7034
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
7037
7035
|
try {
|
|
@@ -7164,7 +7162,7 @@ async function sweepDeferredWorktreesWithRetry(store, deps, opts = {}) {
|
|
|
7164
7162
|
}
|
|
7165
7163
|
return last;
|
|
7166
7164
|
}
|
|
7167
|
-
var defaultSleep = (ms) => new Promise((
|
|
7165
|
+
var defaultSleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
7168
7166
|
async function removeWorktreeWithRecovery(wtPath, deps) {
|
|
7169
7167
|
const maxAttempts = deps.maxAttempts ?? 3;
|
|
7170
7168
|
const backoff = deps.backoffMs ?? [250, 1e3];
|
|
@@ -8035,7 +8033,7 @@ var execFileP4 = (0, import_node_util6.promisify)(import_node_child_process6.exe
|
|
|
8035
8033
|
var DOCKER_TIMEOUT_MS = 15e3;
|
|
8036
8034
|
var EARLY_EXIT_GRACE_MS = 2e3;
|
|
8037
8035
|
function waitForProcessStability(child, graceMs = EARLY_EXIT_GRACE_MS) {
|
|
8038
|
-
return new Promise((
|
|
8036
|
+
return new Promise((resolve5, reject) => {
|
|
8039
8037
|
let settled = false;
|
|
8040
8038
|
const finish = (fn) => {
|
|
8041
8039
|
if (settled) return;
|
|
@@ -8045,7 +8043,7 @@ function waitForProcessStability(child, graceMs = EARLY_EXIT_GRACE_MS) {
|
|
|
8045
8043
|
child.removeAllListeners("exit");
|
|
8046
8044
|
fn();
|
|
8047
8045
|
};
|
|
8048
|
-
const timer = setTimeout(() => finish(
|
|
8046
|
+
const timer = setTimeout(() => finish(resolve5), graceMs);
|
|
8049
8047
|
child.on("error", (err) => finish(() => reject(new Error(`stage process failed to start: ${err.message}`))));
|
|
8050
8048
|
child.on("exit", (code, signal) => {
|
|
8051
8049
|
const detail = code != null ? `code ${code}` : signal ? `signal ${signal}` : "unknown reason";
|
|
@@ -8255,10 +8253,10 @@ function pickStagePort(range, isFree) {
|
|
|
8255
8253
|
throw new Error(`no free stage port in range ${start}-${end} \u2014 every port is in use`);
|
|
8256
8254
|
}
|
|
8257
8255
|
function isPortFree(port) {
|
|
8258
|
-
return new Promise((
|
|
8256
|
+
return new Promise((resolve5) => {
|
|
8259
8257
|
const srv = (0, import_node_net.createServer)();
|
|
8260
|
-
srv.once("error", () =>
|
|
8261
|
-
srv.once("listening", () => srv.close(() =>
|
|
8258
|
+
srv.once("error", () => resolve5(false));
|
|
8259
|
+
srv.once("listening", () => srv.close(() => resolve5(true)));
|
|
8262
8260
|
srv.listen(port, "127.0.0.1");
|
|
8263
8261
|
});
|
|
8264
8262
|
}
|
|
@@ -8469,7 +8467,7 @@ async function killTree(pid) {
|
|
|
8469
8467
|
} catch {
|
|
8470
8468
|
}
|
|
8471
8469
|
}
|
|
8472
|
-
await new Promise((
|
|
8470
|
+
await new Promise((resolve5) => setTimeout(resolve5, 500));
|
|
8473
8471
|
try {
|
|
8474
8472
|
process.kill(-pid, "SIGKILL");
|
|
8475
8473
|
} catch {
|
|
@@ -8490,7 +8488,7 @@ async function waitForHealth(url, timeoutMs, anyStatus = false) {
|
|
|
8490
8488
|
} catch (e) {
|
|
8491
8489
|
last = e.message;
|
|
8492
8490
|
}
|
|
8493
|
-
await new Promise((
|
|
8491
|
+
await new Promise((resolve5) => setTimeout(resolve5, 1e3));
|
|
8494
8492
|
}
|
|
8495
8493
|
throw new Error(`stage health check timed out for ${url}${last ? ` (${last})` : ""}`);
|
|
8496
8494
|
}
|
|
@@ -8907,6 +8905,14 @@ ${line}
|
|
|
8907
8905
|
`;
|
|
8908
8906
|
}
|
|
8909
8907
|
|
|
8908
|
+
// src/issue-view-json.ts
|
|
8909
|
+
var DEFAULT_ISSUE_VIEW_FIELDS = "number,title,state,url,labels,author,assignees,milestone,body";
|
|
8910
|
+
function normalizeIssueViewJsonFields(tokens2) {
|
|
8911
|
+
if (!tokens2 || tokens2.length === 0) return DEFAULT_ISSUE_VIEW_FIELDS;
|
|
8912
|
+
const fields = tokens2.flatMap((token) => token.split(/[,\s]+/)).map((field) => field.trim()).filter(Boolean);
|
|
8913
|
+
return fields.length ? fields.join(",") : DEFAULT_ISSUE_VIEW_FIELDS;
|
|
8914
|
+
}
|
|
8915
|
+
|
|
8910
8916
|
// src/sub-issue.ts
|
|
8911
8917
|
function parseIssueRef(ref) {
|
|
8912
8918
|
const trimmed = ref.trim();
|
|
@@ -9507,7 +9513,7 @@ function bootstrapPlan(repo, repoClass) {
|
|
|
9507
9513
|
{ label: `apply branch protection / allowlist: ${protectedBranches}`, gated: true },
|
|
9508
9514
|
{ label: "attach GitHub Project v2 and register Hub registry META", gated: true },
|
|
9509
9515
|
{ label: "seed README.md and architecture.md", gated: true },
|
|
9510
|
-
{ label: "commit .cursor/rules/<repo>.mdc
|
|
9516
|
+
{ label: "commit .cursor/rules/<repo>.mdc", gated: true },
|
|
9511
9517
|
{ label: `register fanout target on ${repoClass === "content" ? "main" : "development"}`, gated: true }
|
|
9512
9518
|
];
|
|
9513
9519
|
}
|
|
@@ -9801,7 +9807,7 @@ async function runMergeTreePreflight(deps, ours, theirs) {
|
|
|
9801
9807
|
async function predictMergeConflicts(deps, ours, theirs) {
|
|
9802
9808
|
return runMergeTreePreflight(deps, ours, theirs);
|
|
9803
9809
|
}
|
|
9804
|
-
async function mergeWithToleratedResolution(deps, sourceRef, label,
|
|
9810
|
+
async function mergeWithToleratedResolution(deps, sourceRef, label, resolve5, extraTolerated = []) {
|
|
9805
9811
|
try {
|
|
9806
9812
|
await deps.run("git", ["merge", sourceRef, "--no-edit"]);
|
|
9807
9813
|
return;
|
|
@@ -9815,7 +9821,7 @@ async function mergeWithToleratedResolution(deps, sourceRef, label, resolve6, ex
|
|
|
9815
9821
|
unmerged.length === 0 ? `${label} merge failed without conflicted paths \u2014 merge aborted; inspect the repo state and rerun` : `${label} merge conflicts on untolerated path(s): ${blocking.join(", ")} \u2014 merge aborted (the train is misaligned; reconcile the branches via an approved alignment PR, then rerun)`
|
|
9816
9822
|
);
|
|
9817
9823
|
}
|
|
9818
|
-
await deps.run("git", ["checkout", `--${
|
|
9824
|
+
await deps.run("git", ["checkout", `--${resolve5}`, "--", ...unmerged]);
|
|
9819
9825
|
await deps.run("git", ["add", "--", ...unmerged]);
|
|
9820
9826
|
await deps.run("git", ["commit", "--no-edit"]);
|
|
9821
9827
|
}
|
|
@@ -9981,7 +9987,7 @@ function requireProjectMetaForTrain(load, repo) {
|
|
|
9981
9987
|
var CORRELATE_ATTEMPTS = 5;
|
|
9982
9988
|
var CORRELATE_DELAY_MS = 1500;
|
|
9983
9989
|
var CORRELATE_SKEW_SLACK_MS = 1e4;
|
|
9984
|
-
var defaultSleep2 = (ms) => new Promise((
|
|
9990
|
+
var defaultSleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
9985
9991
|
function resolveSleep(deps) {
|
|
9986
9992
|
return deps.sleep ?? defaultSleep2;
|
|
9987
9993
|
}
|
|
@@ -11147,7 +11153,7 @@ function clean2(out) {
|
|
|
11147
11153
|
return out.trim();
|
|
11148
11154
|
}
|
|
11149
11155
|
function sleeper(deps) {
|
|
11150
|
-
return deps.sleep ?? ((ms) => new Promise((
|
|
11156
|
+
return deps.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
|
|
11151
11157
|
}
|
|
11152
11158
|
function normalizeHotfixVersion(input) {
|
|
11153
11159
|
const m = /^v?(\d+\.\d+\.\d+)$/.exec(input.trim());
|
|
@@ -11582,12 +11588,46 @@ async function findInFlightHotfixVersion(deps, ctx, latestMainTag) {
|
|
|
11582
11588
|
return { inFlight: null, superseded };
|
|
11583
11589
|
}
|
|
11584
11590
|
|
|
11585
|
-
// src/
|
|
11586
|
-
var ANNOUNCE_REPO = "mutmutco/MMI-Hub";
|
|
11591
|
+
// src/slack-alert.ts
|
|
11587
11592
|
var SSM_REGION = "eu-central-1";
|
|
11588
11593
|
var SSM_TOKEN_PARAM = "/mmi-future/_org/slack/SLACK_BOT_TOKEN";
|
|
11589
11594
|
var SSM_CHANNEL_PARAM = "/mmi-future/_org/slack/SLACK_ALERTS_CHANNEL";
|
|
11590
11595
|
var SLACK_TIMEOUT_MS = 1e4;
|
|
11596
|
+
async function ssmParameter(deps, name, decrypt) {
|
|
11597
|
+
const args = [
|
|
11598
|
+
"ssm",
|
|
11599
|
+
"get-parameter",
|
|
11600
|
+
"--region",
|
|
11601
|
+
SSM_REGION,
|
|
11602
|
+
"--name",
|
|
11603
|
+
name,
|
|
11604
|
+
"--query",
|
|
11605
|
+
"Parameter.Value",
|
|
11606
|
+
"--output",
|
|
11607
|
+
"text",
|
|
11608
|
+
...decrypt ? ["--with-decryption"] : []
|
|
11609
|
+
];
|
|
11610
|
+
const value = (await deps.run("aws", args)).trim();
|
|
11611
|
+
if (!value || value === "None") throw new Error(`SSM parameter ${name} is empty`);
|
|
11612
|
+
return value;
|
|
11613
|
+
}
|
|
11614
|
+
async function postToAlertsChannel(deps, text) {
|
|
11615
|
+
const token = await ssmParameter(deps, SSM_TOKEN_PARAM, true);
|
|
11616
|
+
const channel = await ssmParameter(deps, SSM_CHANNEL_PARAM, false);
|
|
11617
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
11618
|
+
const res = await fetchImpl("https://slack.com/api/chat.postMessage", {
|
|
11619
|
+
method: "POST",
|
|
11620
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json; charset=utf-8" },
|
|
11621
|
+
body: JSON.stringify({ channel, text, unfurl_links: false }),
|
|
11622
|
+
signal: AbortSignal.timeout(SLACK_TIMEOUT_MS)
|
|
11623
|
+
});
|
|
11624
|
+
const json = await res.json().catch(() => ({}));
|
|
11625
|
+
if (!json.ok) throw new Error(`slack postMessage failed: ${json.error ?? `http ${res.status}`}`);
|
|
11626
|
+
return { status: "posted", note: "posted to the alerts channel" };
|
|
11627
|
+
}
|
|
11628
|
+
|
|
11629
|
+
// src/release-announce.ts
|
|
11630
|
+
var ANNOUNCE_REPO = "mutmutco/MMI-Hub";
|
|
11591
11631
|
var MAX_BULLETS = 6;
|
|
11592
11632
|
var MAX_SUMMARY_LINES = 8;
|
|
11593
11633
|
function neutralizeHubProductBrands(text) {
|
|
@@ -11662,24 +11702,6 @@ function formatAnnouncement(args) {
|
|
|
11662
11702
|
function summaryFileLines(content) {
|
|
11663
11703
|
return content.split("\n").map((l) => l.trim()).filter(Boolean).slice(0, MAX_SUMMARY_LINES).map((l) => escapeMrkdwn(neutralizeHubProductBrands(l.replace(/^[•*-]\s*/, "")))).filter(Boolean).map((l) => `\u2022 ${l}`);
|
|
11664
11704
|
}
|
|
11665
|
-
async function ssmParameter(deps, name, decrypt) {
|
|
11666
|
-
const args = [
|
|
11667
|
-
"ssm",
|
|
11668
|
-
"get-parameter",
|
|
11669
|
-
"--region",
|
|
11670
|
-
SSM_REGION,
|
|
11671
|
-
"--name",
|
|
11672
|
-
name,
|
|
11673
|
-
"--query",
|
|
11674
|
-
"Parameter.Value",
|
|
11675
|
-
"--output",
|
|
11676
|
-
"text",
|
|
11677
|
-
...decrypt ? ["--with-decryption"] : []
|
|
11678
|
-
];
|
|
11679
|
-
const value = (await deps.run("aws", args)).trim();
|
|
11680
|
-
if (!value || value === "None") throw new Error(`SSM parameter ${name} is empty`);
|
|
11681
|
-
return value;
|
|
11682
|
-
}
|
|
11683
11705
|
async function announceRelease(deps, args) {
|
|
11684
11706
|
if (args.repo !== ANNOUNCE_REPO) {
|
|
11685
11707
|
return { status: "skipped", note: `announce is Hub-only \u2014 ${args.repo} skipped` };
|
|
@@ -11701,18 +11723,8 @@ async function announceRelease(deps, args) {
|
|
|
11701
11723
|
}
|
|
11702
11724
|
if (lines.length === 0) lines = curateReleaseNotes(view.body ?? "");
|
|
11703
11725
|
if (lines.length === 0) lines = ["\u2022 maintenance release \u2014 see the notes for details"];
|
|
11704
|
-
const token = await ssmParameter(deps, SSM_TOKEN_PARAM, true);
|
|
11705
|
-
const channel = await ssmParameter(deps, SSM_CHANNEL_PARAM, false);
|
|
11706
11726
|
const text = formatAnnouncement({ repo: args.repo, tag: args.tag, lines, releaseUrl });
|
|
11707
|
-
|
|
11708
|
-
const res = await fetchImpl("https://slack.com/api/chat.postMessage", {
|
|
11709
|
-
method: "POST",
|
|
11710
|
-
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json; charset=utf-8" },
|
|
11711
|
-
body: JSON.stringify({ channel, text, unfurl_links: false }),
|
|
11712
|
-
signal: AbortSignal.timeout(SLACK_TIMEOUT_MS)
|
|
11713
|
-
});
|
|
11714
|
-
const json = await res.json().catch(() => ({}));
|
|
11715
|
-
if (!json.ok) throw new Error(`slack postMessage failed: ${json.error ?? `http ${res.status}`}`);
|
|
11727
|
+
await postToAlertsChannel(deps, text);
|
|
11716
11728
|
return { status: "announced", note: `announced ${args.tag} to the alerts channel` };
|
|
11717
11729
|
} catch (e) {
|
|
11718
11730
|
return { status: "failed", note: `announce failed (release unaffected): ${e.message}` };
|
|
@@ -12269,7 +12281,6 @@ async function verifyBootstrap(repo, repoClass, deps, releaseTrack) {
|
|
|
12269
12281
|
});
|
|
12270
12282
|
}
|
|
12271
12283
|
}
|
|
12272
|
-
checks.push({ ok: await contentExists2(deps, repo, baseBranch, ".cursor/environment.json"), label: "Cursor environment committed" });
|
|
12273
12284
|
const readme = await contentText(deps, repo, baseBranch, "README.md");
|
|
12274
12285
|
checks.push({
|
|
12275
12286
|
ok: readme !== null && readme.includes("## Agent context"),
|
|
@@ -12678,26 +12689,112 @@ async function wikiPublish(deps, opts) {
|
|
|
12678
12689
|
deps.err("wiki publish: <pages-dir> is required");
|
|
12679
12690
|
return false;
|
|
12680
12691
|
}
|
|
12681
|
-
const script = deps.scriptPath();
|
|
12682
|
-
if (!deps.scriptExists(script)) {
|
|
12683
|
-
deps.err(`wiki publish: publish script not found at ${script} \u2014 run from the repo checkout root`);
|
|
12684
|
-
return false;
|
|
12685
|
-
}
|
|
12686
12692
|
const minted = await deps.mint(opts.repo);
|
|
12687
12693
|
if (!minted.ok) {
|
|
12688
12694
|
deps.err(`wiki publish failed: could not mint a scoped wiki token for ${opts.repo}: ${minted.error}`);
|
|
12689
12695
|
return false;
|
|
12690
12696
|
}
|
|
12691
|
-
const
|
|
12692
|
-
|
|
12693
|
-
if (code !== 0) {
|
|
12694
|
-
deps.err(`wiki publish failed: publish script exited ${code} (token was scoped to ${opts.repo}, contents:write, expires ${minted.mint.expiresAt})`);
|
|
12697
|
+
const ok = deps.publish({ repo: opts.repo, token: minted.mint.token, pagesDir: opts.pagesDir });
|
|
12698
|
+
if (!ok) {
|
|
12695
12699
|
return false;
|
|
12696
12700
|
}
|
|
12697
12701
|
deps.log(`wiki published for ${opts.repo} (scoped token, expired/discarded)`);
|
|
12698
12702
|
return true;
|
|
12699
12703
|
}
|
|
12700
12704
|
|
|
12705
|
+
// src/wiki-publish-git.ts
|
|
12706
|
+
var import_node_child_process8 = require("node:child_process");
|
|
12707
|
+
var import_node_fs13 = require("node:fs");
|
|
12708
|
+
var import_node_os5 = require("node:os");
|
|
12709
|
+
var import_node_path14 = require("node:path");
|
|
12710
|
+
var WIKI_STAMP_FILE = ".wiki-state.json";
|
|
12711
|
+
function redactWikiSecrets(msg) {
|
|
12712
|
+
return msg.replace(/x-access-token:[^@\s]+@/gi, "x-access-token:***@").replace(/(AUTHORIZATION:\s*basic\s+)[A-Za-z0-9+/=]+/gi, "$1***");
|
|
12713
|
+
}
|
|
12714
|
+
function publishWikiViaGit(deps, opts) {
|
|
12715
|
+
const dir = deps.mkdtemp();
|
|
12716
|
+
const url = `https://github.com/${opts.repo}.wiki.git`;
|
|
12717
|
+
const authArgs = [
|
|
12718
|
+
"-c",
|
|
12719
|
+
`http.extraheader=AUTHORIZATION: basic ${Buffer.from(`x-access-token:${opts.token}`).toString("base64")}`
|
|
12720
|
+
];
|
|
12721
|
+
try {
|
|
12722
|
+
deps.gitRun(["init", "-q"], dir);
|
|
12723
|
+
deps.gitRun(["remote", "add", "origin", url], dir);
|
|
12724
|
+
if (deps.gitProbe([...authArgs, "fetch", "--depth", "1", "origin", "master"], dir)) {
|
|
12725
|
+
deps.gitRun(["reset", "--hard", "FETCH_HEAD"], dir);
|
|
12726
|
+
}
|
|
12727
|
+
let copied = 0;
|
|
12728
|
+
for (const f of deps.readdir(opts.pagesDir)) {
|
|
12729
|
+
if (f.endsWith(".md")) {
|
|
12730
|
+
deps.copyFile((0, import_node_path14.join)(opts.pagesDir, f), (0, import_node_path14.join)(dir, f));
|
|
12731
|
+
copied++;
|
|
12732
|
+
}
|
|
12733
|
+
}
|
|
12734
|
+
if (!copied) {
|
|
12735
|
+
deps.err(`wiki publish: no .md pages found in ${opts.pagesDir}`);
|
|
12736
|
+
return false;
|
|
12737
|
+
}
|
|
12738
|
+
if (deps.fileExists((0, import_node_path14.join)(opts.pagesDir, WIKI_STAMP_FILE))) {
|
|
12739
|
+
deps.copyFile((0, import_node_path14.join)(opts.pagesDir, WIKI_STAMP_FILE), (0, import_node_path14.join)(dir, WIKI_STAMP_FILE));
|
|
12740
|
+
}
|
|
12741
|
+
deps.gitRun(["add", "-A"], dir);
|
|
12742
|
+
if (deps.gitProbe(["diff", "--cached", "--quiet"], dir)) {
|
|
12743
|
+
deps.log("wiki publish: already up to date");
|
|
12744
|
+
return true;
|
|
12745
|
+
}
|
|
12746
|
+
deps.gitRun(
|
|
12747
|
+
[
|
|
12748
|
+
"-c",
|
|
12749
|
+
"user.email=mmi-github-app[bot]@users.noreply.github.com",
|
|
12750
|
+
"-c",
|
|
12751
|
+
"user.name=mmi-github-app[bot]",
|
|
12752
|
+
"commit",
|
|
12753
|
+
"-m",
|
|
12754
|
+
"docs(wiki): refresh MMI Future project wiki"
|
|
12755
|
+
],
|
|
12756
|
+
dir
|
|
12757
|
+
);
|
|
12758
|
+
try {
|
|
12759
|
+
deps.gitRun([...authArgs, "push", "origin", "HEAD:master"], dir);
|
|
12760
|
+
} catch (e) {
|
|
12761
|
+
deps.err(
|
|
12762
|
+
`wiki publish failed: push to ${opts.repo}.wiki.git rejected: ${redactWikiSecrets(e.message)} \u2014 if the wiki advanced before this push, re-run wiki publish to pick up the latest wiki state`
|
|
12763
|
+
);
|
|
12764
|
+
return false;
|
|
12765
|
+
}
|
|
12766
|
+
deps.log(`wiki publish: published ${copied} page(s) to ${opts.repo} wiki`);
|
|
12767
|
+
return true;
|
|
12768
|
+
} catch (e) {
|
|
12769
|
+
deps.err(`wiki publish failed: ${redactWikiSecrets(e.message)}`);
|
|
12770
|
+
return false;
|
|
12771
|
+
} finally {
|
|
12772
|
+
deps.rm(dir);
|
|
12773
|
+
}
|
|
12774
|
+
}
|
|
12775
|
+
function realWikiGitPublishDeps() {
|
|
12776
|
+
return {
|
|
12777
|
+
mkdtemp: () => (0, import_node_fs13.mkdtempSync)((0, import_node_path14.join)((0, import_node_os5.tmpdir)(), "wiki-")),
|
|
12778
|
+
readdir: (dir) => (0, import_node_fs13.readdirSync)(dir),
|
|
12779
|
+
copyFile: (src, dest) => (0, import_node_fs13.copyFileSync)(src, dest),
|
|
12780
|
+
fileExists: (p) => (0, import_node_fs13.existsSync)(p),
|
|
12781
|
+
rm: (p) => (0, import_node_fs13.rmSync)(p, { recursive: true, force: true }),
|
|
12782
|
+
gitProbe: (args, cwd) => {
|
|
12783
|
+
try {
|
|
12784
|
+
(0, import_node_child_process8.execFileSync)("git", args, { cwd, stdio: "pipe" });
|
|
12785
|
+
return true;
|
|
12786
|
+
} catch {
|
|
12787
|
+
return false;
|
|
12788
|
+
}
|
|
12789
|
+
},
|
|
12790
|
+
gitRun: (args, cwd) => {
|
|
12791
|
+
(0, import_node_child_process8.execFileSync)("git", args, { cwd, stdio: "inherit" });
|
|
12792
|
+
},
|
|
12793
|
+
log: (msg) => console.log(msg),
|
|
12794
|
+
err: (msg) => console.error(msg)
|
|
12795
|
+
};
|
|
12796
|
+
}
|
|
12797
|
+
|
|
12701
12798
|
// src/project-readiness.ts
|
|
12702
12799
|
function stagesForTrack(meta) {
|
|
12703
12800
|
return branchesForTrack(resolveReleaseTrack(meta)).map((b) => b === "development" ? "dev" : b);
|
|
@@ -13811,7 +13908,7 @@ function writeError(res) {
|
|
|
13811
13908
|
}
|
|
13812
13909
|
|
|
13813
13910
|
// src/secrets.ts
|
|
13814
|
-
var
|
|
13911
|
+
var import_node_child_process9 = require("node:child_process");
|
|
13815
13912
|
var OWNER2 = "mutmutco";
|
|
13816
13913
|
var SSM_ROOT = "/mmi-future";
|
|
13817
13914
|
var PROJECT_TIER_SEGMENT = "dev";
|
|
@@ -14542,7 +14639,7 @@ async function secretsCopy(deps, opts) {
|
|
|
14542
14639
|
return true;
|
|
14543
14640
|
}
|
|
14544
14641
|
function defaultSpawn(command, args, env) {
|
|
14545
|
-
const r = (0,
|
|
14642
|
+
const r = (0, import_node_child_process9.spawnSync)(command, args, { stdio: "inherit", env });
|
|
14546
14643
|
if (r.error) throw r.error;
|
|
14547
14644
|
return r.status ?? 1;
|
|
14548
14645
|
}
|
|
@@ -14602,8 +14699,8 @@ async function secretsUse(deps, key, opts) {
|
|
|
14602
14699
|
}
|
|
14603
14700
|
|
|
14604
14701
|
// src/secrets-commands.ts
|
|
14605
|
-
var
|
|
14606
|
-
var
|
|
14702
|
+
var import_node_fs14 = require("node:fs");
|
|
14703
|
+
var import_node_path15 = require("node:path");
|
|
14607
14704
|
var RAILS_CREDENTIALS_DECRYPT_TIMEOUT_MS = 3e4;
|
|
14608
14705
|
var DEFAULT_RAILS_CREDENTIALS_FILE = "config/credentials.yml.enc";
|
|
14609
14706
|
var DEFAULT_RAILS_MASTER_KEY_FILE = "config/master.key";
|
|
@@ -14611,18 +14708,18 @@ function collectMap(value, previous = []) {
|
|
|
14611
14708
|
return [...previous, value];
|
|
14612
14709
|
}
|
|
14613
14710
|
async function decryptRailsCredentials(input) {
|
|
14614
|
-
const appDir = (0,
|
|
14711
|
+
const appDir = (0, import_node_path15.resolve)(input.appDir ?? process.cwd());
|
|
14615
14712
|
const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
14616
14713
|
const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
14617
|
-
const credentialsPath = (0,
|
|
14618
|
-
const masterKeyPath = (0,
|
|
14714
|
+
const credentialsPath = (0, import_node_path15.resolve)(appDir, credentialsFile);
|
|
14715
|
+
const masterKeyPath = (0, import_node_path15.resolve)(appDir, masterKeyFile);
|
|
14619
14716
|
const env = {
|
|
14620
14717
|
...process.env,
|
|
14621
14718
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
14622
14719
|
MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
|
|
14623
14720
|
};
|
|
14624
|
-
if ((0,
|
|
14625
|
-
env.RAILS_MASTER_KEY = (0,
|
|
14721
|
+
if ((0, import_node_fs14.existsSync)(masterKeyPath)) {
|
|
14722
|
+
env.RAILS_MASTER_KEY = (0, import_node_fs14.readFileSync)(masterKeyPath, "utf8").trim();
|
|
14626
14723
|
}
|
|
14627
14724
|
const script = [
|
|
14628
14725
|
'require "json"',
|
|
@@ -14688,7 +14785,7 @@ function registerSecretsCommands(program3) {
|
|
|
14688
14785
|
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) => {
|
|
14689
14786
|
let body;
|
|
14690
14787
|
try {
|
|
14691
|
-
body = (0,
|
|
14788
|
+
body = (0, import_node_fs14.readFileSync)((0, import_node_path15.resolve)(o.file), "utf8");
|
|
14692
14789
|
} catch (e) {
|
|
14693
14790
|
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
14694
14791
|
}
|
|
@@ -14763,7 +14860,7 @@ function registerSecretsCommands(program3) {
|
|
|
14763
14860
|
{
|
|
14764
14861
|
...d,
|
|
14765
14862
|
decryptRailsCredentials,
|
|
14766
|
-
removeFile: (path2) => (0,
|
|
14863
|
+
removeFile: (path2) => (0, import_node_fs14.unlinkSync)((0, import_node_path15.resolve)(o.appDir ?? process.cwd(), path2))
|
|
14767
14864
|
},
|
|
14768
14865
|
{
|
|
14769
14866
|
repo: o.repo,
|
|
@@ -14852,11 +14949,11 @@ function registerEdgeCommands(program3) {
|
|
|
14852
14949
|
}
|
|
14853
14950
|
|
|
14854
14951
|
// src/doctor-run.ts
|
|
14855
|
-
var
|
|
14856
|
-
var
|
|
14952
|
+
var import_node_fs18 = require("node:fs");
|
|
14953
|
+
var import_node_child_process11 = require("node:child_process");
|
|
14857
14954
|
var import_promises2 = require("node:fs/promises");
|
|
14858
|
-
var
|
|
14859
|
-
var
|
|
14955
|
+
var import_node_path18 = require("node:path");
|
|
14956
|
+
var import_node_os7 = require("node:os");
|
|
14860
14957
|
|
|
14861
14958
|
// src/plugin-guard.ts
|
|
14862
14959
|
function buildPluginGuardDecision(i) {
|
|
@@ -14868,140 +14965,20 @@ function buildPluginGuardDecision(i) {
|
|
|
14868
14965
|
function buildGuardSessionStartLine(state, opts = {}) {
|
|
14869
14966
|
if (state === "healthy" || state === "not-org") return { exitCode: 0 };
|
|
14870
14967
|
const recovery = opts.recovery ?? "mmi-cli plugin-heal";
|
|
14968
|
+
const restartHint = opts.restartHint ?? "restart your agent host / reload plugins";
|
|
14871
14969
|
const reason = state === "no-install" ? "MMI plugin is not installed for this user/session" : "MMI plugin is installed but its marketplace/cache is unresolved";
|
|
14872
14970
|
return {
|
|
14873
|
-
line: `[mmi-guard] ${reason}; run ${recovery} and
|
|
14971
|
+
line: `[mmi-guard] ${reason}; run ${recovery} and ${restartHint}.`,
|
|
14874
14972
|
exitCode: 1
|
|
14875
14973
|
};
|
|
14876
14974
|
}
|
|
14877
14975
|
|
|
14878
14976
|
// src/cursor-plugin-seed.ts
|
|
14879
|
-
var
|
|
14880
|
-
var
|
|
14881
|
-
var
|
|
14882
|
-
var
|
|
14977
|
+
var import_node_child_process10 = require("node:child_process");
|
|
14978
|
+
var import_node_fs15 = require("node:fs");
|
|
14979
|
+
var import_node_os6 = require("node:os");
|
|
14980
|
+
var import_node_path16 = require("node:path");
|
|
14883
14981
|
var import_node_util7 = require("node:util");
|
|
14884
|
-
function isSemverVersion(v) {
|
|
14885
|
-
return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
|
|
14886
|
-
}
|
|
14887
|
-
var MMI_HUB_REPO = "mutmutco/MMI-Hub";
|
|
14888
|
-
var CURSOR_THIRD_PARTY_STATE_KEY = "cursor/thirdPartyExtensibilityEnabled";
|
|
14889
|
-
var PLUGIN_JSON_REL = ".cursor-plugin/plugin.json";
|
|
14890
|
-
var execFileBuffer = (0, import_node_util7.promisify)(import_node_child_process9.execFile);
|
|
14891
|
-
function gitFetchReleaseTagArgs(hubCheckout, tag) {
|
|
14892
|
-
return ["-C", hubCheckout, "fetch", "origin", "tag", tag, "--quiet"];
|
|
14893
|
-
}
|
|
14894
|
-
function ghReleaseTarballApiArgs(tag) {
|
|
14895
|
-
return ["api", `repos/${MMI_HUB_REPO}/tarball/refs/tags/${tag}`];
|
|
14896
|
-
}
|
|
14897
|
-
function cursorUserGlobalStatePath() {
|
|
14898
|
-
if (process.platform === "win32") {
|
|
14899
|
-
const base = process.env.APPDATA || (0, import_node_path15.join)((0, import_node_os5.homedir)(), "AppData", "Roaming");
|
|
14900
|
-
return (0, import_node_path15.join)(base, "Cursor", "User", "globalStorage", "state.vscdb");
|
|
14901
|
-
}
|
|
14902
|
-
if (process.platform === "darwin") {
|
|
14903
|
-
return (0, import_node_path15.join)((0, import_node_os5.homedir)(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
14904
|
-
}
|
|
14905
|
-
return (0, import_node_path15.join)((0, import_node_os5.homedir)(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
14906
|
-
}
|
|
14907
|
-
async function readCursorThirdPartyExtensibilityEnabled(execFileP5) {
|
|
14908
|
-
const dbPath = cursorUserGlobalStatePath();
|
|
14909
|
-
if (!(0, import_node_fs14.existsSync)(dbPath)) return void 0;
|
|
14910
|
-
try {
|
|
14911
|
-
const { stdout } = await execFileP5("sqlite3", [dbPath, `SELECT value FROM ItemTable WHERE key = '${CURSOR_THIRD_PARTY_STATE_KEY}';`], {
|
|
14912
|
-
timeout: 5e3
|
|
14913
|
-
});
|
|
14914
|
-
const value = stdout.trim().toLowerCase();
|
|
14915
|
-
if (value === "true") return true;
|
|
14916
|
-
if (value === "false") return false;
|
|
14917
|
-
return void 0;
|
|
14918
|
-
} catch {
|
|
14919
|
-
return void 0;
|
|
14920
|
-
}
|
|
14921
|
-
}
|
|
14922
|
-
function syncDirContents(src, dest) {
|
|
14923
|
-
(0, import_node_fs14.mkdirSync)(dest, { recursive: true });
|
|
14924
|
-
for (const name of (0, import_node_fs14.readdirSync)(dest)) {
|
|
14925
|
-
(0, import_node_fs14.rmSync)((0, import_node_path15.join)(dest, name), { recursive: true, force: true });
|
|
14926
|
-
}
|
|
14927
|
-
(0, import_node_fs14.cpSync)(src, dest, { recursive: true });
|
|
14928
|
-
}
|
|
14929
|
-
function releaseTag(releasedVersion) {
|
|
14930
|
-
return releasedVersion.startsWith("v") ? releasedVersion : `v${releasedVersion}`;
|
|
14931
|
-
}
|
|
14932
|
-
async function extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5) {
|
|
14933
|
-
const tarFile = (0, import_node_path15.join)(tmpRoot, "archive.tar");
|
|
14934
|
-
try {
|
|
14935
|
-
await execFileP5("git", gitFetchReleaseTagArgs(hubCheckout, tag), { timeout: 6e4 });
|
|
14936
|
-
await execFileP5("git", ["-C", hubCheckout, "archive", "--format=tar", `--output=${tarFile}`, tag, "plugins/mmi"], {
|
|
14937
|
-
timeout: 6e4
|
|
14938
|
-
});
|
|
14939
|
-
await execFileP5("tar", ["-xf", tarFile, "-C", tmpRoot], { timeout: 6e4 });
|
|
14940
|
-
const pluginMmi = (0, import_node_path15.join)(tmpRoot, "plugins", "mmi");
|
|
14941
|
-
return (0, import_node_fs14.existsSync)((0, import_node_path15.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
|
|
14942
|
-
} catch {
|
|
14943
|
-
return void 0;
|
|
14944
|
-
}
|
|
14945
|
-
}
|
|
14946
|
-
async function downloadPluginMmiViaGh(tag, tmpRoot) {
|
|
14947
|
-
const tarPath = (0, import_node_path15.join)(tmpRoot, "repo.tgz");
|
|
14948
|
-
try {
|
|
14949
|
-
(0, import_node_fs14.mkdirSync)(tmpRoot, { recursive: true });
|
|
14950
|
-
const { stdout } = await execFileBuffer("gh", ghReleaseTarballApiArgs(tag), {
|
|
14951
|
-
timeout: 12e4,
|
|
14952
|
-
maxBuffer: 100 * 1024 * 1024,
|
|
14953
|
-
encoding: "buffer",
|
|
14954
|
-
windowsHide: true
|
|
14955
|
-
});
|
|
14956
|
-
(0, import_node_fs14.writeFileSync)(tarPath, stdout);
|
|
14957
|
-
await execFileBuffer("tar", ["-xzf", tarPath, "-C", tmpRoot], { timeout: 12e4, windowsHide: true });
|
|
14958
|
-
const top = (0, import_node_fs14.readdirSync)(tmpRoot).find((entry) => entry !== "repo.tgz");
|
|
14959
|
-
if (!top) return void 0;
|
|
14960
|
-
const pluginMmi = (0, import_node_path15.join)(tmpRoot, top, "plugins", "mmi");
|
|
14961
|
-
return (0, import_node_fs14.existsSync)((0, import_node_path15.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
|
|
14962
|
-
} catch {
|
|
14963
|
-
return void 0;
|
|
14964
|
-
}
|
|
14965
|
-
}
|
|
14966
|
-
async function resolvePluginMmiSource(releasedVersion, hubCheckout, tmpRoot, execFileP5) {
|
|
14967
|
-
(0, import_node_fs14.mkdirSync)(tmpRoot, { recursive: true });
|
|
14968
|
-
const tag = releaseTag(releasedVersion);
|
|
14969
|
-
if (hubCheckout) {
|
|
14970
|
-
const fromHub = await extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5);
|
|
14971
|
-
if (fromHub) return fromHub;
|
|
14972
|
-
}
|
|
14973
|
-
return downloadPluginMmiViaGh(tag, (0, import_node_path15.join)(tmpRoot, "gh"));
|
|
14974
|
-
}
|
|
14975
|
-
function cursorPluginPinsNeedingSeed(pins, releasedVersion) {
|
|
14976
|
-
if (!isSemverVersion(releasedVersion)) return pins.filter((pin) => !pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty);
|
|
14977
|
-
return pins.filter((pin) => {
|
|
14978
|
-
if (!pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty) return true;
|
|
14979
|
-
if (isSemverVersion(pin.version) && compareVersions(pin.version, releasedVersion) < 0) return true;
|
|
14980
|
-
return false;
|
|
14981
|
-
});
|
|
14982
|
-
}
|
|
14983
|
-
function cursorPluginCacheSeedTargets(pins, releasedVersion, cacheRoot, cacheRootExists = false) {
|
|
14984
|
-
const needing = cursorPluginPinsNeedingSeed(pins, releasedVersion);
|
|
14985
|
-
if (needing.length > 0) return needing.map((pin) => pin.path);
|
|
14986
|
-
if (pins.length === 0 && cacheRoot && cacheRootExists && isSemverVersion(releasedVersion)) {
|
|
14987
|
-
return [(0, import_node_path15.join)(cacheRoot, `v${releasedVersion.replace(/^v/, "")}`)];
|
|
14988
|
-
}
|
|
14989
|
-
return [];
|
|
14990
|
-
}
|
|
14991
|
-
async function applyCursorPluginCacheSeed(input) {
|
|
14992
|
-
if (!isSemverVersion(input.releasedVersion)) return false;
|
|
14993
|
-
const targets = cursorPluginCacheSeedTargets(input.pins, input.releasedVersion, input.cacheRoot, input.cacheRootExists);
|
|
14994
|
-
if (targets.length === 0) return false;
|
|
14995
|
-
const tmpRoot = await input.mkdtemp("mmi-cursor-seed-");
|
|
14996
|
-
const source = await resolvePluginMmiSource(input.releasedVersion, input.hubCheckout, tmpRoot, input.execFileP);
|
|
14997
|
-
if (!source) return false;
|
|
14998
|
-
input.log(` \u21BB seeding Cursor MMI plugin cache \u2192 ${input.releasedVersion}\u2026`);
|
|
14999
|
-
for (const dest of targets) {
|
|
15000
|
-
syncDirContents(source, dest);
|
|
15001
|
-
}
|
|
15002
|
-
(0, import_node_fs14.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
15003
|
-
return true;
|
|
15004
|
-
}
|
|
15005
14982
|
|
|
15006
14983
|
// src/doctor.ts
|
|
15007
14984
|
var GH_PROJECT_LOGIN_FIX = 'run: gh auth login --hostname github.com --git-protocol https --web --scopes "project"';
|
|
@@ -15285,8 +15262,8 @@ function buildCursorPluginCacheCleanupCheck(input) {
|
|
|
15285
15262
|
label: CURSOR_PLUGIN_CACHE_CLEANUP_LABEL,
|
|
15286
15263
|
fix: CURSOR_PLUGIN_CACHE_CLEANUP_FIX
|
|
15287
15264
|
};
|
|
15288
|
-
if (!input.isOrgRepo || !
|
|
15289
|
-
const semverPins = input.pins.filter((pin) =>
|
|
15265
|
+
if (!input.isOrgRepo || !isSemverVersion(input.releasedVersion)) return base;
|
|
15266
|
+
const semverPins = input.pins.filter((pin) => isSemverVersion(pin.version));
|
|
15290
15267
|
if (semverPins.length === 0) return base;
|
|
15291
15268
|
const activeVersion = semverPins.map((pin) => pin.version).reduce((a, b) => compareVersions(a, b) >= 0 ? a : b);
|
|
15292
15269
|
const protectedVersions = new Set(
|
|
@@ -15336,7 +15313,7 @@ function codexEnabledPluginVersionFromList(jsonText, pluginId = MMI_PLUGIN_ID) {
|
|
|
15336
15313
|
const parsed = JSON.parse(jsonText);
|
|
15337
15314
|
const rows = Array.isArray(parsed?.installed) ? parsed.installed : [];
|
|
15338
15315
|
const row = rows.find((r) => r?.pluginId === pluginId && r.installed === true && r.enabled === true);
|
|
15339
|
-
return
|
|
15316
|
+
return isSemverVersion(row?.version) ? row.version.trim().replace(/^v/, "") : void 0;
|
|
15340
15317
|
} catch {
|
|
15341
15318
|
return void 0;
|
|
15342
15319
|
}
|
|
@@ -15347,9 +15324,9 @@ function buildCodexActiveCacheCheck(input) {
|
|
|
15347
15324
|
label: CODEX_ACTIVE_CACHE_LABEL,
|
|
15348
15325
|
fix: CODEX_PLUGIN_RECOVERY
|
|
15349
15326
|
};
|
|
15350
|
-
if (!input.isOrgRepo || !
|
|
15327
|
+
if (!input.isOrgRepo || !isSemverVersion(input.releasedVersion)) return base;
|
|
15351
15328
|
if (input.codexPresent === false) return base;
|
|
15352
|
-
if (
|
|
15329
|
+
if (isSemverVersion(input.codexActiveVersion)) {
|
|
15353
15330
|
if (compareVersions(input.codexActiveVersion, input.releasedVersion) < 0) {
|
|
15354
15331
|
return {
|
|
15355
15332
|
...base,
|
|
@@ -15361,11 +15338,11 @@ function buildCodexActiveCacheCheck(input) {
|
|
|
15361
15338
|
}
|
|
15362
15339
|
return { ...base, activeCacheVersion: input.codexActiveVersion, releasedVersion: input.releasedVersion };
|
|
15363
15340
|
}
|
|
15364
|
-
if (
|
|
15341
|
+
if (isSemverVersion(input.codexRecordVersion) && compareVersions(input.codexRecordVersion, input.releasedVersion) < 0) {
|
|
15365
15342
|
return base;
|
|
15366
15343
|
}
|
|
15367
15344
|
const activeCacheVersion = highestSemver(input.codexCacheVersions ?? []);
|
|
15368
|
-
const cacheCurrent =
|
|
15345
|
+
const cacheCurrent = isSemverVersion(activeCacheVersion) && compareVersions(activeCacheVersion, input.releasedVersion) >= 0;
|
|
15369
15346
|
if (cacheCurrent) {
|
|
15370
15347
|
return { ...base, activeCacheVersion, releasedVersion: input.releasedVersion };
|
|
15371
15348
|
}
|
|
@@ -15444,7 +15421,7 @@ function buildSessionSkillRootCheck(input) {
|
|
|
15444
15421
|
const sessionVersion = parseSessionSkillRootVersion(input.pluginRoot);
|
|
15445
15422
|
if (!sessionVersion) return base;
|
|
15446
15423
|
const activeCacheVersion = highestSemver(input.cacheVersions ?? []);
|
|
15447
|
-
if (!
|
|
15424
|
+
if (!isSemverVersion(activeCacheVersion)) return { ...base, sessionVersion };
|
|
15448
15425
|
if (compareVersions(sessionVersion, activeCacheVersion) >= 0) {
|
|
15449
15426
|
return { ...base, sessionVersion, activeCacheVersion };
|
|
15450
15427
|
}
|
|
@@ -15526,7 +15503,8 @@ var PLUGIN_SURFACE_HEAL = {
|
|
|
15526
15503
|
],
|
|
15527
15504
|
pluginRunner: "codex",
|
|
15528
15505
|
fix: (surface) => `${CODEX_RECOVERY} # then ${reloadAction(surface)}`,
|
|
15529
|
-
updateRecipe: [CODEX_RECOVERY, "codex plugin list # verify mmi@mutmutco shows the released version"]
|
|
15506
|
+
updateRecipe: [CODEX_RECOVERY, "codex plugin list # verify mmi@mutmutco shows the released version"],
|
|
15507
|
+
guardRecovery: "$env:MMI_AGENT_SURFACE='codex'; mmi-cli plugin-heal"
|
|
15530
15508
|
},
|
|
15531
15509
|
cursor: {
|
|
15532
15510
|
delivery: "cursor-cache",
|
|
@@ -15610,7 +15588,7 @@ function renderSurfaceGuide(label, steps) {
|
|
|
15610
15588
|
}
|
|
15611
15589
|
function highestSemver(versions) {
|
|
15612
15590
|
return versions.reduce((best, v) => {
|
|
15613
|
-
if (!
|
|
15591
|
+
if (!isSemverVersion(v)) return best;
|
|
15614
15592
|
if (best === void 0) return v;
|
|
15615
15593
|
return compareVersions(v, best) > 0 ? v : best;
|
|
15616
15594
|
}, void 0);
|
|
@@ -15622,12 +15600,12 @@ function buildPluginUpdateReport(input) {
|
|
|
15622
15600
|
const opencodePlugin = highestSemver(input.opencodePluginVersions ?? []);
|
|
15623
15601
|
return {
|
|
15624
15602
|
versions: {
|
|
15625
|
-
...
|
|
15603
|
+
...isSemverVersion(input.cliVersion) ? { cli: input.cliVersion } : {},
|
|
15626
15604
|
...claudePlugin ? { claudePlugin } : {},
|
|
15627
15605
|
...codexMarketplace ? { codexMarketplace } : {},
|
|
15628
15606
|
...codexActiveCache ? { codexActiveCache } : {},
|
|
15629
15607
|
...opencodePlugin ? { opencodePlugin } : {},
|
|
15630
|
-
...
|
|
15608
|
+
...isSemverVersion(input.releasedVersion) ? { released: input.releasedVersion } : {},
|
|
15631
15609
|
...input.releasedVersionSource ? { releasedSource: input.releasedVersionSource } : {}
|
|
15632
15610
|
},
|
|
15633
15611
|
recipes: PLUGIN_UPDATE_RECIPES
|
|
@@ -15651,14 +15629,15 @@ function renderPluginVersionBlock(report) {
|
|
|
15651
15629
|
}
|
|
15652
15630
|
function buildDoctorJsonPayload(input) {
|
|
15653
15631
|
return {
|
|
15654
|
-
ok: input.checks
|
|
15632
|
+
ok: doctorExitCode(input.checks) === 0,
|
|
15633
|
+
hasAdvisories: input.checks.some((c) => !c.ok && isAdvisoryDoctorCheck(c)),
|
|
15655
15634
|
checks: input.checks,
|
|
15656
15635
|
updateReport: input.updateReport,
|
|
15657
15636
|
...input.resources.length ? { resources: input.resources } : {}
|
|
15658
15637
|
};
|
|
15659
15638
|
}
|
|
15660
15639
|
var INSTALLED_PLUGIN_VERSION_LABEL = "installed MMI plugin version (vs latest release)";
|
|
15661
|
-
function
|
|
15640
|
+
function isSemverVersion(v) {
|
|
15662
15641
|
return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
|
|
15663
15642
|
}
|
|
15664
15643
|
function staleRecordCommand(surface) {
|
|
@@ -15681,7 +15660,7 @@ function buildInstalledPluginVersionCheck(input) {
|
|
|
15681
15660
|
fix: pluginRecoveryFix(input.surface),
|
|
15682
15661
|
pluginId
|
|
15683
15662
|
};
|
|
15684
|
-
if (!input.isOrgRepo || !
|
|
15663
|
+
if (!input.isOrgRepo || !isSemverVersion(input.releasedVersion)) return base;
|
|
15685
15664
|
if (input.surface === "cursor") return base;
|
|
15686
15665
|
const stale = [];
|
|
15687
15666
|
let sawRecord = false;
|
|
@@ -15691,7 +15670,7 @@ function buildInstalledPluginVersionCheck(input) {
|
|
|
15691
15670
|
if (!Array.isArray(records) || records.length === 0) continue;
|
|
15692
15671
|
sawRecord = true;
|
|
15693
15672
|
const installedVersion = bestRecord(records).version;
|
|
15694
|
-
if (!
|
|
15673
|
+
if (!isSemverVersion(installedVersion)) continue;
|
|
15695
15674
|
if (compareVersions(installedVersion, input.releasedVersion) >= 0) {
|
|
15696
15675
|
currentVersion = currentVersion ?? installedVersion;
|
|
15697
15676
|
} else {
|
|
@@ -15746,14 +15725,14 @@ function buildManagedPluginDriftCheck(input) {
|
|
|
15746
15725
|
for (const source of input.sources) {
|
|
15747
15726
|
if (source.directVersions && descriptor.id in source.directVersions) {
|
|
15748
15727
|
const version2 = source.directVersions[descriptor.id];
|
|
15749
|
-
if (
|
|
15728
|
+
if (isSemverVersion(version2)) versions.push({ surface: source.surface, version: normalize(version2) });
|
|
15750
15729
|
continue;
|
|
15751
15730
|
}
|
|
15752
15731
|
const records = source.installed?.plugins?.[descriptor.id];
|
|
15753
15732
|
if (!Array.isArray(records) || records.length === 0) continue;
|
|
15754
15733
|
const recordVersion = bestRecord(records).version;
|
|
15755
|
-
const version =
|
|
15756
|
-
if (!
|
|
15734
|
+
const version = isSemverVersion(recordVersion) ? recordVersion : highestSemver(source.cacheVersions?.[descriptor.id] ?? []);
|
|
15735
|
+
if (!isSemverVersion(version)) continue;
|
|
15757
15736
|
versions.push({ surface: source.surface, version: normalize(version) });
|
|
15758
15737
|
}
|
|
15759
15738
|
if (versions.length < 2) continue;
|
|
@@ -15768,8 +15747,8 @@ var OPENCODE_VERSION_LABEL = "installed OpenCode MMI adapter version (vs latest
|
|
|
15768
15747
|
function buildOpencodeVersionCheck(input) {
|
|
15769
15748
|
const fix = pluginRecoveryFix("opencode");
|
|
15770
15749
|
const base = { ok: true, label: OPENCODE_VERSION_LABEL, fix };
|
|
15771
|
-
if (!input.isOrgRepo || !
|
|
15772
|
-
if (!
|
|
15750
|
+
if (!input.isOrgRepo || !isSemverVersion(input.releasedVersion)) return base;
|
|
15751
|
+
if (!isSemverVersion(input.installedVersion)) {
|
|
15773
15752
|
return { ...base, ok: false, severityOverride: "hard", releasedVersion: input.releasedVersion };
|
|
15774
15753
|
}
|
|
15775
15754
|
if (compareVersions(input.installedVersion, input.releasedVersion) >= 0) {
|
|
@@ -15786,7 +15765,7 @@ function pickOpencodeActiveVersion(input) {
|
|
|
15786
15765
|
}
|
|
15787
15766
|
function planOpencodePluginCacheQuarantine(input) {
|
|
15788
15767
|
if (!input.cacheExists) return { quarantine: false };
|
|
15789
|
-
if (!
|
|
15768
|
+
if (!isSemverVersion(input.cacheVersion) || !isSemverVersion(input.releasedVersion)) return { quarantine: false };
|
|
15790
15769
|
if (compareVersions(input.cacheVersion, input.releasedVersion) < 0) {
|
|
15791
15770
|
return { quarantine: true, cacheVersion: input.cacheVersion, releasedVersion: input.releasedVersion };
|
|
15792
15771
|
}
|
|
@@ -16022,8 +16001,8 @@ function buildCursorPluginInstallCheck(input) {
|
|
|
16022
16001
|
};
|
|
16023
16002
|
}
|
|
16024
16003
|
}
|
|
16025
|
-
if (input.cacheRootExists &&
|
|
16026
|
-
const stale = input.pins.filter((pin) =>
|
|
16004
|
+
if (input.cacheRootExists && isSemverVersion(input.releasedVersion) && input.pins.some((pin) => isSemverVersion(pin.version) && compareVersions(pin.version, input.releasedVersion) < 0)) {
|
|
16005
|
+
const stale = input.pins.filter((pin) => isSemverVersion(pin.version) && compareVersions(pin.version, input.releasedVersion) < 0).map((pin) => `${pin.version} at ${pin.name}`).join(", ");
|
|
16027
16006
|
return {
|
|
16028
16007
|
...base,
|
|
16029
16008
|
ok: false,
|
|
@@ -16217,18 +16196,19 @@ function isAdvisoryDoctorCheck(check) {
|
|
|
16217
16196
|
function doctorExitCode(checks) {
|
|
16218
16197
|
return checks.some((c) => !c.ok && !isAdvisoryDoctorCheck(c)) ? 1 : 0;
|
|
16219
16198
|
}
|
|
16220
|
-
function renderPluginUpdateReportStaleOnly(report) {
|
|
16199
|
+
function renderPluginUpdateReportStaleOnly(report, allChecksPass = false) {
|
|
16221
16200
|
const v = report.versions;
|
|
16222
16201
|
const released = v.released;
|
|
16223
16202
|
if (!released) return [];
|
|
16224
|
-
const isStale = (current) => Boolean(current &&
|
|
16203
|
+
const isStale = (current) => Boolean(current && isSemverVersion(current) && compareVersions(current, released) < 0);
|
|
16225
16204
|
const blocks = [];
|
|
16226
16205
|
for (const surface of PLUGIN_GUIDE_SURFACES) {
|
|
16227
16206
|
if (!surface.versionKeys.some((k) => isStale(v[k]))) continue;
|
|
16228
16207
|
blocks.push(...renderSurfaceGuide(surface.label, report.recipes[surface.key]));
|
|
16229
16208
|
}
|
|
16230
16209
|
if (!blocks.length) return [];
|
|
16231
|
-
|
|
16210
|
+
const heading = allChecksPass ? "Optional updates available (all checks passed):" : "Update commands (stale surfaces):";
|
|
16211
|
+
return [heading, ...blocks];
|
|
16232
16212
|
}
|
|
16233
16213
|
var DOCTOR_VERBOSE_HINT = "Run mmi-cli doctor --verbose for the full audit checklist + version report.";
|
|
16234
16214
|
function doctorCheckGlyph(check) {
|
|
@@ -16274,7 +16254,7 @@ function doctorHumanLines(input) {
|
|
|
16274
16254
|
if (input.shouldApply && input.pluginReloadRequired) {
|
|
16275
16255
|
lines.push("", `\u21BB ${input.reloadHint ?? "reload"} so the healed plugin/MCP installs load.`);
|
|
16276
16256
|
}
|
|
16277
|
-
const stale = renderPluginUpdateReportStaleOnly(input.updateReport);
|
|
16257
|
+
const stale = renderPluginUpdateReportStaleOnly(input.updateReport, gaps.length === 0);
|
|
16278
16258
|
if (stale.length) {
|
|
16279
16259
|
lines.push("", ...stale);
|
|
16280
16260
|
} else if (!input.verbose) {
|
|
@@ -16344,7 +16324,7 @@ function buildPluginResolvabilityCheck(input) {
|
|
|
16344
16324
|
return {
|
|
16345
16325
|
ok: false,
|
|
16346
16326
|
label: PLUGIN_RESOLVABILITY_LABEL,
|
|
16347
|
-
fix:
|
|
16327
|
+
fix: `run: ${PLUGIN_SURFACE_HEAL[surfaceToken(surface) ?? "claude"]?.recovery ?? PLUGIN_SURFACE_HEAL.claude.recovery}`,
|
|
16348
16328
|
state
|
|
16349
16329
|
};
|
|
16350
16330
|
default: {
|
|
@@ -16354,6 +16334,176 @@ function buildPluginResolvabilityCheck(input) {
|
|
|
16354
16334
|
}
|
|
16355
16335
|
}
|
|
16356
16336
|
|
|
16337
|
+
// src/cursor-plugin-seed.ts
|
|
16338
|
+
function isSemverVersion2(v) {
|
|
16339
|
+
return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
|
|
16340
|
+
}
|
|
16341
|
+
var MMI_HUB_REPO = "mutmutco/MMI-Hub";
|
|
16342
|
+
var CURSOR_THIRD_PARTY_STATE_KEY = "cursor/thirdPartyExtensibilityEnabled";
|
|
16343
|
+
var PLUGIN_JSON_REL = ".cursor-plugin/plugin.json";
|
|
16344
|
+
var execFileBuffer = (0, import_node_util7.promisify)(import_node_child_process10.execFile);
|
|
16345
|
+
function gitFetchReleaseTagArgs(hubCheckout, tag) {
|
|
16346
|
+
return ["-C", hubCheckout, "fetch", "origin", "tag", tag, "--quiet"];
|
|
16347
|
+
}
|
|
16348
|
+
function ghReleaseTarballApiArgs(tag) {
|
|
16349
|
+
return ["api", `repos/${MMI_HUB_REPO}/tarball/refs/tags/${tag}`];
|
|
16350
|
+
}
|
|
16351
|
+
function cursorUserGlobalStatePath() {
|
|
16352
|
+
if (process.platform === "win32") {
|
|
16353
|
+
const base = process.env.APPDATA || (0, import_node_path16.join)((0, import_node_os6.homedir)(), "AppData", "Roaming");
|
|
16354
|
+
return (0, import_node_path16.join)(base, "Cursor", "User", "globalStorage", "state.vscdb");
|
|
16355
|
+
}
|
|
16356
|
+
if (process.platform === "darwin") {
|
|
16357
|
+
return (0, import_node_path16.join)((0, import_node_os6.homedir)(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
16358
|
+
}
|
|
16359
|
+
return (0, import_node_path16.join)((0, import_node_os6.homedir)(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
16360
|
+
}
|
|
16361
|
+
async function readCursorThirdPartyExtensibilityEnabled(execFileP5) {
|
|
16362
|
+
const dbPath = cursorUserGlobalStatePath();
|
|
16363
|
+
if (!(0, import_node_fs15.existsSync)(dbPath)) return void 0;
|
|
16364
|
+
try {
|
|
16365
|
+
const { stdout } = await execFileP5("sqlite3", [dbPath, `SELECT value FROM ItemTable WHERE key = '${CURSOR_THIRD_PARTY_STATE_KEY}';`], {
|
|
16366
|
+
timeout: 5e3
|
|
16367
|
+
});
|
|
16368
|
+
const value = stdout.trim().toLowerCase();
|
|
16369
|
+
if (value === "true") return true;
|
|
16370
|
+
if (value === "false") return false;
|
|
16371
|
+
return void 0;
|
|
16372
|
+
} catch {
|
|
16373
|
+
return void 0;
|
|
16374
|
+
}
|
|
16375
|
+
}
|
|
16376
|
+
function syncDirContents(src, dest) {
|
|
16377
|
+
(0, import_node_fs15.mkdirSync)(dest, { recursive: true });
|
|
16378
|
+
for (const name of (0, import_node_fs15.readdirSync)(dest)) {
|
|
16379
|
+
(0, import_node_fs15.rmSync)((0, import_node_path16.join)(dest, name), { recursive: true, force: true });
|
|
16380
|
+
}
|
|
16381
|
+
(0, import_node_fs15.cpSync)(src, dest, { recursive: true });
|
|
16382
|
+
}
|
|
16383
|
+
function releaseTag(releasedVersion) {
|
|
16384
|
+
return releasedVersion.startsWith("v") ? releasedVersion : `v${releasedVersion}`;
|
|
16385
|
+
}
|
|
16386
|
+
async function extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5) {
|
|
16387
|
+
const tarFile = (0, import_node_path16.join)(tmpRoot, "archive.tar");
|
|
16388
|
+
try {
|
|
16389
|
+
await execFileP5("git", gitFetchReleaseTagArgs(hubCheckout, tag), { timeout: 6e4 });
|
|
16390
|
+
await execFileP5("git", ["-C", hubCheckout, "archive", "--format=tar", `--output=${tarFile}`, tag, "plugins/mmi"], {
|
|
16391
|
+
timeout: 6e4
|
|
16392
|
+
});
|
|
16393
|
+
await execFileP5("tar", ["-xf", tarFile, "-C", tmpRoot], { timeout: 6e4 });
|
|
16394
|
+
const pluginMmi = (0, import_node_path16.join)(tmpRoot, "plugins", "mmi");
|
|
16395
|
+
return (0, import_node_fs15.existsSync)((0, import_node_path16.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
|
|
16396
|
+
} catch {
|
|
16397
|
+
return void 0;
|
|
16398
|
+
}
|
|
16399
|
+
}
|
|
16400
|
+
async function downloadPluginMmiViaGh(tag, tmpRoot) {
|
|
16401
|
+
const tarPath = (0, import_node_path16.join)(tmpRoot, "repo.tgz");
|
|
16402
|
+
try {
|
|
16403
|
+
(0, import_node_fs15.mkdirSync)(tmpRoot, { recursive: true });
|
|
16404
|
+
const { stdout } = await execFileBuffer("gh", ghReleaseTarballApiArgs(tag), {
|
|
16405
|
+
timeout: 12e4,
|
|
16406
|
+
maxBuffer: 100 * 1024 * 1024,
|
|
16407
|
+
encoding: "buffer",
|
|
16408
|
+
windowsHide: true
|
|
16409
|
+
});
|
|
16410
|
+
(0, import_node_fs15.writeFileSync)(tarPath, stdout);
|
|
16411
|
+
await execFileBuffer("tar", ["-xzf", tarPath, "-C", tmpRoot], { timeout: 12e4, windowsHide: true });
|
|
16412
|
+
const top = (0, import_node_fs15.readdirSync)(tmpRoot).find((entry) => entry !== "repo.tgz");
|
|
16413
|
+
if (!top) return void 0;
|
|
16414
|
+
const pluginMmi = (0, import_node_path16.join)(tmpRoot, top, "plugins", "mmi");
|
|
16415
|
+
return (0, import_node_fs15.existsSync)((0, import_node_path16.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
|
|
16416
|
+
} catch {
|
|
16417
|
+
return void 0;
|
|
16418
|
+
}
|
|
16419
|
+
}
|
|
16420
|
+
async function resolvePluginMmiSource(releasedVersion, hubCheckout, tmpRoot, execFileP5) {
|
|
16421
|
+
(0, import_node_fs15.mkdirSync)(tmpRoot, { recursive: true });
|
|
16422
|
+
const tag = releaseTag(releasedVersion);
|
|
16423
|
+
if (hubCheckout) {
|
|
16424
|
+
const fromHub = await extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5);
|
|
16425
|
+
if (fromHub) return fromHub;
|
|
16426
|
+
}
|
|
16427
|
+
return downloadPluginMmiViaGh(tag, (0, import_node_path16.join)(tmpRoot, "gh"));
|
|
16428
|
+
}
|
|
16429
|
+
function cursorPluginPinsNeedingSeed(pins, releasedVersion) {
|
|
16430
|
+
if (!isSemverVersion2(releasedVersion)) return pins.filter((pin) => !pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty);
|
|
16431
|
+
return pins.filter((pin) => {
|
|
16432
|
+
if (!pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty) return true;
|
|
16433
|
+
if (isSemverVersion2(pin.version) && compareVersions(pin.version, releasedVersion) < 0) return true;
|
|
16434
|
+
return false;
|
|
16435
|
+
});
|
|
16436
|
+
}
|
|
16437
|
+
function cursorPluginCacheSeedTargets(pins, releasedVersion, cacheRoot, cacheRootExists = false) {
|
|
16438
|
+
const needing = cursorPluginPinsNeedingSeed(pins, releasedVersion);
|
|
16439
|
+
if (needing.length > 0) return needing.map((pin) => pin.path);
|
|
16440
|
+
if (pins.length === 0 && cacheRoot && cacheRootExists && isSemverVersion2(releasedVersion)) {
|
|
16441
|
+
return [(0, import_node_path16.join)(cacheRoot, `v${releasedVersion.replace(/^v/, "")}`)];
|
|
16442
|
+
}
|
|
16443
|
+
return [];
|
|
16444
|
+
}
|
|
16445
|
+
async function applyCursorPluginCacheSeed(input) {
|
|
16446
|
+
if (!isSemverVersion2(input.releasedVersion)) return false;
|
|
16447
|
+
const targets = cursorPluginCacheSeedTargets(input.pins, input.releasedVersion, input.cacheRoot, input.cacheRootExists);
|
|
16448
|
+
if (targets.length === 0) return false;
|
|
16449
|
+
const tmpRoot = await input.mkdtemp("mmi-cursor-seed-");
|
|
16450
|
+
const source = await resolvePluginMmiSource(input.releasedVersion, input.hubCheckout, tmpRoot, input.execFileP);
|
|
16451
|
+
if (!source) return false;
|
|
16452
|
+
input.log(` \u21BB seeding Cursor MMI plugin cache \u2192 ${input.releasedVersion}\u2026`);
|
|
16453
|
+
for (const dest of targets) {
|
|
16454
|
+
syncDirContents(source, dest);
|
|
16455
|
+
}
|
|
16456
|
+
(0, import_node_fs15.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
16457
|
+
return true;
|
|
16458
|
+
}
|
|
16459
|
+
var CURSOR_ENABLED_PIN_LABEL = "Cursor Team Marketplace enabled plugin pin";
|
|
16460
|
+
function cursorSplitBrainCapabilities() {
|
|
16461
|
+
const skills = OPENCODE_WORKFLOW_COMMANDS.map((c) => `/${c}`).join(", ");
|
|
16462
|
+
return `org skills (${skills}) and SessionStart hooks`;
|
|
16463
|
+
}
|
|
16464
|
+
function cursorEnabledPinRecovery() {
|
|
16465
|
+
return "refresh the MMI Team Marketplace in Cursor Dashboard \u2192 Settings \u2192 Plugins (Update), or run `mmi-cli doctor --apply` to seed the enabled pin; then restart Cursor";
|
|
16466
|
+
}
|
|
16467
|
+
var CURSOR_FOUND_CACHED_MMI_RE = /Found cached plugin:\s*mmi\s+at\s+.*?[\\/]([0-9a-f]{6,40})\b/gi;
|
|
16468
|
+
var CURSOR_MMI_LOADED_RE = /loadClaudePlugin\s+mmi@mutmutco\s+loaded/i;
|
|
16469
|
+
var CURSOR_GIT_AUTH_FAIL_RE = /unable to get password from user/i;
|
|
16470
|
+
var CURSOR_SAFE_PIN_NAME_RE = /^[0-9a-zA-Z._-]+$/;
|
|
16471
|
+
function parseCursorPluginLog(text) {
|
|
16472
|
+
let enabledPin;
|
|
16473
|
+
for (const m of text.matchAll(CURSOR_FOUND_CACHED_MMI_RE)) enabledPin = m[1];
|
|
16474
|
+
const loadSucceeded = CURSOR_MMI_LOADED_RE.test(text);
|
|
16475
|
+
const loadFailed = CURSOR_GIT_AUTH_FAIL_RE.test(text) && !loadSucceeded;
|
|
16476
|
+
return { enabledPin, loadSucceeded, loadFailed };
|
|
16477
|
+
}
|
|
16478
|
+
function buildCursorEnabledPinCheck(input) {
|
|
16479
|
+
const label = CURSOR_ENABLED_PIN_LABEL;
|
|
16480
|
+
const healthy = { ok: true, label, fix: "" };
|
|
16481
|
+
if (!input.isOrgRepo) return healthy;
|
|
16482
|
+
if (!input.enabledPin && !input.loadFailed) return healthy;
|
|
16483
|
+
if (input.loadSucceeded && !input.loadFailed) return healthy;
|
|
16484
|
+
const caps = cursorSplitBrainCapabilities();
|
|
16485
|
+
if (input.loadFailed) {
|
|
16486
|
+
return {
|
|
16487
|
+
ok: false,
|
|
16488
|
+
severityOverride: "hard",
|
|
16489
|
+
label,
|
|
16490
|
+
fix: `Cursor Team Marketplace failed to load mmi@mutmutco${input.enabledPin ? ` (${input.enabledPin})` : ""}: mmi-cli is on PATH and the Hub session is valid, but the MMI plugin did not load \u2014 ${caps} are UNAVAILABLE in Cursor. ${cursorEnabledPinRecovery()}`
|
|
16491
|
+
};
|
|
16492
|
+
}
|
|
16493
|
+
const enabledDir = input.pins.find((p) => p.name === input.enabledPin);
|
|
16494
|
+
const complete = !!enabledDir && !enabledDir.isEmpty && enabledDir.hasPluginJson && enabledDir.hasHooksJson;
|
|
16495
|
+
if (!complete) {
|
|
16496
|
+
const others = input.pins.filter((p) => p.name !== input.enabledPin && CURSOR_SAFE_PIN_NAME_RE.test(p.name)).map((p) => p.name);
|
|
16497
|
+
return {
|
|
16498
|
+
ok: false,
|
|
16499
|
+
severityOverride: "hard",
|
|
16500
|
+
label,
|
|
16501
|
+
fix: `Cursor is configured to load mmi@mutmutco at commit ${input.enabledPin}, but that pin is ${enabledDir ? "incomplete" : "missing"} in the cache${others.length ? ` (a complete pin at ${others.join(", ")} is present but will NOT be loaded)` : ""} \u2014 ${caps} are unavailable in Cursor. ${cursorEnabledPinRecovery()}`
|
|
16502
|
+
};
|
|
16503
|
+
}
|
|
16504
|
+
return healthy;
|
|
16505
|
+
}
|
|
16506
|
+
|
|
16357
16507
|
// src/mcp-reconcile.ts
|
|
16358
16508
|
var PLAYWRIGHT_MCP_SPEC = {
|
|
16359
16509
|
name: "playwright",
|
|
@@ -16662,9 +16812,9 @@ ${block}
|
|
|
16662
16812
|
}
|
|
16663
16813
|
|
|
16664
16814
|
// src/cli-doctor-shared.ts
|
|
16665
|
-
var import_node_fs15 = require("node:fs");
|
|
16666
|
-
var import_node_path16 = require("node:path");
|
|
16667
16815
|
var import_node_fs16 = require("node:fs");
|
|
16816
|
+
var import_node_path17 = require("node:path");
|
|
16817
|
+
var import_node_fs17 = require("node:fs");
|
|
16668
16818
|
var GC_GH_TIMEOUT_MS = 2e4;
|
|
16669
16819
|
async function awsCallerArn() {
|
|
16670
16820
|
try {
|
|
@@ -16710,7 +16860,7 @@ async function localBranchHeads() {
|
|
|
16710
16860
|
}
|
|
16711
16861
|
async function currentRepoWorktreeGitRoot(repoRoot) {
|
|
16712
16862
|
const gitCommonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
16713
|
-
return gitCommonDir ? (0,
|
|
16863
|
+
return gitCommonDir ? (0, import_node_path17.resolve)(repoRoot, gitCommonDir, "worktrees") : "";
|
|
16714
16864
|
}
|
|
16715
16865
|
async function worktreeBranches() {
|
|
16716
16866
|
const { stdout } = await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS });
|
|
@@ -16730,18 +16880,18 @@ function resolveGitdirForWorktreeFile(worktreePath, content) {
|
|
|
16730
16880
|
const match = /^gitdir:\s*(.+)\s*$/im.exec(content);
|
|
16731
16881
|
if (!match?.[1]) return void 0;
|
|
16732
16882
|
const raw = match[1].trim();
|
|
16733
|
-
return (0,
|
|
16883
|
+
return (0, import_node_path17.isAbsolute)(raw) ? raw : (0, import_node_path17.resolve)(worktreePath, raw);
|
|
16734
16884
|
}
|
|
16735
16885
|
function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
16736
16886
|
if (!worktreeGitRoot) return false;
|
|
16737
16887
|
try {
|
|
16738
|
-
const entries = (0,
|
|
16888
|
+
const entries = (0, import_node_fs17.readdirSync)(worktreeGitRoot, { withFileTypes: true });
|
|
16739
16889
|
for (const ent of entries) {
|
|
16740
16890
|
if (!ent.isDirectory()) continue;
|
|
16741
16891
|
try {
|
|
16742
|
-
const gitdirPath = (0,
|
|
16743
|
-
const resolvedGitdir = (0,
|
|
16744
|
-
if (sameWorktreeMetadataPath((0,
|
|
16892
|
+
const gitdirPath = (0, import_node_fs16.readFileSync)((0, import_node_path17.join)(worktreeGitRoot, ent.name, "gitdir"), "utf8").trim();
|
|
16893
|
+
const resolvedGitdir = (0, import_node_path17.isAbsolute)(gitdirPath) ? gitdirPath : (0, import_node_path17.resolve)(worktreeGitRoot, ent.name, gitdirPath);
|
|
16894
|
+
if (sameWorktreeMetadataPath((0, import_node_path17.dirname)(resolvedGitdir), worktreePath)) return true;
|
|
16745
16895
|
} catch {
|
|
16746
16896
|
}
|
|
16747
16897
|
}
|
|
@@ -16751,7 +16901,7 @@ function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
|
16751
16901
|
}
|
|
16752
16902
|
function pathExistsKnown(path2) {
|
|
16753
16903
|
try {
|
|
16754
|
-
(0,
|
|
16904
|
+
(0, import_node_fs17.statSync)(path2);
|
|
16755
16905
|
return true;
|
|
16756
16906
|
} catch (e) {
|
|
16757
16907
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
@@ -16760,10 +16910,10 @@ function pathExistsKnown(path2) {
|
|
|
16760
16910
|
}
|
|
16761
16911
|
}
|
|
16762
16912
|
function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
16763
|
-
const gitPath = (0,
|
|
16913
|
+
const gitPath = (0, import_node_path17.join)(path2, ".git");
|
|
16764
16914
|
let st;
|
|
16765
16915
|
try {
|
|
16766
|
-
st = (0,
|
|
16916
|
+
st = (0, import_node_fs17.lstatSync)(gitPath);
|
|
16767
16917
|
} catch (e) {
|
|
16768
16918
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
16769
16919
|
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
@@ -16780,7 +16930,7 @@ function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
|
16780
16930
|
if (st.isDirectory()) return { path: path2, gitType: "dir" };
|
|
16781
16931
|
if (!st.isFile()) return { path: path2, gitType: "other" };
|
|
16782
16932
|
try {
|
|
16783
|
-
const gitFileContent = (0,
|
|
16933
|
+
const gitFileContent = (0, import_node_fs16.readFileSync)(gitPath, "utf8");
|
|
16784
16934
|
const gitdir = resolveGitdirForWorktreeFile(path2, gitFileContent);
|
|
16785
16935
|
const gitDirExists = gitdir ? pathExistsKnown(gitdir) : false;
|
|
16786
16936
|
return {
|
|
@@ -16797,7 +16947,7 @@ function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
|
16797
16947
|
}
|
|
16798
16948
|
function inspectDeadWorktreeDirContent(path2) {
|
|
16799
16949
|
try {
|
|
16800
|
-
return { entries: (0,
|
|
16950
|
+
return { entries: (0, import_node_fs17.readdirSync)(path2) };
|
|
16801
16951
|
} catch (e) {
|
|
16802
16952
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
16803
16953
|
return { error: code ? `unable to inspect directory contents (${code})` : "unable to inspect directory contents" };
|
|
@@ -16816,8 +16966,8 @@ async function siblingWorktreeDirs() {
|
|
|
16816
16966
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot);
|
|
16817
16967
|
const siblingRoot = siblingMmiWorktreesRoot(repoRoot);
|
|
16818
16968
|
try {
|
|
16819
|
-
const entries = (0,
|
|
16820
|
-
return entries.filter((ent) => ent.isDirectory()).map((ent) => inspectSiblingWorktreeDir((0,
|
|
16969
|
+
const entries = (0, import_node_fs17.readdirSync)(siblingRoot, { withFileTypes: true });
|
|
16970
|
+
return entries.filter((ent) => ent.isDirectory()).map((ent) => inspectSiblingWorktreeDir((0, import_node_path17.join)(siblingRoot, ent.name), worktreeGitRoot)).filter((entry) => Boolean(entry));
|
|
16821
16971
|
} catch {
|
|
16822
16972
|
return [];
|
|
16823
16973
|
}
|
|
@@ -16867,7 +17017,7 @@ async function fetchHubVersionInfo(baseUrl) {
|
|
|
16867
17017
|
}
|
|
16868
17018
|
function readRepoVersion() {
|
|
16869
17019
|
try {
|
|
16870
|
-
return JSON.parse((0,
|
|
17020
|
+
return JSON.parse((0, import_node_fs18.readFileSync)((0, import_node_path18.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
16871
17021
|
} catch {
|
|
16872
17022
|
return void 0;
|
|
16873
17023
|
}
|
|
@@ -16933,15 +17083,15 @@ function reexecAttemptOutcome(strategy, code) {
|
|
|
16933
17083
|
return "result";
|
|
16934
17084
|
}
|
|
16935
17085
|
function spawnReexecAttempt(command, args, env) {
|
|
16936
|
-
return new Promise((
|
|
17086
|
+
return new Promise((resolve5) => {
|
|
16937
17087
|
let settled = false;
|
|
16938
17088
|
const done = (code) => {
|
|
16939
17089
|
if (!settled) {
|
|
16940
17090
|
settled = true;
|
|
16941
|
-
|
|
17091
|
+
resolve5(code);
|
|
16942
17092
|
}
|
|
16943
17093
|
};
|
|
16944
|
-
const child = (0,
|
|
17094
|
+
const child = (0, import_node_child_process11.spawn)(command, args, { stdio: "inherit", env });
|
|
16945
17095
|
child.on("error", () => done(-1));
|
|
16946
17096
|
child.on("exit", (code) => done(code ?? 0));
|
|
16947
17097
|
});
|
|
@@ -17016,30 +17166,42 @@ async function applyPluginHeal(token, surface, log, opts) {
|
|
|
17016
17166
|
}
|
|
17017
17167
|
var installedPluginsPath = (surface = detectSurface(process.env)) => {
|
|
17018
17168
|
const homeDir = surface === "codex" ? ".codex" : ".claude";
|
|
17019
|
-
return (0,
|
|
17169
|
+
return (0, import_node_path18.join)((0, import_node_os7.homedir)(), homeDir, "plugins", "installed_plugins.json");
|
|
17020
17170
|
};
|
|
17021
17171
|
function readInstalledPlugins(surface = detectSurface(process.env)) {
|
|
17022
17172
|
try {
|
|
17023
|
-
return JSON.parse((0,
|
|
17173
|
+
return JSON.parse((0, import_node_fs18.readFileSync)(installedPluginsPath(surface), "utf8"));
|
|
17024
17174
|
} catch {
|
|
17025
17175
|
return null;
|
|
17026
17176
|
}
|
|
17027
17177
|
}
|
|
17178
|
+
function marketplaceCloneCandidates(surface, home) {
|
|
17179
|
+
if (surface === "codex") {
|
|
17180
|
+
return [
|
|
17181
|
+
(0, import_node_path18.join)(home, ".codex", ".tmp", "marketplaces", "mutmutco"),
|
|
17182
|
+
(0, import_node_path18.join)(home, ".codex", "plugins", "marketplaces", "mutmutco")
|
|
17183
|
+
];
|
|
17184
|
+
}
|
|
17185
|
+
return [(0, import_node_path18.join)(home, ".claude", "plugins", "marketplaces", "mutmutco")];
|
|
17186
|
+
}
|
|
17187
|
+
function marketplaceClonePresent(surface, home, exists = import_node_fs18.existsSync) {
|
|
17188
|
+
return marketplaceCloneCandidates(surface, home).some(exists);
|
|
17189
|
+
}
|
|
17028
17190
|
function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRepo = false) {
|
|
17029
17191
|
const homeDir = surface === "codex" ? ".codex" : ".claude";
|
|
17030
17192
|
const installed = readInstalledPlugins(surface);
|
|
17031
17193
|
return {
|
|
17032
17194
|
isOrgRepo,
|
|
17033
17195
|
installRecordPresent: hasUserInstallRecord(installed, MMI_PLUGIN_ID) || hasProjectInstallRecord(installed, MMI_PLUGIN_ID, process.cwd()),
|
|
17034
|
-
marketplaceClonePresent: (
|
|
17035
|
-
pluginCachePresent: (0,
|
|
17196
|
+
marketplaceClonePresent: marketplaceClonePresent(surface, (0, import_node_os7.homedir)()),
|
|
17197
|
+
pluginCachePresent: (0, import_node_fs18.existsSync)((0, import_node_path18.join)((0, import_node_os7.homedir)(), homeDir, "plugins", "cache", "mutmutco", "mmi"))
|
|
17036
17198
|
};
|
|
17037
17199
|
}
|
|
17038
17200
|
function installedPluginSources() {
|
|
17039
17201
|
return ["claude", "codex"].map((surface) => {
|
|
17040
|
-
const recordPath = (0,
|
|
17202
|
+
const recordPath = (0, import_node_path18.join)((0, import_node_os7.homedir)(), `.${surface}`, "plugins", "installed_plugins.json");
|
|
17041
17203
|
try {
|
|
17042
|
-
return { surface, installed: JSON.parse((0,
|
|
17204
|
+
return { surface, installed: JSON.parse((0, import_node_fs18.readFileSync)(recordPath, "utf8")), recordPath };
|
|
17043
17205
|
} catch {
|
|
17044
17206
|
return { surface, installed: null, recordPath };
|
|
17045
17207
|
}
|
|
@@ -17052,8 +17214,8 @@ function managedPluginSurfaceSources() {
|
|
|
17052
17214
|
const segments = parseManagedPluginId(descriptor.id);
|
|
17053
17215
|
if (!segments) continue;
|
|
17054
17216
|
try {
|
|
17055
|
-
cacheVersions[descriptor.id] = (0,
|
|
17056
|
-
(0,
|
|
17217
|
+
cacheVersions[descriptor.id] = (0, import_node_fs18.readdirSync)(
|
|
17218
|
+
(0, import_node_path18.join)((0, import_node_os7.homedir)(), `.${surface}`, "plugins", "cache", segments.marketplace, segments.name),
|
|
17057
17219
|
{ withFileTypes: true }
|
|
17058
17220
|
).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
17059
17221
|
} catch {
|
|
@@ -17073,8 +17235,8 @@ function managedPluginCursorDirectVersions() {
|
|
|
17073
17235
|
const segments = parseManagedPluginId(descriptor.id);
|
|
17074
17236
|
if (!segments) continue;
|
|
17075
17237
|
try {
|
|
17076
|
-
const versions = (0,
|
|
17077
|
-
(0,
|
|
17238
|
+
const versions = (0, import_node_fs18.readdirSync)(
|
|
17239
|
+
(0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cursor", "plugins", "cache", segments.marketplace, segments.name),
|
|
17078
17240
|
{ withFileTypes: true }
|
|
17079
17241
|
).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name);
|
|
17080
17242
|
out[descriptor.id] = highestSemver(versions);
|
|
@@ -17093,7 +17255,7 @@ function managedPluginOpencodeDirectVersions() {
|
|
|
17093
17255
|
}
|
|
17094
17256
|
function readOpencodePackageVersionFrom(packageJsonPath) {
|
|
17095
17257
|
try {
|
|
17096
|
-
const parsed = JSON.parse((0,
|
|
17258
|
+
const parsed = JSON.parse((0, import_node_fs18.readFileSync)(packageJsonPath, "utf8"));
|
|
17097
17259
|
return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : void 0;
|
|
17098
17260
|
} catch {
|
|
17099
17261
|
return void 0;
|
|
@@ -17103,14 +17265,14 @@ function readOpencodePackageVersion(packageName) {
|
|
|
17103
17265
|
const [scope, name] = packageName.startsWith("@") ? packageName.slice(1).split("/", 2) : [void 0, packageName];
|
|
17104
17266
|
if (!name) return void 0;
|
|
17105
17267
|
const diskCandidates = [
|
|
17106
|
-
(0,
|
|
17107
|
-
(0,
|
|
17268
|
+
(0, import_node_path18.join)(opencodeConfigDir(), "node_modules", ...scope ? [`@${scope}`] : [], name, "package.json"),
|
|
17269
|
+
(0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cache", "opencode", "node_modules", ...scope ? [`@${scope}`] : [], name, "package.json")
|
|
17108
17270
|
];
|
|
17109
17271
|
const diskVersion = diskCandidates.map(readOpencodePackageVersionFrom).find((v) => v !== void 0);
|
|
17110
|
-
const packagesRoot = (0,
|
|
17272
|
+
const packagesRoot = (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cache", "opencode", "packages", ...scope ? [`@${scope}`] : []);
|
|
17111
17273
|
let cacheVersion;
|
|
17112
17274
|
try {
|
|
17113
|
-
const cacheVersions = (0,
|
|
17275
|
+
const cacheVersions = (0, import_node_fs18.readdirSync)(packagesRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith(`${name}@`) && !e.name.startsWith(".")).map((e) => readOpencodePackageVersionFrom((0, import_node_path18.join)(packagesRoot, e.name, "node_modules", ...scope ? [`@${scope}`] : [], name, "package.json"))).filter((v) => Boolean(v));
|
|
17114
17276
|
if (cacheVersions.length) cacheVersion = cacheVersions.reduce((lowest, v) => compareVersions(v, lowest) < 0 ? v : lowest);
|
|
17115
17277
|
} catch {
|
|
17116
17278
|
}
|
|
@@ -17118,7 +17280,7 @@ function readOpencodePackageVersion(packageName) {
|
|
|
17118
17280
|
}
|
|
17119
17281
|
function readClaudeSettings() {
|
|
17120
17282
|
try {
|
|
17121
|
-
return JSON.parse((0,
|
|
17283
|
+
return JSON.parse((0, import_node_fs18.readFileSync)((0, import_node_path18.join)(process.cwd(), ".claude", "settings.json"), "utf8"));
|
|
17122
17284
|
} catch {
|
|
17123
17285
|
return null;
|
|
17124
17286
|
}
|
|
@@ -17140,7 +17302,7 @@ function writeProjectInstallRecord(record) {
|
|
|
17140
17302
|
const list = file.plugins[MMI_PLUGIN_ID] ?? [];
|
|
17141
17303
|
list.push(record);
|
|
17142
17304
|
file.plugins[MMI_PLUGIN_ID] = list;
|
|
17143
|
-
(0,
|
|
17305
|
+
(0, import_node_fs18.writeFileSync)(installedPluginsPath(), `${JSON.stringify(file, null, 2)}
|
|
17144
17306
|
`, "utf8");
|
|
17145
17307
|
return true;
|
|
17146
17308
|
} catch {
|
|
@@ -17153,9 +17315,9 @@ function backupAndWriteInstalledPlugins(records, pluginId) {
|
|
|
17153
17315
|
if (!file) return false;
|
|
17154
17316
|
if (!file.plugins) file.plugins = {};
|
|
17155
17317
|
const path2 = installedPluginsPath();
|
|
17156
|
-
(0,
|
|
17318
|
+
(0, import_node_fs18.copyFileSync)(path2, `${path2}.bak`);
|
|
17157
17319
|
file.plugins[pluginId] = records;
|
|
17158
|
-
(0,
|
|
17320
|
+
(0, import_node_fs18.writeFileSync)(path2, `${JSON.stringify(file, null, 2)}
|
|
17159
17321
|
`, "utf8");
|
|
17160
17322
|
return true;
|
|
17161
17323
|
} catch {
|
|
@@ -17163,22 +17325,22 @@ function backupAndWriteInstalledPlugins(records, pluginId) {
|
|
|
17163
17325
|
}
|
|
17164
17326
|
}
|
|
17165
17327
|
function opencodeConfigDir() {
|
|
17166
|
-
return (0,
|
|
17328
|
+
return (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".config", "opencode");
|
|
17167
17329
|
}
|
|
17168
17330
|
function opencodeConfigPath() {
|
|
17169
|
-
return (0,
|
|
17331
|
+
return (0, import_node_path18.join)(opencodeConfigDir(), "opencode.jsonc");
|
|
17170
17332
|
}
|
|
17171
17333
|
function opencodeCommandsDir() {
|
|
17172
|
-
return (0,
|
|
17334
|
+
return (0, import_node_path18.join)(opencodeConfigDir(), "commands");
|
|
17173
17335
|
}
|
|
17174
17336
|
function opencodeSkillsPath() {
|
|
17175
|
-
return (0,
|
|
17337
|
+
return (0, import_node_path18.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "skills");
|
|
17176
17338
|
}
|
|
17177
17339
|
function opencodeConfigSnapshot() {
|
|
17178
17340
|
const path2 = opencodeConfigPath();
|
|
17179
|
-
if (!(0,
|
|
17341
|
+
if (!(0, import_node_fs18.existsSync)(path2)) return { path: path2, hasConfig: false, hasPluginField: false, parseOk: true };
|
|
17180
17342
|
try {
|
|
17181
|
-
const raw = (0,
|
|
17343
|
+
const raw = (0, import_node_fs18.readFileSync)(path2, "utf8");
|
|
17182
17344
|
const parsed = JSON.parse(stripJsonc(raw));
|
|
17183
17345
|
const hasPluginField = Object.prototype.hasOwnProperty.call(parsed, "plugin");
|
|
17184
17346
|
const skillsPaths = Array.isArray(parsed.skills?.paths) ? parsed.skills.paths.filter((p) => typeof p === "string") : void 0;
|
|
@@ -17201,9 +17363,9 @@ function writeOpencodeConfigPlugin(snapshot) {
|
|
|
17201
17363
|
const plan = planOpencodeConfigWrite(snapshot.hasConfig ? snapshot.raw : void 0);
|
|
17202
17364
|
if (plan.action === "already") return true;
|
|
17203
17365
|
if (!plan.text || plan.action === "unsafe") return false;
|
|
17204
|
-
(0,
|
|
17205
|
-
if (snapshot.hasConfig) (0,
|
|
17206
|
-
(0,
|
|
17366
|
+
(0, import_node_fs18.mkdirSync)((0, import_node_path18.dirname)(path2), { recursive: true });
|
|
17367
|
+
if (snapshot.hasConfig) (0, import_node_fs18.copyFileSync)(path2, `${path2}.bak`);
|
|
17368
|
+
(0, import_node_fs18.writeFileSync)(path2, plan.text, "utf8");
|
|
17207
17369
|
return true;
|
|
17208
17370
|
} catch {
|
|
17209
17371
|
return false;
|
|
@@ -17218,9 +17380,9 @@ function writeOpencodeSkillsPath(snapshot, skillsPath) {
|
|
|
17218
17380
|
const normalized = skillsPath.replace(/\\/g, "/");
|
|
17219
17381
|
if (!paths.some((p) => p.replace(/\\/g, "/") === normalized)) paths.push(skillsPath.replace(/\\/g, "/"));
|
|
17220
17382
|
parsed.skills = { ...skills, paths };
|
|
17221
|
-
(0,
|
|
17222
|
-
if (snapshot.hasConfig && (0,
|
|
17223
|
-
(0,
|
|
17383
|
+
(0, import_node_fs18.mkdirSync)((0, import_node_path18.dirname)(snapshot.path), { recursive: true });
|
|
17384
|
+
if (snapshot.hasConfig && (0, import_node_fs18.existsSync)(snapshot.path)) (0, import_node_fs18.copyFileSync)(snapshot.path, `${snapshot.path}.bak`);
|
|
17385
|
+
(0, import_node_fs18.writeFileSync)(snapshot.path, `${JSON.stringify(parsed, null, 2)}
|
|
17224
17386
|
`, "utf8");
|
|
17225
17387
|
return true;
|
|
17226
17388
|
} catch {
|
|
@@ -17229,7 +17391,7 @@ function writeOpencodeSkillsPath(snapshot, skillsPath) {
|
|
|
17229
17391
|
}
|
|
17230
17392
|
function opencodeExistingCommands() {
|
|
17231
17393
|
try {
|
|
17232
|
-
return (0,
|
|
17394
|
+
return (0, import_node_fs18.readdirSync)(opencodeCommandsDir(), { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name.slice(0, -3).toLowerCase());
|
|
17233
17395
|
} catch {
|
|
17234
17396
|
return [];
|
|
17235
17397
|
}
|
|
@@ -17237,9 +17399,9 @@ function opencodeExistingCommands() {
|
|
|
17237
17399
|
function writeOpencodeCommandFiles() {
|
|
17238
17400
|
try {
|
|
17239
17401
|
const dir = opencodeCommandsDir();
|
|
17240
|
-
(0,
|
|
17402
|
+
(0, import_node_fs18.mkdirSync)(dir, { recursive: true });
|
|
17241
17403
|
for (const command of OPENCODE_WORKFLOW_COMMANDS) {
|
|
17242
|
-
(0,
|
|
17404
|
+
(0, import_node_fs18.writeFileSync)((0, import_node_path18.join)(dir, `${command}.md`), opencodeCommandMarkdown(command), "utf8");
|
|
17243
17405
|
}
|
|
17244
17406
|
return true;
|
|
17245
17407
|
} catch {
|
|
@@ -17248,12 +17410,12 @@ function writeOpencodeCommandFiles() {
|
|
|
17248
17410
|
}
|
|
17249
17411
|
function readOpencodeAdapterDiskVersion() {
|
|
17250
17412
|
const candidates = [
|
|
17251
|
-
(0,
|
|
17252
|
-
(0,
|
|
17413
|
+
(0, import_node_path18.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "package.json"),
|
|
17414
|
+
(0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cache", "opencode", "node_modules", "@mutmutco", "opencode-mmi", "package.json")
|
|
17253
17415
|
];
|
|
17254
17416
|
for (const path2 of candidates) {
|
|
17255
17417
|
try {
|
|
17256
|
-
const parsed = JSON.parse((0,
|
|
17418
|
+
const parsed = JSON.parse((0, import_node_fs18.readFileSync)(path2, "utf8"));
|
|
17257
17419
|
if (typeof parsed.version === "string" && parsed.version.trim()) return parsed.version.trim();
|
|
17258
17420
|
} catch {
|
|
17259
17421
|
continue;
|
|
@@ -17269,7 +17431,7 @@ async function forceInstallOpencodeMmiPlugins(snapshot, log) {
|
|
|
17269
17431
|
try {
|
|
17270
17432
|
const specs = opencodeMmiPluginSpecs(snapshot);
|
|
17271
17433
|
log(` \u21BB force-refreshing OpenCode MMI npm plugin(s): ${specs.join(", ")}\u2026`);
|
|
17272
|
-
(0,
|
|
17434
|
+
(0, import_node_fs18.mkdirSync)(opencodeConfigDir(), { recursive: true });
|
|
17273
17435
|
await runHostBin("npm", ["install", "--prefix", opencodeConfigDir(), "--force", ...specs], { timeout: NPM_UPDATE_TIMEOUT_MS });
|
|
17274
17436
|
return true;
|
|
17275
17437
|
} catch {
|
|
@@ -17277,12 +17439,12 @@ async function forceInstallOpencodeMmiPlugins(snapshot, log) {
|
|
|
17277
17439
|
}
|
|
17278
17440
|
}
|
|
17279
17441
|
function opencodePackagesRoot() {
|
|
17280
|
-
return (0,
|
|
17442
|
+
return (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cache", "opencode", "packages", "@mutmutco");
|
|
17281
17443
|
}
|
|
17282
17444
|
function opencodePluginCacheDirs() {
|
|
17283
17445
|
const root = opencodePackagesRoot();
|
|
17284
17446
|
try {
|
|
17285
|
-
return (0,
|
|
17447
|
+
return (0, import_node_fs18.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("opencode-mmi@") && !e.name.startsWith(".")).map((e) => (0, import_node_path18.join)(root, e.name));
|
|
17286
17448
|
} catch {
|
|
17287
17449
|
return [];
|
|
17288
17450
|
}
|
|
@@ -17290,7 +17452,7 @@ function opencodePluginCacheDirs() {
|
|
|
17290
17452
|
function readOpencodeCacheDirVersion(cacheDir) {
|
|
17291
17453
|
try {
|
|
17292
17454
|
const parsed = JSON.parse(
|
|
17293
|
-
(0,
|
|
17455
|
+
(0, import_node_fs18.readFileSync)((0, import_node_path18.join)(cacheDir, "node_modules", "@mutmutco", "opencode-mmi", "package.json"), "utf8")
|
|
17294
17456
|
);
|
|
17295
17457
|
return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : void 0;
|
|
17296
17458
|
} catch {
|
|
@@ -17313,9 +17475,9 @@ function quarantineStaleOpencodePluginCaches(releasedVersion, log) {
|
|
|
17313
17475
|
});
|
|
17314
17476
|
if (!plan.quarantine) continue;
|
|
17315
17477
|
try {
|
|
17316
|
-
const quarantineRoot = (0,
|
|
17317
|
-
(0,
|
|
17318
|
-
(0,
|
|
17478
|
+
const quarantineRoot = (0, import_node_path18.join)(opencodePackagesRoot(), ".mmi-quarantine", stamp);
|
|
17479
|
+
(0, import_node_fs18.mkdirSync)(quarantineRoot, { recursive: true });
|
|
17480
|
+
(0, import_node_fs18.renameSync)(cacheDir, (0, import_node_path18.join)(quarantineRoot, (0, import_node_path18.basename)(cacheDir)));
|
|
17319
17481
|
log(` \u21BB quarantined stale OpenCode plugin cache ${plan.cacheVersion} (< ${plan.releasedVersion}) \u2014 OpenCode reinstalls the current adapter on next start`);
|
|
17320
17482
|
moved = true;
|
|
17321
17483
|
} catch {
|
|
@@ -17341,30 +17503,30 @@ function opencodePluginVersionsForReport() {
|
|
|
17341
17503
|
}
|
|
17342
17504
|
function opencodeDesktopLogsRoot() {
|
|
17343
17505
|
if (process.platform === "win32") {
|
|
17344
|
-
const base = process.env.APPDATA || (0,
|
|
17345
|
-
return (0,
|
|
17506
|
+
const base = process.env.APPDATA || (0, import_node_path18.join)((0, import_node_os7.homedir)(), "AppData", "Roaming");
|
|
17507
|
+
return (0, import_node_path18.join)(base, "ai.opencode.desktop", "logs");
|
|
17346
17508
|
}
|
|
17347
17509
|
if (process.platform === "darwin") {
|
|
17348
|
-
return (0,
|
|
17510
|
+
return (0, import_node_path18.join)((0, import_node_os7.homedir)(), "Library", "Application Support", "ai.opencode.desktop", "logs");
|
|
17349
17511
|
}
|
|
17350
|
-
return (0,
|
|
17512
|
+
return (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".config", "ai.opencode.desktop", "logs");
|
|
17351
17513
|
}
|
|
17352
17514
|
function opencodeDesktopBootstrapSnapshot() {
|
|
17353
17515
|
const root = opencodeDesktopLogsRoot();
|
|
17354
17516
|
try {
|
|
17355
|
-
const sessionDirs = (0,
|
|
17517
|
+
const sessionDirs = (0, import_node_fs18.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => (0, import_node_path18.join)(root, entry.name)).sort((a, b) => (0, import_node_fs18.statSync)(b).mtimeMs - (0, import_node_fs18.statSync)(a).mtimeMs);
|
|
17356
17518
|
const newest = sessionDirs[0];
|
|
17357
17519
|
if (!newest) return [];
|
|
17358
|
-
const logPath = (0,
|
|
17359
|
-
const text = (0,
|
|
17360
|
-
return opencodeAgentDirectoriesFromLog(text).filter((directory) => !(0,
|
|
17520
|
+
const logPath = (0, import_node_path18.join)(newest, "renderer.log");
|
|
17521
|
+
const text = (0, import_node_fs18.readFileSync)(logPath, "utf8");
|
|
17522
|
+
return opencodeAgentDirectoriesFromLog(text).filter((directory) => !(0, import_node_fs18.existsSync)(directory)).map((directory) => ({ directory, logPath }));
|
|
17361
17523
|
} catch {
|
|
17362
17524
|
return [];
|
|
17363
17525
|
}
|
|
17364
17526
|
}
|
|
17365
17527
|
function opencodeLegacyConfigSnapshot() {
|
|
17366
|
-
const legacyPath = (0,
|
|
17367
|
-
if (!(0,
|
|
17528
|
+
const legacyPath = (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".opencode", "opencode.json");
|
|
17529
|
+
if (!(0, import_node_fs18.existsSync)(legacyPath)) return {};
|
|
17368
17530
|
const content = readTextFile(legacyPath);
|
|
17369
17531
|
if (content == null) return {};
|
|
17370
17532
|
const plugins = parseOpencodeLegacyConfigPlugins(content);
|
|
@@ -17376,45 +17538,80 @@ function opencodeLegacyConfigSnapshot() {
|
|
|
17376
17538
|
function quarantineOpencodeLegacyConfig(legacyPath) {
|
|
17377
17539
|
try {
|
|
17378
17540
|
const backupPath = `${legacyPath}.bak`;
|
|
17379
|
-
if ((0,
|
|
17380
|
-
(0,
|
|
17541
|
+
if ((0, import_node_fs18.existsSync)(backupPath)) return false;
|
|
17542
|
+
(0, import_node_fs18.renameSync)(legacyPath, backupPath);
|
|
17381
17543
|
return true;
|
|
17382
17544
|
} catch {
|
|
17383
17545
|
return false;
|
|
17384
17546
|
}
|
|
17385
17547
|
}
|
|
17386
17548
|
function cursorPluginCacheRoot() {
|
|
17387
|
-
return (0,
|
|
17549
|
+
return (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cursor", "plugins", "cache", "mutmutco", "mmi");
|
|
17550
|
+
}
|
|
17551
|
+
function cursorLogsRoot() {
|
|
17552
|
+
if (process.platform === "win32") return (0, import_node_path18.join)((0, import_node_os7.homedir)(), "AppData", "Roaming", "Cursor", "logs");
|
|
17553
|
+
if (process.platform === "darwin") return (0, import_node_path18.join)((0, import_node_os7.homedir)(), "Library", "Application Support", "Cursor", "logs");
|
|
17554
|
+
return (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".config", "Cursor", "logs");
|
|
17555
|
+
}
|
|
17556
|
+
function safeReaddirNames(dir) {
|
|
17557
|
+
try {
|
|
17558
|
+
return (0, import_node_fs18.readdirSync)(dir, { withFileTypes: true }).map((e) => e.name);
|
|
17559
|
+
} catch {
|
|
17560
|
+
return [];
|
|
17561
|
+
}
|
|
17562
|
+
}
|
|
17563
|
+
function readCursorPluginLogText() {
|
|
17564
|
+
const root = cursorLogsRoot();
|
|
17565
|
+
const sessions = safeReaddirNames(root).sort().reverse();
|
|
17566
|
+
for (const session of sessions.slice(0, 5)) {
|
|
17567
|
+
const sessionDir = (0, import_node_path18.join)(root, session);
|
|
17568
|
+
const texts = [];
|
|
17569
|
+
for (const win of safeReaddirNames(sessionDir).filter((n) => n.startsWith("window"))) {
|
|
17570
|
+
const dir = (0, import_node_path18.join)(sessionDir, win, "exthost", "anysphere.cursor-agent-exec");
|
|
17571
|
+
for (const file of safeReaddirNames(dir)) {
|
|
17572
|
+
if (!/^Cursor Plugins.*\.log$/i.test(file)) continue;
|
|
17573
|
+
const path2 = (0, import_node_path18.join)(dir, file);
|
|
17574
|
+
try {
|
|
17575
|
+
texts.push({ text: (0, import_node_fs18.readFileSync)(path2, "utf8"), mtime: (0, import_node_fs18.statSync)(path2).mtimeMs });
|
|
17576
|
+
} catch {
|
|
17577
|
+
}
|
|
17578
|
+
}
|
|
17579
|
+
}
|
|
17580
|
+
if (texts.length) {
|
|
17581
|
+
return texts.sort((a, b) => b.mtime - a.mtime).map((t) => t.text).join("\n");
|
|
17582
|
+
}
|
|
17583
|
+
}
|
|
17584
|
+
return void 0;
|
|
17388
17585
|
}
|
|
17389
17586
|
function cursorPluginCachePinSnapshots() {
|
|
17390
17587
|
const root = cursorPluginCacheRoot();
|
|
17391
17588
|
try {
|
|
17392
|
-
return (0,
|
|
17393
|
-
const path2 = (0,
|
|
17394
|
-
const pluginJson = (0,
|
|
17395
|
-
const hooksJson = (0,
|
|
17396
|
-
const cliBundle = (0,
|
|
17397
|
-
const cursorHook = (0,
|
|
17589
|
+
return (0, import_node_fs18.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => {
|
|
17590
|
+
const path2 = (0, import_node_path18.join)(root, entry.name);
|
|
17591
|
+
const pluginJson = (0, import_node_path18.join)(path2, ".cursor-plugin", "plugin.json");
|
|
17592
|
+
const hooksJson = (0, import_node_path18.join)(path2, "hooks", "hooks.json");
|
|
17593
|
+
const cliBundle = (0, import_node_path18.join)(path2, "cli", "dist", "index.cjs");
|
|
17594
|
+
const cursorHook = (0, import_node_path18.join)(path2, "scripts", "cursor-hook.mjs");
|
|
17398
17595
|
let version;
|
|
17399
17596
|
try {
|
|
17400
|
-
const raw = JSON.parse((0,
|
|
17597
|
+
const raw = JSON.parse((0, import_node_fs18.readFileSync)(pluginJson, "utf8"));
|
|
17401
17598
|
version = typeof raw.version === "string" ? raw.version : void 0;
|
|
17402
17599
|
} catch {
|
|
17403
17600
|
version = void 0;
|
|
17404
17601
|
}
|
|
17405
17602
|
let isEmpty = true;
|
|
17406
17603
|
try {
|
|
17407
|
-
isEmpty = (0,
|
|
17604
|
+
isEmpty = (0, import_node_fs18.readdirSync)(path2).length === 0;
|
|
17408
17605
|
} catch {
|
|
17409
17606
|
isEmpty = true;
|
|
17410
17607
|
}
|
|
17411
17608
|
return {
|
|
17412
17609
|
name: entry.name,
|
|
17413
17610
|
path: path2,
|
|
17414
|
-
hasPluginJson: (0,
|
|
17415
|
-
hasHooksJson: (0,
|
|
17416
|
-
hasCliBundle: (0,
|
|
17417
|
-
hasCursorHookScript: (0,
|
|
17611
|
+
hasPluginJson: (0, import_node_fs18.existsSync)(pluginJson),
|
|
17612
|
+
hasHooksJson: (0, import_node_fs18.existsSync)(hooksJson),
|
|
17613
|
+
hasCliBundle: (0, import_node_fs18.existsSync)(cliBundle),
|
|
17614
|
+
hasCursorHookScript: (0, import_node_fs18.existsSync)(cursorHook),
|
|
17418
17615
|
isEmpty,
|
|
17419
17616
|
version
|
|
17420
17617
|
};
|
|
@@ -17424,19 +17621,19 @@ function cursorPluginCachePinSnapshots() {
|
|
|
17424
17621
|
}
|
|
17425
17622
|
}
|
|
17426
17623
|
function hubCheckoutForCursorSeed() {
|
|
17427
|
-
const manifest = (0,
|
|
17428
|
-
return (0,
|
|
17624
|
+
const manifest = (0, import_node_path18.join)(process.cwd(), "plugins", "mmi", ".cursor-plugin", "plugin.json");
|
|
17625
|
+
return (0, import_node_fs18.existsSync)(manifest) ? process.cwd() : void 0;
|
|
17429
17626
|
}
|
|
17430
17627
|
function mmiPluginCacheRootSnapshots() {
|
|
17431
17628
|
const roots = [
|
|
17432
|
-
{ surface: "claude", root: (0,
|
|
17433
|
-
{ surface: "codex", root: (0,
|
|
17629
|
+
{ surface: "claude", root: (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".claude", "plugins", "cache", "mutmutco", "mmi") },
|
|
17630
|
+
{ surface: "codex", root: (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".codex", "plugins", "cache", "mutmutco", "mmi") }
|
|
17434
17631
|
];
|
|
17435
17632
|
return roots.flatMap(({ surface, root }) => {
|
|
17436
17633
|
try {
|
|
17437
|
-
const entries = (0,
|
|
17634
|
+
const entries = (0, import_node_fs18.readdirSync)(root, { withFileTypes: true }).map((entry) => ({
|
|
17438
17635
|
name: entry.name,
|
|
17439
|
-
path: (0,
|
|
17636
|
+
path: (0, import_node_path18.join)(root, entry.name),
|
|
17440
17637
|
isDirectory: entry.isDirectory()
|
|
17441
17638
|
}));
|
|
17442
17639
|
return [{ surface, root, entries }];
|
|
@@ -17447,7 +17644,7 @@ function mmiPluginCacheRootSnapshots() {
|
|
|
17447
17644
|
}
|
|
17448
17645
|
function hasNestedMmiChild(versionDir) {
|
|
17449
17646
|
try {
|
|
17450
|
-
return (0,
|
|
17647
|
+
return (0, import_node_fs18.statSync)((0, import_node_path18.join)(versionDir, "mmi")).isDirectory();
|
|
17451
17648
|
} catch {
|
|
17452
17649
|
return false;
|
|
17453
17650
|
}
|
|
@@ -17458,10 +17655,10 @@ function nestedPluginTreeSnapshot() {
|
|
|
17458
17655
|
);
|
|
17459
17656
|
}
|
|
17460
17657
|
function uniqueQuarantineTarget(path2) {
|
|
17461
|
-
if (!(0,
|
|
17658
|
+
if (!(0, import_node_fs18.existsSync)(path2)) return path2;
|
|
17462
17659
|
for (let i = 1; i < 100; i += 1) {
|
|
17463
17660
|
const candidate = `${path2}-${i}`;
|
|
17464
|
-
if (!(0,
|
|
17661
|
+
if (!(0, import_node_fs18.existsSync)(candidate)) return candidate;
|
|
17465
17662
|
}
|
|
17466
17663
|
return `${path2}-${Date.now()}`;
|
|
17467
17664
|
}
|
|
@@ -17470,10 +17667,10 @@ function quarantinePluginCacheDirs(plan) {
|
|
|
17470
17667
|
const failed = [];
|
|
17471
17668
|
for (const move of plan) {
|
|
17472
17669
|
try {
|
|
17473
|
-
if (!(0,
|
|
17670
|
+
if (!(0, import_node_fs18.existsSync)(move.from)) continue;
|
|
17474
17671
|
const target = uniqueQuarantineTarget(move.to);
|
|
17475
|
-
(0,
|
|
17476
|
-
(0,
|
|
17672
|
+
(0, import_node_fs18.mkdirSync)((0, import_node_path18.dirname)(target), { recursive: true });
|
|
17673
|
+
(0, import_node_fs18.renameSync)(move.from, target);
|
|
17477
17674
|
moved += 1;
|
|
17478
17675
|
} catch {
|
|
17479
17676
|
failed.push(move);
|
|
@@ -17492,23 +17689,23 @@ async function robocopyMirrorEmpty(emptyDir, target) {
|
|
|
17492
17689
|
}
|
|
17493
17690
|
async function clearNestedPluginTreeDir(targetPath) {
|
|
17494
17691
|
try {
|
|
17495
|
-
if (!(0,
|
|
17692
|
+
if (!(0, import_node_fs18.existsSync)(targetPath)) return true;
|
|
17496
17693
|
if (isWin) {
|
|
17497
|
-
const emptyDir = (0,
|
|
17498
|
-
(0,
|
|
17694
|
+
const emptyDir = (0, import_node_path18.join)((0, import_node_os7.tmpdir)(), `mmi-empty-${Date.now()}`);
|
|
17695
|
+
(0, import_node_fs18.mkdirSync)(emptyDir, { recursive: true });
|
|
17499
17696
|
try {
|
|
17500
17697
|
await robocopyMirrorEmpty(emptyDir, targetPath);
|
|
17501
|
-
(0,
|
|
17698
|
+
(0, import_node_fs18.rmSync)(targetPath, { recursive: true, force: true });
|
|
17502
17699
|
} finally {
|
|
17503
17700
|
try {
|
|
17504
|
-
(0,
|
|
17701
|
+
(0, import_node_fs18.rmSync)(emptyDir, { recursive: true, force: true });
|
|
17505
17702
|
} catch {
|
|
17506
17703
|
}
|
|
17507
17704
|
}
|
|
17508
|
-
return !(0,
|
|
17705
|
+
return !(0, import_node_fs18.existsSync)(targetPath);
|
|
17509
17706
|
}
|
|
17510
|
-
(0,
|
|
17511
|
-
return !(0,
|
|
17707
|
+
(0, import_node_fs18.rmSync)(targetPath, { recursive: true, force: true });
|
|
17708
|
+
return !(0, import_node_fs18.existsSync)(targetPath);
|
|
17512
17709
|
} catch {
|
|
17513
17710
|
return false;
|
|
17514
17711
|
}
|
|
@@ -17521,79 +17718,79 @@ async function applyNestedPluginTreeCleanup(paths, log) {
|
|
|
17521
17718
|
}
|
|
17522
17719
|
return true;
|
|
17523
17720
|
}
|
|
17524
|
-
var gitignorePath = () => (0,
|
|
17721
|
+
var gitignorePath = () => (0, import_node_path18.join)(process.cwd(), ".gitignore");
|
|
17525
17722
|
function readTextFile(path2) {
|
|
17526
17723
|
try {
|
|
17527
|
-
if (!(0,
|
|
17528
|
-
return (0,
|
|
17724
|
+
if (!(0, import_node_fs18.existsSync)(path2)) return null;
|
|
17725
|
+
return (0, import_node_fs18.readFileSync)(path2, "utf8");
|
|
17529
17726
|
} catch {
|
|
17530
17727
|
return null;
|
|
17531
17728
|
}
|
|
17532
17729
|
}
|
|
17533
17730
|
function mcpDirExists(path2) {
|
|
17534
17731
|
try {
|
|
17535
|
-
return (0,
|
|
17732
|
+
return (0, import_node_fs18.existsSync)(path2);
|
|
17536
17733
|
} catch {
|
|
17537
17734
|
return false;
|
|
17538
17735
|
}
|
|
17539
17736
|
}
|
|
17540
17737
|
function mcpConfigTargets() {
|
|
17541
17738
|
const cwd = process.cwd();
|
|
17542
|
-
const home = (0,
|
|
17543
|
-
const cursorProjectDir = (0,
|
|
17544
|
-
const cursorUserDir = (0,
|
|
17545
|
-
const codexDir = (0,
|
|
17739
|
+
const home = (0, import_node_os7.homedir)();
|
|
17740
|
+
const cursorProjectDir = (0, import_node_path18.join)(cwd, ".cursor");
|
|
17741
|
+
const cursorUserDir = (0, import_node_path18.join)(home, ".cursor");
|
|
17742
|
+
const codexDir = (0, import_node_path18.join)(home, ".codex");
|
|
17546
17743
|
return [
|
|
17547
17744
|
// Claude Code project MCP — reconciled if present, never conjured (org seeds .cursor/mcp.json, not this).
|
|
17548
17745
|
{
|
|
17549
17746
|
host: "claude-code",
|
|
17550
17747
|
label: "Claude Code project MCP",
|
|
17551
|
-
path: (0,
|
|
17748
|
+
path: (0, import_node_path18.join)(cwd, ".mcp.json"),
|
|
17552
17749
|
format: "json",
|
|
17553
17750
|
present: true,
|
|
17554
17751
|
// parent is the repo root (cwd); the caller gates the whole reconcile on isOrgRepo
|
|
17555
17752
|
isRepoFile: true,
|
|
17556
17753
|
createWhenFileAbsent: false,
|
|
17557
|
-
content: readTextFile((0,
|
|
17754
|
+
content: readTextFile((0, import_node_path18.join)(cwd, ".mcp.json"))
|
|
17558
17755
|
},
|
|
17559
17756
|
// Cursor project MCP — the bootstrap-seeded surface; restored if its .cursor dir exists but the file is gone.
|
|
17560
17757
|
{
|
|
17561
17758
|
host: "cursor-project",
|
|
17562
17759
|
label: "Cursor project MCP",
|
|
17563
|
-
path: (0,
|
|
17760
|
+
path: (0, import_node_path18.join)(cursorProjectDir, "mcp.json"),
|
|
17564
17761
|
format: "json",
|
|
17565
17762
|
present: mcpDirExists(cursorProjectDir),
|
|
17566
17763
|
isRepoFile: true,
|
|
17567
17764
|
createWhenFileAbsent: true,
|
|
17568
|
-
content: readTextFile((0,
|
|
17765
|
+
content: readTextFile((0, import_node_path18.join)(cursorProjectDir, "mcp.json"))
|
|
17569
17766
|
},
|
|
17570
17767
|
// Cursor user MCP — global; written only when Cursor is installed (~/.cursor exists).
|
|
17571
17768
|
{
|
|
17572
17769
|
host: "cursor-user",
|
|
17573
17770
|
label: "Cursor user MCP",
|
|
17574
|
-
path: (0,
|
|
17771
|
+
path: (0, import_node_path18.join)(cursorUserDir, "mcp.json"),
|
|
17575
17772
|
format: "json",
|
|
17576
17773
|
present: mcpDirExists(cursorUserDir),
|
|
17577
17774
|
isRepoFile: false,
|
|
17578
17775
|
createWhenFileAbsent: true,
|
|
17579
|
-
content: readTextFile((0,
|
|
17776
|
+
content: readTextFile((0, import_node_path18.join)(cursorUserDir, "mcp.json"))
|
|
17580
17777
|
},
|
|
17581
17778
|
// Codex user config (TOML) — global; written only when Codex is installed (~/.codex exists).
|
|
17582
17779
|
{
|
|
17583
17780
|
host: "codex",
|
|
17584
17781
|
label: "Codex user config",
|
|
17585
|
-
path: (0,
|
|
17782
|
+
path: (0, import_node_path18.join)(codexDir, "config.toml"),
|
|
17586
17783
|
format: "toml",
|
|
17587
17784
|
present: mcpDirExists(codexDir),
|
|
17588
17785
|
isRepoFile: false,
|
|
17589
17786
|
createWhenFileAbsent: true,
|
|
17590
|
-
content: readTextFile((0,
|
|
17787
|
+
content: readTextFile((0, import_node_path18.join)(codexDir, "config.toml"))
|
|
17591
17788
|
}
|
|
17592
17789
|
];
|
|
17593
17790
|
}
|
|
17594
17791
|
function writeMcpConfigFile(path2, content) {
|
|
17595
17792
|
try {
|
|
17596
|
-
(0,
|
|
17793
|
+
(0, import_node_fs18.writeFileSync)(path2, content, "utf8");
|
|
17597
17794
|
return true;
|
|
17598
17795
|
} catch {
|
|
17599
17796
|
return false;
|
|
@@ -17673,7 +17870,7 @@ function strayBrowserArtifactPaths() {
|
|
|
17673
17870
|
const cwd = process.cwd();
|
|
17674
17871
|
return STRAY_BROWSER_ARTIFACT_DIRS.filter((rel) => {
|
|
17675
17872
|
try {
|
|
17676
|
-
return (0,
|
|
17873
|
+
return (0, import_node_fs18.existsSync)((0, import_node_path18.join)(cwd, rel));
|
|
17677
17874
|
} catch {
|
|
17678
17875
|
return false;
|
|
17679
17876
|
}
|
|
@@ -17681,14 +17878,14 @@ function strayBrowserArtifactPaths() {
|
|
|
17681
17878
|
}
|
|
17682
17879
|
function readGitignore() {
|
|
17683
17880
|
try {
|
|
17684
|
-
return (0,
|
|
17881
|
+
return (0, import_node_fs18.readFileSync)(gitignorePath(), "utf8");
|
|
17685
17882
|
} catch {
|
|
17686
17883
|
return null;
|
|
17687
17884
|
}
|
|
17688
17885
|
}
|
|
17689
17886
|
function writeGitignore(content) {
|
|
17690
17887
|
try {
|
|
17691
|
-
(0,
|
|
17888
|
+
(0, import_node_fs18.writeFileSync)(gitignorePath(), content, "utf8");
|
|
17692
17889
|
return true;
|
|
17693
17890
|
} catch {
|
|
17694
17891
|
return false;
|
|
@@ -17730,7 +17927,7 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
|
|
|
17730
17927
|
const semverPrefix = /^\d+\.\d+\.\d+/;
|
|
17731
17928
|
const isBehind = (installed2, released) => Boolean(installed2 && released && semverPrefix.test(installed2) && semverPrefix.test(released) && compareVersions(installed2, released) < 0);
|
|
17732
17929
|
const opencodeAdapterStale = isBehind(opencodeInstalledVersionForDoctor(), releasedVersion);
|
|
17733
|
-
const cursorCacheStale = (0,
|
|
17930
|
+
const cursorCacheStale = (0, import_node_fs18.existsSync)(cursorPluginCacheRoot()) && (cursorPluginCachePinSnapshots() ?? []).some((p) => isBehind(p.version, releasedVersion));
|
|
17734
17931
|
const healPlan = doctorHealPlan({
|
|
17735
17932
|
isOrgRepo,
|
|
17736
17933
|
surface,
|
|
@@ -17765,7 +17962,7 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
|
|
|
17765
17962
|
let onPath = pathProbe;
|
|
17766
17963
|
if (!onPath) {
|
|
17767
17964
|
const root = process.env.CLAUDE_PLUGIN_ROOT;
|
|
17768
|
-
if (root && (0,
|
|
17965
|
+
if (root && (0, import_node_fs18.existsSync)(`${root}/bin/mmi-cli${isWin ? ".cmd" : ""}`)) onPath = true;
|
|
17769
17966
|
}
|
|
17770
17967
|
checks.push({ ok: onPath, label: "mmi-cli on PATH", fix: "auto-provisioned at session start \u2014 reopen the session, or install the MMI plugin" });
|
|
17771
17968
|
const reloadHint = reloadAction(surface);
|
|
@@ -17912,7 +18109,7 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
|
|
|
17912
18109
|
checks.push(gitignoreCheck);
|
|
17913
18110
|
checks.push(buildRepoLocalWorktreeCheck({
|
|
17914
18111
|
isOrgRepo,
|
|
17915
|
-
hasRepoLocalWorktrees: (0,
|
|
18112
|
+
hasRepoLocalWorktrees: (0, import_node_fs18.existsSync)((0, import_node_path18.join)(process.cwd(), ".worktrees"))
|
|
17916
18113
|
}));
|
|
17917
18114
|
let driftCheck = buildPluginConfigDriftCheck({ isOrgRepo, installed, surface });
|
|
17918
18115
|
if (!driftCheck.ok && driftCheck.recordsToWrite && repairLocal) {
|
|
@@ -18202,7 +18399,7 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
|
|
|
18202
18399
|
}
|
|
18203
18400
|
checks.push(nestedPluginTreeCheck);
|
|
18204
18401
|
const cursorCacheRoot = cursorPluginCacheRoot();
|
|
18205
|
-
const cursorCacheRootExists = (0,
|
|
18402
|
+
const cursorCacheRootExists = (0, import_node_fs18.existsSync)(cursorCacheRoot);
|
|
18206
18403
|
let cursorPins = cursorPluginCachePinSnapshots() ?? [];
|
|
18207
18404
|
checks.push(
|
|
18208
18405
|
buildCursorPluginCacheCleanupCheck({
|
|
@@ -18230,7 +18427,7 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
|
|
|
18230
18427
|
cacheRootExists: cursorCacheRootExists,
|
|
18231
18428
|
hubCheckout: hubCheckoutForCursorSeed(),
|
|
18232
18429
|
execFileP: execFileP2,
|
|
18233
|
-
mkdtemp: (prefix) => (0, import_promises2.mkdtemp)((0,
|
|
18430
|
+
mkdtemp: (prefix) => (0, import_promises2.mkdtemp)((0, import_node_path18.join)((0, import_node_os7.tmpdir)(), prefix)),
|
|
18234
18431
|
log: (m) => io.err(m)
|
|
18235
18432
|
});
|
|
18236
18433
|
if (seeded) {
|
|
@@ -18263,6 +18460,11 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
|
|
|
18263
18460
|
}
|
|
18264
18461
|
}
|
|
18265
18462
|
checks.push(cursorPluginCheck);
|
|
18463
|
+
if (!opts.banner) {
|
|
18464
|
+
const cursorLogText = readCursorPluginLogText();
|
|
18465
|
+
const enabledPinSignal = cursorLogText ? parseCursorPluginLog(cursorLogText) : {};
|
|
18466
|
+
checks.push(buildCursorEnabledPinCheck({ isOrgRepo, pins: cursorPins, ...enabledPinSignal }));
|
|
18467
|
+
}
|
|
18266
18468
|
const cursorThirdPartyEnabled = await readCursorThirdPartyExtensibilityEnabled(execFileP2);
|
|
18267
18469
|
checks.push(
|
|
18268
18470
|
buildCursorThirdPartyExtensibilityCheck({
|
|
@@ -18420,29 +18622,33 @@ function mergeGuardHook(settings) {
|
|
|
18420
18622
|
next.hooks = hooks;
|
|
18421
18623
|
return next;
|
|
18422
18624
|
}
|
|
18423
|
-
var userScopeSettingsPath = (surface = detectSurface(process.env)) => (0,
|
|
18625
|
+
var userScopeSettingsPath = (surface = detectSurface(process.env)) => (0, import_node_path18.join)((0, import_node_os7.homedir)(), surface === "codex" ? ".codex" : ".claude", "settings.json");
|
|
18424
18626
|
function ensureUserScopeGuardHook(opts = {}) {
|
|
18425
18627
|
const path2 = opts.settingsPath ?? userScopeSettingsPath();
|
|
18426
18628
|
try {
|
|
18427
18629
|
let current = null;
|
|
18428
|
-
if ((0,
|
|
18630
|
+
if ((0, import_node_fs18.existsSync)(path2)) {
|
|
18429
18631
|
try {
|
|
18430
|
-
current = JSON.parse((0,
|
|
18632
|
+
current = JSON.parse((0, import_node_fs18.readFileSync)(path2, "utf8"));
|
|
18431
18633
|
} catch {
|
|
18432
18634
|
return "failed";
|
|
18433
18635
|
}
|
|
18434
18636
|
}
|
|
18435
18637
|
if (settingsHasGuardHook(current)) return "already";
|
|
18436
18638
|
const merged = mergeGuardHook(current);
|
|
18437
|
-
(0,
|
|
18438
|
-
if ((0,
|
|
18439
|
-
(0,
|
|
18639
|
+
(0, import_node_fs18.mkdirSync)((0, import_node_path18.dirname)(path2), { recursive: true });
|
|
18640
|
+
if ((0, import_node_fs18.existsSync)(path2)) (0, import_node_fs18.copyFileSync)(path2, `${path2}.bak`);
|
|
18641
|
+
(0, import_node_fs18.writeFileSync)(path2, `${JSON.stringify(merged, null, 2)}
|
|
18440
18642
|
`, "utf8");
|
|
18441
18643
|
return "written";
|
|
18442
18644
|
} catch {
|
|
18443
18645
|
return "failed";
|
|
18444
18646
|
}
|
|
18445
18647
|
}
|
|
18648
|
+
function guardRecoveryCommand(surface) {
|
|
18649
|
+
const token = surfaceToken(surface);
|
|
18650
|
+
return token ? PLUGIN_SURFACE_HEAL[token]?.guardRecovery : void 0;
|
|
18651
|
+
}
|
|
18446
18652
|
async function runGuard(opts = {}, readOrigin) {
|
|
18447
18653
|
void opts;
|
|
18448
18654
|
try {
|
|
@@ -18450,7 +18656,7 @@ async function runGuard(opts = {}, readOrigin) {
|
|
|
18450
18656
|
const isOrgRepo = readOrigin ? await isOrgRepoRoot(readOrigin) : await isOrgRepoRoot();
|
|
18451
18657
|
const input = snapshotPluginGuardInput(surface, isOrgRepo);
|
|
18452
18658
|
const { state } = buildPluginGuardDecision(input);
|
|
18453
|
-
const { line, exitCode } = buildGuardSessionStartLine(state);
|
|
18659
|
+
const { line, exitCode } = buildGuardSessionStartLine(state, { recovery: guardRecoveryCommand(surface) });
|
|
18454
18660
|
if (line) console.error(line);
|
|
18455
18661
|
process.exitCode = exitCode;
|
|
18456
18662
|
} catch {
|
|
@@ -18570,7 +18776,7 @@ async function applyGcPlan(plan, remote) {
|
|
|
18570
18776
|
cleanupBranch: (branch, expectedHeadOid) => cleanupPrMergeLocalBranch(branch.branch, {
|
|
18571
18777
|
beforeWorktrees,
|
|
18572
18778
|
startingPath: branch.worktreePath,
|
|
18573
|
-
pathExists: (p) => (0,
|
|
18779
|
+
pathExists: (p) => (0, import_node_fs19.existsSync)(p),
|
|
18574
18780
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
18575
18781
|
teardownWorktreeStage,
|
|
18576
18782
|
deferredStore,
|
|
@@ -18593,7 +18799,7 @@ async function applyGcPlan(plan, remote) {
|
|
|
18593
18799
|
for (const wt of plan.worktreeDirs) {
|
|
18594
18800
|
try {
|
|
18595
18801
|
const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, siblingRoot, {
|
|
18596
|
-
realpath: (path2) => (0,
|
|
18802
|
+
realpath: (path2) => (0, import_node_fs19.realpathSync)(path2)
|
|
18597
18803
|
});
|
|
18598
18804
|
if (!cleanupTarget.ok) {
|
|
18599
18805
|
result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
|
|
@@ -18636,13 +18842,13 @@ var program2 = new Command();
|
|
|
18636
18842
|
program2.name("mmi-cli").description("MMI Future CLI \u2014 org rules delivery, Jervaise-only continuity. The engine the plugin SessionStart hook drives.").version(resolveClientVersion()).showHelpAfterError("(run `mmi-cli commands` to list every subcommand + its flags, or `mmi-cli commands --json` to ground against it)");
|
|
18637
18843
|
var rules = program2.command("rules").description("org-managed .gitignore delivery");
|
|
18638
18844
|
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) => {
|
|
18639
|
-
const path2 = (0,
|
|
18640
|
-
const current = (0,
|
|
18845
|
+
const path2 = (0, import_node_path19.join)(process.cwd(), ".gitignore");
|
|
18846
|
+
const current = (0, import_node_fs19.existsSync)(path2) ? (0, import_node_fs19.readFileSync)(path2, "utf8") : null;
|
|
18641
18847
|
const plan = planManagedGitignore(current);
|
|
18642
18848
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
18643
18849
|
if (opts.write) {
|
|
18644
18850
|
if (plan.changed) {
|
|
18645
|
-
(0,
|
|
18851
|
+
(0, import_node_fs19.writeFileSync)(path2, plan.content, "utf8");
|
|
18646
18852
|
console.log(`mmi-cli rules gitignore: updated .gitignore (${drift})`);
|
|
18647
18853
|
} else {
|
|
18648
18854
|
console.log("mmi-cli rules gitignore: up to date");
|
|
@@ -18784,8 +18990,8 @@ function runWorktreeInstall(command, cwd, quiet) {
|
|
|
18784
18990
|
const [bin, ...args] = command.split(" ");
|
|
18785
18991
|
const file = isWin2 ? "cmd.exe" : bin;
|
|
18786
18992
|
const spawnArgs = isWin2 ? ["/c", bin, ...args] : args;
|
|
18787
|
-
return new Promise((
|
|
18788
|
-
const child = (0,
|
|
18993
|
+
return new Promise((resolve5, reject) => {
|
|
18994
|
+
const child = (0, import_node_child_process12.spawn)(file, spawnArgs, { cwd, stdio: quiet ? "ignore" : "inherit", windowsHide: true });
|
|
18789
18995
|
const timer = setTimeout(() => {
|
|
18790
18996
|
try {
|
|
18791
18997
|
child.kill();
|
|
@@ -18799,7 +19005,7 @@ function runWorktreeInstall(command, cwd, quiet) {
|
|
|
18799
19005
|
});
|
|
18800
19006
|
child.on("exit", (code) => {
|
|
18801
19007
|
clearTimeout(timer);
|
|
18802
|
-
if (code === 0)
|
|
19008
|
+
if (code === 0) resolve5();
|
|
18803
19009
|
else reject(new Error(`${command} exited ${code} in ${cwd}`));
|
|
18804
19010
|
});
|
|
18805
19011
|
});
|
|
@@ -18807,7 +19013,7 @@ function runWorktreeInstall(command, cwd, quiet) {
|
|
|
18807
19013
|
async function primaryCheckoutRoot(worktreeRoot) {
|
|
18808
19014
|
try {
|
|
18809
19015
|
const out = (await execFileP2("git", ["-C", worktreeRoot, "rev-parse", "--path-format=absolute", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
|
|
18810
|
-
return out ? (0,
|
|
19016
|
+
return out ? (0, import_node_path19.dirname)(out) : void 0;
|
|
18811
19017
|
} catch {
|
|
18812
19018
|
return void 0;
|
|
18813
19019
|
}
|
|
@@ -18822,26 +19028,26 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
|
|
|
18822
19028
|
function acquireWorktreeSetupLock(worktreeRoot) {
|
|
18823
19029
|
const lockPath = repoRuntimeStatePath(worktreeRoot, "worktree-setup.lock");
|
|
18824
19030
|
const take = () => {
|
|
18825
|
-
const fd = (0,
|
|
19031
|
+
const fd = (0, import_node_fs19.openSync)(lockPath, "wx");
|
|
18826
19032
|
try {
|
|
18827
|
-
(0,
|
|
19033
|
+
(0, import_node_fs19.writeSync)(fd, String(Date.now()));
|
|
18828
19034
|
} finally {
|
|
18829
|
-
(0,
|
|
19035
|
+
(0, import_node_fs19.closeSync)(fd);
|
|
18830
19036
|
}
|
|
18831
19037
|
return () => {
|
|
18832
19038
|
try {
|
|
18833
|
-
(0,
|
|
19039
|
+
(0, import_node_fs19.rmSync)(lockPath, { force: true });
|
|
18834
19040
|
} catch {
|
|
18835
19041
|
}
|
|
18836
19042
|
};
|
|
18837
19043
|
};
|
|
18838
19044
|
try {
|
|
18839
|
-
(0,
|
|
19045
|
+
(0, import_node_fs19.mkdirSync)((0, import_node_path19.dirname)(lockPath), { recursive: true });
|
|
18840
19046
|
return take();
|
|
18841
19047
|
} catch {
|
|
18842
19048
|
try {
|
|
18843
|
-
if (Date.now() - (0,
|
|
18844
|
-
(0,
|
|
19049
|
+
if (Date.now() - (0, import_node_fs19.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
|
|
19050
|
+
(0, import_node_fs19.rmSync)(lockPath, { force: true });
|
|
18845
19051
|
return take();
|
|
18846
19052
|
}
|
|
18847
19053
|
} catch {
|
|
@@ -18980,7 +19186,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
18980
19186
|
try {
|
|
18981
19187
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body];
|
|
18982
19188
|
if (o.repo) args.push("--repo", o.repo);
|
|
18983
|
-
(0,
|
|
19189
|
+
(0, import_node_child_process12.spawn)(process.execPath, [process.argv[1], ...args], {
|
|
18984
19190
|
detached: true,
|
|
18985
19191
|
stdio: "ignore",
|
|
18986
19192
|
windowsHide: true,
|
|
@@ -19073,8 +19279,8 @@ tenant.command("sweep-rc").description("discover (and optionally retire) running
|
|
|
19073
19279
|
async function resolveDnsBounded(host, timeoutMs = 3e3) {
|
|
19074
19280
|
const { lookup } = await import("node:dns/promises");
|
|
19075
19281
|
const probe = lookup(host).then(() => true).catch((e) => dnsErrorToResolution(e?.code));
|
|
19076
|
-
const timeout = new Promise((
|
|
19077
|
-
setTimeout(() =>
|
|
19282
|
+
const timeout = new Promise((resolve5) => {
|
|
19283
|
+
setTimeout(() => resolve5(void 0), timeoutMs).unref?.();
|
|
19078
19284
|
});
|
|
19079
19285
|
return Promise.race([probe, timeout]);
|
|
19080
19286
|
}
|
|
@@ -19336,7 +19542,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
19336
19542
|
const vars = rawValues("--var");
|
|
19337
19543
|
if (o.secretsFile) {
|
|
19338
19544
|
try {
|
|
19339
|
-
vars.push(`secrets=${(0,
|
|
19545
|
+
vars.push(`secrets=${(0, import_node_fs19.readFileSync)(o.secretsFile, "utf8")}`);
|
|
19340
19546
|
} catch (e) {
|
|
19341
19547
|
return fail(`project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
19342
19548
|
}
|
|
@@ -19624,18 +19830,20 @@ issue.command("create").description("create an issue (type \u2192 label) and pri
|
|
|
19624
19830
|
...parentLinkFields(parent, parentLinkError)
|
|
19625
19831
|
}));
|
|
19626
19832
|
});
|
|
19627
|
-
issue.command("view <number>").description("read an issue as structured JSON \u2014 the mmi-cli path for non-board issue reads (#2347)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json
|
|
19833
|
+
issue.command("view <number>").description("read an issue as structured JSON \u2014 the mmi-cli path for non-board issue reads (#2347)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json [fields...]", 'gh --json field list (overrides the default field set). Accepts commas, spaces, or repeated --json flags \u2014 in PowerShell an unquoted comma list is an array literal, so QUOTE it: --json "number,title,body,url"').action(async (number, o) => {
|
|
19628
19834
|
const n = Number(number);
|
|
19629
19835
|
if (!Number.isInteger(n) || n <= 0) return fail("issue view: <number> must be a positive integer");
|
|
19630
19836
|
const repo = await resolveRepo(o.repo);
|
|
19631
19837
|
if (!repo) return fail("issue view: could not resolve repo (pass --repo <owner/repo>)");
|
|
19632
|
-
const fields =
|
|
19838
|
+
const fields = normalizeIssueViewJsonFields(o.json);
|
|
19633
19839
|
try {
|
|
19634
19840
|
const data = await ghJson(["issue", "view", String(n), "--repo", repo, "--json", fields]);
|
|
19635
19841
|
console.log(JSON.stringify(data));
|
|
19636
19842
|
} catch (e) {
|
|
19637
19843
|
const err = e;
|
|
19638
|
-
|
|
19844
|
+
const raw = (err.stderr || err.message || String(e)).trim();
|
|
19845
|
+
const argvNote = o.json ? ` (received --json argv: ${JSON.stringify(o.json)}, normalized to "${fields}")` : "";
|
|
19846
|
+
return fail(`issue view: ${raw}${argvNote}`);
|
|
19639
19847
|
}
|
|
19640
19848
|
});
|
|
19641
19849
|
issue.command("discover-related").description("find related issues for an existing issue and post only high-confidence links").requiredOption("--number <number>", "created issue number").requiredOption("--title <title>", "created issue title").requiredOption("--body <body>", "created issue body").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json", "print candidates instead of posting").action(async (o) => {
|
|
@@ -19904,9 +20112,9 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
|
|
|
19904
20112
|
}
|
|
19905
20113
|
});
|
|
19906
20114
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
19907
|
-
const wfDir = (0,
|
|
19908
|
-
if (!(0,
|
|
19909
|
-
return (0,
|
|
20115
|
+
const wfDir = (0, import_node_path19.join)(cwd, ".github", "workflows");
|
|
20116
|
+
if (!(0, import_node_fs19.existsSync)(wfDir)) return [];
|
|
20117
|
+
return (0, import_node_fs19.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).map((name) => `.github/workflows/${name}`);
|
|
19910
20118
|
}
|
|
19911
20119
|
async function resolveMergeCiPolicyForCheckout(repoOpt) {
|
|
19912
20120
|
const repo = repoOpt ?? await resolveRepo();
|
|
@@ -19925,7 +20133,7 @@ function ciAuditDeps() {
|
|
|
19925
20133
|
// Continuous CI delivery (#1550): the gate re-seed renders from the Hub's on-disk seed templates. The
|
|
19926
20134
|
// reconcile runs IN the Hub checkout, so this is local-file I/O (no network fetch). Path is relative to
|
|
19927
20135
|
// the repo root (e.g. skills/bootstrap/seeds/gate.template.yml).
|
|
19928
|
-
readSeedFile: (path2) => (0,
|
|
20136
|
+
readSeedFile: (path2) => (0, import_node_fs19.existsSync)(path2) ? (0, import_node_fs19.readFileSync)(path2, "utf8") : null
|
|
19929
20137
|
};
|
|
19930
20138
|
}
|
|
19931
20139
|
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) => {
|
|
@@ -19955,7 +20163,7 @@ pr.command("checks-wait <number>").description("bounded wait for PR checks; skip
|
|
|
19955
20163
|
const result = await waitForPrChecks({
|
|
19956
20164
|
resolvePolicy: () => resolveMergeCiPolicyForCheckout(o.repo),
|
|
19957
20165
|
pollChecks: () => pollGhPrChecks(number, repoArgs),
|
|
19958
|
-
sleep: (ms) => new Promise((
|
|
20166
|
+
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
|
|
19959
20167
|
});
|
|
19960
20168
|
if (o.json) printLine(JSON.stringify(result));
|
|
19961
20169
|
else printLine(`pr checks-wait: ${result.status}${result.reason ? ` \u2014 ${result.reason}` : ""}${result.detail ? ` (${result.detail})` : ""}`);
|
|
@@ -19982,7 +20190,7 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
|
|
|
19982
20190
|
waitForChecks: (prNumber, repo) => waitForPrChecks({
|
|
19983
20191
|
resolvePolicy: () => resolveRepoMergeCiPolicy(repo, ciAuditDeps()),
|
|
19984
20192
|
pollChecks: () => pollGhPrChecks(prNumber, repo ? ["--repo", repo] : []),
|
|
19985
|
-
sleep: (ms) => new Promise((
|
|
20193
|
+
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
|
|
19986
20194
|
}),
|
|
19987
20195
|
// #2425: re-probe used ONLY to decide whether a failed `gh pr merge --auto` is worth retrying — a
|
|
19988
20196
|
// fresh read of state/mergeable/checks, independent of the merge call's own (possibly stale) error.
|
|
@@ -20033,7 +20241,7 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
|
|
|
20033
20241
|
while (Date.now() < deadlineMs) {
|
|
20034
20242
|
const stateRead = await readGhPrStateWithRetry(async () => (await execFileP2("gh", ["pr", "view", prNumber, ...args, "--json", "state", "--jq", ".state"], { timeout: GC_GH_TIMEOUT_MS2 })).stdout, { retries: 2, delayMs: 1e3 });
|
|
20035
20243
|
if (stateRead.ok && stateRead.state === "MERGED") return true;
|
|
20036
|
-
await new Promise((
|
|
20244
|
+
await new Promise((resolve5) => setTimeout(resolve5, PR_LAND_POLL_MS));
|
|
20037
20245
|
}
|
|
20038
20246
|
return false;
|
|
20039
20247
|
}
|
|
@@ -20077,7 +20285,7 @@ async function remoteBranchExists2(branch, options = {}) {
|
|
|
20077
20285
|
}
|
|
20078
20286
|
var COMPOSE_TIMEOUT_MS = 12e4;
|
|
20079
20287
|
function spawnDeferredGcSweep() {
|
|
20080
|
-
spawnDetachedSelf(["gc", "sweep-deferred", "--quiet"], { spawn:
|
|
20288
|
+
spawnDetachedSelf(["gc", "sweep-deferred", "--quiet"], { spawn: import_node_child_process12.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
20081
20289
|
}
|
|
20082
20290
|
async function createDeferredWorktreeStore() {
|
|
20083
20291
|
try {
|
|
@@ -20093,7 +20301,7 @@ async function createDeferredWorktreeStore() {
|
|
|
20093
20301
|
},
|
|
20094
20302
|
write: async (entries) => {
|
|
20095
20303
|
try {
|
|
20096
|
-
await (0, import_promises3.mkdir)((0,
|
|
20304
|
+
await (0, import_promises3.mkdir)((0, import_node_path19.dirname)(registryPath), { recursive: true });
|
|
20097
20305
|
await (0, import_promises3.writeFile)(registryPath, serializeDeferredWorktrees(entries), "utf8");
|
|
20098
20306
|
} catch {
|
|
20099
20307
|
}
|
|
@@ -20107,13 +20315,13 @@ var realWorktreeDirRemover = {
|
|
|
20107
20315
|
probe: (p) => {
|
|
20108
20316
|
let st;
|
|
20109
20317
|
try {
|
|
20110
|
-
st = (0,
|
|
20318
|
+
st = (0, import_node_fs19.lstatSync)(p);
|
|
20111
20319
|
} catch {
|
|
20112
20320
|
return null;
|
|
20113
20321
|
}
|
|
20114
20322
|
if (st.isSymbolicLink()) return "link";
|
|
20115
20323
|
try {
|
|
20116
|
-
(0,
|
|
20324
|
+
(0, import_node_fs19.readlinkSync)(p);
|
|
20117
20325
|
return "link";
|
|
20118
20326
|
} catch {
|
|
20119
20327
|
}
|
|
@@ -20121,7 +20329,7 @@ var realWorktreeDirRemover = {
|
|
|
20121
20329
|
},
|
|
20122
20330
|
readdir: (p) => {
|
|
20123
20331
|
try {
|
|
20124
|
-
return (0,
|
|
20332
|
+
return (0, import_node_fs19.readdirSync)(p);
|
|
20125
20333
|
} catch {
|
|
20126
20334
|
return [];
|
|
20127
20335
|
}
|
|
@@ -20130,9 +20338,9 @@ var realWorktreeDirRemover = {
|
|
|
20130
20338
|
// leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
|
|
20131
20339
|
detachLink: (p) => {
|
|
20132
20340
|
try {
|
|
20133
|
-
(0,
|
|
20341
|
+
(0, import_node_fs19.rmdirSync)(p);
|
|
20134
20342
|
} catch {
|
|
20135
|
-
(0,
|
|
20343
|
+
(0, import_node_fs19.unlinkSync)(p);
|
|
20136
20344
|
}
|
|
20137
20345
|
},
|
|
20138
20346
|
removeTree: (p) => (0, import_promises3.rm)(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
|
|
@@ -20147,7 +20355,7 @@ async function resolvePrimaryCheckout(execGit) {
|
|
|
20147
20355
|
function worktreeRemoveDeps(execGit) {
|
|
20148
20356
|
return {
|
|
20149
20357
|
git: execGit,
|
|
20150
|
-
sleep: (ms) => new Promise((
|
|
20358
|
+
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
|
|
20151
20359
|
removeWorktreeDir: async (worktreePath) => removeWorktreeTree(worktreePath, await resolvePrimaryCheckout(execGit), realWorktreeDirRemover)
|
|
20152
20360
|
};
|
|
20153
20361
|
}
|
|
@@ -20162,9 +20370,9 @@ async function worktreeHasStageState(worktreePath) {
|
|
|
20162
20370
|
}
|
|
20163
20371
|
}
|
|
20164
20372
|
function stageStateFileBelongsToWorktree(statePath, worktreePath) {
|
|
20165
|
-
if (!(0,
|
|
20373
|
+
if (!(0, import_node_fs19.existsSync)(statePath)) return false;
|
|
20166
20374
|
try {
|
|
20167
|
-
const state = JSON.parse((0,
|
|
20375
|
+
const state = JSON.parse((0, import_node_fs19.readFileSync)(statePath, "utf8"));
|
|
20168
20376
|
const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
|
|
20169
20377
|
return Boolean(recordedCwd && isPathUnderDirectory(recordedCwd, worktreePath));
|
|
20170
20378
|
} catch {
|
|
@@ -20230,7 +20438,7 @@ pr.command("merge <number>").description("merge a PR (squash by default) and cle
|
|
|
20230
20438
|
localCleanup = await cleanupPrMergeLocalBranch(headRef, {
|
|
20231
20439
|
beforeWorktrees,
|
|
20232
20440
|
startingPath,
|
|
20233
|
-
pathExists: (p) => (0,
|
|
20441
|
+
pathExists: (p) => (0, import_node_fs19.existsSync)(p),
|
|
20234
20442
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
20235
20443
|
teardownWorktreeStage,
|
|
20236
20444
|
deferredStore,
|
|
@@ -20451,7 +20659,7 @@ function stageScopedRunOpts(o) {
|
|
|
20451
20659
|
};
|
|
20452
20660
|
}
|
|
20453
20661
|
function printLine(value) {
|
|
20454
|
-
(0,
|
|
20662
|
+
(0, import_node_fs19.writeSync)(1, `${value}
|
|
20455
20663
|
`);
|
|
20456
20664
|
}
|
|
20457
20665
|
function stageKeepAlive() {
|
|
@@ -20465,8 +20673,8 @@ async function resolveStage() {
|
|
|
20465
20673
|
const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
|
|
20466
20674
|
return decideStage({
|
|
20467
20675
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
20468
|
-
hasCompose: (0,
|
|
20469
|
-
hasEnvExample: (0,
|
|
20676
|
+
hasCompose: (0, import_node_fs19.existsSync)((0, import_node_path19.join)(process.cwd(), "docker-compose.yml")),
|
|
20677
|
+
hasEnvExample: (0, import_node_fs19.existsSync)((0, import_node_path19.join)(process.cwd(), ".env.example"))
|
|
20470
20678
|
});
|
|
20471
20679
|
}
|
|
20472
20680
|
async function fetchStageVaultEnvMerge() {
|
|
@@ -20900,21 +21108,14 @@ ${r.repo}: applied=[${r.applied.join("; ")}] skipped=[${r.skipped.join("; ")}]${
|
|
|
20900
21108
|
if (!audit.ok) process.exitCode = 1;
|
|
20901
21109
|
});
|
|
20902
21110
|
var wiki = program2.command("wiki").description("release wiki lane \u2014 publish generated pages via a short-lived, repo-scoped minted token");
|
|
20903
|
-
wiki.command("publish <pages-dir>").description("mint a 1h repo-scoped contents:write token (master-gated) and publish the generated .md pages to <repo>.wiki.git
|
|
21111
|
+
wiki.command("publish <pages-dir>").description("mint a 1h repo-scoped contents:write token (master-gated) and publish the generated .md pages to <repo>.wiki.git; the token stays in memory (never printed/committed). Runs from any org repo checkout \u2014 the publisher ships inside mmi-cli, no repo-local script required. Fails loud on the credential gap \u2014 never a silent skip").option("--repo <owner/repo>", "target repo (defaults to the cwd repo)").action(async (pagesDir, o) => {
|
|
20904
21112
|
const repo = await resolveRepo(o.repo);
|
|
20905
21113
|
if (!repo) return fail("wiki publish: could not determine the target repo (pass --repo owner/name)");
|
|
20906
21114
|
const cfg = await loadConfig();
|
|
20907
21115
|
const ok = await wikiPublish(
|
|
20908
21116
|
{
|
|
20909
21117
|
mint: (r) => mintWikiToken(r, registryClientDeps(cfg)),
|
|
20910
|
-
|
|
20911
|
-
scriptPath: () => (0, import_node_path18.resolve)(process.cwd(), "scripts", "wiki-publish.mjs"),
|
|
20912
|
-
scriptExists: (p) => (0, import_node_fs18.existsSync)(p),
|
|
20913
|
-
spawn: (command, args, env) => {
|
|
20914
|
-
const r = (0, import_node_child_process11.spawnSync)(command, args, { stdio: "inherit", env });
|
|
20915
|
-
if (r.error) throw r.error;
|
|
20916
|
-
return r.status ?? 1;
|
|
20917
|
-
},
|
|
21118
|
+
publish: (o2) => publishWikiViaGit(realWikiGitPublishDeps(), o2),
|
|
20918
21119
|
log: (msg) => console.log(msg),
|
|
20919
21120
|
err: (msg) => console.error(msg)
|
|
20920
21121
|
},
|
|
@@ -20941,7 +21142,7 @@ bootstrap.command("verify <repo>").description("audit whether an existing repo i
|
|
|
20941
21142
|
client: defaultGitHubClient(),
|
|
20942
21143
|
projectMeta: meta,
|
|
20943
21144
|
deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
|
|
20944
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
21145
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs19.existsSync)(path2) ? (0, import_node_fs19.readFileSync)(path2, "utf8") : null,
|
|
20945
21146
|
// requiredGcpApis is stored as an array by a JSON write, but `project set --var KEY=VALUE` stores a raw
|
|
20946
21147
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
20947
21148
|
requiredGcpApis: (() => {
|
|
@@ -20992,12 +21193,12 @@ bootstrap.command("apply <repo>").description("idempotent seed apply from skills
|
|
|
20992
21193
|
return fail(`bootstrap apply: ${e.message}`);
|
|
20993
21194
|
}
|
|
20994
21195
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
20995
|
-
if (!(0,
|
|
20996
|
-
const manifest = loadBootstrapSeeds((0,
|
|
21196
|
+
if (!(0, import_node_fs19.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; run from the MMI-Hub repo root`);
|
|
21197
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs19.readFileSync)(manifestPath, "utf8"));
|
|
20997
21198
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
20998
21199
|
const slug = parsedRepo.slug;
|
|
20999
21200
|
const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
|
|
21000
|
-
const readFile2 = (p) => (0,
|
|
21201
|
+
const readFile2 = (p) => (0, import_node_fs19.existsSync)(p) ? (0, import_node_fs19.readFileSync)(p, "utf8") : null;
|
|
21001
21202
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
21002
21203
|
const rawVars = {};
|
|
21003
21204
|
for (const value of rawValues("--var")) {
|
|
@@ -21294,16 +21495,16 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
21294
21495
|
const repoClass = o.class;
|
|
21295
21496
|
targets = [{ repo: o.repo, class: repoClass, releaseTrack: repoClass === "content" ? "trunk" : resolveReleaseTrack(meta, void 0, o.repo) }];
|
|
21296
21497
|
} else {
|
|
21297
|
-
const projectsJson = registryProjects ? JSON.stringify({ projects: registryProjects }) : (0,
|
|
21498
|
+
const projectsJson = registryProjects ? JSON.stringify({ projects: registryProjects }) : (0, import_node_fs19.existsSync)("projects.json") ? (0, import_node_fs19.readFileSync)("projects.json", "utf8") : null;
|
|
21298
21499
|
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>");
|
|
21299
|
-
const fanoutJson = (0,
|
|
21500
|
+
const fanoutJson = (0, import_node_fs19.existsSync)(".github/fanout-targets.json") ? (0, import_node_fs19.readFileSync)(".github/fanout-targets.json", "utf8") : null;
|
|
21300
21501
|
targets = loadAccessTargets(projectsJson, fanoutJson);
|
|
21301
21502
|
}
|
|
21302
21503
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
21303
|
-
const fileMatrix = (0,
|
|
21504
|
+
const fileMatrix = (0, import_node_fs19.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs19.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
21304
21505
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
21305
21506
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
21306
|
-
const fileContracts = (0,
|
|
21507
|
+
const fileContracts = (0, import_node_fs19.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs19.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
21307
21508
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
21308
21509
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess);
|
|
21309
21510
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
@@ -21340,7 +21541,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
|
|
|
21340
21541
|
readBoard,
|
|
21341
21542
|
// #1813: warm the slice cache out-of-band (detached) so the ~20s live read
|
|
21342
21543
|
// never costs banner time and next session's glance renders instantly within budget.
|
|
21343
|
-
scheduleRefresh: () => spawnDetachedSelf(["board", "slice-refresh", "--quiet"], { spawn:
|
|
21544
|
+
scheduleRefresh: () => spawnDetachedSelf(["board", "slice-refresh", "--quiet"], { spawn: import_node_child_process12.spawn, execPath: process.execPath, scriptPath: process.argv[1] })
|
|
21344
21545
|
}),
|
|
21345
21546
|
doctor: (io) => runDoctor({ banner: true }, io)
|
|
21346
21547
|
});
|
|
@@ -21348,7 +21549,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
|
|
|
21348
21549
|
for (const line of scratchGcLines(process.cwd())) consoleIo.log(line);
|
|
21349
21550
|
const worktreeBanner = worktreeAutoProvisionBanner(process.cwd());
|
|
21350
21551
|
if (worktreeBanner) {
|
|
21351
|
-
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn:
|
|
21552
|
+
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process12.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
21352
21553
|
consoleIo.log(worktreeBanner);
|
|
21353
21554
|
}
|
|
21354
21555
|
});
|