@mutmutco/cli 2.32.2 → 2.32.4
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 +295 -131
- 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) {
|
|
@@ -3423,7 +3423,7 @@ function resolveRulesBase(orgRulesSource, defaultBase) {
|
|
|
3423
3423
|
}
|
|
3424
3424
|
|
|
3425
3425
|
// src/index.ts
|
|
3426
|
-
var
|
|
3426
|
+
var import_node_child_process10 = require("node:child_process");
|
|
3427
3427
|
|
|
3428
3428
|
// src/cli-shared.ts
|
|
3429
3429
|
var import_promises = require("node:fs/promises");
|
|
@@ -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,126 @@ 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_child_process7 = require("node:child_process");
|
|
8252
|
+
var import_node_fs11 = require("node:fs");
|
|
8253
|
+
var import_node_os4 = require("node:os");
|
|
8254
|
+
var import_node_path11 = require("node:path");
|
|
8255
|
+
var import_node_util6 = require("node:util");
|
|
8256
|
+
function isSemverVersion(v) {
|
|
8257
|
+
return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
|
|
8258
|
+
}
|
|
8259
|
+
var MMI_HUB_REPO = "mutmutco/MMI-Hub";
|
|
8260
|
+
var CURSOR_THIRD_PARTY_STATE_KEY = "cursor/thirdPartyExtensibilityEnabled";
|
|
8261
|
+
var PLUGIN_JSON_REL = ".cursor-plugin/plugin.json";
|
|
8262
|
+
var execFileBuffer = (0, import_node_util6.promisify)(import_node_child_process7.execFile);
|
|
8263
|
+
function gitFetchReleaseTagArgs(hubCheckout, tag) {
|
|
8264
|
+
return ["-C", hubCheckout, "fetch", "origin", "tag", tag, "--quiet"];
|
|
8265
|
+
}
|
|
8266
|
+
function ghReleaseTarballApiArgs(tag) {
|
|
8267
|
+
return ["api", `repos/${MMI_HUB_REPO}/tarball/refs/tags/${tag}`];
|
|
8268
|
+
}
|
|
8269
|
+
function cursorUserGlobalStatePath() {
|
|
8270
|
+
if (process.platform === "win32") {
|
|
8271
|
+
const base2 = process.env.APPDATA || (0, import_node_path11.join)((0, import_node_os4.homedir)(), "AppData", "Roaming");
|
|
8272
|
+
return (0, import_node_path11.join)(base2, "Cursor", "User", "globalStorage", "state.vscdb");
|
|
8273
|
+
}
|
|
8274
|
+
if (process.platform === "darwin") {
|
|
8275
|
+
return (0, import_node_path11.join)((0, import_node_os4.homedir)(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
8276
|
+
}
|
|
8277
|
+
return (0, import_node_path11.join)((0, import_node_os4.homedir)(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
8278
|
+
}
|
|
8279
|
+
async function readCursorThirdPartyExtensibilityEnabled(execFileP5) {
|
|
8280
|
+
const dbPath = cursorUserGlobalStatePath();
|
|
8281
|
+
if (!(0, import_node_fs11.existsSync)(dbPath)) return void 0;
|
|
8282
|
+
try {
|
|
8283
|
+
const { stdout } = await execFileP5("sqlite3", [dbPath, `SELECT value FROM ItemTable WHERE key = '${CURSOR_THIRD_PARTY_STATE_KEY}';`], {
|
|
8284
|
+
timeout: 5e3
|
|
8285
|
+
});
|
|
8286
|
+
const value = stdout.trim().toLowerCase();
|
|
8287
|
+
if (value === "true") return true;
|
|
8288
|
+
if (value === "false") return false;
|
|
8289
|
+
return void 0;
|
|
8290
|
+
} catch {
|
|
8291
|
+
return void 0;
|
|
8292
|
+
}
|
|
8293
|
+
}
|
|
8294
|
+
function syncDirContents(src, dest) {
|
|
8295
|
+
(0, import_node_fs11.mkdirSync)(dest, { recursive: true });
|
|
8296
|
+
for (const name of (0, import_node_fs11.readdirSync)(dest)) {
|
|
8297
|
+
(0, import_node_fs11.rmSync)((0, import_node_path11.join)(dest, name), { recursive: true, force: true });
|
|
8298
|
+
}
|
|
8299
|
+
(0, import_node_fs11.cpSync)(src, dest, { recursive: true });
|
|
8300
|
+
}
|
|
8301
|
+
function releaseTag(releasedVersion) {
|
|
8302
|
+
return releasedVersion.startsWith("v") ? releasedVersion : `v${releasedVersion}`;
|
|
8303
|
+
}
|
|
8304
|
+
async function extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5) {
|
|
8305
|
+
const tarFile = (0, import_node_path11.join)(tmpRoot, "archive.tar");
|
|
8306
|
+
try {
|
|
8307
|
+
await execFileP5("git", gitFetchReleaseTagArgs(hubCheckout, tag), { timeout: 6e4 });
|
|
8308
|
+
await execFileP5("git", ["-C", hubCheckout, "archive", "--format=tar", `--output=${tarFile}`, tag, "plugins/mmi"], {
|
|
8309
|
+
timeout: 6e4
|
|
8310
|
+
});
|
|
8311
|
+
await execFileP5("tar", ["-xf", tarFile, "-C", tmpRoot], { timeout: 6e4 });
|
|
8312
|
+
const pluginMmi = (0, import_node_path11.join)(tmpRoot, "plugins", "mmi");
|
|
8313
|
+
return (0, import_node_fs11.existsSync)((0, import_node_path11.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
|
|
8314
|
+
} catch {
|
|
8315
|
+
return void 0;
|
|
8316
|
+
}
|
|
8317
|
+
}
|
|
8318
|
+
async function downloadPluginMmiViaGh(tag, tmpRoot) {
|
|
8319
|
+
const tarPath = (0, import_node_path11.join)(tmpRoot, "repo.tgz");
|
|
8320
|
+
try {
|
|
8321
|
+
(0, import_node_fs11.mkdirSync)(tmpRoot, { recursive: true });
|
|
8322
|
+
const { stdout } = await execFileBuffer("gh", ghReleaseTarballApiArgs(tag), {
|
|
8323
|
+
timeout: 12e4,
|
|
8324
|
+
maxBuffer: 100 * 1024 * 1024,
|
|
8325
|
+
encoding: "buffer",
|
|
8326
|
+
windowsHide: true
|
|
8327
|
+
});
|
|
8328
|
+
(0, import_node_fs11.writeFileSync)(tarPath, stdout);
|
|
8329
|
+
await execFileBuffer("tar", ["-xzf", tarPath, "-C", tmpRoot], { timeout: 12e4, windowsHide: true });
|
|
8330
|
+
const top = (0, import_node_fs11.readdirSync)(tmpRoot).find((entry) => entry !== "repo.tgz");
|
|
8331
|
+
if (!top) return void 0;
|
|
8332
|
+
const pluginMmi = (0, import_node_path11.join)(tmpRoot, top, "plugins", "mmi");
|
|
8333
|
+
return (0, import_node_fs11.existsSync)((0, import_node_path11.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
|
|
8334
|
+
} catch {
|
|
8335
|
+
return void 0;
|
|
8336
|
+
}
|
|
8337
|
+
}
|
|
8338
|
+
async function resolvePluginMmiSource(releasedVersion, hubCheckout, tmpRoot, execFileP5) {
|
|
8339
|
+
(0, import_node_fs11.mkdirSync)(tmpRoot, { recursive: true });
|
|
8340
|
+
const tag = releaseTag(releasedVersion);
|
|
8341
|
+
if (hubCheckout) {
|
|
8342
|
+
const fromHub = await extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5);
|
|
8343
|
+
if (fromHub) return fromHub;
|
|
8344
|
+
}
|
|
8345
|
+
return downloadPluginMmiViaGh(tag, (0, import_node_path11.join)(tmpRoot, "gh"));
|
|
8346
|
+
}
|
|
8347
|
+
function cursorPluginPinsNeedingSeed(pins, releasedVersion) {
|
|
8348
|
+
if (!isSemverVersion(releasedVersion)) return pins.filter((pin) => !pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty);
|
|
8349
|
+
return pins.filter((pin) => {
|
|
8350
|
+
if (!pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty) return true;
|
|
8351
|
+
if (isSemverVersion(pin.version) && compareVersions(pin.version, releasedVersion) < 0) return true;
|
|
8352
|
+
return false;
|
|
8353
|
+
});
|
|
8354
|
+
}
|
|
8355
|
+
async function applyCursorPluginCacheSeed(input) {
|
|
8356
|
+
if (!isSemverVersion(input.releasedVersion)) return false;
|
|
8357
|
+
const pinsToSeed = cursorPluginPinsNeedingSeed(input.pins, input.releasedVersion);
|
|
8358
|
+
if (pinsToSeed.length === 0) return false;
|
|
8359
|
+
const tmpRoot = await input.mkdtemp("mmi-cursor-seed-");
|
|
8360
|
+
const source = await resolvePluginMmiSource(input.releasedVersion, input.hubCheckout, tmpRoot, input.execFileP);
|
|
8361
|
+
if (!source) return false;
|
|
8362
|
+
input.log(` \u21BB seeding Cursor MMI plugin cache \u2192 ${input.releasedVersion}\u2026`);
|
|
8363
|
+
for (const pin of pinsToSeed) {
|
|
8364
|
+
syncDirContents(source, pin.path);
|
|
8365
|
+
}
|
|
8366
|
+
(0, import_node_fs11.rmSync)(tmpRoot, { recursive: true, force: true });
|
|
8367
|
+
return true;
|
|
8368
|
+
}
|
|
8369
|
+
|
|
8250
8370
|
// src/bootstrap-seeds.ts
|
|
8251
8371
|
var PLACEHOLDER_RE = /\{\{([A-Z0-9_]+)\}\}/g;
|
|
8252
8372
|
function loadBootstrapSeeds(manifestJson) {
|
|
@@ -8622,7 +8742,7 @@ var PLUGIN_UPDATE_RECIPES = {
|
|
|
8622
8742
|
};
|
|
8623
8743
|
function highestSemver(versions) {
|
|
8624
8744
|
return versions.reduce((best, v) => {
|
|
8625
|
-
if (!
|
|
8745
|
+
if (!isSemverVersion2(v)) return best;
|
|
8626
8746
|
if (best === void 0) return v;
|
|
8627
8747
|
return compareVersions(v, best) > 0 ? v : best;
|
|
8628
8748
|
}, void 0);
|
|
@@ -8633,11 +8753,11 @@ function buildPluginUpdateReport(input) {
|
|
|
8633
8753
|
const codexActiveCache = highestSemver(input.codexCacheVersions ?? []);
|
|
8634
8754
|
return {
|
|
8635
8755
|
versions: {
|
|
8636
|
-
...
|
|
8756
|
+
...isSemverVersion2(input.cliVersion) ? { cli: input.cliVersion } : {},
|
|
8637
8757
|
...claudePlugin ? { claudePlugin } : {},
|
|
8638
8758
|
...codexMarketplace ? { codexMarketplace } : {},
|
|
8639
8759
|
...codexActiveCache ? { codexActiveCache } : {},
|
|
8640
|
-
...
|
|
8760
|
+
...isSemverVersion2(input.releasedVersion) ? { released: input.releasedVersion } : {}
|
|
8641
8761
|
},
|
|
8642
8762
|
recipes: PLUGIN_UPDATE_RECIPES
|
|
8643
8763
|
};
|
|
@@ -8667,7 +8787,7 @@ function buildDoctorJsonPayload(input) {
|
|
|
8667
8787
|
};
|
|
8668
8788
|
}
|
|
8669
8789
|
var INSTALLED_PLUGIN_VERSION_LABEL = "installed MMI plugin version (vs latest release)";
|
|
8670
|
-
function
|
|
8790
|
+
function isSemverVersion2(v) {
|
|
8671
8791
|
return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
|
|
8672
8792
|
}
|
|
8673
8793
|
function staleRecordCommand(surface) {
|
|
@@ -8688,7 +8808,7 @@ function buildInstalledPluginVersionCheck(input) {
|
|
|
8688
8808
|
fix: pluginRecoveryFix(input.surface),
|
|
8689
8809
|
pluginId
|
|
8690
8810
|
};
|
|
8691
|
-
if (!input.isOrgRepo || !
|
|
8811
|
+
if (!input.isOrgRepo || !isSemverVersion2(input.releasedVersion)) return base2;
|
|
8692
8812
|
if (input.surface === "cursor") return base2;
|
|
8693
8813
|
const stale = [];
|
|
8694
8814
|
let sawRecord = false;
|
|
@@ -8698,7 +8818,7 @@ function buildInstalledPluginVersionCheck(input) {
|
|
|
8698
8818
|
if (!Array.isArray(records) || records.length === 0) continue;
|
|
8699
8819
|
sawRecord = true;
|
|
8700
8820
|
const installedVersion = bestRecord(records).version;
|
|
8701
|
-
if (!
|
|
8821
|
+
if (!isSemverVersion2(installedVersion)) continue;
|
|
8702
8822
|
if (compareVersions(installedVersion, input.releasedVersion) >= 0) {
|
|
8703
8823
|
currentVersion = currentVersion ?? installedVersion;
|
|
8704
8824
|
} else {
|
|
@@ -8730,15 +8850,16 @@ function joinCachePath(root, ...parts) {
|
|
|
8730
8850
|
function cursorPluginInstallFix(input) {
|
|
8731
8851
|
const logHint = "check %APPDATA%\\Cursor\\logs\\<session>\\window*\\exthost\\anysphere.cursor-agent-exec\\Cursor Plugins.*.log for `unable to get password from user`";
|
|
8732
8852
|
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 = "
|
|
8853
|
+
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
8854
|
const guide = `full recovery: ${CURSOR_MARKETPLACE_INSTALL_GUIDE}`;
|
|
8735
8855
|
if (input.reason === "missing-cache") {
|
|
8736
8856
|
return `${marketplaceRefresh}; ${authSteps}; ${guide}`;
|
|
8737
8857
|
}
|
|
8738
8858
|
const pin = input.pinName ?? "<commit-pin>";
|
|
8739
8859
|
const cacheDir = joinCachePath(input.cacheRoot, pin);
|
|
8860
|
+
const autoSeed = "run `mmi-cli doctor --apply` to seed plugins/mmi from the latest release into the active pin";
|
|
8740
8861
|
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}`;
|
|
8862
|
+
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
8863
|
}
|
|
8743
8864
|
function buildCursorPluginInstallCheck(input) {
|
|
8744
8865
|
const base2 = {
|
|
@@ -8776,18 +8897,29 @@ function buildCursorPluginInstallCheck(input) {
|
|
|
8776
8897
|
};
|
|
8777
8898
|
}
|
|
8778
8899
|
}
|
|
8779
|
-
if (input.surface === "cursor" &&
|
|
8780
|
-
const stale = input.pins.filter((pin) =>
|
|
8900
|
+
if (input.surface === "cursor" && isSemverVersion2(input.releasedVersion) && input.pins.some((pin) => isSemverVersion2(pin.version) && compareVersions(pin.version, input.releasedVersion) < 0)) {
|
|
8901
|
+
const stale = input.pins.filter((pin) => isSemverVersion2(pin.version) && compareVersions(pin.version, input.releasedVersion) < 0).map((pin) => `${pin.version} at ${pin.name}`).join(", ");
|
|
8781
8902
|
return {
|
|
8782
8903
|
...base2,
|
|
8783
8904
|
ok: false,
|
|
8784
8905
|
cacheRoot: input.cacheRoot,
|
|
8785
8906
|
pins: input.pins,
|
|
8786
|
-
fix: `Cursor MMI plugin cache is behind ${input.releasedVersion} (${stale}) \u2014
|
|
8907
|
+
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
8908
|
};
|
|
8788
8909
|
}
|
|
8789
8910
|
return { ...base2, cacheRoot: input.cacheRoot, pins: input.pins };
|
|
8790
8911
|
}
|
|
8912
|
+
var CURSOR_THIRD_PARTY_EXTENSIBILITY_LABEL = "Cursor third-party plugin import";
|
|
8913
|
+
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";
|
|
8914
|
+
function buildCursorThirdPartyExtensibilityCheck(input) {
|
|
8915
|
+
const base2 = {
|
|
8916
|
+
ok: true,
|
|
8917
|
+
label: CURSOR_THIRD_PARTY_EXTENSIBILITY_LABEL,
|
|
8918
|
+
fix: CURSOR_THIRD_PARTY_EXTENSIBILITY_FIX
|
|
8919
|
+
};
|
|
8920
|
+
if (!input.isOrgRepo || input.surface !== "cursor" || input.enabled === void 0) return base2;
|
|
8921
|
+
return { ...base2, ok: !input.enabled };
|
|
8922
|
+
}
|
|
8791
8923
|
function buildCursorHookCliCheck(input) {
|
|
8792
8924
|
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
8925
|
const base2 = { ok: true, label: CURSOR_HOOK_CLI_LABEL, fix };
|
|
@@ -8936,12 +9068,12 @@ async function runStageLiveDown(deps, t) {
|
|
|
8936
9068
|
}
|
|
8937
9069
|
|
|
8938
9070
|
// src/stage-runner.ts
|
|
8939
|
-
var
|
|
8940
|
-
var
|
|
8941
|
-
var
|
|
9071
|
+
var import_node_child_process8 = require("node:child_process");
|
|
9072
|
+
var import_node_fs12 = require("node:fs");
|
|
9073
|
+
var import_node_path12 = require("node:path");
|
|
8942
9074
|
var import_node_net2 = require("node:net");
|
|
8943
|
-
var
|
|
8944
|
-
var execFileP4 = (0,
|
|
9075
|
+
var import_node_util7 = require("node:util");
|
|
9076
|
+
var execFileP4 = (0, import_node_util7.promisify)(import_node_child_process8.execFile);
|
|
8945
9077
|
var EARLY_EXIT_GRACE_MS = 2e3;
|
|
8946
9078
|
function waitForProcessStability(child, graceMs = EARLY_EXIT_GRACE_MS) {
|
|
8947
9079
|
return new Promise((resolve, reject) => {
|
|
@@ -8983,7 +9115,7 @@ function detectStaleEnvFile(exampleContent, targetContent, mtimes) {
|
|
|
8983
9115
|
return void 0;
|
|
8984
9116
|
}
|
|
8985
9117
|
function stageStatePath(cwd = process.cwd()) {
|
|
8986
|
-
return (0,
|
|
9118
|
+
return (0, import_node_path12.join)(cwd, "tmp", "stage", "state.json");
|
|
8987
9119
|
}
|
|
8988
9120
|
var POSIX_ONLY_VERBS = ["cp", "mv", "rm", "ln", "cat", "touch", "chmod", "export"];
|
|
8989
9121
|
function posixOnlyShellProblems(command, field, platform = process.platform) {
|
|
@@ -9045,9 +9177,9 @@ async function shell(command, cwd, timeoutMs) {
|
|
|
9045
9177
|
});
|
|
9046
9178
|
}
|
|
9047
9179
|
function readState(path2) {
|
|
9048
|
-
if (!(0,
|
|
9180
|
+
if (!(0, import_node_fs12.existsSync)(path2)) return null;
|
|
9049
9181
|
try {
|
|
9050
|
-
return JSON.parse((0,
|
|
9182
|
+
return JSON.parse((0, import_node_fs12.readFileSync)(path2, "utf8"));
|
|
9051
9183
|
} catch {
|
|
9052
9184
|
return null;
|
|
9053
9185
|
}
|
|
@@ -9099,7 +9231,7 @@ async function stopStage(opts = {}) {
|
|
|
9099
9231
|
return { ok: true, action: "stop", statePath, message: "no previous stage state found" };
|
|
9100
9232
|
}
|
|
9101
9233
|
await killTree(state.pid);
|
|
9102
|
-
(0,
|
|
9234
|
+
(0, import_node_fs12.rmSync)(statePath, { force: true });
|
|
9103
9235
|
return { ok: true, action: "stop", statePath, pid: state.pid, message: `stopped previous stage pid ${state.pid}` };
|
|
9104
9236
|
}
|
|
9105
9237
|
async function startStage(config = {}, opts = {}) {
|
|
@@ -9108,7 +9240,7 @@ async function startStage(config = {}, opts = {}) {
|
|
|
9108
9240
|
const cwd = opts.cwd ?? process.cwd();
|
|
9109
9241
|
const statePath = opts.statePath ?? stageStatePath(cwd);
|
|
9110
9242
|
const dir = statePath.slice(0, Math.max(statePath.lastIndexOf("/"), statePath.lastIndexOf("\\")));
|
|
9111
|
-
(0,
|
|
9243
|
+
(0, import_node_fs12.mkdirSync)(dir, { recursive: true });
|
|
9112
9244
|
let stagePort;
|
|
9113
9245
|
if (config.portRange) {
|
|
9114
9246
|
const [s, e] = config.portRange;
|
|
@@ -9118,14 +9250,14 @@ async function startStage(config = {}, opts = {}) {
|
|
|
9118
9250
|
}
|
|
9119
9251
|
const sub = (s) => s != null && stagePort != null ? s.replace(/\$\{?STAGE_PORT\}?/g, String(stagePort)) : s;
|
|
9120
9252
|
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,
|
|
9253
|
+
const target = (0, import_node_path12.join)(cwd, config.ensureEnv.target);
|
|
9254
|
+
const example = (0, import_node_path12.join)(cwd, config.ensureEnv.example);
|
|
9255
|
+
if (!(0, import_node_fs12.existsSync)(target) && (0, import_node_fs12.existsSync)(example)) {
|
|
9256
|
+
(0, import_node_fs12.copyFileSync)(example, target);
|
|
9257
|
+
} else if ((0, import_node_fs12.existsSync)(target) && (0, import_node_fs12.existsSync)(example)) {
|
|
9258
|
+
const stale = detectStaleEnvFile((0, import_node_fs12.readFileSync)(example, "utf8"), (0, import_node_fs12.readFileSync)(target, "utf8"), {
|
|
9259
|
+
exampleMtimeMs: (0, import_node_fs12.statSync)(example).mtimeMs,
|
|
9260
|
+
targetMtimeMs: (0, import_node_fs12.statSync)(target).mtimeMs
|
|
9129
9261
|
});
|
|
9130
9262
|
if (stale) {
|
|
9131
9263
|
const msg = `stale ${config.ensureEnv.target} (${stale}) \u2014 delete it or refresh from ${config.ensureEnv.example} before re-running /stage`;
|
|
@@ -9137,7 +9269,7 @@ async function startStage(config = {}, opts = {}) {
|
|
|
9137
9269
|
const extraEnv = {};
|
|
9138
9270
|
for (const [k, v] of Object.entries(config.env ?? {})) extraEnv[k] = sub(v) ?? v;
|
|
9139
9271
|
const up = sub(config.up.trim());
|
|
9140
|
-
const child = (0,
|
|
9272
|
+
const child = (0, import_node_child_process8.spawn)(up, {
|
|
9141
9273
|
cwd,
|
|
9142
9274
|
shell: true,
|
|
9143
9275
|
// POSIX-only: the process group exists for the group-kill in stopStage. On win32 teardown is
|
|
@@ -9156,13 +9288,13 @@ async function startStage(config = {}, opts = {}) {
|
|
|
9156
9288
|
healthUrl: sub(config.healthUrl?.trim()) || void 0,
|
|
9157
9289
|
port: stagePort
|
|
9158
9290
|
};
|
|
9159
|
-
(0,
|
|
9291
|
+
(0, import_node_fs12.writeFileSync)(statePath, JSON.stringify(state, null, 2), "utf8");
|
|
9160
9292
|
try {
|
|
9161
9293
|
if (state.healthUrl) await waitForHealth(state.healthUrl, opts.timeoutMs ?? 6e4, config.healthAnyStatus);
|
|
9162
9294
|
else await waitForProcessStability(child);
|
|
9163
9295
|
} catch (e) {
|
|
9164
9296
|
await killTree(state.pid);
|
|
9165
|
-
(0,
|
|
9297
|
+
(0, import_node_fs12.rmSync)(statePath, { force: true });
|
|
9166
9298
|
throw e;
|
|
9167
9299
|
}
|
|
9168
9300
|
const result = { ok: true, action: "start", statePath, pid: state.pid, port: stagePort, message: `started stage pid ${state.pid}${stagePort != null ? ` on port ${stagePort}` : ""}` };
|
|
@@ -10228,12 +10360,12 @@ async function runTenantRedeploy(deps, options) {
|
|
|
10228
10360
|
}
|
|
10229
10361
|
|
|
10230
10362
|
// src/hotfix-coverage.ts
|
|
10231
|
-
var
|
|
10363
|
+
var import_node_child_process9 = require("node:child_process");
|
|
10232
10364
|
var CHERRY_TRAILER = /\(cherry picked from commit ([0-9a-f]{7,40})\)/g;
|
|
10233
10365
|
function checkHotfixCoverage(options = {}) {
|
|
10234
10366
|
const { cwd = process.cwd(), mainRef = "origin/main", rcRef = "origin/rc", manifestPaths = [] } = options;
|
|
10235
10367
|
const ack = (options.ack ?? []).filter(Boolean);
|
|
10236
|
-
const git = options.git ?? ((args, opts) => (0,
|
|
10368
|
+
const git = options.git ?? ((args, opts) => (0, import_node_child_process9.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
|
|
10237
10369
|
const revList = (range) => {
|
|
10238
10370
|
const out = git(["rev-list", "--no-merges", range]).trim();
|
|
10239
10371
|
return out ? out.split("\n") : [];
|
|
@@ -10902,7 +11034,7 @@ async function announceRelease(deps, args) {
|
|
|
10902
11034
|
}
|
|
10903
11035
|
|
|
10904
11036
|
// src/port-registry.ts
|
|
10905
|
-
var
|
|
11037
|
+
var import_node_fs13 = require("node:fs");
|
|
10906
11038
|
|
|
10907
11039
|
// ../infra/port-geometry.mjs
|
|
10908
11040
|
var PORT_BLOCK = 100;
|
|
@@ -10916,8 +11048,8 @@ function nextPortBlock(registry2) {
|
|
|
10916
11048
|
return [base2, base2 + PORT_SPAN];
|
|
10917
11049
|
}
|
|
10918
11050
|
function loadPortRegistry(path2) {
|
|
10919
|
-
if (!(0,
|
|
10920
|
-
const raw = JSON.parse((0,
|
|
11051
|
+
if (!(0, import_node_fs13.existsSync)(path2)) return {};
|
|
11052
|
+
const raw = JSON.parse((0, import_node_fs13.readFileSync)(path2, "utf8"));
|
|
10921
11053
|
const out = {};
|
|
10922
11054
|
for (const [key, value] of Object.entries(raw)) {
|
|
10923
11055
|
if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
|
|
@@ -10931,9 +11063,9 @@ function ensurePortRange(repo, path2) {
|
|
|
10931
11063
|
const existing = registry2[repo];
|
|
10932
11064
|
if (existing) return existing;
|
|
10933
11065
|
const range = nextPortBlock(registry2);
|
|
10934
|
-
const raw = (0,
|
|
11066
|
+
const raw = (0, import_node_fs13.existsSync)(path2) ? JSON.parse((0, import_node_fs13.readFileSync)(path2, "utf8")) : {};
|
|
10935
11067
|
raw[repo] = range;
|
|
10936
|
-
(0,
|
|
11068
|
+
(0, import_node_fs13.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
|
|
10937
11069
|
return range;
|
|
10938
11070
|
}
|
|
10939
11071
|
function portCursorSeed(registry2) {
|
|
@@ -12719,10 +12851,10 @@ function parseKbTree(stdout, prefix) {
|
|
|
12719
12851
|
}
|
|
12720
12852
|
|
|
12721
12853
|
// src/plan.ts
|
|
12722
|
-
var
|
|
12854
|
+
var import_node_path13 = require("node:path");
|
|
12723
12855
|
var PLANS_DIR = "plans";
|
|
12724
|
-
var META_FILE = (0,
|
|
12725
|
-
var planPath = (slug) => (0,
|
|
12856
|
+
var META_FILE = (0, import_node_path13.join)(PLANS_DIR, ".plan-meta.json");
|
|
12857
|
+
var planPath = (slug) => (0, import_node_path13.join)(PLANS_DIR, `${slug}.md`);
|
|
12726
12858
|
var metaKey = (project2, slug) => `${project2}/${slug}`;
|
|
12727
12859
|
function parseMeta(raw) {
|
|
12728
12860
|
if (!raw) return {};
|
|
@@ -12747,7 +12879,7 @@ function hashContent(s) {
|
|
|
12747
12879
|
function staleHint(slug) {
|
|
12748
12880
|
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
12881
|
}
|
|
12750
|
-
var INDEX_FILE = (0,
|
|
12882
|
+
var INDEX_FILE = (0, import_node_path13.join)(PLANS_DIR, ".index.json");
|
|
12751
12883
|
var INDEX_TTL_MS = 6e4;
|
|
12752
12884
|
function parseIndex(raw) {
|
|
12753
12885
|
if (!raw) return null;
|
|
@@ -12776,7 +12908,7 @@ function mergeIndex(idx, scope, plans, now) {
|
|
|
12776
12908
|
const mergedScope = idx.scope === null ? null : [.../* @__PURE__ */ new Set([...idx.scope, ...scope])];
|
|
12777
12909
|
return { fetchedAt: now, scope: mergedScope, plans: [...kept, ...plans] };
|
|
12778
12910
|
}
|
|
12779
|
-
var QUEUE_FILE = (0,
|
|
12911
|
+
var QUEUE_FILE = (0, import_node_path13.join)(PLANS_DIR, ".sync-queue.json");
|
|
12780
12912
|
var QUEUE_MAX_ATTEMPTS = 10;
|
|
12781
12913
|
function isValidQueueEntry(e) {
|
|
12782
12914
|
if (!e || typeof e !== "object") return false;
|
|
@@ -13225,11 +13357,11 @@ async function planGraduate(deps, slug, opts = {}) {
|
|
|
13225
13357
|
}
|
|
13226
13358
|
|
|
13227
13359
|
// src/atomic-write.ts
|
|
13228
|
-
var
|
|
13360
|
+
var import_node_fs14 = require("node:fs");
|
|
13229
13361
|
function atomicWriteFileSync(path2, content) {
|
|
13230
13362
|
const tmp = `${path2}.${process.pid}.tmp`;
|
|
13231
|
-
(0,
|
|
13232
|
-
(0,
|
|
13363
|
+
(0, import_node_fs14.writeFileSync)(tmp, content, "utf8");
|
|
13364
|
+
(0, import_node_fs14.renameSync)(tmp, path2);
|
|
13233
13365
|
}
|
|
13234
13366
|
|
|
13235
13367
|
// src/oauth.ts
|
|
@@ -13460,7 +13592,7 @@ async function fetchHubVersionInfo(baseUrl) {
|
|
|
13460
13592
|
}
|
|
13461
13593
|
function readRepoVersion() {
|
|
13462
13594
|
try {
|
|
13463
|
-
return JSON.parse((0,
|
|
13595
|
+
return JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path14.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
13464
13596
|
} catch {
|
|
13465
13597
|
return void 0;
|
|
13466
13598
|
}
|
|
@@ -13570,10 +13702,10 @@ async function runRulesSync(opts, io = consoleIo) {
|
|
|
13570
13702
|
for (const entry of fetched) {
|
|
13571
13703
|
if ("error" in entry) continue;
|
|
13572
13704
|
const { file, source } = entry;
|
|
13573
|
-
const current = (0,
|
|
13705
|
+
const current = (0, import_node_fs15.existsSync)(file) ? await (0, import_promises5.readFile)(file, "utf8") : null;
|
|
13574
13706
|
if (needsUpdate(source, current)) {
|
|
13575
13707
|
const slash = file.lastIndexOf("/");
|
|
13576
|
-
if (slash > 0) (0,
|
|
13708
|
+
if (slash > 0) (0, import_node_fs15.mkdirSync)(file.slice(0, slash), { recursive: true });
|
|
13577
13709
|
await (0, import_promises5.writeFile)(file, normalizeEol(source), "utf8");
|
|
13578
13710
|
changed++;
|
|
13579
13711
|
if (!opts.quiet) io.log(`mmi-cli rules: updated ${file}`);
|
|
@@ -13599,7 +13731,7 @@ async function runDocsSync(opts, io = consoleIo) {
|
|
|
13599
13731
|
return null;
|
|
13600
13732
|
}
|
|
13601
13733
|
},
|
|
13602
|
-
localContent: async (f) => (0,
|
|
13734
|
+
localContent: async (f) => (0, import_node_fs15.existsSync)(f) ? await (0, import_promises5.readFile)(f, "utf8") : null,
|
|
13603
13735
|
writeDoc: async (f, c) => {
|
|
13604
13736
|
await (0, import_promises5.writeFile)(f, c, "utf8");
|
|
13605
13737
|
}
|
|
@@ -13748,7 +13880,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
13748
13880
|
try {
|
|
13749
13881
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body];
|
|
13750
13882
|
if (o.repo) args.push("--repo", o.repo);
|
|
13751
|
-
(0,
|
|
13883
|
+
(0, import_node_child_process10.spawn)(process.execPath, [process.argv[1], ...args], {
|
|
13752
13884
|
detached: true,
|
|
13753
13885
|
stdio: "ignore",
|
|
13754
13886
|
windowsHide: true,
|
|
@@ -13762,7 +13894,7 @@ function detachPlanSync() {
|
|
|
13762
13894
|
if (planSyncDetached) return;
|
|
13763
13895
|
planSyncDetached = true;
|
|
13764
13896
|
try {
|
|
13765
|
-
(0,
|
|
13897
|
+
(0, import_node_child_process10.spawn)(process.execPath, [process.argv[1], "northstar", "sync", "--quiet"], {
|
|
13766
13898
|
detached: true,
|
|
13767
13899
|
stdio: "ignore",
|
|
13768
13900
|
windowsHide: true,
|
|
@@ -13772,7 +13904,7 @@ function detachPlanSync() {
|
|
|
13772
13904
|
}
|
|
13773
13905
|
}
|
|
13774
13906
|
function makePlanDeps(cfg, io = consoleIo) {
|
|
13775
|
-
const ensureDir = () => (0,
|
|
13907
|
+
const ensureDir = () => (0, import_node_fs15.mkdirSync)(PLANS_DIR, { recursive: true });
|
|
13776
13908
|
return {
|
|
13777
13909
|
apiUrl: cfg.sagaApiUrl,
|
|
13778
13910
|
fetch: (url, init = {}) => fetch(url, { ...init, signal: init.signal ?? AbortSignal.timeout(1e4) }),
|
|
@@ -13780,24 +13912,24 @@ function makePlanDeps(cfg, io = consoleIo) {
|
|
|
13780
13912
|
project: async () => (await sagaKey(cfg)).project,
|
|
13781
13913
|
readLocal: (slug) => {
|
|
13782
13914
|
try {
|
|
13783
|
-
return (0,
|
|
13915
|
+
return (0, import_node_fs15.readFileSync)(planPath(slug), "utf8");
|
|
13784
13916
|
} catch {
|
|
13785
13917
|
return null;
|
|
13786
13918
|
}
|
|
13787
13919
|
},
|
|
13788
13920
|
writeLocal: (slug, content) => {
|
|
13789
13921
|
ensureDir();
|
|
13790
|
-
(0,
|
|
13922
|
+
(0, import_node_fs15.writeFileSync)(planPath(slug), content, "utf8");
|
|
13791
13923
|
},
|
|
13792
13924
|
removeLocal: (slug) => {
|
|
13793
13925
|
try {
|
|
13794
|
-
(0,
|
|
13926
|
+
(0, import_node_fs15.rmSync)(planPath(slug));
|
|
13795
13927
|
} catch {
|
|
13796
13928
|
}
|
|
13797
13929
|
},
|
|
13798
13930
|
readMetaRaw: () => {
|
|
13799
13931
|
try {
|
|
13800
|
-
return (0,
|
|
13932
|
+
return (0, import_node_fs15.readFileSync)(META_FILE, "utf8");
|
|
13801
13933
|
} catch {
|
|
13802
13934
|
return null;
|
|
13803
13935
|
}
|
|
@@ -13808,7 +13940,7 @@ function makePlanDeps(cfg, io = consoleIo) {
|
|
|
13808
13940
|
},
|
|
13809
13941
|
readIndexRaw: () => {
|
|
13810
13942
|
try {
|
|
13811
|
-
return (0,
|
|
13943
|
+
return (0, import_node_fs15.readFileSync)(INDEX_FILE, "utf8");
|
|
13812
13944
|
} catch {
|
|
13813
13945
|
return null;
|
|
13814
13946
|
}
|
|
@@ -13819,7 +13951,7 @@ function makePlanDeps(cfg, io = consoleIo) {
|
|
|
13819
13951
|
},
|
|
13820
13952
|
readQueueRaw: () => {
|
|
13821
13953
|
try {
|
|
13822
|
-
return (0,
|
|
13954
|
+
return (0, import_node_fs15.readFileSync)(QUEUE_FILE, "utf8");
|
|
13823
13955
|
} catch {
|
|
13824
13956
|
return null;
|
|
13825
13957
|
}
|
|
@@ -13841,7 +13973,7 @@ function openInEditor(path2) {
|
|
|
13841
13973
|
return;
|
|
13842
13974
|
}
|
|
13843
13975
|
try {
|
|
13844
|
-
(0,
|
|
13976
|
+
(0, import_node_child_process10.spawn)(editor, [path2], { stdio: "inherit" });
|
|
13845
13977
|
} catch {
|
|
13846
13978
|
console.log(`open ${path2} manually`);
|
|
13847
13979
|
}
|
|
@@ -14675,7 +14807,7 @@ async function createDeferredWorktreeStore() {
|
|
|
14675
14807
|
},
|
|
14676
14808
|
write: async (entries) => {
|
|
14677
14809
|
try {
|
|
14678
|
-
await (0, import_promises5.mkdir)((0,
|
|
14810
|
+
await (0, import_promises5.mkdir)((0, import_node_path14.dirname)(registryPath), { recursive: true });
|
|
14679
14811
|
await (0, import_promises5.writeFile)(registryPath, serializeDeferredWorktrees(entries), "utf8");
|
|
14680
14812
|
} catch {
|
|
14681
14813
|
}
|
|
@@ -14694,7 +14826,7 @@ function worktreeRemoveDeps(execGit) {
|
|
|
14694
14826
|
}
|
|
14695
14827
|
function teardownWorktreeStage(worktreePath) {
|
|
14696
14828
|
return runWorktreeStageTeardown(worktreePath, {
|
|
14697
|
-
hasStageState: (wt) => (0,
|
|
14829
|
+
hasStageState: (wt) => (0, import_node_fs15.existsSync)(stageStatePath(wt)),
|
|
14698
14830
|
stopRecordedStage: async (wt) => (await stopStage({ cwd: wt })).pid,
|
|
14699
14831
|
listComposeProjects: async () => {
|
|
14700
14832
|
const { stdout } = await execFileP2("docker", ["compose", "ls", "--all", "--format", "json"], { timeout: GC_GH_TIMEOUT_MS });
|
|
@@ -14757,7 +14889,7 @@ pr.command("merge <number>").description("merge a PR (squash by default) and cle
|
|
|
14757
14889
|
} : await cleanupPrMergeLocalBranch(headRef, {
|
|
14758
14890
|
beforeWorktrees,
|
|
14759
14891
|
startingPath,
|
|
14760
|
-
pathExists: (p) => (0,
|
|
14892
|
+
pathExists: (p) => (0, import_node_fs15.existsSync)(p),
|
|
14761
14893
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
14762
14894
|
teardownWorktreeStage,
|
|
14763
14895
|
deferredStore,
|
|
@@ -14910,7 +15042,7 @@ function rawValues(flag) {
|
|
|
14910
15042
|
return out;
|
|
14911
15043
|
}
|
|
14912
15044
|
function printLine(value) {
|
|
14913
|
-
(0,
|
|
15045
|
+
(0, import_node_fs15.writeSync)(1, `${value}
|
|
14914
15046
|
`);
|
|
14915
15047
|
}
|
|
14916
15048
|
function stageKeepAlive() {
|
|
@@ -14927,8 +15059,8 @@ async function resolveStage() {
|
|
|
14927
15059
|
local,
|
|
14928
15060
|
shell: shellFor(),
|
|
14929
15061
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
14930
|
-
hasCompose: (0,
|
|
14931
|
-
hasEnvExample: (0,
|
|
15062
|
+
hasCompose: (0, import_node_fs15.existsSync)((0, import_node_path14.join)(process.cwd(), "docker-compose.yml")),
|
|
15063
|
+
hasEnvExample: (0, import_node_fs15.existsSync)((0, import_node_path14.join)(process.cwd(), ".env.example"))
|
|
14932
15064
|
});
|
|
14933
15065
|
}
|
|
14934
15066
|
function stageStepsFor(res, stops = true) {
|
|
@@ -14964,9 +15096,9 @@ program2.command("port-range <repo>").description("assign (idempotently) + print
|
|
|
14964
15096
|
printLine(o.json ? JSON.stringify({ repo, portRange: [start2, end2], source: "meta" }) : `${repo}: stage.portRange [${start2}, ${end2}]`);
|
|
14965
15097
|
return;
|
|
14966
15098
|
}
|
|
14967
|
-
const path2 = (0,
|
|
15099
|
+
const path2 = (0, import_node_path14.join)(process.cwd(), "infra", "port-ranges.json");
|
|
14968
15100
|
const allocate = async (seed) => {
|
|
14969
|
-
const { stdout } = await execFileP2("node", [(0,
|
|
15101
|
+
const { stdout } = await execFileP2("node", [(0, import_node_path14.join)(process.cwd(), "infra", "port-ddb.mjs"), String(seed)], { timeout: 15e3 });
|
|
14970
15102
|
const parsed = JSON.parse(stdout);
|
|
14971
15103
|
if (!Array.isArray(parsed.range) || parsed.range.length !== 2) throw new Error("port-ddb: no range in output");
|
|
14972
15104
|
return parsed.range;
|
|
@@ -15300,7 +15432,7 @@ bootstrap.command("verify <repo>").description("audit whether an existing repo i
|
|
|
15300
15432
|
const report = await verifyBootstrap(repo, o.class, {
|
|
15301
15433
|
client: defaultGitHubClient(),
|
|
15302
15434
|
projectMeta: meta,
|
|
15303
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
15435
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs15.existsSync)(path2) ? (0, import_node_fs15.readFileSync)(path2, "utf8") : null,
|
|
15304
15436
|
// requiredGcpApis is stored as an array by a JSON write, but `project set --var KEY=VALUE` stores a raw
|
|
15305
15437
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
15306
15438
|
requiredGcpApis: (() => {
|
|
@@ -15343,12 +15475,12 @@ bootstrap.command("apply <repo>").description("idempotent seed apply from skills
|
|
|
15343
15475
|
return fail(`bootstrap apply: ${e.message}`);
|
|
15344
15476
|
}
|
|
15345
15477
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
15346
|
-
if (!(0,
|
|
15347
|
-
const manifest = loadBootstrapSeeds((0,
|
|
15478
|
+
if (!(0, import_node_fs15.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; run from the MMI-Hub repo root`);
|
|
15479
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs15.readFileSync)(manifestPath, "utf8"));
|
|
15348
15480
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
15349
15481
|
const slug = parsedRepo.slug;
|
|
15350
15482
|
const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
|
|
15351
|
-
const readFile5 = (p) => (0,
|
|
15483
|
+
const readFile5 = (p) => (0, import_node_fs15.existsSync)(p) ? (0, import_node_fs15.readFileSync)(p, "utf8") : null;
|
|
15352
15484
|
const enc2 = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
15353
15485
|
const rawVars = {};
|
|
15354
15486
|
for (const value of rawValues("--var")) {
|
|
@@ -15557,16 +15689,16 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
15557
15689
|
if (o.class !== "deployable" && o.class !== "content") return failGraceful("access audit: --class must be deployable or content");
|
|
15558
15690
|
targets = [{ repo: o.repo, class: o.class }];
|
|
15559
15691
|
} else {
|
|
15560
|
-
const projectsJson = registryProjects ? JSON.stringify({ projects: registryProjects }) : (0,
|
|
15692
|
+
const projectsJson = registryProjects ? JSON.stringify({ projects: registryProjects }) : (0, import_node_fs15.existsSync)("projects.json") ? (0, import_node_fs15.readFileSync)("projects.json", "utf8") : null;
|
|
15561
15693
|
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,
|
|
15694
|
+
const fanoutJson = (0, import_node_fs15.existsSync)(".github/fanout-targets.json") ? (0, import_node_fs15.readFileSync)(".github/fanout-targets.json", "utf8") : null;
|
|
15563
15695
|
targets = loadAccessTargets(projectsJson, fanoutJson);
|
|
15564
15696
|
}
|
|
15565
15697
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
15566
|
-
const fileMatrix = (0,
|
|
15698
|
+
const fileMatrix = (0, import_node_fs15.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs15.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
15567
15699
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
15568
15700
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
15569
|
-
const fileContracts = (0,
|
|
15701
|
+
const fileContracts = (0, import_node_fs15.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs15.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
15570
15702
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
15571
15703
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess);
|
|
15572
15704
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
@@ -15575,20 +15707,20 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
15575
15707
|
var isWin = process.platform === "win32";
|
|
15576
15708
|
var installedPluginsPath = (surface = detectSurface(process.env)) => {
|
|
15577
15709
|
const homeDir = surface === "codex" ? ".codex" : ".claude";
|
|
15578
|
-
return (0,
|
|
15710
|
+
return (0, import_node_path14.join)((0, import_node_os5.homedir)(), homeDir, "plugins", "installed_plugins.json");
|
|
15579
15711
|
};
|
|
15580
15712
|
function readInstalledPlugins() {
|
|
15581
15713
|
try {
|
|
15582
|
-
return JSON.parse((0,
|
|
15714
|
+
return JSON.parse((0, import_node_fs15.readFileSync)(installedPluginsPath(), "utf8"));
|
|
15583
15715
|
} catch {
|
|
15584
15716
|
return null;
|
|
15585
15717
|
}
|
|
15586
15718
|
}
|
|
15587
15719
|
function installedPluginSources() {
|
|
15588
15720
|
return ["claude", "codex"].map((surface) => {
|
|
15589
|
-
const recordPath = (0,
|
|
15721
|
+
const recordPath = (0, import_node_path14.join)((0, import_node_os5.homedir)(), `.${surface}`, "plugins", "installed_plugins.json");
|
|
15590
15722
|
try {
|
|
15591
|
-
return { surface, installed: JSON.parse((0,
|
|
15723
|
+
return { surface, installed: JSON.parse((0, import_node_fs15.readFileSync)(recordPath, "utf8")), recordPath };
|
|
15592
15724
|
} catch {
|
|
15593
15725
|
return { surface, installed: null, recordPath };
|
|
15594
15726
|
}
|
|
@@ -15596,7 +15728,7 @@ function installedPluginSources() {
|
|
|
15596
15728
|
}
|
|
15597
15729
|
function readClaudeSettings() {
|
|
15598
15730
|
try {
|
|
15599
|
-
return JSON.parse((0,
|
|
15731
|
+
return JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path14.join)(process.cwd(), ".claude", "settings.json"), "utf8"));
|
|
15600
15732
|
} catch {
|
|
15601
15733
|
return null;
|
|
15602
15734
|
}
|
|
@@ -15618,7 +15750,7 @@ function writeProjectInstallRecord(record) {
|
|
|
15618
15750
|
const list = file.plugins[MMI_PLUGIN_ID] ?? [];
|
|
15619
15751
|
list.push(record);
|
|
15620
15752
|
file.plugins[MMI_PLUGIN_ID] = list;
|
|
15621
|
-
(0,
|
|
15753
|
+
(0, import_node_fs15.writeFileSync)(installedPluginsPath(), `${JSON.stringify(file, null, 2)}
|
|
15622
15754
|
`, "utf8");
|
|
15623
15755
|
return true;
|
|
15624
15756
|
} catch {
|
|
@@ -15631,9 +15763,9 @@ function backupAndWriteInstalledPlugins(records, pluginId) {
|
|
|
15631
15763
|
if (!file) return false;
|
|
15632
15764
|
if (!file.plugins) file.plugins = {};
|
|
15633
15765
|
const path2 = installedPluginsPath();
|
|
15634
|
-
(0,
|
|
15766
|
+
(0, import_node_fs15.copyFileSync)(path2, `${path2}.bak`);
|
|
15635
15767
|
file.plugins[pluginId] = records;
|
|
15636
|
-
(0,
|
|
15768
|
+
(0, import_node_fs15.writeFileSync)(path2, `${JSON.stringify(file, null, 2)}
|
|
15637
15769
|
`, "utf8");
|
|
15638
15770
|
return true;
|
|
15639
15771
|
} catch {
|
|
@@ -15641,35 +15773,35 @@ function backupAndWriteInstalledPlugins(records, pluginId) {
|
|
|
15641
15773
|
}
|
|
15642
15774
|
}
|
|
15643
15775
|
function cursorPluginCacheRoot() {
|
|
15644
|
-
return (0,
|
|
15776
|
+
return (0, import_node_path14.join)((0, import_node_os5.homedir)(), ".cursor", "plugins", "cache", "mmi", "mmi");
|
|
15645
15777
|
}
|
|
15646
15778
|
function cursorPluginCachePinSnapshots() {
|
|
15647
15779
|
const root = cursorPluginCacheRoot();
|
|
15648
15780
|
try {
|
|
15649
|
-
return (0,
|
|
15650
|
-
const path2 = (0,
|
|
15651
|
-
const pluginJson = (0,
|
|
15652
|
-
const hooksJson = (0,
|
|
15653
|
-
const cliBundle = (0,
|
|
15781
|
+
return (0, import_node_fs15.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => {
|
|
15782
|
+
const path2 = (0, import_node_path14.join)(root, entry.name);
|
|
15783
|
+
const pluginJson = (0, import_node_path14.join)(path2, ".cursor-plugin", "plugin.json");
|
|
15784
|
+
const hooksJson = (0, import_node_path14.join)(path2, "hooks", "hooks.json");
|
|
15785
|
+
const cliBundle = (0, import_node_path14.join)(path2, "cli", "dist", "index.cjs");
|
|
15654
15786
|
let version;
|
|
15655
15787
|
try {
|
|
15656
|
-
const raw = JSON.parse((0,
|
|
15788
|
+
const raw = JSON.parse((0, import_node_fs15.readFileSync)(pluginJson, "utf8"));
|
|
15657
15789
|
version = typeof raw.version === "string" ? raw.version : void 0;
|
|
15658
15790
|
} catch {
|
|
15659
15791
|
version = void 0;
|
|
15660
15792
|
}
|
|
15661
15793
|
let isEmpty = true;
|
|
15662
15794
|
try {
|
|
15663
|
-
isEmpty = (0,
|
|
15795
|
+
isEmpty = (0, import_node_fs15.readdirSync)(path2).length === 0;
|
|
15664
15796
|
} catch {
|
|
15665
15797
|
isEmpty = true;
|
|
15666
15798
|
}
|
|
15667
15799
|
return {
|
|
15668
15800
|
name: entry.name,
|
|
15669
15801
|
path: path2,
|
|
15670
|
-
hasPluginJson: (0,
|
|
15671
|
-
hasHooksJson: (0,
|
|
15672
|
-
hasCliBundle: (0,
|
|
15802
|
+
hasPluginJson: (0, import_node_fs15.existsSync)(pluginJson),
|
|
15803
|
+
hasHooksJson: (0, import_node_fs15.existsSync)(hooksJson),
|
|
15804
|
+
hasCliBundle: (0, import_node_fs15.existsSync)(cliBundle),
|
|
15673
15805
|
isEmpty,
|
|
15674
15806
|
version
|
|
15675
15807
|
};
|
|
@@ -15679,19 +15811,19 @@ function cursorPluginCachePinSnapshots() {
|
|
|
15679
15811
|
}
|
|
15680
15812
|
}
|
|
15681
15813
|
function hubCheckoutForCursorSeed() {
|
|
15682
|
-
const manifest = (0,
|
|
15683
|
-
return (0,
|
|
15814
|
+
const manifest = (0, import_node_path14.join)(process.cwd(), "plugins", "mmi", ".cursor-plugin", "plugin.json");
|
|
15815
|
+
return (0, import_node_fs15.existsSync)(manifest) ? process.cwd() : void 0;
|
|
15684
15816
|
}
|
|
15685
15817
|
function mmiPluginCacheRootSnapshots() {
|
|
15686
15818
|
const roots = [
|
|
15687
|
-
{ surface: "claude", root: (0,
|
|
15688
|
-
{ surface: "codex", root: (0,
|
|
15819
|
+
{ surface: "claude", root: (0, import_node_path14.join)((0, import_node_os5.homedir)(), ".claude", "plugins", "cache", "mmi", "mmi") },
|
|
15820
|
+
{ surface: "codex", root: (0, import_node_path14.join)((0, import_node_os5.homedir)(), ".codex", "plugins", "cache", "mmi", "mmi") }
|
|
15689
15821
|
];
|
|
15690
15822
|
return roots.flatMap(({ surface, root }) => {
|
|
15691
15823
|
try {
|
|
15692
|
-
const entries = (0,
|
|
15824
|
+
const entries = (0, import_node_fs15.readdirSync)(root, { withFileTypes: true }).map((entry) => ({
|
|
15693
15825
|
name: entry.name,
|
|
15694
|
-
path: (0,
|
|
15826
|
+
path: (0, import_node_path14.join)(root, entry.name),
|
|
15695
15827
|
isDirectory: entry.isDirectory()
|
|
15696
15828
|
}));
|
|
15697
15829
|
return [{ surface, root, entries }];
|
|
@@ -15702,7 +15834,7 @@ function mmiPluginCacheRootSnapshots() {
|
|
|
15702
15834
|
}
|
|
15703
15835
|
function hasNestedMmiChild(versionDir) {
|
|
15704
15836
|
try {
|
|
15705
|
-
return (0,
|
|
15837
|
+
return (0, import_node_fs15.statSync)((0, import_node_path14.join)(versionDir, "mmi")).isDirectory();
|
|
15706
15838
|
} catch {
|
|
15707
15839
|
return false;
|
|
15708
15840
|
}
|
|
@@ -15713,10 +15845,10 @@ function nestedPluginTreeSnapshot() {
|
|
|
15713
15845
|
);
|
|
15714
15846
|
}
|
|
15715
15847
|
function uniqueQuarantineTarget(path2) {
|
|
15716
|
-
if (!(0,
|
|
15848
|
+
if (!(0, import_node_fs15.existsSync)(path2)) return path2;
|
|
15717
15849
|
for (let i = 1; i < 100; i += 1) {
|
|
15718
15850
|
const candidate = `${path2}-${i}`;
|
|
15719
|
-
if (!(0,
|
|
15851
|
+
if (!(0, import_node_fs15.existsSync)(candidate)) return candidate;
|
|
15720
15852
|
}
|
|
15721
15853
|
return `${path2}-${Date.now()}`;
|
|
15722
15854
|
}
|
|
@@ -15724,32 +15856,32 @@ function quarantinePluginCacheDirs(plan2) {
|
|
|
15724
15856
|
let moved = 0;
|
|
15725
15857
|
for (const move of plan2) {
|
|
15726
15858
|
try {
|
|
15727
|
-
if (!(0,
|
|
15859
|
+
if (!(0, import_node_fs15.existsSync)(move.from)) continue;
|
|
15728
15860
|
const target = uniqueQuarantineTarget(move.to);
|
|
15729
|
-
(0,
|
|
15730
|
-
(0,
|
|
15861
|
+
(0, import_node_fs15.mkdirSync)((0, import_node_path14.dirname)(target), { recursive: true });
|
|
15862
|
+
(0, import_node_fs15.renameSync)(move.from, target);
|
|
15731
15863
|
moved += 1;
|
|
15732
15864
|
} catch {
|
|
15733
15865
|
}
|
|
15734
15866
|
}
|
|
15735
15867
|
return moved;
|
|
15736
15868
|
}
|
|
15737
|
-
var gitignorePath = () => (0,
|
|
15869
|
+
var gitignorePath = () => (0, import_node_path14.join)(process.cwd(), ".gitignore");
|
|
15738
15870
|
function readTextFile(path2) {
|
|
15739
15871
|
try {
|
|
15740
|
-
if (!(0,
|
|
15741
|
-
return (0,
|
|
15872
|
+
if (!(0, import_node_fs15.existsSync)(path2)) return null;
|
|
15873
|
+
return (0, import_node_fs15.readFileSync)(path2, "utf8");
|
|
15742
15874
|
} catch {
|
|
15743
15875
|
return null;
|
|
15744
15876
|
}
|
|
15745
15877
|
}
|
|
15746
15878
|
function playwrightMcpConfigSnapshots() {
|
|
15747
15879
|
const cwd = process.cwd();
|
|
15748
|
-
const home = (0,
|
|
15880
|
+
const home = (0, import_node_os5.homedir)();
|
|
15749
15881
|
const candidates = [
|
|
15750
|
-
(0,
|
|
15751
|
-
(0,
|
|
15752
|
-
(0,
|
|
15882
|
+
(0, import_node_path14.join)(cwd, ".cursor", "mcp.json"),
|
|
15883
|
+
(0, import_node_path14.join)(home, ".cursor", "mcp.json"),
|
|
15884
|
+
(0, import_node_path14.join)(home, ".codex", "config.toml")
|
|
15753
15885
|
];
|
|
15754
15886
|
const out = [];
|
|
15755
15887
|
for (const path2 of candidates) {
|
|
@@ -15762,7 +15894,7 @@ function strayBrowserArtifactPaths() {
|
|
|
15762
15894
|
const cwd = process.cwd();
|
|
15763
15895
|
return STRAY_BROWSER_ARTIFACT_DIRS.filter((rel) => {
|
|
15764
15896
|
try {
|
|
15765
|
-
return (0,
|
|
15897
|
+
return (0, import_node_fs15.existsSync)((0, import_node_path14.join)(cwd, rel));
|
|
15766
15898
|
} catch {
|
|
15767
15899
|
return false;
|
|
15768
15900
|
}
|
|
@@ -15770,14 +15902,14 @@ function strayBrowserArtifactPaths() {
|
|
|
15770
15902
|
}
|
|
15771
15903
|
function readGitignore() {
|
|
15772
15904
|
try {
|
|
15773
|
-
return (0,
|
|
15905
|
+
return (0, import_node_fs15.readFileSync)(gitignorePath(), "utf8");
|
|
15774
15906
|
} catch {
|
|
15775
15907
|
return null;
|
|
15776
15908
|
}
|
|
15777
15909
|
}
|
|
15778
15910
|
function writeGitignore(content) {
|
|
15779
15911
|
try {
|
|
15780
|
-
(0,
|
|
15912
|
+
(0, import_node_fs15.writeFileSync)(gitignorePath(), content, "utf8");
|
|
15781
15913
|
return true;
|
|
15782
15914
|
} catch {
|
|
15783
15915
|
return false;
|
|
@@ -15816,7 +15948,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
15816
15948
|
let onPath = pathProbe;
|
|
15817
15949
|
if (!onPath) {
|
|
15818
15950
|
const root = process.env.CLAUDE_PLUGIN_ROOT;
|
|
15819
|
-
if (root && (0,
|
|
15951
|
+
if (root && (0, import_node_fs15.existsSync)(`${root}/bin/mmi-cli${isWin ? ".cmd" : ""}`)) onPath = true;
|
|
15820
15952
|
}
|
|
15821
15953
|
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
15954
|
const surface = detectSurface(process.env);
|
|
@@ -15945,18 +16077,50 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
15945
16077
|
})
|
|
15946
16078
|
);
|
|
15947
16079
|
const cursorCacheRoot = cursorPluginCacheRoot();
|
|
16080
|
+
let cursorPins = cursorPluginCachePinSnapshots() ?? [];
|
|
16081
|
+
let cursorPluginCheck = buildCursorPluginInstallCheck({
|
|
16082
|
+
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
16083
|
+
surface,
|
|
16084
|
+
cacheRoot: cursorCacheRoot,
|
|
16085
|
+
cacheRootExists: (0, import_node_fs15.existsSync)(cursorCacheRoot),
|
|
16086
|
+
pins: cursorPins,
|
|
16087
|
+
hubCheckout: hubCheckoutForCursorSeed(),
|
|
16088
|
+
releasedVersion
|
|
16089
|
+
});
|
|
16090
|
+
if (!cursorPluginCheck.ok && repairLocal) {
|
|
16091
|
+
const seeded = await applyCursorPluginCacheSeed({
|
|
16092
|
+
pins: cursorPins,
|
|
16093
|
+
releasedVersion,
|
|
16094
|
+
hubCheckout: hubCheckoutForCursorSeed(),
|
|
16095
|
+
execFileP: execFileP2,
|
|
16096
|
+
mkdtemp: (prefix) => (0, import_promises5.mkdtemp)((0, import_node_path14.join)((0, import_node_os5.tmpdir)(), prefix)),
|
|
16097
|
+
log: (m) => io.err(m)
|
|
16098
|
+
});
|
|
16099
|
+
if (seeded) {
|
|
16100
|
+
cursorPins = cursorPluginCachePinSnapshots() ?? [];
|
|
16101
|
+
cursorPluginCheck = buildCursorPluginInstallCheck({
|
|
16102
|
+
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
16103
|
+
surface,
|
|
16104
|
+
cacheRoot: cursorCacheRoot,
|
|
16105
|
+
cacheRootExists: (0, import_node_fs15.existsSync)(cursorCacheRoot),
|
|
16106
|
+
pins: cursorPins,
|
|
16107
|
+
hubCheckout: hubCheckoutForCursorSeed(),
|
|
16108
|
+
releasedVersion
|
|
16109
|
+
});
|
|
16110
|
+
if (cursorPluginCheck.ok) {
|
|
16111
|
+
io.err(` \u21BB seeded Cursor MMI plugin cache \u2192 ${releasedVersion ?? "latest"} \u2014 ${reloadAction(surface)}`);
|
|
16112
|
+
}
|
|
16113
|
+
}
|
|
16114
|
+
}
|
|
16115
|
+
checks.push(cursorPluginCheck);
|
|
16116
|
+
const cursorThirdPartyEnabled = await readCursorThirdPartyExtensibilityEnabled(execFileP2);
|
|
15948
16117
|
checks.push(
|
|
15949
|
-
|
|
16118
|
+
buildCursorThirdPartyExtensibilityCheck({
|
|
15950
16119
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
15951
16120
|
surface,
|
|
15952
|
-
|
|
15953
|
-
cacheRootExists: (0, import_node_fs14.existsSync)(cursorCacheRoot),
|
|
15954
|
-
pins: cursorPluginCachePinSnapshots() ?? [],
|
|
15955
|
-
hubCheckout: hubCheckoutForCursorSeed(),
|
|
15956
|
-
releasedVersion
|
|
16121
|
+
enabled: cursorThirdPartyEnabled
|
|
15957
16122
|
})
|
|
15958
16123
|
);
|
|
15959
|
-
const cursorPins = cursorPluginCachePinSnapshots() ?? [];
|
|
15960
16124
|
checks.push(
|
|
15961
16125
|
buildCursorHookCliCheck({
|
|
15962
16126
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
@@ -16016,7 +16180,7 @@ program2.command("session-start").description("run the SessionStart verbs (rules
|
|
|
16016
16180
|
} catch (e) {
|
|
16017
16181
|
console.error(`[mmi-hook] saga session failed: ${e.message}`);
|
|
16018
16182
|
}
|
|
16019
|
-
spawnDetachedSelf(["docs", "sync", "--quiet"], { spawn:
|
|
16183
|
+
spawnDetachedSelf(["docs", "sync", "--quiet"], { spawn: import_node_child_process10.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
16020
16184
|
let northstarInjected = false;
|
|
16021
16185
|
const { parallel, sequential } = buildSessionStartPlan({
|
|
16022
16186
|
rulesSync: (io) => runRulesSync({ quiet: true }, io),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutmutco/cli",
|
|
3
|
-
"version": "2.32.
|
|
3
|
+
"version": "2.32.4",
|
|
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",
|