@mutmutco/cli 2.32.2 → 2.32.3
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 +270 -120
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -3392,7 +3392,7 @@ var program = new Command();
|
|
|
3392
3392
|
|
|
3393
3393
|
// src/index.ts
|
|
3394
3394
|
var import_promises5 = require("node:fs/promises");
|
|
3395
|
-
var
|
|
3395
|
+
var import_node_fs15 = require("node:fs");
|
|
3396
3396
|
|
|
3397
3397
|
// src/rules-sync.ts
|
|
3398
3398
|
function normalizeEol(s) {
|
|
@@ -6857,8 +6857,8 @@ async function runNorthstarContext(io, deps) {
|
|
|
6857
6857
|
}
|
|
6858
6858
|
|
|
6859
6859
|
// src/index.ts
|
|
6860
|
-
var
|
|
6861
|
-
var
|
|
6860
|
+
var import_node_path14 = require("node:path");
|
|
6861
|
+
var import_node_os5 = require("node:os");
|
|
6862
6862
|
|
|
6863
6863
|
// src/gh-create.ts
|
|
6864
6864
|
var import_promises4 = require("node:fs/promises");
|
|
@@ -8247,6 +8247,112 @@ function decideStage(inputs) {
|
|
|
8247
8247
|
return { source: "none", gap, staleIgnored: stale || void 0, staleFields: stale ? staleFields : void 0, registryError: registry2.error };
|
|
8248
8248
|
}
|
|
8249
8249
|
|
|
8250
|
+
// src/cursor-plugin-seed.ts
|
|
8251
|
+
var import_node_fs11 = require("node:fs");
|
|
8252
|
+
var import_node_os4 = require("node:os");
|
|
8253
|
+
var import_node_path11 = require("node:path");
|
|
8254
|
+
function isSemverVersion(v) {
|
|
8255
|
+
return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
|
|
8256
|
+
}
|
|
8257
|
+
var MMI_HUB_REPO = "mutmutco/MMI-Hub";
|
|
8258
|
+
var CURSOR_THIRD_PARTY_STATE_KEY = "cursor/thirdPartyExtensibilityEnabled";
|
|
8259
|
+
var PLUGIN_JSON_REL = ".cursor-plugin/plugin.json";
|
|
8260
|
+
function cursorUserGlobalStatePath() {
|
|
8261
|
+
if (process.platform === "win32") {
|
|
8262
|
+
const base2 = process.env.APPDATA || (0, import_node_path11.join)((0, import_node_os4.homedir)(), "AppData", "Roaming");
|
|
8263
|
+
return (0, import_node_path11.join)(base2, "Cursor", "User", "globalStorage", "state.vscdb");
|
|
8264
|
+
}
|
|
8265
|
+
if (process.platform === "darwin") {
|
|
8266
|
+
return (0, import_node_path11.join)((0, import_node_os4.homedir)(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
8267
|
+
}
|
|
8268
|
+
return (0, import_node_path11.join)((0, import_node_os4.homedir)(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
8269
|
+
}
|
|
8270
|
+
async function readCursorThirdPartyExtensibilityEnabled(execFileP5) {
|
|
8271
|
+
const dbPath = cursorUserGlobalStatePath();
|
|
8272
|
+
if (!(0, import_node_fs11.existsSync)(dbPath)) return void 0;
|
|
8273
|
+
try {
|
|
8274
|
+
const { stdout } = await execFileP5("sqlite3", [dbPath, `SELECT value FROM ItemTable WHERE key = '${CURSOR_THIRD_PARTY_STATE_KEY}';`], {
|
|
8275
|
+
timeout: 5e3
|
|
8276
|
+
});
|
|
8277
|
+
const value = stdout.trim().toLowerCase();
|
|
8278
|
+
if (value === "true") return true;
|
|
8279
|
+
if (value === "false") return false;
|
|
8280
|
+
return void 0;
|
|
8281
|
+
} catch {
|
|
8282
|
+
return void 0;
|
|
8283
|
+
}
|
|
8284
|
+
}
|
|
8285
|
+
function syncDirContents(src, dest) {
|
|
8286
|
+
(0, import_node_fs11.mkdirSync)(dest, { recursive: true });
|
|
8287
|
+
for (const name of (0, import_node_fs11.readdirSync)(dest)) {
|
|
8288
|
+
(0, import_node_fs11.rmSync)((0, import_node_path11.join)(dest, name), { recursive: true, force: true });
|
|
8289
|
+
}
|
|
8290
|
+
(0, import_node_fs11.cpSync)(src, dest, { recursive: true });
|
|
8291
|
+
}
|
|
8292
|
+
function releaseTag(releasedVersion) {
|
|
8293
|
+
return releasedVersion.startsWith("v") ? releasedVersion : `v${releasedVersion}`;
|
|
8294
|
+
}
|
|
8295
|
+
async function extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5) {
|
|
8296
|
+
const tarFile = (0, import_node_path11.join)(tmpRoot, "archive.tar");
|
|
8297
|
+
try {
|
|
8298
|
+
await execFileP5("git", ["-C", hubCheckout, "fetch", "origin", `tag ${tag}`, "--quiet"], { timeout: 6e4 });
|
|
8299
|
+
await execFileP5("git", ["-C", hubCheckout, "archive", "--format=tar", `--output=${tarFile}`, tag, "plugins/mmi"], {
|
|
8300
|
+
timeout: 6e4
|
|
8301
|
+
});
|
|
8302
|
+
await execFileP5("tar", ["-xf", tarFile, "-C", tmpRoot], { timeout: 6e4 });
|
|
8303
|
+
const pluginMmi = (0, import_node_path11.join)(tmpRoot, "plugins", "mmi");
|
|
8304
|
+
return (0, import_node_fs11.existsSync)((0, import_node_path11.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
|
|
8305
|
+
} catch {
|
|
8306
|
+
return void 0;
|
|
8307
|
+
}
|
|
8308
|
+
}
|
|
8309
|
+
async function downloadPluginMmiViaGh(tag, tmpRoot, execFileP5) {
|
|
8310
|
+
const tarPath = (0, import_node_path11.join)(tmpRoot, "repo.tgz");
|
|
8311
|
+
try {
|
|
8312
|
+
await execFileP5("gh", ["api", `repos/${MMI_HUB_REPO}/tarball/refs/tags/${tag}`, "--output", tarPath], {
|
|
8313
|
+
timeout: 12e4
|
|
8314
|
+
});
|
|
8315
|
+
await execFileP5("tar", ["-xzf", tarPath, "-C", tmpRoot], { timeout: 12e4 });
|
|
8316
|
+
const top = (0, import_node_fs11.readdirSync)(tmpRoot).find((entry) => entry !== "repo.tgz");
|
|
8317
|
+
if (!top) return void 0;
|
|
8318
|
+
const pluginMmi = (0, import_node_path11.join)(tmpRoot, top, "plugins", "mmi");
|
|
8319
|
+
return (0, import_node_fs11.existsSync)((0, import_node_path11.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
|
|
8320
|
+
} catch {
|
|
8321
|
+
return void 0;
|
|
8322
|
+
}
|
|
8323
|
+
}
|
|
8324
|
+
async function resolvePluginMmiSource(releasedVersion, hubCheckout, tmpRoot, execFileP5) {
|
|
8325
|
+
(0, import_node_fs11.mkdirSync)(tmpRoot, { recursive: true });
|
|
8326
|
+
const tag = releaseTag(releasedVersion);
|
|
8327
|
+
if (hubCheckout) {
|
|
8328
|
+
const fromHub = await extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5);
|
|
8329
|
+
if (fromHub) return fromHub;
|
|
8330
|
+
}
|
|
8331
|
+
return downloadPluginMmiViaGh(tag, (0, import_node_path11.join)(tmpRoot, "gh"), execFileP5);
|
|
8332
|
+
}
|
|
8333
|
+
function cursorPluginPinsNeedingSeed(pins, releasedVersion) {
|
|
8334
|
+
if (!isSemverVersion(releasedVersion)) return pins.filter((pin) => !pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty);
|
|
8335
|
+
return pins.filter((pin) => {
|
|
8336
|
+
if (!pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty) return true;
|
|
8337
|
+
if (isSemverVersion(pin.version) && compareVersions(pin.version, releasedVersion) < 0) return true;
|
|
8338
|
+
return false;
|
|
8339
|
+
});
|
|
8340
|
+
}
|
|
8341
|
+
async function applyCursorPluginCacheSeed(input) {
|
|
8342
|
+
if (!isSemverVersion(input.releasedVersion)) return false;
|
|
8343
|
+
const pinsToSeed = cursorPluginPinsNeedingSeed(input.pins, input.releasedVersion);
|
|
8344
|
+
if (pinsToSeed.length === 0) return false;
|
|
8345
|
+
const tmpRoot = await input.mkdtemp("mmi-cursor-seed-");
|
|
8346
|
+
const source = await resolvePluginMmiSource(input.releasedVersion, input.hubCheckout, tmpRoot, input.execFileP);
|
|
8347
|
+
if (!source) return false;
|
|
8348
|
+
input.log(` \u21BB seeding Cursor MMI plugin cache \u2192 ${input.releasedVersion}\u2026`);
|
|
8349
|
+
for (const pin of pinsToSeed) {
|
|
8350
|
+
syncDirContents(source, pin.path);
|
|
8351
|
+
}
|
|
8352
|
+
(0, import_node_fs11.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
8353
|
+
return true;
|
|
8354
|
+
}
|
|
8355
|
+
|
|
8250
8356
|
// src/bootstrap-seeds.ts
|
|
8251
8357
|
var PLACEHOLDER_RE = /\{\{([A-Z0-9_]+)\}\}/g;
|
|
8252
8358
|
function loadBootstrapSeeds(manifestJson) {
|
|
@@ -8622,7 +8728,7 @@ var PLUGIN_UPDATE_RECIPES = {
|
|
|
8622
8728
|
};
|
|
8623
8729
|
function highestSemver(versions) {
|
|
8624
8730
|
return versions.reduce((best, v) => {
|
|
8625
|
-
if (!
|
|
8731
|
+
if (!isSemverVersion2(v)) return best;
|
|
8626
8732
|
if (best === void 0) return v;
|
|
8627
8733
|
return compareVersions(v, best) > 0 ? v : best;
|
|
8628
8734
|
}, void 0);
|
|
@@ -8633,11 +8739,11 @@ function buildPluginUpdateReport(input) {
|
|
|
8633
8739
|
const codexActiveCache = highestSemver(input.codexCacheVersions ?? []);
|
|
8634
8740
|
return {
|
|
8635
8741
|
versions: {
|
|
8636
|
-
...
|
|
8742
|
+
...isSemverVersion2(input.cliVersion) ? { cli: input.cliVersion } : {},
|
|
8637
8743
|
...claudePlugin ? { claudePlugin } : {},
|
|
8638
8744
|
...codexMarketplace ? { codexMarketplace } : {},
|
|
8639
8745
|
...codexActiveCache ? { codexActiveCache } : {},
|
|
8640
|
-
...
|
|
8746
|
+
...isSemverVersion2(input.releasedVersion) ? { released: input.releasedVersion } : {}
|
|
8641
8747
|
},
|
|
8642
8748
|
recipes: PLUGIN_UPDATE_RECIPES
|
|
8643
8749
|
};
|
|
@@ -8667,7 +8773,7 @@ function buildDoctorJsonPayload(input) {
|
|
|
8667
8773
|
};
|
|
8668
8774
|
}
|
|
8669
8775
|
var INSTALLED_PLUGIN_VERSION_LABEL = "installed MMI plugin version (vs latest release)";
|
|
8670
|
-
function
|
|
8776
|
+
function isSemverVersion2(v) {
|
|
8671
8777
|
return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
|
|
8672
8778
|
}
|
|
8673
8779
|
function staleRecordCommand(surface) {
|
|
@@ -8688,7 +8794,7 @@ function buildInstalledPluginVersionCheck(input) {
|
|
|
8688
8794
|
fix: pluginRecoveryFix(input.surface),
|
|
8689
8795
|
pluginId
|
|
8690
8796
|
};
|
|
8691
|
-
if (!input.isOrgRepo || !
|
|
8797
|
+
if (!input.isOrgRepo || !isSemverVersion2(input.releasedVersion)) return base2;
|
|
8692
8798
|
if (input.surface === "cursor") return base2;
|
|
8693
8799
|
const stale = [];
|
|
8694
8800
|
let sawRecord = false;
|
|
@@ -8698,7 +8804,7 @@ function buildInstalledPluginVersionCheck(input) {
|
|
|
8698
8804
|
if (!Array.isArray(records) || records.length === 0) continue;
|
|
8699
8805
|
sawRecord = true;
|
|
8700
8806
|
const installedVersion = bestRecord(records).version;
|
|
8701
|
-
if (!
|
|
8807
|
+
if (!isSemverVersion2(installedVersion)) continue;
|
|
8702
8808
|
if (compareVersions(installedVersion, input.releasedVersion) >= 0) {
|
|
8703
8809
|
currentVersion = currentVersion ?? installedVersion;
|
|
8704
8810
|
} else {
|
|
@@ -8730,15 +8836,16 @@ function joinCachePath(root, ...parts) {
|
|
|
8730
8836
|
function cursorPluginInstallFix(input) {
|
|
8731
8837
|
const logHint = "check %APPDATA%\\Cursor\\logs\\<session>\\window*\\exthost\\anysphere.cursor-agent-exec\\Cursor Plugins.*.log for `unable to get password from user`";
|
|
8732
8838
|
const authSteps = "ensure GitHub SSH auth works for Cursor's background git (start ssh-agent, add your GitHub key, verify `ssh -T git@github.com`) \u2014 Cursor disables credential.helper during marketplace clone";
|
|
8733
|
-
const marketplaceRefresh = "
|
|
8839
|
+
const marketplaceRefresh = "when Dashboard \u2192 Settings \u2192 Plugins shows Update next to the MMI Team Marketplace, refresh it there; otherwise run `mmi-cli doctor --apply` to seed the cache from the latest release";
|
|
8734
8840
|
const guide = `full recovery: ${CURSOR_MARKETPLACE_INSTALL_GUIDE}`;
|
|
8735
8841
|
if (input.reason === "missing-cache") {
|
|
8736
8842
|
return `${marketplaceRefresh}; ${authSteps}; ${guide}`;
|
|
8737
8843
|
}
|
|
8738
8844
|
const pin = input.pinName ?? "<commit-pin>";
|
|
8739
8845
|
const cacheDir = joinCachePath(input.cacheRoot, pin);
|
|
8846
|
+
const autoSeed = "run `mmi-cli doctor --apply` to seed plugins/mmi from the latest release into the active pin";
|
|
8740
8847
|
const localSeed = input.hubCheckout ? `temporary fallback: copy ${joinCachePath(input.hubCheckout, "plugins", "mmi")} to ${cacheDir}, then restart Cursor` : `temporary fallback: copy plugins/mmi from a local MMI-Hub checkout to ${cacheDir}, then restart Cursor`;
|
|
8741
|
-
return `Cursor plugin cache at ${cacheDir} is empty or missing ${CURSOR_PLUGIN_JSON_REL} or ${CURSOR_HOOKS_JSON_REL} \u2014 ${marketplaceRefresh}; ${authSteps}; ${localSeed}; ${logHint}; ${guide}`;
|
|
8848
|
+
return `Cursor plugin cache at ${cacheDir} is empty or missing ${CURSOR_PLUGIN_JSON_REL} or ${CURSOR_HOOKS_JSON_REL} \u2014 ${autoSeed}; ${marketplaceRefresh}; ${authSteps}; ${localSeed}; ${logHint}; ${guide}`;
|
|
8742
8849
|
}
|
|
8743
8850
|
function buildCursorPluginInstallCheck(input) {
|
|
8744
8851
|
const base2 = {
|
|
@@ -8776,18 +8883,29 @@ function buildCursorPluginInstallCheck(input) {
|
|
|
8776
8883
|
};
|
|
8777
8884
|
}
|
|
8778
8885
|
}
|
|
8779
|
-
if (input.surface === "cursor" &&
|
|
8780
|
-
const stale = input.pins.filter((pin) =>
|
|
8886
|
+
if (input.surface === "cursor" && isSemverVersion2(input.releasedVersion) && input.pins.some((pin) => isSemverVersion2(pin.version) && compareVersions(pin.version, input.releasedVersion) < 0)) {
|
|
8887
|
+
const stale = input.pins.filter((pin) => isSemverVersion2(pin.version) && compareVersions(pin.version, input.releasedVersion) < 0).map((pin) => `${pin.version} at ${pin.name}`).join(", ");
|
|
8781
8888
|
return {
|
|
8782
8889
|
...base2,
|
|
8783
8890
|
ok: false,
|
|
8784
8891
|
cacheRoot: input.cacheRoot,
|
|
8785
8892
|
pins: input.pins,
|
|
8786
|
-
fix: `Cursor MMI plugin cache is behind ${input.releasedVersion} (${stale}) \u2014
|
|
8893
|
+
fix: `Cursor MMI plugin cache is behind ${input.releasedVersion} (${stale}) \u2014 run \`mmi-cli doctor --apply\` to seed the cache from the latest release (effective after restart Cursor); when Dashboard \u2192 Settings \u2192 Plugins shows Update next to the MMI Team Marketplace, a master-admin can refresh the pin there instead; ${CURSOR_MARKETPLACE_INSTALL_GUIDE}`
|
|
8787
8894
|
};
|
|
8788
8895
|
}
|
|
8789
8896
|
return { ...base2, cacheRoot: input.cacheRoot, pins: input.pins };
|
|
8790
8897
|
}
|
|
8898
|
+
var CURSOR_THIRD_PARTY_EXTENSIBILITY_LABEL = "Cursor third-party plugin import";
|
|
8899
|
+
var CURSOR_THIRD_PARTY_EXTENSIBILITY_FIX = "disable Cursor Settings \u2192 Rules, Skills, Subagents \u2192 Include third-party Plugins, Skills, and other configs (cursor/thirdPartyExtensibilityEnabled) \u2014 it imports Claude/Codex hooks that break the MMI Team Marketplace plugin; use MMI from the Team Marketplace only, then restart Cursor";
|
|
8900
|
+
function buildCursorThirdPartyExtensibilityCheck(input) {
|
|
8901
|
+
const base2 = {
|
|
8902
|
+
ok: true,
|
|
8903
|
+
label: CURSOR_THIRD_PARTY_EXTENSIBILITY_LABEL,
|
|
8904
|
+
fix: CURSOR_THIRD_PARTY_EXTENSIBILITY_FIX
|
|
8905
|
+
};
|
|
8906
|
+
if (!input.isOrgRepo || input.surface !== "cursor" || input.enabled === void 0) return base2;
|
|
8907
|
+
return { ...base2, ok: !input.enabled };
|
|
8908
|
+
}
|
|
8791
8909
|
function buildCursorHookCliCheck(input) {
|
|
8792
8910
|
const fix = "update the MMI Team Marketplace plugin (releases ship cli/dist under plugins/mmi/cli/dist) or install mmi-cli on PATH \u2014 Cursor hooks fall back to PATH when the bundled CLI is missing";
|
|
8793
8911
|
const base2 = { ok: true, label: CURSOR_HOOK_CLI_LABEL, fix };
|
|
@@ -8937,8 +9055,8 @@ async function runStageLiveDown(deps, t) {
|
|
|
8937
9055
|
|
|
8938
9056
|
// src/stage-runner.ts
|
|
8939
9057
|
var import_node_child_process7 = require("node:child_process");
|
|
8940
|
-
var
|
|
8941
|
-
var
|
|
9058
|
+
var import_node_fs12 = require("node:fs");
|
|
9059
|
+
var import_node_path12 = require("node:path");
|
|
8942
9060
|
var import_node_net2 = require("node:net");
|
|
8943
9061
|
var import_node_util6 = require("node:util");
|
|
8944
9062
|
var execFileP4 = (0, import_node_util6.promisify)(import_node_child_process7.execFile);
|
|
@@ -8983,7 +9101,7 @@ function detectStaleEnvFile(exampleContent, targetContent, mtimes) {
|
|
|
8983
9101
|
return void 0;
|
|
8984
9102
|
}
|
|
8985
9103
|
function stageStatePath(cwd = process.cwd()) {
|
|
8986
|
-
return (0,
|
|
9104
|
+
return (0, import_node_path12.join)(cwd, "tmp", "stage", "state.json");
|
|
8987
9105
|
}
|
|
8988
9106
|
var POSIX_ONLY_VERBS = ["cp", "mv", "rm", "ln", "cat", "touch", "chmod", "export"];
|
|
8989
9107
|
function posixOnlyShellProblems(command, field, platform = process.platform) {
|
|
@@ -9045,9 +9163,9 @@ async function shell(command, cwd, timeoutMs) {
|
|
|
9045
9163
|
});
|
|
9046
9164
|
}
|
|
9047
9165
|
function readState(path2) {
|
|
9048
|
-
if (!(0,
|
|
9166
|
+
if (!(0, import_node_fs12.existsSync)(path2)) return null;
|
|
9049
9167
|
try {
|
|
9050
|
-
return JSON.parse((0,
|
|
9168
|
+
return JSON.parse((0, import_node_fs12.readFileSync)(path2, "utf8"));
|
|
9051
9169
|
} catch {
|
|
9052
9170
|
return null;
|
|
9053
9171
|
}
|
|
@@ -9099,7 +9217,7 @@ async function stopStage(opts = {}) {
|
|
|
9099
9217
|
return { ok: true, action: "stop", statePath, message: "no previous stage state found" };
|
|
9100
9218
|
}
|
|
9101
9219
|
await killTree(state.pid);
|
|
9102
|
-
(0,
|
|
9220
|
+
(0, import_node_fs12.rmSync)(statePath, { force: true });
|
|
9103
9221
|
return { ok: true, action: "stop", statePath, pid: state.pid, message: `stopped previous stage pid ${state.pid}` };
|
|
9104
9222
|
}
|
|
9105
9223
|
async function startStage(config = {}, opts = {}) {
|
|
@@ -9108,7 +9226,7 @@ async function startStage(config = {}, opts = {}) {
|
|
|
9108
9226
|
const cwd = opts.cwd ?? process.cwd();
|
|
9109
9227
|
const statePath = opts.statePath ?? stageStatePath(cwd);
|
|
9110
9228
|
const dir = statePath.slice(0, Math.max(statePath.lastIndexOf("/"), statePath.lastIndexOf("\\")));
|
|
9111
|
-
(0,
|
|
9229
|
+
(0, import_node_fs12.mkdirSync)(dir, { recursive: true });
|
|
9112
9230
|
let stagePort;
|
|
9113
9231
|
if (config.portRange) {
|
|
9114
9232
|
const [s, e] = config.portRange;
|
|
@@ -9118,14 +9236,14 @@ async function startStage(config = {}, opts = {}) {
|
|
|
9118
9236
|
}
|
|
9119
9237
|
const sub = (s) => s != null && stagePort != null ? s.replace(/\$\{?STAGE_PORT\}?/g, String(stagePort)) : s;
|
|
9120
9238
|
if (config.ensureEnv) {
|
|
9121
|
-
const target = (0,
|
|
9122
|
-
const example = (0,
|
|
9123
|
-
if (!(0,
|
|
9124
|
-
(0,
|
|
9125
|
-
} else if ((0,
|
|
9126
|
-
const stale = detectStaleEnvFile((0,
|
|
9127
|
-
exampleMtimeMs: (0,
|
|
9128
|
-
targetMtimeMs: (0,
|
|
9239
|
+
const target = (0, import_node_path12.join)(cwd, config.ensureEnv.target);
|
|
9240
|
+
const example = (0, import_node_path12.join)(cwd, config.ensureEnv.example);
|
|
9241
|
+
if (!(0, import_node_fs12.existsSync)(target) && (0, import_node_fs12.existsSync)(example)) {
|
|
9242
|
+
(0, import_node_fs12.copyFileSync)(example, target);
|
|
9243
|
+
} else if ((0, import_node_fs12.existsSync)(target) && (0, import_node_fs12.existsSync)(example)) {
|
|
9244
|
+
const stale = detectStaleEnvFile((0, import_node_fs12.readFileSync)(example, "utf8"), (0, import_node_fs12.readFileSync)(target, "utf8"), {
|
|
9245
|
+
exampleMtimeMs: (0, import_node_fs12.statSync)(example).mtimeMs,
|
|
9246
|
+
targetMtimeMs: (0, import_node_fs12.statSync)(target).mtimeMs
|
|
9129
9247
|
});
|
|
9130
9248
|
if (stale) {
|
|
9131
9249
|
const msg = `stale ${config.ensureEnv.target} (${stale}) \u2014 delete it or refresh from ${config.ensureEnv.example} before re-running /stage`;
|
|
@@ -9156,13 +9274,13 @@ async function startStage(config = {}, opts = {}) {
|
|
|
9156
9274
|
healthUrl: sub(config.healthUrl?.trim()) || void 0,
|
|
9157
9275
|
port: stagePort
|
|
9158
9276
|
};
|
|
9159
|
-
(0,
|
|
9277
|
+
(0, import_node_fs12.writeFileSync)(statePath, JSON.stringify(state, null, 2), "utf8");
|
|
9160
9278
|
try {
|
|
9161
9279
|
if (state.healthUrl) await waitForHealth(state.healthUrl, opts.timeoutMs ?? 6e4, config.healthAnyStatus);
|
|
9162
9280
|
else await waitForProcessStability(child);
|
|
9163
9281
|
} catch (e) {
|
|
9164
9282
|
await killTree(state.pid);
|
|
9165
|
-
(0,
|
|
9283
|
+
(0, import_node_fs12.rmSync)(statePath, { force: true });
|
|
9166
9284
|
throw e;
|
|
9167
9285
|
}
|
|
9168
9286
|
const result = { ok: true, action: "start", statePath, pid: state.pid, port: stagePort, message: `started stage pid ${state.pid}${stagePort != null ? ` on port ${stagePort}` : ""}` };
|
|
@@ -10902,7 +11020,7 @@ async function announceRelease(deps, args) {
|
|
|
10902
11020
|
}
|
|
10903
11021
|
|
|
10904
11022
|
// src/port-registry.ts
|
|
10905
|
-
var
|
|
11023
|
+
var import_node_fs13 = require("node:fs");
|
|
10906
11024
|
|
|
10907
11025
|
// ../infra/port-geometry.mjs
|
|
10908
11026
|
var PORT_BLOCK = 100;
|
|
@@ -10916,8 +11034,8 @@ function nextPortBlock(registry2) {
|
|
|
10916
11034
|
return [base2, base2 + PORT_SPAN];
|
|
10917
11035
|
}
|
|
10918
11036
|
function loadPortRegistry(path2) {
|
|
10919
|
-
if (!(0,
|
|
10920
|
-
const raw = JSON.parse((0,
|
|
11037
|
+
if (!(0, import_node_fs13.existsSync)(path2)) return {};
|
|
11038
|
+
const raw = JSON.parse((0, import_node_fs13.readFileSync)(path2, "utf8"));
|
|
10921
11039
|
const out = {};
|
|
10922
11040
|
for (const [key, value] of Object.entries(raw)) {
|
|
10923
11041
|
if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
|
|
@@ -10931,9 +11049,9 @@ function ensurePortRange(repo, path2) {
|
|
|
10931
11049
|
const existing = registry2[repo];
|
|
10932
11050
|
if (existing) return existing;
|
|
10933
11051
|
const range = nextPortBlock(registry2);
|
|
10934
|
-
const raw = (0,
|
|
11052
|
+
const raw = (0, import_node_fs13.existsSync)(path2) ? JSON.parse((0, import_node_fs13.readFileSync)(path2, "utf8")) : {};
|
|
10935
11053
|
raw[repo] = range;
|
|
10936
|
-
(0,
|
|
11054
|
+
(0, import_node_fs13.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
|
|
10937
11055
|
return range;
|
|
10938
11056
|
}
|
|
10939
11057
|
function portCursorSeed(registry2) {
|
|
@@ -12719,10 +12837,10 @@ function parseKbTree(stdout, prefix) {
|
|
|
12719
12837
|
}
|
|
12720
12838
|
|
|
12721
12839
|
// src/plan.ts
|
|
12722
|
-
var
|
|
12840
|
+
var import_node_path13 = require("node:path");
|
|
12723
12841
|
var PLANS_DIR = "plans";
|
|
12724
|
-
var META_FILE = (0,
|
|
12725
|
-
var planPath = (slug) => (0,
|
|
12842
|
+
var META_FILE = (0, import_node_path13.join)(PLANS_DIR, ".plan-meta.json");
|
|
12843
|
+
var planPath = (slug) => (0, import_node_path13.join)(PLANS_DIR, `${slug}.md`);
|
|
12726
12844
|
var metaKey = (project2, slug) => `${project2}/${slug}`;
|
|
12727
12845
|
function parseMeta(raw) {
|
|
12728
12846
|
if (!raw) return {};
|
|
@@ -12747,7 +12865,7 @@ function hashContent(s) {
|
|
|
12747
12865
|
function staleHint(slug) {
|
|
12748
12866
|
return `remote "${slug}" is newer \u2014 run \`mmi-cli northstar pull ${slug}\` first (your local is based on an older version), or re-push with \`--force\` to overwrite`;
|
|
12749
12867
|
}
|
|
12750
|
-
var INDEX_FILE = (0,
|
|
12868
|
+
var INDEX_FILE = (0, import_node_path13.join)(PLANS_DIR, ".index.json");
|
|
12751
12869
|
var INDEX_TTL_MS = 6e4;
|
|
12752
12870
|
function parseIndex(raw) {
|
|
12753
12871
|
if (!raw) return null;
|
|
@@ -12776,7 +12894,7 @@ function mergeIndex(idx, scope, plans, now) {
|
|
|
12776
12894
|
const mergedScope = idx.scope === null ? null : [.../* @__PURE__ */ new Set([...idx.scope, ...scope])];
|
|
12777
12895
|
return { fetchedAt: now, scope: mergedScope, plans: [...kept, ...plans] };
|
|
12778
12896
|
}
|
|
12779
|
-
var QUEUE_FILE = (0,
|
|
12897
|
+
var QUEUE_FILE = (0, import_node_path13.join)(PLANS_DIR, ".sync-queue.json");
|
|
12780
12898
|
var QUEUE_MAX_ATTEMPTS = 10;
|
|
12781
12899
|
function isValidQueueEntry(e) {
|
|
12782
12900
|
if (!e || typeof e !== "object") return false;
|
|
@@ -13225,11 +13343,11 @@ async function planGraduate(deps, slug, opts = {}) {
|
|
|
13225
13343
|
}
|
|
13226
13344
|
|
|
13227
13345
|
// src/atomic-write.ts
|
|
13228
|
-
var
|
|
13346
|
+
var import_node_fs14 = require("node:fs");
|
|
13229
13347
|
function atomicWriteFileSync(path2, content) {
|
|
13230
13348
|
const tmp = `${path2}.${process.pid}.tmp`;
|
|
13231
|
-
(0,
|
|
13232
|
-
(0,
|
|
13349
|
+
(0, import_node_fs14.writeFileSync)(tmp, content, "utf8");
|
|
13350
|
+
(0, import_node_fs14.renameSync)(tmp, path2);
|
|
13233
13351
|
}
|
|
13234
13352
|
|
|
13235
13353
|
// src/oauth.ts
|
|
@@ -13460,7 +13578,7 @@ async function fetchHubVersionInfo(baseUrl) {
|
|
|
13460
13578
|
}
|
|
13461
13579
|
function readRepoVersion() {
|
|
13462
13580
|
try {
|
|
13463
|
-
return JSON.parse((0,
|
|
13581
|
+
return JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path14.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
13464
13582
|
} catch {
|
|
13465
13583
|
return void 0;
|
|
13466
13584
|
}
|
|
@@ -13570,10 +13688,10 @@ async function runRulesSync(opts, io = consoleIo) {
|
|
|
13570
13688
|
for (const entry of fetched) {
|
|
13571
13689
|
if ("error" in entry) continue;
|
|
13572
13690
|
const { file, source } = entry;
|
|
13573
|
-
const current = (0,
|
|
13691
|
+
const current = (0, import_node_fs15.existsSync)(file) ? await (0, import_promises5.readFile)(file, "utf8") : null;
|
|
13574
13692
|
if (needsUpdate(source, current)) {
|
|
13575
13693
|
const slash = file.lastIndexOf("/");
|
|
13576
|
-
if (slash > 0) (0,
|
|
13694
|
+
if (slash > 0) (0, import_node_fs15.mkdirSync)(file.slice(0, slash), { recursive: true });
|
|
13577
13695
|
await (0, import_promises5.writeFile)(file, normalizeEol(source), "utf8");
|
|
13578
13696
|
changed++;
|
|
13579
13697
|
if (!opts.quiet) io.log(`mmi-cli rules: updated ${file}`);
|
|
@@ -13599,7 +13717,7 @@ async function runDocsSync(opts, io = consoleIo) {
|
|
|
13599
13717
|
return null;
|
|
13600
13718
|
}
|
|
13601
13719
|
},
|
|
13602
|
-
localContent: async (f) => (0,
|
|
13720
|
+
localContent: async (f) => (0, import_node_fs15.existsSync)(f) ? await (0, import_promises5.readFile)(f, "utf8") : null,
|
|
13603
13721
|
writeDoc: async (f, c) => {
|
|
13604
13722
|
await (0, import_promises5.writeFile)(f, c, "utf8");
|
|
13605
13723
|
}
|
|
@@ -13772,7 +13890,7 @@ function detachPlanSync() {
|
|
|
13772
13890
|
}
|
|
13773
13891
|
}
|
|
13774
13892
|
function makePlanDeps(cfg, io = consoleIo) {
|
|
13775
|
-
const ensureDir = () => (0,
|
|
13893
|
+
const ensureDir = () => (0, import_node_fs15.mkdirSync)(PLANS_DIR, { recursive: true });
|
|
13776
13894
|
return {
|
|
13777
13895
|
apiUrl: cfg.sagaApiUrl,
|
|
13778
13896
|
fetch: (url, init = {}) => fetch(url, { ...init, signal: init.signal ?? AbortSignal.timeout(1e4) }),
|
|
@@ -13780,24 +13898,24 @@ function makePlanDeps(cfg, io = consoleIo) {
|
|
|
13780
13898
|
project: async () => (await sagaKey(cfg)).project,
|
|
13781
13899
|
readLocal: (slug) => {
|
|
13782
13900
|
try {
|
|
13783
|
-
return (0,
|
|
13901
|
+
return (0, import_node_fs15.readFileSync)(planPath(slug), "utf8");
|
|
13784
13902
|
} catch {
|
|
13785
13903
|
return null;
|
|
13786
13904
|
}
|
|
13787
13905
|
},
|
|
13788
13906
|
writeLocal: (slug, content) => {
|
|
13789
13907
|
ensureDir();
|
|
13790
|
-
(0,
|
|
13908
|
+
(0, import_node_fs15.writeFileSync)(planPath(slug), content, "utf8");
|
|
13791
13909
|
},
|
|
13792
13910
|
removeLocal: (slug) => {
|
|
13793
13911
|
try {
|
|
13794
|
-
(0,
|
|
13912
|
+
(0, import_node_fs15.rmSync)(planPath(slug));
|
|
13795
13913
|
} catch {
|
|
13796
13914
|
}
|
|
13797
13915
|
},
|
|
13798
13916
|
readMetaRaw: () => {
|
|
13799
13917
|
try {
|
|
13800
|
-
return (0,
|
|
13918
|
+
return (0, import_node_fs15.readFileSync)(META_FILE, "utf8");
|
|
13801
13919
|
} catch {
|
|
13802
13920
|
return null;
|
|
13803
13921
|
}
|
|
@@ -13808,7 +13926,7 @@ function makePlanDeps(cfg, io = consoleIo) {
|
|
|
13808
13926
|
},
|
|
13809
13927
|
readIndexRaw: () => {
|
|
13810
13928
|
try {
|
|
13811
|
-
return (0,
|
|
13929
|
+
return (0, import_node_fs15.readFileSync)(INDEX_FILE, "utf8");
|
|
13812
13930
|
} catch {
|
|
13813
13931
|
return null;
|
|
13814
13932
|
}
|
|
@@ -13819,7 +13937,7 @@ function makePlanDeps(cfg, io = consoleIo) {
|
|
|
13819
13937
|
},
|
|
13820
13938
|
readQueueRaw: () => {
|
|
13821
13939
|
try {
|
|
13822
|
-
return (0,
|
|
13940
|
+
return (0, import_node_fs15.readFileSync)(QUEUE_FILE, "utf8");
|
|
13823
13941
|
} catch {
|
|
13824
13942
|
return null;
|
|
13825
13943
|
}
|
|
@@ -14675,7 +14793,7 @@ async function createDeferredWorktreeStore() {
|
|
|
14675
14793
|
},
|
|
14676
14794
|
write: async (entries) => {
|
|
14677
14795
|
try {
|
|
14678
|
-
await (0, import_promises5.mkdir)((0,
|
|
14796
|
+
await (0, import_promises5.mkdir)((0, import_node_path14.dirname)(registryPath), { recursive: true });
|
|
14679
14797
|
await (0, import_promises5.writeFile)(registryPath, serializeDeferredWorktrees(entries), "utf8");
|
|
14680
14798
|
} catch {
|
|
14681
14799
|
}
|
|
@@ -14694,7 +14812,7 @@ function worktreeRemoveDeps(execGit) {
|
|
|
14694
14812
|
}
|
|
14695
14813
|
function teardownWorktreeStage(worktreePath) {
|
|
14696
14814
|
return runWorktreeStageTeardown(worktreePath, {
|
|
14697
|
-
hasStageState: (wt) => (0,
|
|
14815
|
+
hasStageState: (wt) => (0, import_node_fs15.existsSync)(stageStatePath(wt)),
|
|
14698
14816
|
stopRecordedStage: async (wt) => (await stopStage({ cwd: wt })).pid,
|
|
14699
14817
|
listComposeProjects: async () => {
|
|
14700
14818
|
const { stdout } = await execFileP2("docker", ["compose", "ls", "--all", "--format", "json"], { timeout: GC_GH_TIMEOUT_MS });
|
|
@@ -14757,7 +14875,7 @@ pr.command("merge <number>").description("merge a PR (squash by default) and cle
|
|
|
14757
14875
|
} : await cleanupPrMergeLocalBranch(headRef, {
|
|
14758
14876
|
beforeWorktrees,
|
|
14759
14877
|
startingPath,
|
|
14760
|
-
pathExists: (p) => (0,
|
|
14878
|
+
pathExists: (p) => (0, import_node_fs15.existsSync)(p),
|
|
14761
14879
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
14762
14880
|
teardownWorktreeStage,
|
|
14763
14881
|
deferredStore,
|
|
@@ -14910,7 +15028,7 @@ function rawValues(flag) {
|
|
|
14910
15028
|
return out;
|
|
14911
15029
|
}
|
|
14912
15030
|
function printLine(value) {
|
|
14913
|
-
(0,
|
|
15031
|
+
(0, import_node_fs15.writeSync)(1, `${value}
|
|
14914
15032
|
`);
|
|
14915
15033
|
}
|
|
14916
15034
|
function stageKeepAlive() {
|
|
@@ -14927,8 +15045,8 @@ async function resolveStage() {
|
|
|
14927
15045
|
local,
|
|
14928
15046
|
shell: shellFor(),
|
|
14929
15047
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
14930
|
-
hasCompose: (0,
|
|
14931
|
-
hasEnvExample: (0,
|
|
15048
|
+
hasCompose: (0, import_node_fs15.existsSync)((0, import_node_path14.join)(process.cwd(), "docker-compose.yml")),
|
|
15049
|
+
hasEnvExample: (0, import_node_fs15.existsSync)((0, import_node_path14.join)(process.cwd(), ".env.example"))
|
|
14932
15050
|
});
|
|
14933
15051
|
}
|
|
14934
15052
|
function stageStepsFor(res, stops = true) {
|
|
@@ -14964,9 +15082,9 @@ program2.command("port-range <repo>").description("assign (idempotently) + print
|
|
|
14964
15082
|
printLine(o.json ? JSON.stringify({ repo, portRange: [start2, end2], source: "meta" }) : `${repo}: stage.portRange [${start2}, ${end2}]`);
|
|
14965
15083
|
return;
|
|
14966
15084
|
}
|
|
14967
|
-
const path2 = (0,
|
|
15085
|
+
const path2 = (0, import_node_path14.join)(process.cwd(), "infra", "port-ranges.json");
|
|
14968
15086
|
const allocate = async (seed) => {
|
|
14969
|
-
const { stdout } = await execFileP2("node", [(0,
|
|
15087
|
+
const { stdout } = await execFileP2("node", [(0, import_node_path14.join)(process.cwd(), "infra", "port-ddb.mjs"), String(seed)], { timeout: 15e3 });
|
|
14970
15088
|
const parsed = JSON.parse(stdout);
|
|
14971
15089
|
if (!Array.isArray(parsed.range) || parsed.range.length !== 2) throw new Error("port-ddb: no range in output");
|
|
14972
15090
|
return parsed.range;
|
|
@@ -15300,7 +15418,7 @@ bootstrap.command("verify <repo>").description("audit whether an existing repo i
|
|
|
15300
15418
|
const report = await verifyBootstrap(repo, o.class, {
|
|
15301
15419
|
client: defaultGitHubClient(),
|
|
15302
15420
|
projectMeta: meta,
|
|
15303
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
15421
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs15.existsSync)(path2) ? (0, import_node_fs15.readFileSync)(path2, "utf8") : null,
|
|
15304
15422
|
// requiredGcpApis is stored as an array by a JSON write, but `project set --var KEY=VALUE` stores a raw
|
|
15305
15423
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
15306
15424
|
requiredGcpApis: (() => {
|
|
@@ -15343,12 +15461,12 @@ bootstrap.command("apply <repo>").description("idempotent seed apply from skills
|
|
|
15343
15461
|
return fail(`bootstrap apply: ${e.message}`);
|
|
15344
15462
|
}
|
|
15345
15463
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
15346
|
-
if (!(0,
|
|
15347
|
-
const manifest = loadBootstrapSeeds((0,
|
|
15464
|
+
if (!(0, import_node_fs15.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; run from the MMI-Hub repo root`);
|
|
15465
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs15.readFileSync)(manifestPath, "utf8"));
|
|
15348
15466
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
15349
15467
|
const slug = parsedRepo.slug;
|
|
15350
15468
|
const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
|
|
15351
|
-
const readFile5 = (p) => (0,
|
|
15469
|
+
const readFile5 = (p) => (0, import_node_fs15.existsSync)(p) ? (0, import_node_fs15.readFileSync)(p, "utf8") : null;
|
|
15352
15470
|
const enc2 = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
15353
15471
|
const rawVars = {};
|
|
15354
15472
|
for (const value of rawValues("--var")) {
|
|
@@ -15557,16 +15675,16 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
15557
15675
|
if (o.class !== "deployable" && o.class !== "content") return failGraceful("access audit: --class must be deployable or content");
|
|
15558
15676
|
targets = [{ repo: o.repo, class: o.class }];
|
|
15559
15677
|
} else {
|
|
15560
|
-
const projectsJson = registryProjects ? JSON.stringify({ projects: registryProjects }) : (0,
|
|
15678
|
+
const projectsJson = registryProjects ? JSON.stringify({ projects: registryProjects }) : (0, import_node_fs15.existsSync)("projects.json") ? (0, import_node_fs15.readFileSync)("projects.json", "utf8") : null;
|
|
15561
15679
|
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>");
|
|
15562
|
-
const fanoutJson = (0,
|
|
15680
|
+
const fanoutJson = (0, import_node_fs15.existsSync)(".github/fanout-targets.json") ? (0, import_node_fs15.readFileSync)(".github/fanout-targets.json", "utf8") : null;
|
|
15563
15681
|
targets = loadAccessTargets(projectsJson, fanoutJson);
|
|
15564
15682
|
}
|
|
15565
15683
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
15566
|
-
const fileMatrix = (0,
|
|
15684
|
+
const fileMatrix = (0, import_node_fs15.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs15.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
15567
15685
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
15568
15686
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
15569
|
-
const fileContracts = (0,
|
|
15687
|
+
const fileContracts = (0, import_node_fs15.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs15.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
15570
15688
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
15571
15689
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess);
|
|
15572
15690
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
@@ -15575,20 +15693,20 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
15575
15693
|
var isWin = process.platform === "win32";
|
|
15576
15694
|
var installedPluginsPath = (surface = detectSurface(process.env)) => {
|
|
15577
15695
|
const homeDir = surface === "codex" ? ".codex" : ".claude";
|
|
15578
|
-
return (0,
|
|
15696
|
+
return (0, import_node_path14.join)((0, import_node_os5.homedir)(), homeDir, "plugins", "installed_plugins.json");
|
|
15579
15697
|
};
|
|
15580
15698
|
function readInstalledPlugins() {
|
|
15581
15699
|
try {
|
|
15582
|
-
return JSON.parse((0,
|
|
15700
|
+
return JSON.parse((0, import_node_fs15.readFileSync)(installedPluginsPath(), "utf8"));
|
|
15583
15701
|
} catch {
|
|
15584
15702
|
return null;
|
|
15585
15703
|
}
|
|
15586
15704
|
}
|
|
15587
15705
|
function installedPluginSources() {
|
|
15588
15706
|
return ["claude", "codex"].map((surface) => {
|
|
15589
|
-
const recordPath = (0,
|
|
15707
|
+
const recordPath = (0, import_node_path14.join)((0, import_node_os5.homedir)(), `.${surface}`, "plugins", "installed_plugins.json");
|
|
15590
15708
|
try {
|
|
15591
|
-
return { surface, installed: JSON.parse((0,
|
|
15709
|
+
return { surface, installed: JSON.parse((0, import_node_fs15.readFileSync)(recordPath, "utf8")), recordPath };
|
|
15592
15710
|
} catch {
|
|
15593
15711
|
return { surface, installed: null, recordPath };
|
|
15594
15712
|
}
|
|
@@ -15596,7 +15714,7 @@ function installedPluginSources() {
|
|
|
15596
15714
|
}
|
|
15597
15715
|
function readClaudeSettings() {
|
|
15598
15716
|
try {
|
|
15599
|
-
return JSON.parse((0,
|
|
15717
|
+
return JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path14.join)(process.cwd(), ".claude", "settings.json"), "utf8"));
|
|
15600
15718
|
} catch {
|
|
15601
15719
|
return null;
|
|
15602
15720
|
}
|
|
@@ -15618,7 +15736,7 @@ function writeProjectInstallRecord(record) {
|
|
|
15618
15736
|
const list = file.plugins[MMI_PLUGIN_ID] ?? [];
|
|
15619
15737
|
list.push(record);
|
|
15620
15738
|
file.plugins[MMI_PLUGIN_ID] = list;
|
|
15621
|
-
(0,
|
|
15739
|
+
(0, import_node_fs15.writeFileSync)(installedPluginsPath(), `${JSON.stringify(file, null, 2)}
|
|
15622
15740
|
`, "utf8");
|
|
15623
15741
|
return true;
|
|
15624
15742
|
} catch {
|
|
@@ -15631,9 +15749,9 @@ function backupAndWriteInstalledPlugins(records, pluginId) {
|
|
|
15631
15749
|
if (!file) return false;
|
|
15632
15750
|
if (!file.plugins) file.plugins = {};
|
|
15633
15751
|
const path2 = installedPluginsPath();
|
|
15634
|
-
(0,
|
|
15752
|
+
(0, import_node_fs15.copyFileSync)(path2, `${path2}.bak`);
|
|
15635
15753
|
file.plugins[pluginId] = records;
|
|
15636
|
-
(0,
|
|
15754
|
+
(0, import_node_fs15.writeFileSync)(path2, `${JSON.stringify(file, null, 2)}
|
|
15637
15755
|
`, "utf8");
|
|
15638
15756
|
return true;
|
|
15639
15757
|
} catch {
|
|
@@ -15641,35 +15759,35 @@ function backupAndWriteInstalledPlugins(records, pluginId) {
|
|
|
15641
15759
|
}
|
|
15642
15760
|
}
|
|
15643
15761
|
function cursorPluginCacheRoot() {
|
|
15644
|
-
return (0,
|
|
15762
|
+
return (0, import_node_path14.join)((0, import_node_os5.homedir)(), ".cursor", "plugins", "cache", "mmi", "mmi");
|
|
15645
15763
|
}
|
|
15646
15764
|
function cursorPluginCachePinSnapshots() {
|
|
15647
15765
|
const root = cursorPluginCacheRoot();
|
|
15648
15766
|
try {
|
|
15649
|
-
return (0,
|
|
15650
|
-
const path2 = (0,
|
|
15651
|
-
const pluginJson = (0,
|
|
15652
|
-
const hooksJson = (0,
|
|
15653
|
-
const cliBundle = (0,
|
|
15767
|
+
return (0, import_node_fs15.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => {
|
|
15768
|
+
const path2 = (0, import_node_path14.join)(root, entry.name);
|
|
15769
|
+
const pluginJson = (0, import_node_path14.join)(path2, ".cursor-plugin", "plugin.json");
|
|
15770
|
+
const hooksJson = (0, import_node_path14.join)(path2, "hooks", "hooks.json");
|
|
15771
|
+
const cliBundle = (0, import_node_path14.join)(path2, "cli", "dist", "index.cjs");
|
|
15654
15772
|
let version;
|
|
15655
15773
|
try {
|
|
15656
|
-
const raw = JSON.parse((0,
|
|
15774
|
+
const raw = JSON.parse((0, import_node_fs15.readFileSync)(pluginJson, "utf8"));
|
|
15657
15775
|
version = typeof raw.version === "string" ? raw.version : void 0;
|
|
15658
15776
|
} catch {
|
|
15659
15777
|
version = void 0;
|
|
15660
15778
|
}
|
|
15661
15779
|
let isEmpty = true;
|
|
15662
15780
|
try {
|
|
15663
|
-
isEmpty = (0,
|
|
15781
|
+
isEmpty = (0, import_node_fs15.readdirSync)(path2).length === 0;
|
|
15664
15782
|
} catch {
|
|
15665
15783
|
isEmpty = true;
|
|
15666
15784
|
}
|
|
15667
15785
|
return {
|
|
15668
15786
|
name: entry.name,
|
|
15669
15787
|
path: path2,
|
|
15670
|
-
hasPluginJson: (0,
|
|
15671
|
-
hasHooksJson: (0,
|
|
15672
|
-
hasCliBundle: (0,
|
|
15788
|
+
hasPluginJson: (0, import_node_fs15.existsSync)(pluginJson),
|
|
15789
|
+
hasHooksJson: (0, import_node_fs15.existsSync)(hooksJson),
|
|
15790
|
+
hasCliBundle: (0, import_node_fs15.existsSync)(cliBundle),
|
|
15673
15791
|
isEmpty,
|
|
15674
15792
|
version
|
|
15675
15793
|
};
|
|
@@ -15679,19 +15797,19 @@ function cursorPluginCachePinSnapshots() {
|
|
|
15679
15797
|
}
|
|
15680
15798
|
}
|
|
15681
15799
|
function hubCheckoutForCursorSeed() {
|
|
15682
|
-
const manifest = (0,
|
|
15683
|
-
return (0,
|
|
15800
|
+
const manifest = (0, import_node_path14.join)(process.cwd(), "plugins", "mmi", ".cursor-plugin", "plugin.json");
|
|
15801
|
+
return (0, import_node_fs15.existsSync)(manifest) ? process.cwd() : void 0;
|
|
15684
15802
|
}
|
|
15685
15803
|
function mmiPluginCacheRootSnapshots() {
|
|
15686
15804
|
const roots = [
|
|
15687
|
-
{ surface: "claude", root: (0,
|
|
15688
|
-
{ surface: "codex", root: (0,
|
|
15805
|
+
{ surface: "claude", root: (0, import_node_path14.join)((0, import_node_os5.homedir)(), ".claude", "plugins", "cache", "mmi", "mmi") },
|
|
15806
|
+
{ surface: "codex", root: (0, import_node_path14.join)((0, import_node_os5.homedir)(), ".codex", "plugins", "cache", "mmi", "mmi") }
|
|
15689
15807
|
];
|
|
15690
15808
|
return roots.flatMap(({ surface, root }) => {
|
|
15691
15809
|
try {
|
|
15692
|
-
const entries = (0,
|
|
15810
|
+
const entries = (0, import_node_fs15.readdirSync)(root, { withFileTypes: true }).map((entry) => ({
|
|
15693
15811
|
name: entry.name,
|
|
15694
|
-
path: (0,
|
|
15812
|
+
path: (0, import_node_path14.join)(root, entry.name),
|
|
15695
15813
|
isDirectory: entry.isDirectory()
|
|
15696
15814
|
}));
|
|
15697
15815
|
return [{ surface, root, entries }];
|
|
@@ -15702,7 +15820,7 @@ function mmiPluginCacheRootSnapshots() {
|
|
|
15702
15820
|
}
|
|
15703
15821
|
function hasNestedMmiChild(versionDir) {
|
|
15704
15822
|
try {
|
|
15705
|
-
return (0,
|
|
15823
|
+
return (0, import_node_fs15.statSync)((0, import_node_path14.join)(versionDir, "mmi")).isDirectory();
|
|
15706
15824
|
} catch {
|
|
15707
15825
|
return false;
|
|
15708
15826
|
}
|
|
@@ -15713,10 +15831,10 @@ function nestedPluginTreeSnapshot() {
|
|
|
15713
15831
|
);
|
|
15714
15832
|
}
|
|
15715
15833
|
function uniqueQuarantineTarget(path2) {
|
|
15716
|
-
if (!(0,
|
|
15834
|
+
if (!(0, import_node_fs15.existsSync)(path2)) return path2;
|
|
15717
15835
|
for (let i = 1; i < 100; i += 1) {
|
|
15718
15836
|
const candidate = `${path2}-${i}`;
|
|
15719
|
-
if (!(0,
|
|
15837
|
+
if (!(0, import_node_fs15.existsSync)(candidate)) return candidate;
|
|
15720
15838
|
}
|
|
15721
15839
|
return `${path2}-${Date.now()}`;
|
|
15722
15840
|
}
|
|
@@ -15724,32 +15842,32 @@ function quarantinePluginCacheDirs(plan2) {
|
|
|
15724
15842
|
let moved = 0;
|
|
15725
15843
|
for (const move of plan2) {
|
|
15726
15844
|
try {
|
|
15727
|
-
if (!(0,
|
|
15845
|
+
if (!(0, import_node_fs15.existsSync)(move.from)) continue;
|
|
15728
15846
|
const target = uniqueQuarantineTarget(move.to);
|
|
15729
|
-
(0,
|
|
15730
|
-
(0,
|
|
15847
|
+
(0, import_node_fs15.mkdirSync)((0, import_node_path14.dirname)(target), { recursive: true });
|
|
15848
|
+
(0, import_node_fs15.renameSync)(move.from, target);
|
|
15731
15849
|
moved += 1;
|
|
15732
15850
|
} catch {
|
|
15733
15851
|
}
|
|
15734
15852
|
}
|
|
15735
15853
|
return moved;
|
|
15736
15854
|
}
|
|
15737
|
-
var gitignorePath = () => (0,
|
|
15855
|
+
var gitignorePath = () => (0, import_node_path14.join)(process.cwd(), ".gitignore");
|
|
15738
15856
|
function readTextFile(path2) {
|
|
15739
15857
|
try {
|
|
15740
|
-
if (!(0,
|
|
15741
|
-
return (0,
|
|
15858
|
+
if (!(0, import_node_fs15.existsSync)(path2)) return null;
|
|
15859
|
+
return (0, import_node_fs15.readFileSync)(path2, "utf8");
|
|
15742
15860
|
} catch {
|
|
15743
15861
|
return null;
|
|
15744
15862
|
}
|
|
15745
15863
|
}
|
|
15746
15864
|
function playwrightMcpConfigSnapshots() {
|
|
15747
15865
|
const cwd = process.cwd();
|
|
15748
|
-
const home = (0,
|
|
15866
|
+
const home = (0, import_node_os5.homedir)();
|
|
15749
15867
|
const candidates = [
|
|
15750
|
-
(0,
|
|
15751
|
-
(0,
|
|
15752
|
-
(0,
|
|
15868
|
+
(0, import_node_path14.join)(cwd, ".cursor", "mcp.json"),
|
|
15869
|
+
(0, import_node_path14.join)(home, ".cursor", "mcp.json"),
|
|
15870
|
+
(0, import_node_path14.join)(home, ".codex", "config.toml")
|
|
15753
15871
|
];
|
|
15754
15872
|
const out = [];
|
|
15755
15873
|
for (const path2 of candidates) {
|
|
@@ -15762,7 +15880,7 @@ function strayBrowserArtifactPaths() {
|
|
|
15762
15880
|
const cwd = process.cwd();
|
|
15763
15881
|
return STRAY_BROWSER_ARTIFACT_DIRS.filter((rel) => {
|
|
15764
15882
|
try {
|
|
15765
|
-
return (0,
|
|
15883
|
+
return (0, import_node_fs15.existsSync)((0, import_node_path14.join)(cwd, rel));
|
|
15766
15884
|
} catch {
|
|
15767
15885
|
return false;
|
|
15768
15886
|
}
|
|
@@ -15770,14 +15888,14 @@ function strayBrowserArtifactPaths() {
|
|
|
15770
15888
|
}
|
|
15771
15889
|
function readGitignore() {
|
|
15772
15890
|
try {
|
|
15773
|
-
return (0,
|
|
15891
|
+
return (0, import_node_fs15.readFileSync)(gitignorePath(), "utf8");
|
|
15774
15892
|
} catch {
|
|
15775
15893
|
return null;
|
|
15776
15894
|
}
|
|
15777
15895
|
}
|
|
15778
15896
|
function writeGitignore(content) {
|
|
15779
15897
|
try {
|
|
15780
|
-
(0,
|
|
15898
|
+
(0, import_node_fs15.writeFileSync)(gitignorePath(), content, "utf8");
|
|
15781
15899
|
return true;
|
|
15782
15900
|
} catch {
|
|
15783
15901
|
return false;
|
|
@@ -15816,7 +15934,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
15816
15934
|
let onPath = pathProbe;
|
|
15817
15935
|
if (!onPath) {
|
|
15818
15936
|
const root = process.env.CLAUDE_PLUGIN_ROOT;
|
|
15819
|
-
if (root && (0,
|
|
15937
|
+
if (root && (0, import_node_fs15.existsSync)(`${root}/bin/mmi-cli${isWin ? ".cmd" : ""}`)) onPath = true;
|
|
15820
15938
|
}
|
|
15821
15939
|
checks.push({ ok: onPath, label: "mmi-cli on PATH", fix: "auto-provisioned at session start \u2014 reopen the session, or install the MMI plugin" });
|
|
15822
15940
|
const surface = detectSurface(process.env);
|
|
@@ -15945,18 +16063,50 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
15945
16063
|
})
|
|
15946
16064
|
);
|
|
15947
16065
|
const cursorCacheRoot = cursorPluginCacheRoot();
|
|
16066
|
+
let cursorPins = cursorPluginCachePinSnapshots() ?? [];
|
|
16067
|
+
let cursorPluginCheck = buildCursorPluginInstallCheck({
|
|
16068
|
+
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
16069
|
+
surface,
|
|
16070
|
+
cacheRoot: cursorCacheRoot,
|
|
16071
|
+
cacheRootExists: (0, import_node_fs15.existsSync)(cursorCacheRoot),
|
|
16072
|
+
pins: cursorPins,
|
|
16073
|
+
hubCheckout: hubCheckoutForCursorSeed(),
|
|
16074
|
+
releasedVersion
|
|
16075
|
+
});
|
|
16076
|
+
if (!cursorPluginCheck.ok && repairLocal) {
|
|
16077
|
+
const seeded = await applyCursorPluginCacheSeed({
|
|
16078
|
+
pins: cursorPins,
|
|
16079
|
+
releasedVersion,
|
|
16080
|
+
hubCheckout: hubCheckoutForCursorSeed(),
|
|
16081
|
+
execFileP: execFileP2,
|
|
16082
|
+
mkdtemp: (prefix) => (0, import_promises5.mkdtemp)((0, import_node_path14.join)((0, import_node_os5.tmpdir)(), prefix)),
|
|
16083
|
+
log: (m) => io.err(m)
|
|
16084
|
+
});
|
|
16085
|
+
if (seeded) {
|
|
16086
|
+
cursorPins = cursorPluginCachePinSnapshots() ?? [];
|
|
16087
|
+
cursorPluginCheck = buildCursorPluginInstallCheck({
|
|
16088
|
+
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
16089
|
+
surface,
|
|
16090
|
+
cacheRoot: cursorCacheRoot,
|
|
16091
|
+
cacheRootExists: (0, import_node_fs15.existsSync)(cursorCacheRoot),
|
|
16092
|
+
pins: cursorPins,
|
|
16093
|
+
hubCheckout: hubCheckoutForCursorSeed(),
|
|
16094
|
+
releasedVersion
|
|
16095
|
+
});
|
|
16096
|
+
if (cursorPluginCheck.ok) {
|
|
16097
|
+
io.err(` \u21BB seeded Cursor MMI plugin cache \u2192 ${releasedVersion ?? "latest"} \u2014 ${reloadAction(surface)}`);
|
|
16098
|
+
}
|
|
16099
|
+
}
|
|
16100
|
+
}
|
|
16101
|
+
checks.push(cursorPluginCheck);
|
|
16102
|
+
const cursorThirdPartyEnabled = await readCursorThirdPartyExtensibilityEnabled(execFileP2);
|
|
15948
16103
|
checks.push(
|
|
15949
|
-
|
|
16104
|
+
buildCursorThirdPartyExtensibilityCheck({
|
|
15950
16105
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
15951
16106
|
surface,
|
|
15952
|
-
|
|
15953
|
-
cacheRootExists: (0, import_node_fs14.existsSync)(cursorCacheRoot),
|
|
15954
|
-
pins: cursorPluginCachePinSnapshots() ?? [],
|
|
15955
|
-
hubCheckout: hubCheckoutForCursorSeed(),
|
|
15956
|
-
releasedVersion
|
|
16107
|
+
enabled: cursorThirdPartyEnabled
|
|
15957
16108
|
})
|
|
15958
16109
|
);
|
|
15959
|
-
const cursorPins = cursorPluginCachePinSnapshots() ?? [];
|
|
15960
16110
|
checks.push(
|
|
15961
16111
|
buildCursorHookCliCheck({
|
|
15962
16112
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutmutco/cli",
|
|
3
|
-
"version": "2.32.
|
|
3
|
+
"version": "2.32.3",
|
|
4
4
|
"description": "MMI Future CLI — delivers the org rules (whole-file), plus saga and KB access. The cross-IDE engine the plugin's SessionStart hook drives.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|