@mutmutco/cli 3.73.0 → 3.74.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 +313 -91
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -3417,7 +3417,7 @@ var program = new Command();
|
|
|
3417
3417
|
// src/index.ts
|
|
3418
3418
|
var import_promises8 = require("node:fs/promises");
|
|
3419
3419
|
var import_node_fs33 = require("node:fs");
|
|
3420
|
-
var
|
|
3420
|
+
var import_node_child_process15 = require("node:child_process");
|
|
3421
3421
|
|
|
3422
3422
|
// src/cli-shared.ts
|
|
3423
3423
|
var import_node_child_process3 = require("node:child_process");
|
|
@@ -13078,8 +13078,8 @@ function selectPrunablePluginVersions(names, currentVersion) {
|
|
|
13078
13078
|
}
|
|
13079
13079
|
return newestFirst.filter((v) => !keep.has(v));
|
|
13080
13080
|
}
|
|
13081
|
-
function
|
|
13082
|
-
return `${
|
|
13081
|
+
function pluginCacheRootForConfig(configRoot) {
|
|
13082
|
+
return `${configRoot}/plugins/cache/mutmutco/mmi`;
|
|
13083
13083
|
}
|
|
13084
13084
|
function runningPluginVersion(env, cliVersion) {
|
|
13085
13085
|
const root = env.CLAUDE_PLUGIN_ROOT?.trim();
|
|
@@ -13087,11 +13087,11 @@ function runningPluginVersion(env, cliVersion) {
|
|
|
13087
13087
|
if (leaf && isVersionDirName(leaf)) return leaf;
|
|
13088
13088
|
return cliVersion && isVersionDirName(cliVersion) ? cliVersion : void 0;
|
|
13089
13089
|
}
|
|
13090
|
-
function
|
|
13091
|
-
return `${
|
|
13090
|
+
function pluginCacheStagingRootForConfig(configRoot) {
|
|
13091
|
+
return `${configRoot}/plugins/cache`;
|
|
13092
13092
|
}
|
|
13093
|
-
function
|
|
13094
|
-
return `${
|
|
13093
|
+
function installedPluginsPathForConfig(configRoot) {
|
|
13094
|
+
return `${configRoot}/plugins/installed_plugins.json`;
|
|
13095
13095
|
}
|
|
13096
13096
|
var STAGING_DIR = /^temp_git_\d{13}_[a-z0-9]+$/i;
|
|
13097
13097
|
function isStagingDirName(name) {
|
|
@@ -13163,13 +13163,17 @@ function humanStagingAge(ms) {
|
|
|
13163
13163
|
return `${Math.floor(hours / 24)}d old`;
|
|
13164
13164
|
}
|
|
13165
13165
|
function buildPluginCachePlan(home, running, deps, opts = {}) {
|
|
13166
|
-
const
|
|
13167
|
-
const
|
|
13166
|
+
const configRoot = opts.configRoot ?? `${home}/.claude`;
|
|
13167
|
+
const cacheRoot = pluginCacheRootForConfig(configRoot);
|
|
13168
|
+
const stagingRoot = pluginCacheStagingRootForConfig(configRoot);
|
|
13168
13169
|
let stagingEntries;
|
|
13169
|
-
|
|
13170
|
-
|
|
13171
|
-
|
|
13172
|
-
|
|
13170
|
+
if (opts.includeStaging === false) stagingEntries = [];
|
|
13171
|
+
else {
|
|
13172
|
+
try {
|
|
13173
|
+
stagingEntries = deps.listStagingDirs(stagingRoot);
|
|
13174
|
+
} catch {
|
|
13175
|
+
stagingEntries = [];
|
|
13176
|
+
}
|
|
13173
13177
|
}
|
|
13174
13178
|
let referenced;
|
|
13175
13179
|
try {
|
|
@@ -16579,8 +16583,10 @@ function runDocRefs(root, deps = {}) {
|
|
|
16579
16583
|
const listDocs = deps.listDocs ?? defaultListDocs;
|
|
16580
16584
|
const isIgnored = deps.isIgnored ?? ((paths) => defaultIsIgnored(root, paths));
|
|
16581
16585
|
const commandPaths = deps.commandPaths ?? null;
|
|
16586
|
+
const walked = listDocs(root);
|
|
16587
|
+
const ignoredDocs = walked.length ? isIgnored(walked) : /* @__PURE__ */ new Set();
|
|
16582
16588
|
const docs2 = Object.fromEntries(
|
|
16583
|
-
|
|
16589
|
+
walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile7((0, import_node_path16.join)(root, rel))]).filter(([, body]) => body != null)
|
|
16584
16590
|
);
|
|
16585
16591
|
const findings = [
|
|
16586
16592
|
...checkPins(root, readFile7, docs2).findings,
|
|
@@ -21605,7 +21611,7 @@ var import_node_fs27 = require("node:fs");
|
|
|
21605
21611
|
var import_promises6 = require("node:fs/promises");
|
|
21606
21612
|
var import_node_path26 = require("node:path");
|
|
21607
21613
|
var import_node_os8 = require("node:os");
|
|
21608
|
-
var
|
|
21614
|
+
var import_node_child_process13 = require("node:child_process");
|
|
21609
21615
|
|
|
21610
21616
|
// src/board-advance.ts
|
|
21611
21617
|
function repoOf2(ref) {
|
|
@@ -21772,6 +21778,7 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
|
|
|
21772
21778
|
|
|
21773
21779
|
// src/plugin-guard-io.ts
|
|
21774
21780
|
var import_node_fs25 = require("node:fs");
|
|
21781
|
+
var import_node_child_process12 = require("node:child_process");
|
|
21775
21782
|
var import_node_path24 = require("node:path");
|
|
21776
21783
|
var import_node_os6 = require("node:os");
|
|
21777
21784
|
|
|
@@ -21797,9 +21804,10 @@ function buildPluginGuardLine(state, opts = {}) {
|
|
|
21797
21804
|
var isWin = process.platform === "win32";
|
|
21798
21805
|
var MMI_PLUGIN_ID = "mmi@mutmutco";
|
|
21799
21806
|
var LEGACY_MMI_MARKETPLACE = "mmi";
|
|
21807
|
+
var CODEX_MARKETPLACE = "mutmutco";
|
|
21800
21808
|
function detectSurface(env) {
|
|
21801
21809
|
const has = (k) => Boolean(env[k]?.trim());
|
|
21802
|
-
if (env.MMI_AGENT_SURFACE === "codex" || has("
|
|
21810
|
+
if (env.MMI_AGENT_SURFACE === "codex" || has("CODEX_THREAD_ID") || has("CODEX_MANAGED_BY_NPM") || has("CODEX_MANAGED_PACKAGE_ROOT") || (env.CLAUDE_PLUGIN_ROOT ?? "").includes(".codex")) {
|
|
21803
21811
|
return "codex";
|
|
21804
21812
|
}
|
|
21805
21813
|
if (env.MMI_AGENT_SURFACE === "kimi" || has("KIMI_PLUGIN_ROOT") || has("KIMI_CODE_HOME")) {
|
|
@@ -21852,6 +21860,7 @@ function reloadAction(surface) {
|
|
|
21852
21860
|
}
|
|
21853
21861
|
}
|
|
21854
21862
|
var CLAUDE_RECOVERY = `claude plugin marketplace remove ${LEGACY_MMI_MARKETPLACE} && claude plugin marketplace remove mutmutco && claude plugin marketplace add mutmutco/MMI-Hub --ref main && claude plugin install mmi@mutmutco`;
|
|
21863
|
+
var CODEX_RECOVERY = "codex plugin remove mmi@mutmutco && codex plugin marketplace remove mutmutco && codex plugin marketplace add mutmutco/MMI-Hub --ref main && codex plugin add mmi@mutmutco";
|
|
21855
21864
|
var PLUGIN_SURFACE_HEAL = {
|
|
21856
21865
|
claude: {
|
|
21857
21866
|
delivery: "plugin-cli",
|
|
@@ -21865,6 +21874,18 @@ var PLUGIN_SURFACE_HEAL = {
|
|
|
21865
21874
|
],
|
|
21866
21875
|
fix: (surface) => `${CLAUDE_RECOVERY} # then ${reloadAction(surface)} to reload MMI commands`,
|
|
21867
21876
|
updateRecipe: [CLAUDE_RECOVERY]
|
|
21877
|
+
},
|
|
21878
|
+
codex: {
|
|
21879
|
+
delivery: "plugin-cli",
|
|
21880
|
+
recovery: CODEX_RECOVERY,
|
|
21881
|
+
healSteps: [
|
|
21882
|
+
{ args: ["plugin", "remove", MMI_PLUGIN_ID], gated: false },
|
|
21883
|
+
{ args: ["plugin", "marketplace", "remove", CODEX_MARKETPLACE], gated: false },
|
|
21884
|
+
{ args: ["plugin", "marketplace", "add", "mutmutco/MMI-Hub", "--ref", "main"], gated: true },
|
|
21885
|
+
{ args: ["plugin", "add", MMI_PLUGIN_ID], gated: true }
|
|
21886
|
+
],
|
|
21887
|
+
fix: (surface) => `${CODEX_RECOVERY} # then ${reloadAction(surface)} and review /hooks`,
|
|
21888
|
+
updateRecipe: [CODEX_RECOVERY]
|
|
21868
21889
|
}
|
|
21869
21890
|
};
|
|
21870
21891
|
function nonClaudeSurfaceHealMessage(surface) {
|
|
@@ -21913,33 +21934,97 @@ var NPM_VIEW_TIMEOUT_MS = 15e3;
|
|
|
21913
21934
|
function runHostBin(bin, args, opts) {
|
|
21914
21935
|
return isWin ? execFileP2("cmd.exe", ["/c", bin, ...args], opts) : execFileP2(bin, args, opts);
|
|
21915
21936
|
}
|
|
21916
|
-
function
|
|
21917
|
-
if (surface === "codex") return ".codex";
|
|
21918
|
-
if (surface === "kimi") return ".kimi-code";
|
|
21919
|
-
return ".claude";
|
|
21937
|
+
function surfaceConfigRoot(surface, env = process.env, home = (0, import_node_os6.homedir)()) {
|
|
21938
|
+
if (surface === "codex") return env.CODEX_HOME?.trim() || (0, import_node_path24.join)(home, ".codex");
|
|
21939
|
+
if (surface === "kimi") return env.KIMI_CODE_HOME?.trim() || (0, import_node_path24.join)(home, ".kimi-code");
|
|
21940
|
+
return (0, import_node_path24.join)(home, ".claude");
|
|
21920
21941
|
}
|
|
21921
|
-
var
|
|
21922
|
-
return (0, import_node_path24.join)((
|
|
21942
|
+
var installedPluginsPath = (surface = detectSurface(process.env)) => {
|
|
21943
|
+
return (0, import_node_path24.join)(surfaceConfigRoot(surface), "plugins", "installed_plugins.json");
|
|
21923
21944
|
};
|
|
21924
21945
|
function readInstalledPlugins(surface = detectSurface(process.env)) {
|
|
21925
21946
|
try {
|
|
21926
|
-
return JSON.parse((0, import_node_fs25.readFileSync)(
|
|
21947
|
+
return JSON.parse((0, import_node_fs25.readFileSync)(installedPluginsPath(surface), "utf8"));
|
|
21927
21948
|
} catch {
|
|
21928
21949
|
return null;
|
|
21929
21950
|
}
|
|
21930
21951
|
}
|
|
21931
|
-
function marketplaceCloneCandidates(surface, home) {
|
|
21952
|
+
function marketplaceCloneCandidates(surface, home, env = process.env) {
|
|
21932
21953
|
if (surface === "codex") {
|
|
21954
|
+
const root = surfaceConfigRoot(surface, env, home);
|
|
21933
21955
|
return [
|
|
21934
|
-
(0, import_node_path24.join)(
|
|
21935
|
-
(0, import_node_path24.join)(
|
|
21956
|
+
(0, import_node_path24.join)(root, ".tmp", "marketplaces", CODEX_MARKETPLACE),
|
|
21957
|
+
(0, import_node_path24.join)(root, "plugins", "marketplaces", CODEX_MARKETPLACE)
|
|
21936
21958
|
];
|
|
21937
21959
|
}
|
|
21938
21960
|
if (surface === "kimi") return [];
|
|
21939
21961
|
return [(0, import_node_path24.join)(home, ".claude", "plugins", "marketplaces", "mutmutco")];
|
|
21940
21962
|
}
|
|
21941
|
-
function marketplaceClonePresent(surface, home, exists = import_node_fs25.existsSync) {
|
|
21942
|
-
return marketplaceCloneCandidates(surface, home).some(exists);
|
|
21963
|
+
function marketplaceClonePresent(surface, home, exists = import_node_fs25.existsSync, env = process.env) {
|
|
21964
|
+
return marketplaceCloneCandidates(surface, home, env).some(exists);
|
|
21965
|
+
}
|
|
21966
|
+
function runHostBinSync(bin, args) {
|
|
21967
|
+
return isWin ? (0, import_node_child_process12.execFileSync)("cmd.exe", ["/c", bin, ...args], {
|
|
21968
|
+
encoding: "utf8",
|
|
21969
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
21970
|
+
timeout: 15e3
|
|
21971
|
+
}) : (0, import_node_child_process12.execFileSync)(bin, args, {
|
|
21972
|
+
encoding: "utf8",
|
|
21973
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
21974
|
+
timeout: 15e3
|
|
21975
|
+
});
|
|
21976
|
+
}
|
|
21977
|
+
function codexPluginStatus() {
|
|
21978
|
+
try {
|
|
21979
|
+
const raw = runHostBinSync("codex", ["plugin", "list", "--json"]);
|
|
21980
|
+
const parsed = JSON.parse(raw);
|
|
21981
|
+
const plugin = parsed.installed?.find((entry) => entry.pluginId === MMI_PLUGIN_ID);
|
|
21982
|
+
return {
|
|
21983
|
+
installed: plugin?.installed === true,
|
|
21984
|
+
enabled: plugin?.enabled === true,
|
|
21985
|
+
...plugin?.version ? { version: plugin.version } : {}
|
|
21986
|
+
};
|
|
21987
|
+
} catch {
|
|
21988
|
+
return { installed: false, enabled: false };
|
|
21989
|
+
}
|
|
21990
|
+
}
|
|
21991
|
+
function countCodexHookCommands(path2) {
|
|
21992
|
+
try {
|
|
21993
|
+
const parsed = JSON.parse((0, import_node_fs25.readFileSync)(path2, "utf8"));
|
|
21994
|
+
let count = 0;
|
|
21995
|
+
for (const groups of Object.values(parsed.hooks ?? {})) {
|
|
21996
|
+
for (const group of groups) {
|
|
21997
|
+
count += (group.hooks ?? []).filter((hook) => hook.type === "command").length;
|
|
21998
|
+
}
|
|
21999
|
+
}
|
|
22000
|
+
return count;
|
|
22001
|
+
} catch {
|
|
22002
|
+
return 0;
|
|
22003
|
+
}
|
|
22004
|
+
}
|
|
22005
|
+
function codexHookTrustState(status = codexPluginStatus()) {
|
|
22006
|
+
if (!status.installed || !status.version) {
|
|
22007
|
+
return { applicable: false, trusted: false, trustedCount: 0, requiredCount: 0 };
|
|
22008
|
+
}
|
|
22009
|
+
const root = surfaceConfigRoot("codex");
|
|
22010
|
+
const hooksPath = (0, import_node_path24.join)(root, "plugins", "cache", CODEX_MARKETPLACE, "mmi", status.version, "hooks", "codex-hooks.json");
|
|
22011
|
+
const requiredCount = countCodexHookCommands(hooksPath);
|
|
22012
|
+
let config = "";
|
|
22013
|
+
try {
|
|
22014
|
+
config = (0, import_node_fs25.readFileSync)((0, import_node_path24.join)(root, "config.toml"), "utf8");
|
|
22015
|
+
} catch {
|
|
22016
|
+
return { applicable: true, trusted: false, trustedCount: 0, requiredCount };
|
|
22017
|
+
}
|
|
22018
|
+
const trustedCount = (config.match(/\[hooks\.state\."mmi@mutmutco:hooks\/codex-hooks\.json:[^"]+"\]/g) ?? []).length;
|
|
22019
|
+
return {
|
|
22020
|
+
applicable: true,
|
|
22021
|
+
// Codex stores a hash beside each approval row and intentionally leaves stale rows behind. The hash
|
|
22022
|
+
// input is not a public host contract, so row count can prove only that approvals EXISTED — never that
|
|
22023
|
+
// the current commands are trusted. `/hooks` is the only honest freshness check.
|
|
22024
|
+
trusted: false,
|
|
22025
|
+
trustedCount,
|
|
22026
|
+
requiredCount
|
|
22027
|
+
};
|
|
21943
22028
|
}
|
|
21944
22029
|
async function fetchNpmReleasedVersion() {
|
|
21945
22030
|
try {
|
|
@@ -21960,17 +22045,20 @@ async function npmSelfUpdateCli(target) {
|
|
|
21960
22045
|
}
|
|
21961
22046
|
}
|
|
21962
22047
|
function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRepo = false) {
|
|
21963
|
-
const
|
|
22048
|
+
const root = surfaceConfigRoot(surface);
|
|
21964
22049
|
const installed = readInstalledPlugins(surface);
|
|
22050
|
+
const codexStatus = surface === "codex" ? codexPluginStatus() : void 0;
|
|
21965
22051
|
return {
|
|
21966
22052
|
isOrgRepo,
|
|
21967
22053
|
installRecordPresent: hasUserInstallRecord(installed, MMI_PLUGIN_ID) || hasProjectInstallRecord(installed, MMI_PLUGIN_ID, process.cwd()),
|
|
21968
22054
|
marketplaceClonePresent: marketplaceClonePresent(surface, (0, import_node_os6.homedir)()),
|
|
21969
22055
|
// Kimi keeps no plugin cache dir — installs are copied to plugins/managed/<id> and run from there.
|
|
21970
|
-
pluginCachePresent: surface === "kimi" ? (0, import_node_fs25.existsSync)((0, import_node_path24.join)(
|
|
22056
|
+
pluginCachePresent: surface === "kimi" ? (0, import_node_fs25.existsSync)((0, import_node_path24.join)(root, "plugins", "managed", "mmi")) : surface === "codex" ? Boolean(
|
|
22057
|
+
codexStatus?.installed && codexStatus.enabled && codexStatus.version && (0, import_node_fs25.existsSync)((0, import_node_path24.join)(root, "plugins", "cache", CODEX_MARKETPLACE, "mmi", codexStatus.version))
|
|
22058
|
+
) : (0, import_node_fs25.existsSync)((0, import_node_path24.join)(root, "plugins", "cache", "mutmutco", "mmi"))
|
|
21971
22059
|
};
|
|
21972
22060
|
}
|
|
21973
|
-
function
|
|
22061
|
+
function activePluginGuardState(isOrgRepo) {
|
|
21974
22062
|
return buildPluginGuardDecision(snapshotPluginGuardInput(detectSurface(process.env), isOrgRepo)).state;
|
|
21975
22063
|
}
|
|
21976
22064
|
async function runClaudePlugin(args) {
|
|
@@ -21981,6 +22069,14 @@ async function runClaudePlugin(args) {
|
|
|
21981
22069
|
return false;
|
|
21982
22070
|
}
|
|
21983
22071
|
}
|
|
22072
|
+
async function runCodexPlugin(args) {
|
|
22073
|
+
try {
|
|
22074
|
+
await runHostBin("codex", args, { timeout: CLAUDE_PLUGIN_TIMEOUT_MS });
|
|
22075
|
+
return true;
|
|
22076
|
+
} catch {
|
|
22077
|
+
return false;
|
|
22078
|
+
}
|
|
22079
|
+
}
|
|
21984
22080
|
async function marketplaceAddRefSupported(bin) {
|
|
21985
22081
|
try {
|
|
21986
22082
|
const { stdout, stderr } = await runHostBin(bin, ["plugin", "marketplace", "add", "--help"], {
|
|
@@ -22007,18 +22103,23 @@ function pluginReadGrantNote(login = "<your-github-login>") {
|
|
|
22007
22103
|
gh api -X PUT repos/${PLUGIN_READ_REPO2}/collaborators/${login} -f permission=pull`;
|
|
22008
22104
|
}
|
|
22009
22105
|
async function applyPluginHeal(surface, log, opts) {
|
|
22010
|
-
|
|
22011
|
-
|
|
22106
|
+
const token = surfaceToken(surface);
|
|
22107
|
+
if (token !== "claude" && token !== "codex") return false;
|
|
22108
|
+
if (!opts?.force && !PLUGIN_SURFACE_HEAL[token]) return false;
|
|
22109
|
+
const descriptor = PLUGIN_SURFACE_HEAL[token];
|
|
22110
|
+
const tableSteps = descriptor.healSteps;
|
|
22012
22111
|
if (!tableSteps) return false;
|
|
22013
|
-
const
|
|
22112
|
+
const bin = token;
|
|
22113
|
+
const refSupported = await marketplaceAddRefSupported(bin);
|
|
22014
22114
|
const { steps } = adaptHealStepsForRefSupport(tableSteps, refSupported);
|
|
22015
|
-
log(healBannerLine(
|
|
22115
|
+
log(healBannerLine(bin, token, refSupported));
|
|
22016
22116
|
const pinsPath = (0, import_node_path24.join)((0, import_node_os6.homedir)(), ...KNOWN_MARKETPLACES_RELATIVE);
|
|
22017
|
-
const pins = captureMarketplacePins(readKnownMarketplacesFile(pinsPath), [MMI_MARKETPLACE_NAME, JERV_MARKETPLACE_NAME]);
|
|
22117
|
+
const pins = token === "claude" ? captureMarketplacePins(readKnownMarketplacesFile(pinsPath), [MMI_MARKETPLACE_NAME, JERV_MARKETPLACE_NAME]) : /* @__PURE__ */ new Map();
|
|
22018
22118
|
for (const step of steps) {
|
|
22019
|
-
|
|
22119
|
+
const ok = token === "claude" ? await runClaudePlugin([...step.args]) : await runCodexPlugin([...step.args]);
|
|
22120
|
+
if (healStepAborts(step, ok)) return false;
|
|
22020
22121
|
}
|
|
22021
|
-
const restored = restoreMarketplacePinsOnDisk(pinsPath, pins);
|
|
22122
|
+
const restored = token === "claude" ? restoreMarketplacePinsOnDisk(pinsPath, pins) : void 0;
|
|
22022
22123
|
if (restored) log(` ${restored}`);
|
|
22023
22124
|
return true;
|
|
22024
22125
|
}
|
|
@@ -22034,6 +22135,23 @@ async function healClaudePluginForDoctor(surface = detectSurface(process.env)) {
|
|
|
22034
22135
|
detail: ok ? `marketplace remove \u2192 add \u2192 install succeeded${pinNote ? `; ${pinNote}` : ""}` : `\`claude plugin\` reinstall failed or was skipped${steps.length ? ` (${steps[steps.length - 1]})` : ""}`
|
|
22035
22136
|
};
|
|
22036
22137
|
}
|
|
22138
|
+
async function healActivePluginForDoctor(surface = detectSurface(process.env)) {
|
|
22139
|
+
const token = surfaceToken(surface);
|
|
22140
|
+
if (token !== "claude" && token !== "codex") {
|
|
22141
|
+
return { ok: false, detail: `not a supported plugin surface (${surface})` };
|
|
22142
|
+
}
|
|
22143
|
+
if (token === "claude") return healClaudePluginForDoctor(surface);
|
|
22144
|
+
const steps = [];
|
|
22145
|
+
const applied = await applyPluginHeal(surface, (msg) => steps.push(msg.trim()));
|
|
22146
|
+
const snapshot = applied ? snapshotPluginGuardInput(surface, true) : void 0;
|
|
22147
|
+
const guardState = snapshot ? buildPluginGuardDecision(snapshot).state : "unresolved";
|
|
22148
|
+
const ok = applied && guardState === "healthy";
|
|
22149
|
+
const verification = snapshot ? `record=${snapshot.installRecordPresent ? "yes" : "no"}, marketplace=${snapshot.marketplaceClonePresent ? "yes" : "no"}, enabled-cache=${snapshot.pluginCachePresent ? "yes" : "no"}` : "reinstall steps did not complete";
|
|
22150
|
+
return {
|
|
22151
|
+
ok,
|
|
22152
|
+
detail: ok ? `marketplace remove \u2192 add --ref main \u2192 plugin add succeeded; full guard verified (${verification})` : `\`codex plugin\` reinstall failed full-guard verification (${verification})${steps.length ? `; ${steps[steps.length - 1]}` : ""}`
|
|
22153
|
+
};
|
|
22154
|
+
}
|
|
22037
22155
|
function readKnownMarketplacesFile(path2) {
|
|
22038
22156
|
try {
|
|
22039
22157
|
return (0, import_node_fs25.existsSync)(path2) ? (0, import_node_fs25.readFileSync)(path2, "utf8") : void 0;
|
|
@@ -22059,9 +22177,10 @@ function restoreMarketplacePinsOnDisk(path2, pins) {
|
|
|
22059
22177
|
return failed.length ? `restore did NOT take for ${failed.map(([n]) => n).join(", ")} \u2014 re-pin by hand` : `re-pinned ${[...pins.keys()].join(", ")} (auto-update + catalog ref survive the reinstall)`;
|
|
22060
22178
|
}
|
|
22061
22179
|
async function runGuard(readOrigin) {
|
|
22180
|
+
const surface = detectSurface(process.env);
|
|
22062
22181
|
try {
|
|
22063
|
-
const
|
|
22064
|
-
if (
|
|
22182
|
+
const token = surfaceToken(surface);
|
|
22183
|
+
if (token !== "claude" && token !== "codex") {
|
|
22065
22184
|
process.exitCode = 0;
|
|
22066
22185
|
return;
|
|
22067
22186
|
}
|
|
@@ -22070,24 +22189,39 @@ async function runGuard(readOrigin) {
|
|
|
22070
22189
|
const { state } = buildPluginGuardDecision(input);
|
|
22071
22190
|
const { line, exitCode } = buildPluginGuardLine(state);
|
|
22072
22191
|
if (line) console.error(line);
|
|
22192
|
+
if (token === "codex" && exitCode === 0) {
|
|
22193
|
+
const trust = codexHookTrustState();
|
|
22194
|
+
if (trust.applicable && !trust.trusted) {
|
|
22195
|
+
console.error(`[mmi-guard] MMI Codex hook trust cannot be verified non-interactively (${trust.trustedCount}/${trust.requiredCount} approval rows present); run /hooks and review the current hashes.`);
|
|
22196
|
+
}
|
|
22197
|
+
}
|
|
22073
22198
|
process.exitCode = exitCode;
|
|
22074
22199
|
} catch {
|
|
22075
|
-
|
|
22200
|
+
if (surfaceToken(surface) === "codex") {
|
|
22201
|
+
console.error("[mmi-guard] Could not inspect the active Codex plugin; run `mmi-cli plugin heal`.");
|
|
22202
|
+
process.exitCode = 1;
|
|
22203
|
+
} else {
|
|
22204
|
+
process.exitCode = 0;
|
|
22205
|
+
}
|
|
22076
22206
|
}
|
|
22077
22207
|
}
|
|
22078
22208
|
async function runPluginHeal(surface = detectSurface(process.env)) {
|
|
22079
|
-
|
|
22080
|
-
|
|
22209
|
+
const token = surfaceToken(surface);
|
|
22210
|
+
if (token !== "claude" && token !== "codex") {
|
|
22211
|
+
console.log(nonClaudeSurfaceHealMessage(token ?? void 0));
|
|
22081
22212
|
return;
|
|
22082
22213
|
}
|
|
22083
|
-
const descriptor = PLUGIN_SURFACE_HEAL
|
|
22084
|
-
const
|
|
22214
|
+
const descriptor = PLUGIN_SURFACE_HEAL[token];
|
|
22215
|
+
const applied = await applyPluginHeal(surface, console.log, { force: true });
|
|
22216
|
+
const healed = token === "codex" ? applied && buildPluginGuardDecision(snapshotPluginGuardInput(surface, true)).state === "healthy" : applied;
|
|
22085
22217
|
if (healed) {
|
|
22086
|
-
|
|
22218
|
+
const trust = token === "codex" ? " Then run /hooks and review + trust the MMI hooks." : "";
|
|
22219
|
+
console.log(` \u2713 MMI plugin reinstalled \u2014 ${reloadAction(surface)} to load MMI commands.${trust}`);
|
|
22087
22220
|
} else {
|
|
22088
|
-
|
|
22221
|
+
process.exitCode = 1;
|
|
22222
|
+
const refSupported = await marketplaceAddRefSupported(token);
|
|
22089
22223
|
const recovery = refSupported ? descriptor.recovery : recoveryWithoutRef(descriptor.recovery);
|
|
22090
|
-
const note = refAbsenceNote(
|
|
22224
|
+
const note = refAbsenceNote(token, refSupported);
|
|
22091
22225
|
console.log(` \u2717 Auto-heal failed or was skipped. Run manually:
|
|
22092
22226
|
${recovery}${note}${pluginReadGrantNote()}`);
|
|
22093
22227
|
}
|
|
@@ -22649,7 +22783,7 @@ async function remoteBranchExists2(branch, options = {}) {
|
|
|
22649
22783
|
}
|
|
22650
22784
|
var COMPOSE_TIMEOUT_MS = 12e4;
|
|
22651
22785
|
function spawnDeferredGcSweep() {
|
|
22652
|
-
spawnDetachedSelf(["worktree", "gc", "sweep-deferred", "--quiet"], { spawn:
|
|
22786
|
+
spawnDetachedSelf(["worktree", "gc", "sweep-deferred", "--quiet"], { spawn: import_node_child_process13.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
22653
22787
|
}
|
|
22654
22788
|
async function createDeferredWorktreeStore() {
|
|
22655
22789
|
try {
|
|
@@ -23606,7 +23740,7 @@ ${spec.body ?? ""}`;
|
|
|
23606
23740
|
const hash = (0, import_node_crypto5.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
|
|
23607
23741
|
return `${batchKey}:${hash}`;
|
|
23608
23742
|
}
|
|
23609
|
-
var BATCH_SPEC_KEYS = /* @__PURE__ */ new Set(["type", "title", "body", "priority", "labels", "parent", "repo"]);
|
|
23743
|
+
var BATCH_SPEC_KEYS = /* @__PURE__ */ new Set(["type", "title", "body", "priority", "labels", "label", "parent", "repo"]);
|
|
23610
23744
|
function validateBatchSpecs(specs) {
|
|
23611
23745
|
const errors = [];
|
|
23612
23746
|
const validated = [];
|
|
@@ -23623,6 +23757,11 @@ function validateBatchSpecs(specs) {
|
|
|
23623
23757
|
errors.push({ row, error: `unknown key(s): ${unknown.join(", ")} \u2014 expected only: ${[...BATCH_SPEC_KEYS].join(", ")}` });
|
|
23624
23758
|
continue;
|
|
23625
23759
|
}
|
|
23760
|
+
if (spec.label !== void 0) {
|
|
23761
|
+
const alias = Array.isArray(spec.label) ? spec.label : [spec.label];
|
|
23762
|
+
spec.labels = [...spec.labels ?? [], ...alias];
|
|
23763
|
+
delete spec.label;
|
|
23764
|
+
}
|
|
23626
23765
|
if (spec.repo !== void 0 && !/^[\w.-]+\/[\w.-]+$/.test(spec.repo)) {
|
|
23627
23766
|
errors.push({ row, error: `bad repo "${spec.repo}" \u2014 expected owner/repo` });
|
|
23628
23767
|
continue;
|
|
@@ -25197,6 +25336,10 @@ function pluginHealTrigger(probe) {
|
|
|
25197
25336
|
}
|
|
25198
25337
|
function checkClaudePlugin(probe) {
|
|
25199
25338
|
const { installed, released, guardState } = probe;
|
|
25339
|
+
const codex = probe.surface === "codex";
|
|
25340
|
+
const id = codex ? "codex-plugin" : "claude-plugin";
|
|
25341
|
+
const label = codex ? "Codex plugin" : "Claude plugin";
|
|
25342
|
+
const restart = codex ? "restart Codex" : "restart Claude";
|
|
25200
25343
|
const evidence = [
|
|
25201
25344
|
`installed: ${installed ?? "(none)"}`,
|
|
25202
25345
|
// #3485 item 7: when the value was reused from the banner's once-a-day cache, say so and say how old.
|
|
@@ -25206,38 +25349,57 @@ function checkClaudePlugin(probe) {
|
|
|
25206
25349
|
const trigger = pluginHealTrigger(probe);
|
|
25207
25350
|
if (trigger === "unresolved") {
|
|
25208
25351
|
return {
|
|
25209
|
-
id
|
|
25352
|
+
id,
|
|
25210
25353
|
ok: false,
|
|
25211
|
-
label
|
|
25354
|
+
label,
|
|
25212
25355
|
detail: guardState === "no-install" ? "not installed" : "unresolved (marketplace/cache missing)",
|
|
25213
|
-
fix:
|
|
25356
|
+
fix: `run \`mmi-cli doctor --apply\` (or \`mmi-cli plugin heal\`) to reinstall the MMI marketplace + plugin, then ${restart}`,
|
|
25214
25357
|
verbose: evidence
|
|
25215
25358
|
};
|
|
25216
25359
|
}
|
|
25217
25360
|
if (trigger === "behind") {
|
|
25218
25361
|
return {
|
|
25219
|
-
id
|
|
25362
|
+
id,
|
|
25220
25363
|
ok: false,
|
|
25221
|
-
label
|
|
25364
|
+
label,
|
|
25222
25365
|
detail: `${installed} \u2192 ${released}`,
|
|
25223
25366
|
// Name the CLI verb that actually fixes it, like every other red row — `/plugin` is the manual
|
|
25224
25367
|
// fallback, not the first resort (#3282).
|
|
25225
|
-
fix:
|
|
25368
|
+
fix: `run \`mmi-cli doctor --apply\` (or \`mmi-cli plugin heal\`) to reinstall it, then ${restart}`,
|
|
25226
25369
|
verbose: evidence
|
|
25227
25370
|
};
|
|
25228
25371
|
}
|
|
25229
25372
|
if (!released) {
|
|
25230
25373
|
return {
|
|
25231
|
-
id
|
|
25374
|
+
id,
|
|
25232
25375
|
ok: false,
|
|
25233
25376
|
reportOnly: true,
|
|
25234
|
-
label
|
|
25377
|
+
label,
|
|
25235
25378
|
detail: `${installed ?? "installed"} \u2014 freshness UNKNOWN, the published version could not be read`,
|
|
25236
25379
|
fix: "check it directly: `npm view @mutmutco/cli version`, then `mmi-cli plugin heal` if behind",
|
|
25237
25380
|
verbose: evidence
|
|
25238
25381
|
};
|
|
25239
25382
|
}
|
|
25240
|
-
return { id
|
|
25383
|
+
return { id, ok: true, label, ...installed ? { detail: installed } : {}, verbose: evidence };
|
|
25384
|
+
}
|
|
25385
|
+
function checkCodexHookTrust(probe) {
|
|
25386
|
+
if (!probe?.applicable) return null;
|
|
25387
|
+
const evidence = [
|
|
25388
|
+
`stored approval rows: ${probe.trustedCount}/${probe.requiredCount}`,
|
|
25389
|
+
"current command hashes: verify interactively in /hooks"
|
|
25390
|
+
];
|
|
25391
|
+
if (probe.trusted) {
|
|
25392
|
+
return { id: "codex-hook-trust", ok: true, label: "Codex hook trust", detail: "trusted", verbose: evidence };
|
|
25393
|
+
}
|
|
25394
|
+
return {
|
|
25395
|
+
id: "codex-hook-trust",
|
|
25396
|
+
ok: true,
|
|
25397
|
+
warn: true,
|
|
25398
|
+
label: "Codex hook trust",
|
|
25399
|
+
detail: probe.requiredCount > 0 ? `${probe.trustedCount}/${probe.requiredCount} approval rows present \u2014 current hashes unverified` : "hook bundle could not be verified",
|
|
25400
|
+
fix: probe.requiredCount > 0 ? "Codex does not allow silent hook approval; run `/hooks`, review the MMI commands, and trust them" : "run `mmi-cli plugin heal`, restart Codex, then review and trust MMI under `/hooks`",
|
|
25401
|
+
verbose: evidence
|
|
25402
|
+
};
|
|
25241
25403
|
}
|
|
25242
25404
|
function checkCliVersion(input, releasedNote) {
|
|
25243
25405
|
const report = buildVersionLagReport(input);
|
|
@@ -25475,6 +25637,8 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
25475
25637
|
]);
|
|
25476
25638
|
const ghInstalled2 = login ? true : await deps.ghInstalled();
|
|
25477
25639
|
const installed = deps.installedPluginVersion();
|
|
25640
|
+
const pluginSurface = deps.pluginSurface?.() ?? "claude-cli";
|
|
25641
|
+
const codexSurface = pluginSurface === "codex";
|
|
25478
25642
|
const checks = [];
|
|
25479
25643
|
let restartPending = false;
|
|
25480
25644
|
checks.push(checkGithubAuth({ login, ghInstalled: ghInstalled2, reach }));
|
|
@@ -25501,10 +25665,18 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
25501
25665
|
}
|
|
25502
25666
|
}
|
|
25503
25667
|
const releasedNote = deps.releasedVersionNote?.();
|
|
25504
|
-
const pluginProbe = {
|
|
25668
|
+
const pluginProbe = {
|
|
25669
|
+
installed,
|
|
25670
|
+
released,
|
|
25671
|
+
guardState: deps.pluginGuardState(isOrgRepo),
|
|
25672
|
+
releasedNote,
|
|
25673
|
+
surface: pluginSurface
|
|
25674
|
+
};
|
|
25505
25675
|
const healTrigger = pluginHealTrigger(pluginProbe);
|
|
25676
|
+
let pluginHealed = false;
|
|
25506
25677
|
if (applyEnv && deps.healPlugin && healTrigger) {
|
|
25507
25678
|
const heal = await deps.healPlugin();
|
|
25679
|
+
pluginHealed = heal.ok;
|
|
25508
25680
|
const measured = healTrigger === "behind" ? `${installed} \u2192 ${released}` : "unresolved install";
|
|
25509
25681
|
const healEvidence = [
|
|
25510
25682
|
`installed: ${installed ?? "(none)"}`,
|
|
@@ -25513,22 +25685,22 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
25513
25685
|
`heal: ${heal.detail}`
|
|
25514
25686
|
];
|
|
25515
25687
|
checks.push(heal.ok ? {
|
|
25516
|
-
id: "claude-plugin",
|
|
25688
|
+
id: codexSurface ? "codex-plugin" : "claude-plugin",
|
|
25517
25689
|
ok: true,
|
|
25518
|
-
label: "Claude plugin",
|
|
25519
|
-
detail: `${measured} \u2014 reinstalled via the MMI marketplace (remove \u2192 add \u2192 install)`,
|
|
25690
|
+
label: codexSurface ? "Codex plugin" : "Claude plugin",
|
|
25691
|
+
detail: `${measured} \u2014 reinstalled via the MMI marketplace (remove \u2192 add \u2192 ${codexSurface ? "add" : "install"})${codexSurface ? "; review trust in /hooks" : ""}`,
|
|
25520
25692
|
verbose: healEvidence
|
|
25521
25693
|
} : {
|
|
25522
|
-
id: "claude-plugin",
|
|
25694
|
+
id: codexSurface ? "codex-plugin" : "claude-plugin",
|
|
25523
25695
|
ok: false,
|
|
25524
|
-
label: "Claude plugin",
|
|
25696
|
+
label: codexSurface ? "Codex plugin" : "Claude plugin",
|
|
25525
25697
|
detail: measured,
|
|
25526
25698
|
// #3489: a heal skipped because another doctor holds the env-heal lock is a real ✗ — this run did
|
|
25527
25699
|
// not fix what it found — but it is not this invocation's to clear. The other process is doing it;
|
|
25528
25700
|
// waiting is the correct response and a re-run finds it healed. A heal that RAN and failed is a
|
|
25529
25701
|
// genuine gap and still gates.
|
|
25530
25702
|
...heal.skipped ? { reportOnly: true } : {},
|
|
25531
|
-
fix: heal.skipped ? `${heal.detail} \u2014 another doctor on this machine is healing it now; re-run once it finishes` : `auto-heal failed (${heal.detail}) \u2014 run \`mmi-cli plugin heal\`, then restart Claude`,
|
|
25703
|
+
fix: heal.skipped ? `${heal.detail} \u2014 another doctor on this machine is healing it now; re-run once it finishes` : `auto-heal failed (${heal.detail}) \u2014 run \`mmi-cli plugin heal\`, then ${codexSurface ? "restart Codex" : "restart Claude"}`,
|
|
25532
25704
|
verbose: healEvidence
|
|
25533
25705
|
});
|
|
25534
25706
|
if (!heal.skipped) restartPending = true;
|
|
@@ -25539,6 +25711,10 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
25539
25711
|
if (!plugin.ok) restartPending = true;
|
|
25540
25712
|
}
|
|
25541
25713
|
}
|
|
25714
|
+
if (codexSurface && (pluginProbe.guardState === "healthy" || pluginHealed)) {
|
|
25715
|
+
const trust = checkCodexHookTrust(deps.pluginTrustState?.());
|
|
25716
|
+
if (trust) checks.push(trust);
|
|
25717
|
+
}
|
|
25542
25718
|
const cliInput = { currentVersion: deps.currentCliVersion(), releasedVersion: released };
|
|
25543
25719
|
const cliReport = buildVersionLagReport(cliInput);
|
|
25544
25720
|
if (applyEnv && deps.updateCli && versionAutoUpdateAction(cliReport) === "npm") {
|
|
@@ -25672,7 +25848,12 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
25672
25848
|
const exitCode = doctorReportExitCode(checks);
|
|
25673
25849
|
if (opts.json) {
|
|
25674
25850
|
const payload = opts.verbose ? checks : checks.map(({ verbose: _evidence, ...check }) => check);
|
|
25675
|
-
io.log(JSON.stringify({
|
|
25851
|
+
io.log(JSON.stringify({
|
|
25852
|
+
checks: payload,
|
|
25853
|
+
restartPending,
|
|
25854
|
+
...restartPending ? { restartAction: codexSurface ? "restart Codex" : "restart Claude" } : {},
|
|
25855
|
+
exitCode
|
|
25856
|
+
}, null, 2));
|
|
25676
25857
|
return exitCode;
|
|
25677
25858
|
}
|
|
25678
25859
|
if (opts.banner) {
|
|
@@ -25681,10 +25862,12 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
25681
25862
|
io.log(renderReport([c], { restartPending: false }));
|
|
25682
25863
|
if (opts.verbose) for (const evidence of c.verbose ?? []) io.log(`${VERBOSE_INDENT}${evidence}`);
|
|
25683
25864
|
}
|
|
25684
|
-
if (restartPending) io.log(RESTART_LINE);
|
|
25865
|
+
if (restartPending) io.log(codexSurface ? "\u21BB Restart Codex to finish." : RESTART_LINE);
|
|
25685
25866
|
return 0;
|
|
25686
25867
|
}
|
|
25687
|
-
|
|
25868
|
+
const rendered = renderDoctorText(checks, { verbose: Boolean(opts.verbose), restartPending: restartPending && !codexSurface });
|
|
25869
|
+
io.log(restartPending && codexSurface ? `${rendered}
|
|
25870
|
+
\u21BB Restart Codex to finish.` : rendered);
|
|
25688
25871
|
return exitCode;
|
|
25689
25872
|
}
|
|
25690
25873
|
|
|
@@ -25770,9 +25953,9 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
25770
25953
|
var import_node_fs32 = require("node:fs");
|
|
25771
25954
|
var import_node_os10 = require("node:os");
|
|
25772
25955
|
var import_node_path30 = require("node:path");
|
|
25773
|
-
var
|
|
25956
|
+
var import_node_child_process14 = require("node:child_process");
|
|
25774
25957
|
var import_node_util8 = require("node:util");
|
|
25775
|
-
var execFileP6 = (0, import_node_util8.promisify)(
|
|
25958
|
+
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process14.execFile);
|
|
25776
25959
|
var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
25777
25960
|
function installedClaudePluginVersion() {
|
|
25778
25961
|
try {
|
|
@@ -25786,9 +25969,28 @@ function installedClaudePluginVersion() {
|
|
|
25786
25969
|
return void 0;
|
|
25787
25970
|
}
|
|
25788
25971
|
}
|
|
25972
|
+
function installedActivePluginVersion(surface = detectSurface(process.env)) {
|
|
25973
|
+
if (surface !== "codex") return installedClaudePluginVersion();
|
|
25974
|
+
try {
|
|
25975
|
+
const raw = process.platform === "win32" ? (0, import_node_child_process14.execFileSync)("cmd.exe", ["/c", "codex", "plugin", "list", "--json"], {
|
|
25976
|
+
encoding: "utf8",
|
|
25977
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
25978
|
+
timeout: 15e3
|
|
25979
|
+
}) : (0, import_node_child_process14.execFileSync)("codex", ["plugin", "list", "--json"], {
|
|
25980
|
+
encoding: "utf8",
|
|
25981
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
25982
|
+
timeout: 15e3
|
|
25983
|
+
});
|
|
25984
|
+
const parsed = JSON.parse(raw);
|
|
25985
|
+
const plugin = parsed.installed?.find((entry) => entry.pluginId === MMI_PLUGIN_ID2 && entry.installed === true && entry.enabled === true);
|
|
25986
|
+
return plugin?.version;
|
|
25987
|
+
} catch {
|
|
25988
|
+
return void 0;
|
|
25989
|
+
}
|
|
25990
|
+
}
|
|
25789
25991
|
function worktreeRootSync() {
|
|
25790
25992
|
try {
|
|
25791
|
-
const out = (0,
|
|
25993
|
+
const out = (0, import_node_child_process14.execFileSync)("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
25792
25994
|
let root = out.endsWith("\n") ? out.slice(0, -1) : out;
|
|
25793
25995
|
if (process.platform === "win32" && root.endsWith("\r")) root = root.slice(0, -1);
|
|
25794
25996
|
return root || null;
|
|
@@ -25933,8 +26135,10 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
25933
26135
|
githubRepoReach: githubRepoReachProbe,
|
|
25934
26136
|
awsCallerArn,
|
|
25935
26137
|
isOrgRepo: () => isOrgRepoRoot(),
|
|
25936
|
-
installedPluginVersion:
|
|
25937
|
-
pluginGuardState:
|
|
26138
|
+
installedPluginVersion: installedActivePluginVersion,
|
|
26139
|
+
pluginGuardState: activePluginGuardState,
|
|
26140
|
+
pluginSurface: () => detectSurface(process.env),
|
|
26141
|
+
pluginTrustState: () => codexHookTrustState(),
|
|
25938
26142
|
releasedVersion: throttled ? throttled.read : fetchNpmReleasedVersion,
|
|
25939
26143
|
releasedVersionNote: throttled ? throttled.note : void 0,
|
|
25940
26144
|
// #3272: the --apply self-heal for a stale running CLI — npm global, shadows any plugin shim (#2879).
|
|
@@ -25945,7 +26149,10 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
25945
26149
|
updateCli: (target) => withEnvHealLock("npm global CLI self-update", () => npmSelfUpdateCli(target)),
|
|
25946
26150
|
// #3282: the --apply self-heal for a stale/unresolved Claude plugin — the same marketplace reinstall
|
|
25947
26151
|
// `mmi-cli plugin heal` drives. Takes effect on the next Claude reload, so the row asks for a restart.
|
|
25948
|
-
healPlugin: () =>
|
|
26152
|
+
healPlugin: () => {
|
|
26153
|
+
const surface = detectSurface(process.env);
|
|
26154
|
+
return withEnvHealLock(`${surface === "codex" ? "Codex" : "Claude"} plugin reinstall`, () => healActivePluginForDoctor(surface));
|
|
26155
|
+
},
|
|
25949
26156
|
currentCliVersion: resolveClientVersion,
|
|
25950
26157
|
readGitignore,
|
|
25951
26158
|
writeGitignore,
|
|
@@ -25980,10 +26187,14 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
25980
26187
|
return { personal, app };
|
|
25981
26188
|
},
|
|
25982
26189
|
pluginCache: () => {
|
|
26190
|
+
const surface = detectSurface(process.env);
|
|
26191
|
+
const configRoot = surfaceConfigRoot(surface);
|
|
26192
|
+
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
25983
26193
|
const plan = buildPluginCachePlan(
|
|
25984
26194
|
(0, import_node_os11.homedir)(),
|
|
25985
|
-
|
|
25986
|
-
pluginCacheFsDeps(
|
|
26195
|
+
running,
|
|
26196
|
+
pluginCacheFsDeps(configRoot, () => 0),
|
|
26197
|
+
{ configRoot, includeStaging: surface !== "codex" }
|
|
25987
26198
|
);
|
|
25988
26199
|
return {
|
|
25989
26200
|
stale: plan.prune,
|
|
@@ -26032,6 +26243,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
26032
26243
|
// which branch it would pick one up from. Two local file reads, no network, fail-soft to no rows.
|
|
26033
26244
|
marketplaceRows: () => {
|
|
26034
26245
|
try {
|
|
26246
|
+
if (detectSurface(process.env) === "codex") return [];
|
|
26035
26247
|
const home = (0, import_node_os11.homedir)();
|
|
26036
26248
|
return marketplaceRows(
|
|
26037
26249
|
MMI_MARKETPLACE_NAME,
|
|
@@ -26423,7 +26635,7 @@ function runWorktreeInstall(command, cwd, quiet) {
|
|
|
26423
26635
|
const file = isWin2 ? "cmd.exe" : bin;
|
|
26424
26636
|
const spawnArgs = isWin2 ? ["/c", bin, ...args] : args;
|
|
26425
26637
|
return new Promise((resolve5, reject) => {
|
|
26426
|
-
const child2 = (0,
|
|
26638
|
+
const child2 = (0, import_node_child_process15.spawn)(file, spawnArgs, { cwd, stdio: quiet ? "ignore" : "inherit", windowsHide: true });
|
|
26427
26639
|
const timer = setTimeout(() => {
|
|
26428
26640
|
try {
|
|
26429
26641
|
child2.kill();
|
|
@@ -26694,7 +26906,7 @@ function scheduleRelatedDiscovery(o) {
|
|
|
26694
26906
|
try {
|
|
26695
26907
|
const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
|
|
26696
26908
|
if (o.repo) args.push("--repo", o.repo);
|
|
26697
|
-
spawnDetachedSelf(args, { spawn:
|
|
26909
|
+
spawnDetachedSelf(args, { spawn: import_node_child_process15.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
|
|
26698
26910
|
} catch {
|
|
26699
26911
|
}
|
|
26700
26912
|
}
|
|
@@ -28389,8 +28601,8 @@ function directoryBytes(path2) {
|
|
|
28389
28601
|
function listDirEntries(dir) {
|
|
28390
28602
|
return (0, import_node_fs33.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
28391
28603
|
}
|
|
28392
|
-
function readInstalledPluginRefs(
|
|
28393
|
-
const p =
|
|
28604
|
+
function readInstalledPluginRefs(configRoot) {
|
|
28605
|
+
const p = installedPluginsPathForConfig(configRoot);
|
|
28394
28606
|
if (!(0, import_node_fs33.existsSync)(p)) return [];
|
|
28395
28607
|
try {
|
|
28396
28608
|
return installedPluginPaths((0, import_node_fs33.readFileSync)(p, "utf8"));
|
|
@@ -28398,7 +28610,7 @@ function readInstalledPluginRefs(home) {
|
|
|
28398
28610
|
return null;
|
|
28399
28611
|
}
|
|
28400
28612
|
}
|
|
28401
|
-
function pluginCacheFsDeps(
|
|
28613
|
+
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
28402
28614
|
return {
|
|
28403
28615
|
exists: (p) => (0, import_node_fs33.existsSync)(p),
|
|
28404
28616
|
listVersionDirs: (root) => (0, import_node_fs33.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
@@ -28410,14 +28622,14 @@ function pluginCacheFsDeps(home, dirBytes) {
|
|
|
28410
28622
|
return { name: d.name, mtimeMs: Date.now() };
|
|
28411
28623
|
}
|
|
28412
28624
|
}),
|
|
28413
|
-
readInstalledPluginPaths: () => readInstalledPluginRefs(
|
|
28625
|
+
readInstalledPluginPaths: () => readInstalledPluginRefs(configRoot),
|
|
28414
28626
|
now: () => Date.now()
|
|
28415
28627
|
};
|
|
28416
28628
|
}
|
|
28417
|
-
function stagingApplyFsGuard(
|
|
28418
|
-
const stagingRoot =
|
|
28629
|
+
function stagingApplyFsGuard(configRoot) {
|
|
28630
|
+
const stagingRoot = pluginCacheStagingRootForConfig(configRoot);
|
|
28419
28631
|
return {
|
|
28420
|
-
referencedPaths: () => readInstalledPluginRefs(
|
|
28632
|
+
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
28421
28633
|
mtimeMs: (name) => {
|
|
28422
28634
|
const p = (0, import_node_path31.join)(stagingRoot, name);
|
|
28423
28635
|
if (!(0, import_node_fs33.existsSync)(p)) return null;
|
|
@@ -28431,14 +28643,24 @@ function stagingApplyFsGuard(home) {
|
|
|
28431
28643
|
};
|
|
28432
28644
|
}
|
|
28433
28645
|
program2.command("plugin-prune").description(`prune stale cached MMI plugin versions (keeps running + newest, ${PLUGIN_CACHE_KEEP} total) and orphaned temp_git_* staging dirs; dry-run unless --apply (#2903, #2990)`).option("--apply", "actually delete the stale version dirs + orphaned staging dirs (default: report only)").option("--json", "machine-readable output").action((o) => {
|
|
28646
|
+
const surface = detectSurface(process.env);
|
|
28647
|
+
const configRoot = surfaceConfigRoot(surface);
|
|
28648
|
+
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
28649
|
+
if (o.apply && surface === "codex" && !running) {
|
|
28650
|
+
const detail = "plugin prune refused: Codex could not identify the enabled MMI version; no cache was deleted";
|
|
28651
|
+
if (o.json) console.log(JSON.stringify({ ok: false, error: detail }));
|
|
28652
|
+
else console.error(detail);
|
|
28653
|
+
process.exitCode = 1;
|
|
28654
|
+
return;
|
|
28655
|
+
}
|
|
28434
28656
|
const plan = buildPluginCachePlan(
|
|
28435
28657
|
(0, import_node_os11.homedir)(),
|
|
28436
|
-
|
|
28437
|
-
pluginCacheFsDeps(
|
|
28438
|
-
{ withBytes: true }
|
|
28658
|
+
running,
|
|
28659
|
+
pluginCacheFsDeps(configRoot, directoryBytes),
|
|
28660
|
+
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
28439
28661
|
);
|
|
28440
28662
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
28441
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs33.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(
|
|
28663
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs33.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
28442
28664
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
28443
28665
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
28444
28666
|
else console.log(renderPluginCachePlan(plan, result));
|
|
@@ -28501,7 +28723,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
|
|
|
28501
28723
|
for (const line of scratchGcLines(process.cwd())) bannerIo.log(line);
|
|
28502
28724
|
const worktreeBanner = worktreeAutoProvisionBanner(process.cwd());
|
|
28503
28725
|
if (worktreeBanner) {
|
|
28504
|
-
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn:
|
|
28726
|
+
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process15.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
28505
28727
|
bannerIo.log(worktreeBanner);
|
|
28506
28728
|
}
|
|
28507
28729
|
if (isLinkedWorktree(process.cwd())) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutmutco/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.74.0",
|
|
4
4
|
"description": "MMI Future CLI — the org dev toolbox (board, registry, keyless secrets, release train, bootstrap, doctor) and the cross-IDE engine the MMI plugin's skills and gates drive on Claude, Codex, and Kimi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|