@mutmutco/cli 2.63.0 → 2.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.cjs +125 -74
- package/package.json +1 -1
- package/dist/saga.cjs +0 -5245
package/dist/main.cjs
CHANGED
|
@@ -3439,7 +3439,7 @@ function resolveRulesBase(orgRulesSource, defaultBase) {
|
|
|
3439
3439
|
}
|
|
3440
3440
|
|
|
3441
3441
|
// src/index.ts
|
|
3442
|
-
var
|
|
3442
|
+
var import_node_child_process11 = require("node:child_process");
|
|
3443
3443
|
|
|
3444
3444
|
// src/cli-shared.ts
|
|
3445
3445
|
var import_promises = require("node:fs/promises");
|
|
@@ -13518,6 +13518,7 @@ function writeError(res) {
|
|
|
13518
13518
|
}
|
|
13519
13519
|
|
|
13520
13520
|
// src/secrets.ts
|
|
13521
|
+
var import_node_child_process8 = require("node:child_process");
|
|
13521
13522
|
var OWNER2 = "mutmutco";
|
|
13522
13523
|
var SSM_ROOT = "/mmi-future";
|
|
13523
13524
|
var PROJECT_TIER_SEGMENT = "dev";
|
|
@@ -13568,7 +13569,8 @@ function formatVaultPointer(p) {
|
|
|
13568
13569
|
...Object.entries(p.wellKnown).map(([k, keys]) => ` ${k}: ${keys.join(", ")}`),
|
|
13569
13570
|
``,
|
|
13570
13571
|
`enumerate actual keys: mmi-cli secrets list`,
|
|
13571
|
-
`
|
|
13572
|
+
`use one keyless: mmi-cli secrets use <stage>/<KEY> -- <cmd> (inject, never printed; raw get is master-only, WS6 #2184)`,
|
|
13573
|
+
`validate one: mmi-cli secrets verify <KEY>`,
|
|
13572
13574
|
`set a key: mmi-cli secrets set <stage>/<KEY> (value via stdin; project-admin self-serves own repo)`,
|
|
13573
13575
|
`import Rails creds: mmi-cli secrets import-rails-credentials --stage main --map secret_key_base=SECRET_KEY_BASE`,
|
|
13574
13576
|
`copy provider keys: mmi-cli secrets copy --from rc --to dev --keys RECALL_API_KEY,GEMINI_API_KEY`
|
|
@@ -13664,7 +13666,7 @@ async function fetchSecretValue(deps, key, opts) {
|
|
|
13664
13666
|
const res = await deps.fetch(`${deps.apiUrl}/secrets/get`, {
|
|
13665
13667
|
method: "POST",
|
|
13666
13668
|
headers: await deps.headers({ "content-type": "application/json" }),
|
|
13667
|
-
body: JSON.stringify({ repo, key, ...slug ? { slug } : {} }),
|
|
13669
|
+
body: JSON.stringify({ repo, key, use: true, ...slug ? { slug } : {} }),
|
|
13668
13670
|
signal: AbortSignal.timeout(TIMEOUT_MS)
|
|
13669
13671
|
});
|
|
13670
13672
|
if (!res.ok) return null;
|
|
@@ -13718,7 +13720,7 @@ no vault credentials visible`;
|
|
|
13718
13720
|
"",
|
|
13719
13721
|
...lines,
|
|
13720
13722
|
"",
|
|
13721
|
-
"+ =
|
|
13723
|
+
"+ = usable now. Consume keyless: `mmi-cli secrets use <KEY> -- <cmd>` (inject, never printed); validate: `mmi-cli secrets verify <KEY>`. Raw `get` (prints the value) is master-only (WS6 #2184).",
|
|
13722
13724
|
" Cross-slug (org-infra): `mmi-cli secrets get --slug cloudflare GLOBAL_API_KEY` (master-gated)."
|
|
13723
13725
|
].join("\n");
|
|
13724
13726
|
}
|
|
@@ -14244,21 +14246,64 @@ async function secretsCopy(deps, opts) {
|
|
|
14244
14246
|
}
|
|
14245
14247
|
return true;
|
|
14246
14248
|
}
|
|
14249
|
+
function defaultSpawn(command, args, env) {
|
|
14250
|
+
const r = (0, import_node_child_process8.spawnSync)(command, args, { stdio: "inherit", env });
|
|
14251
|
+
if (r.error) throw r.error;
|
|
14252
|
+
return r.status ?? 1;
|
|
14253
|
+
}
|
|
14247
14254
|
async function secretsUse(deps, key, opts) {
|
|
14248
|
-
const
|
|
14249
|
-
|
|
14250
|
-
|
|
14251
|
-
|
|
14252
|
-
|
|
14253
|
-
|
|
14254
|
-
|
|
14255
|
-
|
|
14256
|
-
|
|
14257
|
-
|
|
14258
|
-
|
|
14259
|
-
|
|
14260
|
-
|
|
14261
|
-
|
|
14255
|
+
const command = opts.command ?? [];
|
|
14256
|
+
if (command.length === 0) {
|
|
14257
|
+
const slug2 = await vaultSlug(deps, opts);
|
|
14258
|
+
const tier = classifyTier(slug2, key);
|
|
14259
|
+
const path2 = secretParamName(slug2, key);
|
|
14260
|
+
deps.log(
|
|
14261
|
+
[
|
|
14262
|
+
`${key} \u2192 ${path2} (${tier} tier)`,
|
|
14263
|
+
"",
|
|
14264
|
+
"Consume it WITHOUT committing it:",
|
|
14265
|
+
` \u2022 Keyless run: \`mmi-cli secrets use ${key}${opts.slug ? ` --slug ${opts.slug}` : ""} -- <command>\` injects it into the command's env (never printed). A granted non-master can USE an org secret this way.`,
|
|
14266
|
+
` \u2022 Runtime / agents: read it keylessly at runtime via the box's OIDC role. Never bake it into an image or commit it.`,
|
|
14267
|
+
` \u2022 CI (GitHub Actions): the workflow assumes its OIDC role and runs \`aws ssm get-parameter --with-decryption --name ${path2}\` \u2014 no GitHub secret.`,
|
|
14268
|
+
tier === "project" ? " \u2022 Bare keys default to dev/. Use an explicit rc/<KEY> or main/<KEY> when the stage needs its own value." : " \u2022 For your own product repo, project-admins self-serve this stage key. Org-infra/cross-slug keys remain master-gated unless granted."
|
|
14269
|
+
].join("\n")
|
|
14270
|
+
);
|
|
14271
|
+
return;
|
|
14272
|
+
}
|
|
14273
|
+
if (!isValidSecretKey(key)) {
|
|
14274
|
+
deps.err(`invalid secret key ${JSON.stringify(key)}`);
|
|
14275
|
+
return false;
|
|
14276
|
+
}
|
|
14277
|
+
const repo = await targetRepo(deps, opts);
|
|
14278
|
+
const slug = opts.slug?.toLowerCase();
|
|
14279
|
+
const res = await deps.fetch(`${deps.apiUrl}/secrets/get`, {
|
|
14280
|
+
method: "POST",
|
|
14281
|
+
headers: await deps.headers({ "content-type": "application/json" }),
|
|
14282
|
+
body: JSON.stringify({ repo, key, use: true, ...slug ? { slug } : {} }),
|
|
14283
|
+
signal: AbortSignal.timeout(TIMEOUT_MS)
|
|
14284
|
+
});
|
|
14285
|
+
if (!res.ok) {
|
|
14286
|
+
const body = await readJsonBody(res);
|
|
14287
|
+
if (res.status === 404 && body.code === "secret_not_found") {
|
|
14288
|
+
deps.err(`Secret ${key} was not found.`);
|
|
14289
|
+
deps.err(`Approved escalation: mmi-cli secrets request ${key} --repo ${repo} --reason "<why it is needed>"`);
|
|
14290
|
+
return false;
|
|
14291
|
+
}
|
|
14292
|
+
deps.err(
|
|
14293
|
+
await upgradeMessage(res, body) ?? (res.status === 403 ? `secrets use: not authorized for ${key} (HTTP 403) \u2014 ask the master for a \`secrets grant\`${errorDetail(body)}` : `secrets use failed: HTTP ${res.status}${errorDetail(body)}`)
|
|
14294
|
+
);
|
|
14295
|
+
return false;
|
|
14296
|
+
}
|
|
14297
|
+
const { value } = await res.json();
|
|
14298
|
+
if (!value) {
|
|
14299
|
+
deps.err(`secrets use: no value returned for ${key}`);
|
|
14300
|
+
return false;
|
|
14301
|
+
}
|
|
14302
|
+
const envName = (opts.name ?? secretLeafName(key)).toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
14303
|
+
const [cmd, ...args] = command;
|
|
14304
|
+
const run = deps.spawn ?? defaultSpawn;
|
|
14305
|
+
const code = run(cmd, args, { ...process.env, [envName]: value });
|
|
14306
|
+
return code === 0;
|
|
14262
14307
|
}
|
|
14263
14308
|
|
|
14264
14309
|
// src/secrets-commands.ts
|
|
@@ -14378,7 +14423,7 @@ function registerSecretsCommands(program3) {
|
|
|
14378
14423
|
const ok = await secretsPreflight(d, { repo: o.repo, stage: o.stage, required });
|
|
14379
14424
|
if (!ok) process.exitCode = 1;
|
|
14380
14425
|
});
|
|
14381
|
-
secrets.command("get <key>").description("print one secret value
|
|
14426
|
+
secrets.command("get <key>").description("print one secret value, RAW \u2014 MASTER-ONLY (WS6 #2184); non-master: `secrets use` to consume keyless or `secrets verify` to validate").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--slug <slug>", "vault slug override for org-infra namespaces (cloudflare, shared, \u2026) \u2014 master-gated").action((key, o) => withSecrets(async (d) => {
|
|
14382
14427
|
const ok = await secretsGet(d, key, o);
|
|
14383
14428
|
if (!ok) process.exitCode = 1;
|
|
14384
14429
|
}));
|
|
@@ -14439,7 +14484,10 @@ function registerSecretsCommands(program3) {
|
|
|
14439
14484
|
if (!ok) process.exitCode = 1;
|
|
14440
14485
|
}));
|
|
14441
14486
|
secrets.command("rm <key>").description("remove a secret (project tier self-serve; org tier needs a grant)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").action((key, o) => withSecrets((d) => secretsRemove(d, key, o)));
|
|
14442
|
-
secrets.command("use <key>").description("
|
|
14487
|
+
secrets.command("use <key> [command...]").description("consume a secret KEYLESS: `use <key> -- <cmd>` injects it into the command env (never printed); a granted non-master can use an org secret. No command prints guidance only.").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--slug <slug>", "org-infra namespace (e.g. _org) for a granted org secret").option("--name <ENVVAR>", "env var name to inject under (default: the key leaf, UPPER_SNAKE)").action((key, command, o) => withSecrets(async (d) => {
|
|
14488
|
+
const ok = await secretsUse(d, key, { ...o, command });
|
|
14489
|
+
if (ok === false) process.exitCode = 1;
|
|
14490
|
+
}));
|
|
14443
14491
|
secrets.command("grant <repo> <login> <key>").description("MASTER-ONLY: grant a project-admin standing access to a specific org-tier secret").action((repo, login, key) => withSecrets((d) => secretsGrant(d, repo, login, key, {})));
|
|
14444
14492
|
secrets.command("revoke <repo> <login> <key>").description("MASTER-ONLY: withdraw a previously granted org-tier secret access").action((repo, login, key) => withSecrets((d) => secretsRevoke(d, repo, login, key, {})));
|
|
14445
14493
|
}
|
|
@@ -14510,7 +14558,7 @@ function registerEdgeCommands(program3) {
|
|
|
14510
14558
|
|
|
14511
14559
|
// src/doctor-run.ts
|
|
14512
14560
|
var import_node_fs16 = require("node:fs");
|
|
14513
|
-
var
|
|
14561
|
+
var import_node_child_process10 = require("node:child_process");
|
|
14514
14562
|
var import_promises3 = require("node:fs/promises");
|
|
14515
14563
|
var import_node_path16 = require("node:path");
|
|
14516
14564
|
var import_node_os5 = require("node:os");
|
|
@@ -14533,7 +14581,7 @@ function buildGuardSessionStartLine(state, opts = {}) {
|
|
|
14533
14581
|
}
|
|
14534
14582
|
|
|
14535
14583
|
// src/cursor-plugin-seed.ts
|
|
14536
|
-
var
|
|
14584
|
+
var import_node_child_process9 = require("node:child_process");
|
|
14537
14585
|
var import_node_fs13 = require("node:fs");
|
|
14538
14586
|
var import_node_os4 = require("node:os");
|
|
14539
14587
|
var import_node_path14 = require("node:path");
|
|
@@ -14544,7 +14592,7 @@ function isSemverVersion(v) {
|
|
|
14544
14592
|
var MMI_HUB_REPO = "mutmutco/MMI-Hub";
|
|
14545
14593
|
var CURSOR_THIRD_PARTY_STATE_KEY = "cursor/thirdPartyExtensibilityEnabled";
|
|
14546
14594
|
var PLUGIN_JSON_REL = ".cursor-plugin/plugin.json";
|
|
14547
|
-
var execFileBuffer = (0, import_node_util7.promisify)(
|
|
14595
|
+
var execFileBuffer = (0, import_node_util7.promisify)(import_node_child_process9.execFile);
|
|
14548
14596
|
function gitFetchReleaseTagArgs(hubCheckout, tag) {
|
|
14549
14597
|
return ["-C", hubCheckout, "fetch", "origin", "tag", tag, "--quiet"];
|
|
14550
14598
|
}
|
|
@@ -15398,7 +15446,7 @@ function buildOpencodeDesktopBootstrapCheck(input) {
|
|
|
15398
15446
|
return { ...base, ok: false, fix: `${OPENCODE_DESKTOP_BOOTSTRAP_FIX} (stale: ${dirs})`, issues: [...input.issues] };
|
|
15399
15447
|
}
|
|
15400
15448
|
var OPENCODE_LEGACY_CONFIG_LABEL = "OpenCode legacy ~/.opencode config (stale plugin entries)";
|
|
15401
|
-
var OPENCODE_LEGACY_CONFIG_FIX = 'remove or rename ~/.opencode/opencode.json (legacy path); configure OpenCode at ~/.config/opencode/opencode.jsonc with "plugin": ["@mutmutco/opencode-mmi"]
|
|
15449
|
+
var OPENCODE_LEGACY_CONFIG_FIX = 'remove or rename ~/.opencode/opencode.json (legacy path); configure OpenCode at ~/.config/opencode/opencode.jsonc with "plugin": ["@mutmutco/opencode-mmi"]';
|
|
15402
15450
|
function parseOpencodeLegacyConfigPlugins(content) {
|
|
15403
15451
|
try {
|
|
15404
15452
|
const parsed = JSON.parse(content);
|
|
@@ -16019,7 +16067,7 @@ function reexecMmiCli(args) {
|
|
|
16019
16067
|
}
|
|
16020
16068
|
};
|
|
16021
16069
|
const env = { ...process.env, [DOCTOR_POST_SELF_UPDATE_ENV]: "1" };
|
|
16022
|
-
const child = isWin ? (0,
|
|
16070
|
+
const child = isWin ? (0, import_node_child_process10.spawn)("cmd.exe", ["/c", "mmi-cli", ...args], { stdio: "inherit", env }) : (0, import_node_child_process10.spawn)("mmi-cli", args, { stdio: "inherit", env });
|
|
16023
16071
|
child.on("error", () => done(-1));
|
|
16024
16072
|
child.on("exit", (code) => done(code ?? 0));
|
|
16025
16073
|
});
|
|
@@ -16503,7 +16551,7 @@ function writeGitignore(content) {
|
|
|
16503
16551
|
return false;
|
|
16504
16552
|
}
|
|
16505
16553
|
}
|
|
16506
|
-
async function runDoctor(opts, io = consoleIo) {
|
|
16554
|
+
async function runDoctor(opts, io = consoleIo, readOrigin) {
|
|
16507
16555
|
if (opts.guide) {
|
|
16508
16556
|
if (opts.json) io.log(JSON.stringify({ resources: [MMI_AGENTIC_ONBOARDING_GUIDE] }, null, 2));
|
|
16509
16557
|
else io.log(MMI_AGENTIC_ONBOARDING_GUIDE.url);
|
|
@@ -16516,14 +16564,17 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16516
16564
|
const checks = [];
|
|
16517
16565
|
const REWRITE_KEY = "url.https://github.com/.insteadOf";
|
|
16518
16566
|
const CLONE_FIX = 'run: git config --global url."https://github.com/".insteadOf "git@github.com:"';
|
|
16519
|
-
const [login, pathProbe, releasedVersion, cfg, callerArn, cloneProbe] = await Promise.all([
|
|
16567
|
+
const [login, pathProbe, releasedVersion, cfg, callerArn, cloneProbe, isOrgRepo] = await Promise.all([
|
|
16520
16568
|
githubLogin(),
|
|
16521
16569
|
execFileP2(isWin ? "where" : "which", ["mmi-cli"]).then(() => true).catch(() => false),
|
|
16522
16570
|
fetchReleasedVersion(),
|
|
16523
16571
|
loadConfig(),
|
|
16524
16572
|
awsCallerArn(),
|
|
16525
|
-
execFileP2("git", ["config", "--global", "--get-all", REWRITE_KEY]).then(({ stdout }) => stdout.split("\n").some((l) => l.trim() === "git@github.com:")).catch(() => false)
|
|
16573
|
+
execFileP2("git", ["config", "--global", "--get-all", REWRITE_KEY]).then(({ stdout }) => stdout.split("\n").some((l) => l.trim() === "git@github.com:")).catch(() => false),
|
|
16526
16574
|
// unset → repair below
|
|
16575
|
+
// hub-v3 step-back: org-membership gates the repo-local checks/repairs by the git `origin` remote,
|
|
16576
|
+
// not the now-always-defaulted `cfg.sagaApiUrl`. Independent git read — resolved concurrently here.
|
|
16577
|
+
readOrigin ? isOrgRepoRoot(readOrigin) : isOrgRepoRoot()
|
|
16527
16578
|
]);
|
|
16528
16579
|
const surface = detectSurface(process.env);
|
|
16529
16580
|
const versionReportProbe = buildVersionLagReport({
|
|
@@ -16538,18 +16589,18 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16538
16589
|
const opencodeAdapterStale = isBehind(opencodeInstalledVersionForDoctor(), releasedVersion);
|
|
16539
16590
|
const cursorCacheStale = (0, import_node_fs16.existsSync)(cursorPluginCacheRoot()) && (cursorPluginCachePinSnapshots() ?? []).some((p) => isBehind(p.version, releasedVersion));
|
|
16540
16591
|
const healPlan = doctorHealPlan({
|
|
16541
|
-
isOrgRepo
|
|
16592
|
+
isOrgRepo,
|
|
16542
16593
|
surface,
|
|
16543
16594
|
versionReport: versionReportProbe,
|
|
16544
16595
|
hasPluginRoot: Boolean(process.env.CLAUDE_PLUGIN_ROOT),
|
|
16545
16596
|
installedVersionCheck: buildInstalledPluginVersionCheck({
|
|
16546
|
-
isOrgRepo
|
|
16597
|
+
isOrgRepo,
|
|
16547
16598
|
sources: sourcesForHealPlan,
|
|
16548
16599
|
releasedVersion,
|
|
16549
16600
|
surface
|
|
16550
16601
|
}),
|
|
16551
16602
|
legacyPluginCheck: buildLegacyPluginInstallCheck({
|
|
16552
|
-
isOrgRepo
|
|
16603
|
+
isOrgRepo,
|
|
16553
16604
|
sources: sourcesForHealPlan
|
|
16554
16605
|
}),
|
|
16555
16606
|
opencodeAdapterStale,
|
|
@@ -16608,7 +16659,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16608
16659
|
const installedVersion = resolveClientVersion();
|
|
16609
16660
|
checks.push(
|
|
16610
16661
|
buildHubCompatCheck({
|
|
16611
|
-
isOrgRepo
|
|
16662
|
+
isOrgRepo,
|
|
16612
16663
|
versionInfo: hubVersionInfo,
|
|
16613
16664
|
installedVersion
|
|
16614
16665
|
})
|
|
@@ -16616,7 +16667,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16616
16667
|
if (runExtended) {
|
|
16617
16668
|
checks.push(
|
|
16618
16669
|
buildHubDeployFreshnessCheck({
|
|
16619
|
-
isOrgRepo
|
|
16670
|
+
isOrgRepo,
|
|
16620
16671
|
deployedHubVersion: hubVersionInfo?.hubVersion,
|
|
16621
16672
|
installedVersion,
|
|
16622
16673
|
releasedVersion
|
|
@@ -16639,7 +16690,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16639
16690
|
const installed = readInstalledPlugins();
|
|
16640
16691
|
const claudeSettings = readClaudeSettings();
|
|
16641
16692
|
let pluginCheck = buildPluginInstallRecordCheck({
|
|
16642
|
-
isOrgRepo
|
|
16693
|
+
isOrgRepo,
|
|
16643
16694
|
settings: claudeSettings,
|
|
16644
16695
|
installed,
|
|
16645
16696
|
projectPath: process.cwd(),
|
|
@@ -16654,7 +16705,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16654
16705
|
}
|
|
16655
16706
|
checks.push(pluginCheck);
|
|
16656
16707
|
let legacyPluginCheck = buildLegacyPluginInstallCheck({
|
|
16657
|
-
isOrgRepo
|
|
16708
|
+
isOrgRepo,
|
|
16658
16709
|
sources: installedPluginSources(),
|
|
16659
16710
|
surface
|
|
16660
16711
|
});
|
|
@@ -16663,7 +16714,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16663
16714
|
const codexLegacy = legacyPluginCheck.staleSurfaces?.includes("codex") ?? false;
|
|
16664
16715
|
if (claudeLegacy && await applyPluginHeal("claude", surface, (m) => io.err(m), { force: true })) {
|
|
16665
16716
|
legacyPluginCheck = buildLegacyPluginInstallCheck({
|
|
16666
|
-
isOrgRepo
|
|
16717
|
+
isOrgRepo,
|
|
16667
16718
|
sources: installedPluginSources(),
|
|
16668
16719
|
surface
|
|
16669
16720
|
});
|
|
@@ -16673,7 +16724,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16673
16724
|
}
|
|
16674
16725
|
if (!legacyPluginCheck.ok && codexLegacy && await applyPluginHeal("codex", surface, (m) => io.err(m), { force: true })) {
|
|
16675
16726
|
legacyPluginCheck = buildLegacyPluginInstallCheck({
|
|
16676
|
-
isOrgRepo
|
|
16727
|
+
isOrgRepo,
|
|
16677
16728
|
sources: installedPluginSources(),
|
|
16678
16729
|
surface
|
|
16679
16730
|
});
|
|
@@ -16683,7 +16734,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16683
16734
|
}
|
|
16684
16735
|
}
|
|
16685
16736
|
checks.push(legacyPluginCheck);
|
|
16686
|
-
let gitignoreCheck = buildGitignoreManagedBlockCheck({ isOrgRepo
|
|
16737
|
+
let gitignoreCheck = buildGitignoreManagedBlockCheck({ isOrgRepo, content: readGitignore() });
|
|
16687
16738
|
const gitignoreDecision = decideGitignoreRepair(gitignoreCheck, { repoWritesAllowed, repairFull });
|
|
16688
16739
|
gitignoreCheck = gitignoreDecision.check;
|
|
16689
16740
|
if (gitignoreDecision.action === "suppress") {
|
|
@@ -16701,7 +16752,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16701
16752
|
}
|
|
16702
16753
|
}
|
|
16703
16754
|
checks.push(gitignoreCheck);
|
|
16704
|
-
let driftCheck = buildPluginConfigDriftCheck({ isOrgRepo
|
|
16755
|
+
let driftCheck = buildPluginConfigDriftCheck({ isOrgRepo, installed, surface });
|
|
16705
16756
|
if (!driftCheck.ok && driftCheck.recordsToWrite && repairLocal) {
|
|
16706
16757
|
if (backupAndWriteInstalledPlugins(driftCheck.recordsToWrite, driftCheck.pluginId)) {
|
|
16707
16758
|
driftCheck = { ...driftCheck, ok: true };
|
|
@@ -16711,18 +16762,18 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16711
16762
|
checks.push(driftCheck);
|
|
16712
16763
|
checks.push(
|
|
16713
16764
|
buildPluginResolvabilityCheck({
|
|
16714
|
-
...snapshotPluginGuardInput(surface,
|
|
16765
|
+
...snapshotPluginGuardInput(surface, isOrgRepo),
|
|
16715
16766
|
surface
|
|
16716
16767
|
})
|
|
16717
16768
|
);
|
|
16718
|
-
if (repairFull &&
|
|
16769
|
+
if (repairFull && isOrgRepo && (surfaceToken(surface) === "claude" || surfaceToken(surface) === "codex")) {
|
|
16719
16770
|
const guardResult = ensureUserScopeGuardHook();
|
|
16720
16771
|
if (guardResult === "written") {
|
|
16721
16772
|
io.err(` \u21BB installed user-scope MMI guard hook (${surface === "codex" ? "~/.codex" : "~/.claude"}/settings.json) \u2014 survives a plugin prune`);
|
|
16722
16773
|
}
|
|
16723
16774
|
}
|
|
16724
16775
|
let installedVersionCheck = buildInstalledPluginVersionCheck({
|
|
16725
|
-
isOrgRepo
|
|
16776
|
+
isOrgRepo,
|
|
16726
16777
|
sources: installedPluginSources(),
|
|
16727
16778
|
releasedVersion,
|
|
16728
16779
|
surface
|
|
@@ -16730,7 +16781,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16730
16781
|
if (!installedVersionCheck.ok && (repairFull || repairLocal)) {
|
|
16731
16782
|
const crossSurfaceRepair = Boolean(opts.apply) || !opts.json && !opts.banner && !opts.preflight;
|
|
16732
16783
|
const canDriveSurface = async (token) => surfaceToken(surface) === token || crossSurfaceRepair && await hostBinAvailable(token);
|
|
16733
|
-
const rereadInstalled = () => buildInstalledPluginVersionCheck({ isOrgRepo
|
|
16784
|
+
const rereadInstalled = () => buildInstalledPluginVersionCheck({ isOrgRepo, sources: installedPluginSources(), releasedVersion, surface });
|
|
16734
16785
|
const claudeStale = installedVersionCheck.staleSurfaces?.some((s) => s.surface === "claude") ?? false;
|
|
16735
16786
|
if (claudeStale && await canDriveSurface("claude") && await applyPluginHeal("claude", surface, (m) => io.err(m), { force: true })) {
|
|
16736
16787
|
const healed = rereadInstalled();
|
|
@@ -16755,7 +16806,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16755
16806
|
const inspectOpenCode = surface === "opencode" || openCodeConfigSnapshot.hasConfig || runExtended;
|
|
16756
16807
|
if (inspectOpenCode) {
|
|
16757
16808
|
let opencodeConfigCheck = buildOpencodeConfigPluginCheck({
|
|
16758
|
-
isOrgRepo
|
|
16809
|
+
isOrgRepo,
|
|
16759
16810
|
configPath: openCodeConfigSnapshot.path,
|
|
16760
16811
|
hasConfig: openCodeConfigSnapshot.hasConfig,
|
|
16761
16812
|
hasPluginField: openCodeConfigSnapshot.hasPluginField,
|
|
@@ -16766,7 +16817,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16766
16817
|
if (writeOpencodeConfigPlugin(openCodeConfigSnapshot)) {
|
|
16767
16818
|
openCodeConfigSnapshot = opencodeConfigSnapshot();
|
|
16768
16819
|
opencodeConfigCheck = buildOpencodeConfigPluginCheck({
|
|
16769
|
-
isOrgRepo
|
|
16820
|
+
isOrgRepo,
|
|
16770
16821
|
configPath: openCodeConfigSnapshot.path,
|
|
16771
16822
|
hasConfig: openCodeConfigSnapshot.hasConfig,
|
|
16772
16823
|
hasPluginField: openCodeConfigSnapshot.hasPluginField,
|
|
@@ -16782,7 +16833,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16782
16833
|
checks.push(opencodeConfigCheck);
|
|
16783
16834
|
let opencodeInstalledVersion = opencodeInstalledVersionForDoctor();
|
|
16784
16835
|
let opencodeVersionCheck = buildOpencodeVersionCheck({
|
|
16785
|
-
isOrgRepo
|
|
16836
|
+
isOrgRepo,
|
|
16786
16837
|
installedVersion: opencodeInstalledVersion,
|
|
16787
16838
|
releasedVersion
|
|
16788
16839
|
});
|
|
@@ -16790,7 +16841,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16790
16841
|
if (await forceInstallOpencodeMmiPlugins(openCodeConfigSnapshot, (m) => io.err(m))) {
|
|
16791
16842
|
opencodeInstalledVersion = readOpencodeAdapterDiskVersion() ?? opencodeInstalledVersion;
|
|
16792
16843
|
opencodeVersionCheck = buildOpencodeVersionCheck({
|
|
16793
|
-
isOrgRepo
|
|
16844
|
+
isOrgRepo,
|
|
16794
16845
|
installedVersion: opencodeInstalledVersion,
|
|
16795
16846
|
releasedVersion
|
|
16796
16847
|
});
|
|
@@ -16802,7 +16853,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16802
16853
|
}
|
|
16803
16854
|
checks.push(opencodeVersionCheck);
|
|
16804
16855
|
let surfaceAssetsCheck = buildOpencodeSurfaceAssetsCheck({
|
|
16805
|
-
isOrgRepo
|
|
16856
|
+
isOrgRepo,
|
|
16806
16857
|
configPath: openCodeConfigSnapshot.path,
|
|
16807
16858
|
commandsDir: opencodeCommandsDir(),
|
|
16808
16859
|
existingCommands: opencodeExistingCommands(),
|
|
@@ -16815,7 +16866,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16815
16866
|
if (wroteCommands || wroteSkills) {
|
|
16816
16867
|
openCodeConfigSnapshot = opencodeConfigSnapshot();
|
|
16817
16868
|
surfaceAssetsCheck = buildOpencodeSurfaceAssetsCheck({
|
|
16818
|
-
isOrgRepo
|
|
16869
|
+
isOrgRepo,
|
|
16819
16870
|
configPath: openCodeConfigSnapshot.path,
|
|
16820
16871
|
commandsDir: opencodeCommandsDir(),
|
|
16821
16872
|
existingCommands: opencodeExistingCommands(),
|
|
@@ -16830,13 +16881,13 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16830
16881
|
}
|
|
16831
16882
|
checks.push(surfaceAssetsCheck);
|
|
16832
16883
|
checks.push(buildOpencodeDesktopBootstrapCheck({
|
|
16833
|
-
isOrgRepo
|
|
16884
|
+
isOrgRepo,
|
|
16834
16885
|
surface,
|
|
16835
16886
|
issues: opencodeDesktopBootstrapSnapshot()
|
|
16836
16887
|
}));
|
|
16837
16888
|
}
|
|
16838
16889
|
const legacyOpenCodeConfig = opencodeLegacyConfigSnapshot();
|
|
16839
|
-
if (
|
|
16890
|
+
if (isOrgRepo && legacyOpenCodeConfig.stalePlugins?.length) {
|
|
16840
16891
|
let legacyOpenCodeCheck = buildOpencodeLegacyConfigCheck({
|
|
16841
16892
|
isOrgRepo: true,
|
|
16842
16893
|
legacyPath: legacyOpenCodeConfig.legacyPath,
|
|
@@ -16856,7 +16907,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16856
16907
|
};
|
|
16857
16908
|
const codexCacheVersions = () => mmiPluginCacheRootSnapshots().filter((r) => r.surface === "codex").flatMap((r) => r.entries.filter((e) => e.isDirectory).map((e) => e.name));
|
|
16858
16909
|
let cacheCleanupCheck = buildMmiPluginCacheCleanupCheck({
|
|
16859
|
-
isOrgRepo
|
|
16910
|
+
isOrgRepo,
|
|
16860
16911
|
roots: mmiPluginCacheRootSnapshots(),
|
|
16861
16912
|
activeVersion: resolveClientVersion(),
|
|
16862
16913
|
releasedVersion,
|
|
@@ -16873,7 +16924,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16873
16924
|
}
|
|
16874
16925
|
cacheCleanupCheck = {
|
|
16875
16926
|
...buildMmiPluginCacheCleanupCheck({
|
|
16876
|
-
isOrgRepo
|
|
16927
|
+
isOrgRepo,
|
|
16877
16928
|
roots: mmiPluginCacheRootSnapshots(),
|
|
16878
16929
|
activeVersion: resolveClientVersion(),
|
|
16879
16930
|
releasedVersion,
|
|
@@ -16887,7 +16938,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16887
16938
|
}
|
|
16888
16939
|
checks.push(cacheCleanupCheck);
|
|
16889
16940
|
let codexActiveCacheCheck = buildCodexActiveCacheCheck({
|
|
16890
|
-
isOrgRepo
|
|
16941
|
+
isOrgRepo,
|
|
16891
16942
|
releasedVersion,
|
|
16892
16943
|
codexCacheVersions: codexCacheVersions(),
|
|
16893
16944
|
codexRecordVersion: codexRecordVersion()
|
|
@@ -16898,7 +16949,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16898
16949
|
markPluginReloadRequired();
|
|
16899
16950
|
io.err(` \u21BB restored Codex MMI plugin cache \u2192 ${releasedVersion ?? "latest"} via codex plugin \u2014 ${reloadAction("codex")} to load the new commands`);
|
|
16900
16951
|
codexActiveCacheCheck = buildCodexActiveCacheCheck({
|
|
16901
|
-
isOrgRepo
|
|
16952
|
+
isOrgRepo,
|
|
16902
16953
|
releasedVersion,
|
|
16903
16954
|
codexCacheVersions: codexCacheVersions(),
|
|
16904
16955
|
codexRecordVersion: codexRecordVersion()
|
|
@@ -16907,7 +16958,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16907
16958
|
}
|
|
16908
16959
|
checks.push(codexActiveCacheCheck);
|
|
16909
16960
|
let nestedPluginTreeCheck = buildNestedPluginTreeCheck({
|
|
16910
|
-
isOrgRepo
|
|
16961
|
+
isOrgRepo,
|
|
16911
16962
|
isWindows: isWin,
|
|
16912
16963
|
entries: nestedPluginTreeSnapshot()
|
|
16913
16964
|
});
|
|
@@ -16915,7 +16966,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16915
16966
|
const nestedPaths = nestedPluginTreeCheck.nested.map((n) => n.path);
|
|
16916
16967
|
if (await applyNestedPluginTreeCleanup(nestedPaths, (m) => io.err(m))) {
|
|
16917
16968
|
nestedPluginTreeCheck = buildNestedPluginTreeCheck({
|
|
16918
|
-
isOrgRepo
|
|
16969
|
+
isOrgRepo,
|
|
16919
16970
|
isWindows: isWin,
|
|
16920
16971
|
entries: nestedPluginTreeSnapshot()
|
|
16921
16972
|
});
|
|
@@ -16926,7 +16977,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16926
16977
|
markPluginReloadRequired();
|
|
16927
16978
|
io.err(` \u21BB reinstalled MMI plugin after nested-cache cleanup \u2014 ${reloadAction(surface)} to load MMI commands`);
|
|
16928
16979
|
nestedPluginTreeCheck = buildNestedPluginTreeCheck({
|
|
16929
|
-
isOrgRepo
|
|
16980
|
+
isOrgRepo,
|
|
16930
16981
|
isWindows: isWin,
|
|
16931
16982
|
entries: nestedPluginTreeSnapshot()
|
|
16932
16983
|
});
|
|
@@ -16937,7 +16988,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16937
16988
|
const cursorCacheRoot = cursorPluginCacheRoot();
|
|
16938
16989
|
let cursorPins = cursorPluginCachePinSnapshots() ?? [];
|
|
16939
16990
|
let cursorPluginCheck = buildCursorPluginInstallCheck({
|
|
16940
|
-
isOrgRepo
|
|
16991
|
+
isOrgRepo,
|
|
16941
16992
|
surface,
|
|
16942
16993
|
cacheRoot: cursorCacheRoot,
|
|
16943
16994
|
cacheRootExists: (0, import_node_fs16.existsSync)(cursorCacheRoot),
|
|
@@ -16957,7 +17008,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16957
17008
|
if (seeded) {
|
|
16958
17009
|
cursorPins = cursorPluginCachePinSnapshots() ?? [];
|
|
16959
17010
|
cursorPluginCheck = buildCursorPluginInstallCheck({
|
|
16960
|
-
isOrgRepo
|
|
17011
|
+
isOrgRepo,
|
|
16961
17012
|
surface,
|
|
16962
17013
|
cacheRoot: cursorCacheRoot,
|
|
16963
17014
|
cacheRootExists: (0, import_node_fs16.existsSync)(cursorCacheRoot),
|
|
@@ -16974,14 +17025,14 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16974
17025
|
const cursorThirdPartyEnabled = await readCursorThirdPartyExtensibilityEnabled(execFileP2);
|
|
16975
17026
|
checks.push(
|
|
16976
17027
|
buildCursorThirdPartyExtensibilityCheck({
|
|
16977
|
-
isOrgRepo
|
|
17028
|
+
isOrgRepo,
|
|
16978
17029
|
surface,
|
|
16979
17030
|
enabled: cursorThirdPartyEnabled
|
|
16980
17031
|
})
|
|
16981
17032
|
);
|
|
16982
17033
|
checks.push(
|
|
16983
17034
|
buildCursorHookCliCheck({
|
|
16984
|
-
isOrgRepo
|
|
17035
|
+
isOrgRepo,
|
|
16985
17036
|
surface,
|
|
16986
17037
|
pins: cursorPins,
|
|
16987
17038
|
mmiCliOnPath: onPath
|
|
@@ -16991,19 +17042,19 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
16991
17042
|
const playwrightMcpConfigs = playwrightMcpConfigSnapshots();
|
|
16992
17043
|
checks.push(
|
|
16993
17044
|
buildPlaywrightMcpVisionCapCheck({
|
|
16994
|
-
isOrgRepo
|
|
17045
|
+
isOrgRepo,
|
|
16995
17046
|
configs: playwrightMcpConfigs
|
|
16996
17047
|
})
|
|
16997
17048
|
);
|
|
16998
17049
|
checks.push(
|
|
16999
17050
|
buildPlaywrightMcpOutputDirCheck({
|
|
17000
|
-
isOrgRepo
|
|
17051
|
+
isOrgRepo,
|
|
17001
17052
|
configs: playwrightMcpConfigs
|
|
17002
17053
|
})
|
|
17003
17054
|
);
|
|
17004
17055
|
checks.push(
|
|
17005
17056
|
buildBrowserArtifactsCheck({
|
|
17006
|
-
isOrgRepo
|
|
17057
|
+
isOrgRepo,
|
|
17007
17058
|
strayPaths: strayBrowserArtifactPaths()
|
|
17008
17059
|
})
|
|
17009
17060
|
);
|
|
@@ -17122,12 +17173,12 @@ function ensureUserScopeGuardHook(opts = {}) {
|
|
|
17122
17173
|
return "failed";
|
|
17123
17174
|
}
|
|
17124
17175
|
}
|
|
17125
|
-
async function runGuard(opts = {}) {
|
|
17176
|
+
async function runGuard(opts = {}, readOrigin) {
|
|
17126
17177
|
void opts;
|
|
17127
17178
|
try {
|
|
17128
17179
|
const surface = detectSurface(process.env);
|
|
17129
|
-
const
|
|
17130
|
-
const input = snapshotPluginGuardInput(surface,
|
|
17180
|
+
const isOrgRepo = readOrigin ? await isOrgRepoRoot(readOrigin) : await isOrgRepoRoot();
|
|
17181
|
+
const input = snapshotPluginGuardInput(surface, isOrgRepo);
|
|
17131
17182
|
const { state } = buildPluginGuardDecision(input);
|
|
17132
17183
|
const { line, exitCode } = buildGuardSessionStartLine(state);
|
|
17133
17184
|
if (line) console.error(line);
|
|
@@ -17551,7 +17602,7 @@ function runWorktreeInstall(command, cwd, quiet) {
|
|
|
17551
17602
|
const file = isWin2 ? "cmd.exe" : bin;
|
|
17552
17603
|
const spawnArgs = isWin2 ? ["/c", bin, ...args] : args;
|
|
17553
17604
|
return new Promise((resolve5, reject) => {
|
|
17554
|
-
const child = (0,
|
|
17605
|
+
const child = (0, import_node_child_process11.spawn)(file, spawnArgs, { cwd, stdio: quiet ? "ignore" : "inherit", windowsHide: true });
|
|
17555
17606
|
const timer = setTimeout(() => {
|
|
17556
17607
|
try {
|
|
17557
17608
|
child.kill();
|
|
@@ -17746,7 +17797,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
17746
17797
|
try {
|
|
17747
17798
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body];
|
|
17748
17799
|
if (o.repo) args.push("--repo", o.repo);
|
|
17749
|
-
(0,
|
|
17800
|
+
(0, import_node_child_process11.spawn)(process.execPath, [process.argv[1], ...args], {
|
|
17750
17801
|
detached: true,
|
|
17751
17802
|
stdio: "ignore",
|
|
17752
17803
|
windowsHide: true,
|
|
@@ -18800,7 +18851,7 @@ async function remoteBranchExists2(branch, options = {}) {
|
|
|
18800
18851
|
}
|
|
18801
18852
|
var COMPOSE_TIMEOUT_MS = 12e4;
|
|
18802
18853
|
function spawnDeferredGcSweep() {
|
|
18803
|
-
spawnDetachedSelf(["gc", "sweep-deferred", "--quiet"], { spawn:
|
|
18854
|
+
spawnDetachedSelf(["gc", "sweep-deferred", "--quiet"], { spawn: import_node_child_process11.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
18804
18855
|
}
|
|
18805
18856
|
async function createDeferredWorktreeStore() {
|
|
18806
18857
|
try {
|
|
@@ -19963,7 +20014,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
|
|
|
19963
20014
|
return;
|
|
19964
20015
|
}
|
|
19965
20016
|
if (!await isOrgRepoRoot()) return;
|
|
19966
|
-
spawnDetachedSelf(["docs", "sync", "--quiet"], { spawn:
|
|
20017
|
+
spawnDetachedSelf(["docs", "sync", "--quiet"], { spawn: import_node_child_process11.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
19967
20018
|
spawnDeferredGcSweep();
|
|
19968
20019
|
const { parallel, sequential } = buildSessionStartPlan({
|
|
19969
20020
|
// whoami (#879): surface the resolved human so agents act --for them without asking. Silent
|
|
@@ -19981,7 +20032,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
|
|
|
19981
20032
|
readBoard,
|
|
19982
20033
|
// #1813: warm the slice cache out-of-band (detached, like docs sync) so the ~20s live read
|
|
19983
20034
|
// never costs banner time and next session's glance renders instantly within budget.
|
|
19984
|
-
scheduleRefresh: () => spawnDetachedSelf(["board", "slice-refresh", "--quiet"], { spawn:
|
|
20035
|
+
scheduleRefresh: () => spawnDetachedSelf(["board", "slice-refresh", "--quiet"], { spawn: import_node_child_process11.spawn, execPath: process.execPath, scriptPath: process.argv[1] })
|
|
19985
20036
|
}),
|
|
19986
20037
|
doctor: (io) => runDoctor({ banner: true }, io)
|
|
19987
20038
|
});
|
|
@@ -19989,7 +20040,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
|
|
|
19989
20040
|
for (const line of scratchGcLines(process.cwd())) consoleIo.log(line);
|
|
19990
20041
|
const worktreeBanner = worktreeAutoProvisionBanner(process.cwd());
|
|
19991
20042
|
if (worktreeBanner) {
|
|
19992
|
-
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn:
|
|
20043
|
+
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process11.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
19993
20044
|
consoleIo.log(worktreeBanner);
|
|
19994
20045
|
}
|
|
19995
20046
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutmutco/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.65.0",
|
|
4
4
|
"description": "MMI Future CLI — delivers the org rules (whole-file), Jervaise-only continuity, and KB access. The cross-IDE engine the plugin's SessionStart hook drives.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|