@mutmutco/cli 3.96.0 → 3.97.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 +431 -113
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -5235,7 +5235,7 @@ function deferredWorktreesRegistryPath(gitDir) {
|
|
|
5235
5235
|
return `${base}/mmi-deferred-worktrees.json`;
|
|
5236
5236
|
}
|
|
5237
5237
|
function parseDeferredWorktreesFile(text) {
|
|
5238
|
-
const parsed = JSON.parse(text);
|
|
5238
|
+
const parsed = JSON.parse(text.replace(/^\uFEFF/, ""));
|
|
5239
5239
|
if (!parsed || !Array.isArray(parsed.entries)) return [];
|
|
5240
5240
|
return parsed.entries.filter((e) => Boolean(e) && typeof e === "object" && typeof e.path === "string" && typeof e.branch === "string" && e.reason === "lock-held").map((e) => ({ ...e, registeredAt: e.registeredAt || (/* @__PURE__ */ new Date(0)).toISOString() }));
|
|
5241
5241
|
}
|
|
@@ -6999,7 +6999,7 @@ function commandLadderHint() {
|
|
|
6999
6999
|
}
|
|
7000
7000
|
|
|
7001
7001
|
// src/index.ts
|
|
7002
|
-
var
|
|
7002
|
+
var import_node_path37 = require("node:path");
|
|
7003
7003
|
|
|
7004
7004
|
// src/merge-ci-policy.ts
|
|
7005
7005
|
function resolveMergeCiPolicy(input) {
|
|
@@ -8788,6 +8788,9 @@ function detectSurface(env) {
|
|
|
8788
8788
|
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")) {
|
|
8789
8789
|
return "codex";
|
|
8790
8790
|
}
|
|
8791
|
+
if (env.MMI_AGENT_SURFACE === "jervcode" || has("PI_SESSION_ID")) {
|
|
8792
|
+
return "jervcode";
|
|
8793
|
+
}
|
|
8791
8794
|
if (env.MMI_AGENT_SURFACE === "kimi" || has("KIMI_PLUGIN_ROOT") || has("KIMI_CODE_HOME")) {
|
|
8792
8795
|
return "kimi";
|
|
8793
8796
|
}
|
|
@@ -8817,6 +8820,8 @@ function surfaceToken(surface) {
|
|
|
8817
8820
|
return "kilo";
|
|
8818
8821
|
case "cursor":
|
|
8819
8822
|
return "cursor";
|
|
8823
|
+
case "jervcode":
|
|
8824
|
+
return "jervcode";
|
|
8820
8825
|
case "opencode":
|
|
8821
8826
|
return "opencode";
|
|
8822
8827
|
case "shell":
|
|
@@ -8838,6 +8843,8 @@ function reloadAction(surface) {
|
|
|
8838
8843
|
return "restart OpenCode";
|
|
8839
8844
|
case "cursor":
|
|
8840
8845
|
return "reload the Cursor window";
|
|
8846
|
+
case "jervcode":
|
|
8847
|
+
return "restart JervCode (the package set is read at launch)";
|
|
8841
8848
|
case "claude-cli":
|
|
8842
8849
|
case "shell":
|
|
8843
8850
|
default:
|
|
@@ -8905,7 +8912,7 @@ function nonClaudeSurfaceHealMessage(surface) {
|
|
|
8905
8912
|
if (surface === "cursor") {
|
|
8906
8913
|
return "Cursor ships an MMI plugin (#3920). Install or repair its managed local checkout with:\n mmi-cli plugin heal\n Then reload the Cursor window. For one-off CLI use, pass --plugin-dir <MMI-Hub checkout>.\n Update the CLI too: npm i -g @mutmutco/cli";
|
|
8907
8914
|
}
|
|
8908
|
-
return "No Hub-shipped MMI plugin on this host \u2014 the Hub ships plugins for Claude, Codex, Kimi, Cursor, and
|
|
8915
|
+
return "No Hub-shipped MMI plugin on this host \u2014 the Hub ships plugins for Claude, Codex, Kimi, Cursor, Kilo, and JervCode only.\n Update the CLI instead: npm i -g @mutmutco/cli (org rules ride an AGENTS.md authored outside the Hub)";
|
|
8909
8916
|
}
|
|
8910
8917
|
function healStepAborts(step, ok) {
|
|
8911
8918
|
return !ok && step.gated;
|
|
@@ -8953,6 +8960,7 @@ function surfaceConfigRoot(surface, env = process.env, home = (0, import_node_os
|
|
|
8953
8960
|
if (surface === "kimi") return env.KIMI_CODE_HOME?.trim() || (0, import_node_path14.join)(home, ".kimi-code");
|
|
8954
8961
|
if (surface === "kilo") return env.KILO_CONFIG_DIR?.trim() || (0, import_node_path14.join)(home, ".config", "kilo");
|
|
8955
8962
|
if (surface === "cursor") return (0, import_node_path14.join)(home, ".cursor");
|
|
8963
|
+
if (surface === "jervcode") return env.PI_CODING_AGENT_DIR?.trim() || (0, import_node_path14.join)(home, ".pi", "agent");
|
|
8956
8964
|
return (0, import_node_path14.join)(home, ".claude");
|
|
8957
8965
|
}
|
|
8958
8966
|
var installedPluginsPath = (surface = detectSurface(process.env)) => {
|
|
@@ -8976,6 +8984,7 @@ function marketplaceCloneCandidates(surface, home, env = process.env) {
|
|
|
8976
8984
|
if (surface === "kimi") return [];
|
|
8977
8985
|
if (surface === "kilo") return [];
|
|
8978
8986
|
if (surface === "cursor") return [];
|
|
8987
|
+
if (surface === "jervcode") return [];
|
|
8979
8988
|
return [(0, import_node_path14.join)(home, ".claude", "plugins", "marketplaces", "mutmutco")];
|
|
8980
8989
|
}
|
|
8981
8990
|
function marketplaceClonePresent(surface, home, exists = import_node_fs15.existsSync, env = process.env) {
|
|
@@ -9104,22 +9113,170 @@ function kimiPluginTreeHealthy(root, exists = import_node_fs15.existsSync) {
|
|
|
9104
9113
|
"scripts/hook-run.mjs"
|
|
9105
9114
|
].every((path2) => exists((0, import_node_path14.join)(root, ...path2.split("/"))));
|
|
9106
9115
|
}
|
|
9116
|
+
var JERVCODE_WRAPPER_DIR = ".pi-plugin";
|
|
9117
|
+
function normalizePiEntry(value) {
|
|
9118
|
+
return decodeURIComponent(value.replace(/^file:\/\/\/?/, "")).replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
9119
|
+
}
|
|
9120
|
+
function piEntryFsPath(entry) {
|
|
9121
|
+
return decodeURIComponent(entry.replace(/^file:\/\/\/?/, ""));
|
|
9122
|
+
}
|
|
9123
|
+
function samePiEntry(entry, packagePath) {
|
|
9124
|
+
return typeof entry === "string" && normalizePiEntry(entry) === normalizePiEntry(packagePath);
|
|
9125
|
+
}
|
|
9126
|
+
function jervcodePackageFamily(entry) {
|
|
9127
|
+
if (typeof entry !== "string") return null;
|
|
9128
|
+
const parts = normalizePiEntry(entry).split("/");
|
|
9129
|
+
if (parts.length < 3 || parts[parts.length - 1] !== JERVCODE_WRAPPER_DIR) return null;
|
|
9130
|
+
if (!/^\d+\.\d+\.\d+$/.test(parts[parts.length - 2])) return null;
|
|
9131
|
+
parts.splice(parts.length - 2, 1);
|
|
9132
|
+
return parts.join("/");
|
|
9133
|
+
}
|
|
9134
|
+
function isMmiOwnedPiEntry(entry) {
|
|
9135
|
+
if (typeof entry !== "string") return false;
|
|
9136
|
+
try {
|
|
9137
|
+
const pkg = JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path14.join)(piEntryFsPath(entry), "package.json"), "utf8"));
|
|
9138
|
+
return pkg.name === "mmi";
|
|
9139
|
+
} catch {
|
|
9140
|
+
return false;
|
|
9141
|
+
}
|
|
9142
|
+
}
|
|
9143
|
+
function mergeMmiPiPackageEntries(entries, packagePath) {
|
|
9144
|
+
const family = jervcodePackageFamily(packagePath);
|
|
9145
|
+
const others = entries.filter((entry) => {
|
|
9146
|
+
if (samePiEntry(entry, packagePath)) return false;
|
|
9147
|
+
if (family === null || jervcodePackageFamily(entry) !== family) return true;
|
|
9148
|
+
return !isMmiOwnedPiEntry(entry);
|
|
9149
|
+
});
|
|
9150
|
+
const already = entries.some((entry) => samePiEntry(entry, packagePath));
|
|
9151
|
+
const superseded = entries.length - others.length - (already ? 1 : 0);
|
|
9152
|
+
return {
|
|
9153
|
+
next: [...others, packagePath],
|
|
9154
|
+
superseded,
|
|
9155
|
+
changed: !(already && others.length === entries.length - 1)
|
|
9156
|
+
};
|
|
9157
|
+
}
|
|
9158
|
+
function readPiSettings(path2) {
|
|
9159
|
+
if (!(0, import_node_fs15.existsSync)(path2)) return void 0;
|
|
9160
|
+
try {
|
|
9161
|
+
const parsed = JSON.parse((0, import_node_fs15.readFileSync)(path2, "utf8"));
|
|
9162
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
9163
|
+
return parsed;
|
|
9164
|
+
} catch {
|
|
9165
|
+
return null;
|
|
9166
|
+
}
|
|
9167
|
+
}
|
|
9168
|
+
function mmiPiWrapperEntry(env = process.env, home = (0, import_node_os4.homedir)()) {
|
|
9169
|
+
const settings = readPiSettings((0, import_node_path14.join)(surfaceConfigRoot("jervcode", env, home), "settings.json"));
|
|
9170
|
+
const entries = Array.isArray(settings?.packages) ? settings.packages : [];
|
|
9171
|
+
for (const entry of entries) {
|
|
9172
|
+
if (typeof entry !== "string") continue;
|
|
9173
|
+
if (jervcodePackageFamily(entry)?.endsWith("/mutmutco/mmi/" + JERVCODE_WRAPPER_DIR) || isMmiOwnedPiEntry(entry)) {
|
|
9174
|
+
return entry;
|
|
9175
|
+
}
|
|
9176
|
+
}
|
|
9177
|
+
return null;
|
|
9178
|
+
}
|
|
9179
|
+
function mmiPiWrapperHealthy(entry) {
|
|
9180
|
+
if (!entry) return false;
|
|
9181
|
+
const wrapper = piEntryFsPath(entry);
|
|
9182
|
+
return isMmiOwnedPiEntry(entry) && (0, import_node_fs15.existsSync)((0, import_node_path14.join)((0, import_node_path14.dirname)(wrapper), "skills", "mmi", "SKILL.md"));
|
|
9183
|
+
}
|
|
9184
|
+
function findMmiPiSourceClone(home = (0, import_node_os4.homedir)()) {
|
|
9185
|
+
const cacheRoot = (0, import_node_path14.join)(home, ".claude", "plugins", "cache", "mutmutco", "mmi");
|
|
9186
|
+
let best = null;
|
|
9187
|
+
try {
|
|
9188
|
+
for (const entry of (0, import_node_fs15.readdirSync)(cacheRoot, { withFileTypes: true })) {
|
|
9189
|
+
if (!entry.isDirectory() || !/^\d+\.\d+\.\d+$/.test(entry.name)) continue;
|
|
9190
|
+
if (!(0, import_node_fs15.existsSync)((0, import_node_path14.join)(cacheRoot, entry.name, JERVCODE_WRAPPER_DIR, "package.json"))) continue;
|
|
9191
|
+
if (!best || compareVersions(entry.name, best.version) > 0) {
|
|
9192
|
+
best = { path: (0, import_node_path14.join)(cacheRoot, entry.name), version: entry.name };
|
|
9193
|
+
}
|
|
9194
|
+
}
|
|
9195
|
+
} catch {
|
|
9196
|
+
return null;
|
|
9197
|
+
}
|
|
9198
|
+
return best;
|
|
9199
|
+
}
|
|
9200
|
+
function healJervCodePackageRegistration(opts = {}) {
|
|
9201
|
+
const env = opts.env ?? process.env;
|
|
9202
|
+
if (env.PI_SESSION_ID?.trim()) {
|
|
9203
|
+
return {
|
|
9204
|
+
available: true,
|
|
9205
|
+
ok: true,
|
|
9206
|
+
changed: false,
|
|
9207
|
+
version: null,
|
|
9208
|
+
detail: "skipped \u2014 live JervCode/Pi seat (PI_SESSION_ID set); the package set is read at launch, so this registration is deferred to an out-of-session doctor run"
|
|
9209
|
+
};
|
|
9210
|
+
}
|
|
9211
|
+
const home = opts.home ?? (0, import_node_os4.homedir)();
|
|
9212
|
+
const agentDir = surfaceConfigRoot("jervcode", env, home);
|
|
9213
|
+
if (!(0, import_node_fs15.existsSync)(agentDir)) {
|
|
9214
|
+
return { available: false, ok: true, changed: false, version: null, detail: "skipped \u2014 no Pi/JervCode install (no agent config dir)" };
|
|
9215
|
+
}
|
|
9216
|
+
const clone = opts.clone === void 0 ? findMmiPiSourceClone(home) : opts.clone;
|
|
9217
|
+
if (!clone) {
|
|
9218
|
+
return { available: true, ok: true, changed: false, version: null, detail: "skipped \u2014 no installed MMI clone carries the pi wrapper (install the Claude plugin first)" };
|
|
9219
|
+
}
|
|
9220
|
+
const packagePath = `${clone.path.replace(/\\/g, "/").replace(/\/+$/, "")}/${JERVCODE_WRAPPER_DIR}`;
|
|
9221
|
+
const settingsPath = (0, import_node_path14.join)(agentDir, "settings.json");
|
|
9222
|
+
const settings = readPiSettings(settingsPath);
|
|
9223
|
+
if (settings === null) {
|
|
9224
|
+
return {
|
|
9225
|
+
available: true,
|
|
9226
|
+
ok: false,
|
|
9227
|
+
changed: false,
|
|
9228
|
+
version: clone.version,
|
|
9229
|
+
detail: `package NOT registered \u2014 ${settingsPath} is not a strict JSON object, so mmi left it alone; add ${packagePath} to its packages array by hand`
|
|
9230
|
+
};
|
|
9231
|
+
}
|
|
9232
|
+
const current = settings ?? {};
|
|
9233
|
+
const entries = Array.isArray(current.packages) ? current.packages : [];
|
|
9234
|
+
const merged = mergeMmiPiPackageEntries(entries, packagePath);
|
|
9235
|
+
if (!merged.changed) {
|
|
9236
|
+
return { available: true, ok: true, changed: false, version: clone.version, detail: `holds ${clone.version}` };
|
|
9237
|
+
}
|
|
9238
|
+
current.packages = merged.next;
|
|
9239
|
+
try {
|
|
9240
|
+
(0, import_node_fs15.mkdirSync)((0, import_node_path14.dirname)(settingsPath), { recursive: true });
|
|
9241
|
+
const tmp = `${settingsPath}.tmp-${process.pid}`;
|
|
9242
|
+
(0, import_node_fs15.writeFileSync)(tmp, `${JSON.stringify(current, null, 2)}
|
|
9243
|
+
`, "utf8");
|
|
9244
|
+
try {
|
|
9245
|
+
(0, import_node_fs15.renameSync)(tmp, settingsPath);
|
|
9246
|
+
} catch (renameError) {
|
|
9247
|
+
(0, import_node_fs15.rmSync)(tmp, { force: true });
|
|
9248
|
+
throw renameError;
|
|
9249
|
+
}
|
|
9250
|
+
} catch (error) {
|
|
9251
|
+
return { available: true, ok: false, changed: false, version: clone.version, detail: `package NOT registered \u2014 ${error.message}` };
|
|
9252
|
+
}
|
|
9253
|
+
const beside = `package registered in settings.json (${merged.next.length - 1} other package(s) untouched)`;
|
|
9254
|
+
return {
|
|
9255
|
+
available: true,
|
|
9256
|
+
ok: true,
|
|
9257
|
+
changed: true,
|
|
9258
|
+
version: clone.version,
|
|
9259
|
+
detail: `${clone.version}: ${merged.superseded > 0 ? `${beside}, superseding ${merged.superseded} earlier mmi version(s)` : beside}`
|
|
9260
|
+
};
|
|
9261
|
+
}
|
|
9107
9262
|
function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRepo = false) {
|
|
9108
9263
|
const root = surfaceConfigRoot(surface);
|
|
9109
9264
|
const installed = readInstalledPlugins(surface);
|
|
9110
9265
|
const codexStatus = surface === "codex" ? codexPluginStatus() : void 0;
|
|
9266
|
+
const piEntry = surface === "jervcode" ? mmiPiWrapperEntry() : null;
|
|
9111
9267
|
return {
|
|
9112
9268
|
isOrgRepo,
|
|
9113
9269
|
installRecordPresent: hasUserInstallRecord(installed, MMI_PLUGIN_ID) || hasProjectInstallRecord(installed, MMI_PLUGIN_ID, process.cwd()) || // Kimi's managed plugin directory is its native install record; it has no Claude-style ledger.
|
|
9114
9270
|
surface === "kimi" && (0, import_node_fs15.existsSync)((0, import_node_path14.join)(root, "plugins", "managed", "mmi")) || // kilo-p1: the install record is the config file itself.
|
|
9115
|
-
surface === "kilo" && kiloConfigListsPlugin(root) ||
|
|
9271
|
+
surface === "kilo" && kiloConfigListsPlugin(root) || // #4188: jervcode's install record is the settings-file packages[] entry itself.
|
|
9272
|
+
surface === "jervcode" && piEntry !== null || surface === "cursor" && (0, import_node_fs15.existsSync)(cursorLocalPluginRoot()),
|
|
9116
9273
|
// Kilo has no marketplace to clone — the config file IS the install record, so this dimension of
|
|
9117
|
-
// the shared guard table is vacuously satisfied.
|
|
9118
|
-
marketplaceClonePresent: surface === "kimi" || surface === "kilo" || surface === "cursor" ? true : marketplaceClonePresent(surface, (0, import_node_os4.homedir)()),
|
|
9274
|
+
// the shared guard table is vacuously satisfied. Same for jervcode's settings entry.
|
|
9275
|
+
marketplaceClonePresent: surface === "kimi" || surface === "kilo" || surface === "cursor" || surface === "jervcode" ? true : marketplaceClonePresent(surface, (0, import_node_os4.homedir)()),
|
|
9119
9276
|
// Kimi keeps no plugin cache dir — installs are copied to plugins/managed/<id> and run from there.
|
|
9120
9277
|
// Kilo (kilo-p1) keeps no cache dir either: the plugin's server() provisions ~/.kilo behind the
|
|
9121
9278
|
// version stamp, so the stamp's presence is the cache signal.
|
|
9122
|
-
pluginCachePresent: surface === "kilo" ? (0, import_node_fs15.existsSync)((0, import_node_path14.join)((0, import_node_os4.homedir)(), ".kilo", ".mmi-kilo-version")) : surface === "kimi" ? kimiPluginTreeHealthy((0, import_node_path14.join)(root, "plugins", "managed", "mmi")) : surface === "cursor" ? cursorPluginTreeHealthy(cursorLocalPluginRoot()) : surface === "codex" ? Boolean(
|
|
9279
|
+
pluginCachePresent: surface === "jervcode" ? mmiPiWrapperHealthy(piEntry) : surface === "kilo" ? (0, import_node_fs15.existsSync)((0, import_node_path14.join)((0, import_node_os4.homedir)(), ".kilo", ".mmi-kilo-version")) : surface === "kimi" ? kimiPluginTreeHealthy((0, import_node_path14.join)(root, "plugins", "managed", "mmi")) : surface === "cursor" ? cursorPluginTreeHealthy(cursorLocalPluginRoot()) : surface === "codex" ? Boolean(
|
|
9123
9280
|
codexStatus?.installed && codexStatus.enabled && codexStatus.version && (0, import_node_fs15.existsSync)((0, import_node_path14.join)(root, "plugins", "cache", CODEX_MARKETPLACE, "mmi", codexStatus.version))
|
|
9124
9281
|
) : (0, import_node_fs15.existsSync)((0, import_node_path14.join)(root, "plugins", "cache", "mutmutco", "mmi"))
|
|
9125
9282
|
};
|
|
@@ -9298,13 +9455,26 @@ async function healClaudePluginForDoctor(surface = detectSurface(process.env), o
|
|
|
9298
9455
|
detail: ok ? `marketplace remove \u2192 add \u2192 install succeeded${pinNote ? `; ${pinNote}` : ""}` : `\`claude plugin\` reinstall failed or was skipped${steps.length ? ` (${steps[steps.length - 1]})` : ""}`
|
|
9299
9456
|
};
|
|
9300
9457
|
}
|
|
9458
|
+
async function healJervCodeForDoctor() {
|
|
9459
|
+
const heal = healJervCodePackageRegistration();
|
|
9460
|
+
if (!heal.ok) return { ok: false, detail: heal.detail };
|
|
9461
|
+
const snapshot = snapshotPluginGuardInput("jervcode", true);
|
|
9462
|
+
const state = buildPluginGuardDecision(snapshot).state;
|
|
9463
|
+
const verification = `record=${snapshot.installRecordPresent ? "yes" : "no"}, payload=${snapshot.pluginCachePresent ? "yes" : "no"}`;
|
|
9464
|
+
if (!heal.changed && heal.detail.startsWith("skipped")) return { ok: true, detail: heal.detail };
|
|
9465
|
+
return {
|
|
9466
|
+
ok: state === "healthy",
|
|
9467
|
+
detail: state === "healthy" ? `${heal.detail}; full guard verified (${verification})` : `pi package registration did not verify (${verification}); ${heal.detail}`
|
|
9468
|
+
};
|
|
9469
|
+
}
|
|
9301
9470
|
async function healActivePluginForDoctor(surface = detectSurface(process.env), onStep) {
|
|
9302
9471
|
const token = surfaceToken(surface);
|
|
9303
|
-
if (token !== "claude" && token !== "codex" && token !== "cursor" && token !== "kilo") {
|
|
9472
|
+
if (token !== "claude" && token !== "codex" && token !== "cursor" && token !== "kilo" && token !== "jervcode") {
|
|
9304
9473
|
return { ok: false, detail: `not a supported plugin surface (${surface})` };
|
|
9305
9474
|
}
|
|
9306
9475
|
if (token === "claude") return healClaudePluginForDoctor(surface, onStep);
|
|
9307
9476
|
if (token === "cursor") return installCursorPluginCheckout();
|
|
9477
|
+
if (token === "jervcode") return healJervCodeForDoctor();
|
|
9308
9478
|
const steps = [];
|
|
9309
9479
|
const applied = await applyPluginHeal(surface, (msg) => {
|
|
9310
9480
|
const line = msg.trim();
|
|
@@ -9421,7 +9591,7 @@ async function runGuard(readOrigin) {
|
|
|
9421
9591
|
const surface = detectSurface(process.env);
|
|
9422
9592
|
try {
|
|
9423
9593
|
const token = surfaceToken(surface);
|
|
9424
|
-
if (token !== "claude" && token !== "codex" && token !== "cursor" && token !== "kilo") {
|
|
9594
|
+
if (token !== "claude" && token !== "codex" && token !== "cursor" && token !== "kilo" && token !== "jervcode") {
|
|
9425
9595
|
process.exitCode = 0;
|
|
9426
9596
|
return;
|
|
9427
9597
|
}
|
|
@@ -9441,8 +9611,8 @@ async function runGuard(readOrigin) {
|
|
|
9441
9611
|
if (surfaceToken(surface) === "codex") {
|
|
9442
9612
|
console.error("[mmi-guard] Could not inspect the active Codex plugin; run `mmi-cli plugin heal`.");
|
|
9443
9613
|
process.exitCode = 1;
|
|
9444
|
-
} else if (surfaceToken(surface) === "kilo" || surfaceToken(surface) === "cursor") {
|
|
9445
|
-
const host = surfaceToken(surface) === "cursor" ? "Cursor" : "Kilo";
|
|
9614
|
+
} else if (surfaceToken(surface) === "kilo" || surfaceToken(surface) === "cursor" || surfaceToken(surface) === "jervcode") {
|
|
9615
|
+
const host = surfaceToken(surface) === "cursor" ? "Cursor" : surfaceToken(surface) === "jervcode" ? "JervCode" : "Kilo";
|
|
9446
9616
|
console.error(`[mmi-guard] Could not inspect the active ${host} plugin; run \`mmi-cli plugin heal\`.`);
|
|
9447
9617
|
process.exitCode = 1;
|
|
9448
9618
|
} else {
|
|
@@ -9452,10 +9622,21 @@ async function runGuard(readOrigin) {
|
|
|
9452
9622
|
}
|
|
9453
9623
|
async function runPluginHeal(surface = detectSurface(process.env)) {
|
|
9454
9624
|
const token = surfaceToken(surface);
|
|
9455
|
-
if (token !== "claude" && token !== "codex" && token !== "cursor" && token !== "kilo") {
|
|
9625
|
+
if (token !== "claude" && token !== "codex" && token !== "cursor" && token !== "kilo" && token !== "jervcode") {
|
|
9456
9626
|
console.log(nonClaudeSurfaceHealMessage(token ?? void 0));
|
|
9457
9627
|
return;
|
|
9458
9628
|
}
|
|
9629
|
+
if (token === "jervcode") {
|
|
9630
|
+
const result = await healJervCodeForDoctor();
|
|
9631
|
+
console.log(` \u21BB ${result.detail}`);
|
|
9632
|
+
if (result.ok) {
|
|
9633
|
+
console.log(` \u2713 MMI pi package registered \u2014 ${reloadAction(surface)} to load MMI skills.`);
|
|
9634
|
+
} else {
|
|
9635
|
+
process.exitCode = 1;
|
|
9636
|
+
console.log(" \u2717 Auto-heal failed or was skipped. Install the Claude MMI plugin first (it owns the referenced clone), then rerun `mmi-cli plugin heal`.");
|
|
9637
|
+
}
|
|
9638
|
+
return;
|
|
9639
|
+
}
|
|
9459
9640
|
const descriptor = PLUGIN_SURFACE_HEAL[token];
|
|
9460
9641
|
const cursorResult = token === "cursor" ? await installCursorPluginCheckout() : void 0;
|
|
9461
9642
|
if (cursorResult) console.log(` \u21BB ${cursorResult.detail}`);
|
|
@@ -17227,7 +17408,7 @@ function applyChecklistCheck(body, query, checked) {
|
|
|
17227
17408
|
// src/command-taxonomy.ts
|
|
17228
17409
|
var COMMAND_METADATA = /* @__PURE__ */ Symbol.for("mmi.commandTaxonomy.metadata");
|
|
17229
17410
|
var PRIMARY_GROUPS = [
|
|
17230
|
-
["Orient", ["onboard", "status", "next", "doctor", "whoami", "commands", "explain"]],
|
|
17411
|
+
["Orient", ["onboard", "status", "next", "find", "doctor", "whoami", "commands", "explain"]],
|
|
17231
17412
|
["Plan and work", ["board", "issue", "worktree", "stage"]],
|
|
17232
17413
|
["Review and ship", ["pr", "ci", "rcand", "release", "hotfix", "train"]],
|
|
17233
17414
|
// `tests` sits beside `docs` deliberately: both are deterministic, repo-local gates a workflow
|
|
@@ -17237,7 +17418,7 @@ var PRIMARY_GROUPS = [
|
|
|
17237
17418
|
["Coordinate and improve", ["wave", "report", "skill-lesson"]]
|
|
17238
17419
|
];
|
|
17239
17420
|
var OPERATIONAL_TOP_LEVEL = /* @__PURE__ */ new Set(["org", "runtime", "plugin"]);
|
|
17240
|
-
var SUPPORT_PRIMARY = /* @__PURE__ */ new Set(["doctor", "whoami", "commands", "explain", "docs", "repo-index", "tests", "spawn", "wave", "report", "skill-lesson"]);
|
|
17421
|
+
var SUPPORT_PRIMARY = /* @__PURE__ */ new Set(["doctor", "whoami", "commands", "explain", "docs", "repo-index", "find", "tests", "spawn", "wave", "report", "skill-lesson"]);
|
|
17241
17422
|
var TOP_LEVEL_ORDER = /* @__PURE__ */ new Map();
|
|
17242
17423
|
var HELP_GROUP_ORDER = /* @__PURE__ */ new Map();
|
|
17243
17424
|
var topLevelPosition = 0;
|
|
@@ -17248,6 +17429,10 @@ for (const [helpGroup, names] of PRIMARY_GROUPS) {
|
|
|
17248
17429
|
HELP_GROUP_ORDER.set("Operations", HELP_GROUP_ORDER.size);
|
|
17249
17430
|
for (const name of OPERATIONAL_TOP_LEVEL) TOP_LEVEL_ORDER.set(name, topLevelPosition++);
|
|
17250
17431
|
var PATH_OVERRIDES = {
|
|
17432
|
+
// #4184: the estate-search door reads as Orient, while rebuild/publish/gc/sync-estate stay
|
|
17433
|
+
// Setup and support — that split is the point.
|
|
17434
|
+
"repo-index search": { category: "support", discovery: "primary", help_group: "Orient" },
|
|
17435
|
+
"repo-index status": { category: "support", discovery: "primary", help_group: "Orient" },
|
|
17251
17436
|
"secrets find": { category: "admin", discovery: "all-only", help_group: "Operations" },
|
|
17252
17437
|
"secrets catalog": { category: "admin", discovery: "all-only", help_group: "Operations" },
|
|
17253
17438
|
"secrets doctor": { category: "admin", discovery: "all-only", help_group: "Operations" },
|
|
@@ -17277,6 +17462,7 @@ var COMMAND_OWNERSHIP = {
|
|
|
17277
17462
|
secrets: { module_owner: "cli/src/secrets-commands.ts", consumer: "authenticated-operator" },
|
|
17278
17463
|
docs: { module_owner: "cli/src/docs-index-command.ts", consumer: "repo-gates" },
|
|
17279
17464
|
"repo-index": { module_owner: "cli/src/repo-index.ts", consumer: "agent-session" },
|
|
17465
|
+
find: { module_owner: "cli/src/repo-index.ts", consumer: "agent-session" },
|
|
17280
17466
|
tests: { module_owner: "cli/src/test-policy-core.ts", consumer: "repo-gates" },
|
|
17281
17467
|
spawn: { module_owner: "cli/src/spawn-policy-core.ts", consumer: "repo-gates" },
|
|
17282
17468
|
wave: { module_owner: "cli/src/wave-land.ts", consumer: "campaign-orchestrator" },
|
|
@@ -19825,17 +20011,56 @@ async function syncEstateRepoIndex(opts) {
|
|
|
19825
20011
|
|
|
19826
20012
|
// src/repo-index-health.ts
|
|
19827
20013
|
var import_node_fs24 = require("node:fs");
|
|
19828
|
-
|
|
19829
|
-
|
|
19830
|
-
|
|
19831
|
-
|
|
19832
|
-
|
|
19833
|
-
|
|
20014
|
+
|
|
20015
|
+
// testdata/repo-index-golden-queries.json
|
|
20016
|
+
var repo_index_golden_queries_default = {
|
|
20017
|
+
schema: 1,
|
|
20018
|
+
staleThresholdHours: 12,
|
|
20019
|
+
hubRepo: "mutmutco/MMI-Hub",
|
|
20020
|
+
hubEmbMinCoverage: 0.5,
|
|
20021
|
+
maxSyncEmbeds: 100,
|
|
20022
|
+
queries: [
|
|
20023
|
+
{
|
|
20024
|
+
id: "rebuild-symbol-lexical",
|
|
20025
|
+
q: "rebuildRepoIndex",
|
|
20026
|
+
mode: "lexical",
|
|
20027
|
+
expect: {
|
|
20028
|
+
topRepos: ["mutmutco/MMI-Hub"],
|
|
20029
|
+
topPaths: ["cli/src/repo-index.ts"],
|
|
20030
|
+
minScore: 0.1
|
|
20031
|
+
}
|
|
20032
|
+
},
|
|
20033
|
+
{
|
|
20034
|
+
id: "schedules-register-semantic",
|
|
20035
|
+
q: "schedules register",
|
|
20036
|
+
mode: "semantic",
|
|
20037
|
+
expect: {
|
|
20038
|
+
topRepos: ["mutmutco/MMI-Hub"],
|
|
20039
|
+
minScore: 0.05
|
|
20040
|
+
}
|
|
20041
|
+
}
|
|
20042
|
+
]
|
|
20043
|
+
};
|
|
20044
|
+
|
|
20045
|
+
// src/repo-index-health.ts
|
|
20046
|
+
function assertGoldenSuite(raw, source) {
|
|
19834
20047
|
if (!raw || raw.schema !== 1 || !Array.isArray(raw.queries)) {
|
|
19835
|
-
throw new Error(`invalid golden suite at ${
|
|
20048
|
+
throw new Error(`invalid golden suite at ${source}`);
|
|
19836
20049
|
}
|
|
19837
20050
|
return raw;
|
|
19838
20051
|
}
|
|
20052
|
+
function loadGoldenSuite(path2) {
|
|
20053
|
+
let text;
|
|
20054
|
+
try {
|
|
20055
|
+
text = (0, import_node_fs24.readFileSync)(path2, "utf8");
|
|
20056
|
+
} catch (e) {
|
|
20057
|
+
throw new Error(`golden suite unreadable at ${path2}: ${e.message}`);
|
|
20058
|
+
}
|
|
20059
|
+
return assertGoldenSuite(JSON.parse(text), path2);
|
|
20060
|
+
}
|
|
20061
|
+
function defaultGoldenSuite() {
|
|
20062
|
+
return assertGoldenSuite(repo_index_golden_queries_default, "bundled repo-index-golden-queries.json");
|
|
20063
|
+
}
|
|
19839
20064
|
function evaluateQueryHits(query, hits) {
|
|
19840
20065
|
if (!hits.length) {
|
|
19841
20066
|
return { ok: false, code: "empty-hits", detail: `${query.id}: no hits for ${JSON.stringify(query.q)}` };
|
|
@@ -19972,7 +20197,7 @@ async function runRepoIndexHealth(opts) {
|
|
|
19972
20197
|
// src/spawn-policy-core.ts
|
|
19973
20198
|
var import_node_child_process13 = require("node:child_process");
|
|
19974
20199
|
var import_node_fs25 = require("node:fs");
|
|
19975
|
-
var
|
|
20200
|
+
var import_node_path22 = require("node:path");
|
|
19976
20201
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
19977
20202
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
19978
20203
|
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
@@ -20058,7 +20283,7 @@ function runSpawnPolicy(root) {
|
|
|
20058
20283
|
for (const file of files) {
|
|
20059
20284
|
let raw;
|
|
20060
20285
|
try {
|
|
20061
|
-
raw = (0, import_node_fs25.readFileSync)((0,
|
|
20286
|
+
raw = (0, import_node_fs25.readFileSync)((0, import_node_path22.join)(root, file), "utf8");
|
|
20062
20287
|
} catch {
|
|
20063
20288
|
continue;
|
|
20064
20289
|
}
|
|
@@ -20077,7 +20302,7 @@ function runSpawnPolicy(root) {
|
|
|
20077
20302
|
// src/test-policy-core.ts
|
|
20078
20303
|
var import_node_child_process14 = require("node:child_process");
|
|
20079
20304
|
var import_node_fs26 = require("node:fs");
|
|
20080
|
-
var
|
|
20305
|
+
var import_node_path23 = require("node:path");
|
|
20081
20306
|
var POLICY_FILE = "test-policy.json";
|
|
20082
20307
|
var TEST_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
|
20083
20308
|
var PY_TEST_RE = /(?:^|\/)test_[^/]*\.py$|_test\.py$/;
|
|
@@ -20130,7 +20355,7 @@ function isTestPath(path2) {
|
|
|
20130
20355
|
return TEST_RE.test(path2) || PY_TEST_RE.test(path2);
|
|
20131
20356
|
}
|
|
20132
20357
|
function loadPolicy(root, readFile9 = readFileOrNull2) {
|
|
20133
|
-
const raw = readFile9((0,
|
|
20358
|
+
const raw = readFile9((0, import_node_path23.join)(root, POLICY_FILE));
|
|
20134
20359
|
if (raw == null) return { mandatory: [], declared: false };
|
|
20135
20360
|
try {
|
|
20136
20361
|
return { ...JSON.parse(raw), declared: true };
|
|
@@ -20168,11 +20393,11 @@ function classify(changed, policy, present = () => false) {
|
|
|
20168
20393
|
return { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected };
|
|
20169
20394
|
}
|
|
20170
20395
|
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs26.existsSync)(path2)) {
|
|
20171
|
-
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0,
|
|
20396
|
+
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path23.join)(root, p)));
|
|
20172
20397
|
}
|
|
20173
20398
|
function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs26.existsSync)(path2)) {
|
|
20174
20399
|
const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
|
|
20175
|
-
return [...new Set(declared)].filter((p) => !exists((0,
|
|
20400
|
+
return [...new Set(declared)].filter((p) => !exists((0, import_node_path23.join)(root, p)));
|
|
20176
20401
|
}
|
|
20177
20402
|
function evaluate(changed, policy, present = () => false) {
|
|
20178
20403
|
const { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected } = classify(changed, policy, present);
|
|
@@ -20360,7 +20585,7 @@ function runTestPolicy(root, deps = {}) {
|
|
|
20360
20585
|
const refusal = deps.changed ? null : untrustworthyRange(root, base);
|
|
20361
20586
|
const changed = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
|
|
20362
20587
|
const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride(base, root) : { override: null, refusals: [] };
|
|
20363
|
-
const present = (path2) => exists((0,
|
|
20588
|
+
const present = (path2) => exists((0, import_node_path23.join)(root, path2));
|
|
20364
20589
|
const removedByThisDiff = removedPaths(changed);
|
|
20365
20590
|
const staleFindings = [];
|
|
20366
20591
|
const unresolved = unresolvedProtectedEntries(policy, root, exists).filter((p) => !removedByThisDiff.has(p));
|
|
@@ -20517,7 +20742,7 @@ function docsAuditStatus(fetch2, opts) {
|
|
|
20517
20742
|
|
|
20518
20743
|
// src/project-info-sync.ts
|
|
20519
20744
|
var import_node_fs27 = require("node:fs");
|
|
20520
|
-
var
|
|
20745
|
+
var import_node_path24 = require("node:path");
|
|
20521
20746
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
20522
20747
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
20523
20748
|
projectV2 { id }
|
|
@@ -20562,7 +20787,7 @@ function sharedName(entries, fallback) {
|
|
|
20562
20787
|
}
|
|
20563
20788
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
20564
20789
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
20565
|
-
const readmePath = (0,
|
|
20790
|
+
const readmePath = (0, import_node_path24.join)(repoRoot2, "README.md");
|
|
20566
20791
|
if (!(0, import_node_fs27.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
20567
20792
|
const entries = entriesFor(project2, projects);
|
|
20568
20793
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
@@ -20588,8 +20813,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
20588
20813
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
20589
20814
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
20590
20815
|
const orgDocs = [
|
|
20591
|
-
(0, import_node_fs27.existsSync)((0,
|
|
20592
|
-
(0, import_node_fs27.existsSync)((0,
|
|
20816
|
+
(0, import_node_fs27.existsSync)((0, import_node_path24.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
|
|
20817
|
+
(0, import_node_fs27.existsSync)((0, import_node_path24.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
|
|
20593
20818
|
].filter(Boolean);
|
|
20594
20819
|
if (orgDocs.length) lines.push("", "## Organisation docs", "", ...orgDocs);
|
|
20595
20820
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines.join("\n")}
|
|
@@ -21449,7 +21674,7 @@ function writeError(res) {
|
|
|
21449
21674
|
|
|
21450
21675
|
// src/secrets-commands.ts
|
|
21451
21676
|
var import_node_fs28 = require("node:fs");
|
|
21452
|
-
var
|
|
21677
|
+
var import_node_path25 = require("node:path");
|
|
21453
21678
|
var import_node_os10 = require("node:os");
|
|
21454
21679
|
|
|
21455
21680
|
// src/project-runtime.ts
|
|
@@ -21573,11 +21798,11 @@ function collectMap(value, previous = []) {
|
|
|
21573
21798
|
return [...previous, value];
|
|
21574
21799
|
}
|
|
21575
21800
|
async function decryptRailsCredentials(input) {
|
|
21576
|
-
const appDir = (0,
|
|
21801
|
+
const appDir = (0, import_node_path25.resolve)(input.appDir ?? process.cwd());
|
|
21577
21802
|
const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
21578
21803
|
const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
21579
|
-
const credentialsPath = (0,
|
|
21580
|
-
const masterKeyPath = (0,
|
|
21804
|
+
const credentialsPath = (0, import_node_path25.resolve)(appDir, credentialsFile);
|
|
21805
|
+
const masterKeyPath = (0, import_node_path25.resolve)(appDir, masterKeyFile);
|
|
21581
21806
|
const env = {
|
|
21582
21807
|
...process.env,
|
|
21583
21808
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
@@ -21594,8 +21819,8 @@ async function decryptRailsCredentials(input) {
|
|
|
21594
21819
|
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
21595
21820
|
"puts JSON.generate(config.config)"
|
|
21596
21821
|
].join("\n");
|
|
21597
|
-
const scriptDir = (0, import_node_fs28.mkdtempSync)((0,
|
|
21598
|
-
const scriptPath = (0,
|
|
21822
|
+
const scriptDir = (0, import_node_fs28.mkdtempSync)((0, import_node_path25.join)((0, import_node_os10.tmpdir)(), "mmi-rails-decrypt-"));
|
|
21823
|
+
const scriptPath = (0, import_node_path25.join)(scriptDir, "decrypt.rb");
|
|
21599
21824
|
(0, import_node_fs28.writeFileSync)(scriptPath, script, "utf8");
|
|
21600
21825
|
try {
|
|
21601
21826
|
const args = ["exec", "ruby", scriptPath];
|
|
@@ -21698,7 +21923,7 @@ function registerSecretsCommands(program3) {
|
|
|
21698
21923
|
let body;
|
|
21699
21924
|
if (o.file) {
|
|
21700
21925
|
try {
|
|
21701
|
-
body = (0, import_node_fs28.readFileSync)((0,
|
|
21926
|
+
body = (0, import_node_fs28.readFileSync)((0, import_node_path25.resolve)(o.file), "utf8");
|
|
21702
21927
|
} catch (e) {
|
|
21703
21928
|
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
21704
21929
|
}
|
|
@@ -21803,7 +22028,7 @@ function registerSecretsCommands(program3) {
|
|
|
21803
22028
|
{
|
|
21804
22029
|
...d,
|
|
21805
22030
|
decryptRailsCredentials,
|
|
21806
|
-
removeFile: (path2) => (0, import_node_fs28.unlinkSync)((0,
|
|
22031
|
+
removeFile: (path2) => (0, import_node_fs28.unlinkSync)((0, import_node_path25.resolve)(o.appDir ?? process.cwd(), path2))
|
|
21807
22032
|
},
|
|
21808
22033
|
{
|
|
21809
22034
|
repo: o.repo,
|
|
@@ -22859,7 +23084,7 @@ function registerSchedulesCommands(program3) {
|
|
|
22859
23084
|
|
|
22860
23085
|
// src/file-lock.ts
|
|
22861
23086
|
var import_promises5 = require("node:fs/promises");
|
|
22862
|
-
var
|
|
23087
|
+
var import_node_path26 = require("node:path");
|
|
22863
23088
|
var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
22864
23089
|
var IMMEDIATE_RETRY_BUDGET = 3;
|
|
22865
23090
|
var FileLockBusyError = class extends Error {
|
|
@@ -22944,7 +23169,7 @@ async function releaseFileLock(lockPath, guard) {
|
|
|
22944
23169
|
}
|
|
22945
23170
|
async function withFileLock(lockPath, opts, fn) {
|
|
22946
23171
|
const resolved = resolveFileLockOpts(opts);
|
|
22947
|
-
await (0, import_promises5.mkdir)((0,
|
|
23172
|
+
await (0, import_promises5.mkdir)((0, import_node_path26.dirname)(lockPath), { recursive: true }).catch(() => void 0);
|
|
22948
23173
|
const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
|
|
22949
23174
|
try {
|
|
22950
23175
|
return await fn();
|
|
@@ -22955,7 +23180,7 @@ async function withFileLock(lockPath, opts, fn) {
|
|
|
22955
23180
|
|
|
22956
23181
|
// src/schedules-lift-command.ts
|
|
22957
23182
|
var import_promises6 = require("node:fs/promises");
|
|
22958
|
-
var
|
|
23183
|
+
var import_node_path27 = require("node:path");
|
|
22959
23184
|
|
|
22960
23185
|
// src/schedules-lift.ts
|
|
22961
23186
|
var SCHEDULE_HEADER_FIELDS = ["schedule", "what", "owner", "cadence", "executor", "llm", "output", "breaks", "kill"];
|
|
@@ -23061,7 +23286,7 @@ async function readWorkflowFiles(dir) {
|
|
|
23061
23286
|
const files = [];
|
|
23062
23287
|
for (const name of names.sort()) {
|
|
23063
23288
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
23064
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises6.readFile)((0,
|
|
23289
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises6.readFile)((0, import_node_path27.join)(dir, name), "utf8") });
|
|
23065
23290
|
}
|
|
23066
23291
|
return files;
|
|
23067
23292
|
}
|
|
@@ -23605,7 +23830,7 @@ function registerQueryCommands(program3) {
|
|
|
23605
23830
|
// src/bootstrap-commands.ts
|
|
23606
23831
|
var import_node_fs30 = require("node:fs");
|
|
23607
23832
|
var import_node_os11 = require("node:os");
|
|
23608
|
-
var
|
|
23833
|
+
var import_node_path28 = require("node:path");
|
|
23609
23834
|
|
|
23610
23835
|
// src/bootstrap-drift.ts
|
|
23611
23836
|
function byteComparableSeeds(manifest, cls) {
|
|
@@ -24474,7 +24699,7 @@ function registerBootstrapCommands(program3) {
|
|
|
24474
24699
|
const readFile9 = (p) => (0, import_node_fs30.existsSync)(p) ? (0, import_node_fs30.readFileSync)(p, "utf8") : null;
|
|
24475
24700
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
24476
24701
|
const putSeed = async (target, content, ref, sha) => {
|
|
24477
|
-
const tmp = (0,
|
|
24702
|
+
const tmp = (0, import_node_path28.join)((0, import_node_os11.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
24478
24703
|
(0, import_node_fs30.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
24479
24704
|
try {
|
|
24480
24705
|
await gh(contentPutInputArgs(repo, target, tmp));
|
|
@@ -24738,11 +24963,11 @@ LIVE apply to ${repo}:
|
|
|
24738
24963
|
|
|
24739
24964
|
// src/stage-commands.ts
|
|
24740
24965
|
var import_node_fs32 = require("node:fs");
|
|
24741
|
-
var
|
|
24966
|
+
var import_node_path30 = require("node:path");
|
|
24742
24967
|
|
|
24743
24968
|
// src/port-registry.ts
|
|
24744
24969
|
var import_node_fs31 = require("node:fs");
|
|
24745
|
-
var
|
|
24970
|
+
var import_node_path29 = require("node:path");
|
|
24746
24971
|
|
|
24747
24972
|
// ../infra/port-geometry.mjs
|
|
24748
24973
|
var PORT_BLOCK = 100;
|
|
@@ -24795,22 +25020,22 @@ function existingPortRange(repo, registry2) {
|
|
|
24795
25020
|
return registry2[repo] ?? null;
|
|
24796
25021
|
}
|
|
24797
25022
|
function portRangeInfraAt(root, source) {
|
|
24798
|
-
const registryPath = (0,
|
|
24799
|
-
const ddbScriptPath = (0,
|
|
25023
|
+
const registryPath = (0, import_node_path29.join)(root, "infra", "port-ranges.json");
|
|
25024
|
+
const ddbScriptPath = (0, import_node_path29.join)(root, "infra", "port-ddb.mjs");
|
|
24800
25025
|
if (!(0, import_node_fs31.existsSync)(registryPath) || !(0, import_node_fs31.existsSync)(ddbScriptPath)) return null;
|
|
24801
25026
|
return { root, source, registryPath, ddbScriptPath };
|
|
24802
25027
|
}
|
|
24803
25028
|
function resolvePortRangeInfra(cwd, packageDir) {
|
|
24804
25029
|
const direct = portRangeInfraAt(cwd, "cwd");
|
|
24805
25030
|
if (direct) return direct;
|
|
24806
|
-
for (let dir = cwd; ; dir = (0,
|
|
24807
|
-
const sibling = portRangeInfraAt((0,
|
|
25031
|
+
for (let dir = cwd; ; dir = (0, import_node_path29.dirname)(dir)) {
|
|
25032
|
+
const sibling = portRangeInfraAt((0, import_node_path29.join)(dir, "MMI-Hub"), "sibling-hub");
|
|
24808
25033
|
if (sibling) return sibling;
|
|
24809
|
-
const parent = (0,
|
|
25034
|
+
const parent = (0, import_node_path29.dirname)(dir);
|
|
24810
25035
|
if (parent === dir) break;
|
|
24811
25036
|
}
|
|
24812
25037
|
if (packageDir) {
|
|
24813
|
-
const pkgRoot = (0,
|
|
25038
|
+
const pkgRoot = (0, import_node_path29.join)(packageDir, "..", "..");
|
|
24814
25039
|
const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
|
|
24815
25040
|
if (pkgFrom) return pkgFrom;
|
|
24816
25041
|
}
|
|
@@ -25004,8 +25229,8 @@ function registerStageCommands(program3) {
|
|
|
25004
25229
|
const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
|
|
25005
25230
|
return decideStage({
|
|
25006
25231
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
25007
|
-
hasCompose: (0, import_node_fs32.existsSync)((0,
|
|
25008
|
-
hasEnvExample: (0, import_node_fs32.existsSync)((0,
|
|
25232
|
+
hasCompose: (0, import_node_fs32.existsSync)((0, import_node_path30.join)(process.cwd(), "docker-compose.yml")),
|
|
25233
|
+
hasEnvExample: (0, import_node_fs32.existsSync)((0, import_node_path30.join)(process.cwd(), ".env.example"))
|
|
25009
25234
|
});
|
|
25010
25235
|
}
|
|
25011
25236
|
async function fetchStageVaultEnvMerge() {
|
|
@@ -25445,7 +25670,7 @@ function registerBoardCommands(program3) {
|
|
|
25445
25670
|
// src/merge-cleanup.ts
|
|
25446
25671
|
var import_node_fs33 = require("node:fs");
|
|
25447
25672
|
var import_promises8 = require("node:fs/promises");
|
|
25448
|
-
var
|
|
25673
|
+
var import_node_path32 = require("node:path");
|
|
25449
25674
|
var import_node_os12 = require("node:os");
|
|
25450
25675
|
var import_node_child_process16 = require("node:child_process");
|
|
25451
25676
|
|
|
@@ -25533,7 +25758,7 @@ function boardAdvanceFailureMessage(result) {
|
|
|
25533
25758
|
|
|
25534
25759
|
// src/deferred-registry-store.ts
|
|
25535
25760
|
var import_promises7 = require("node:fs/promises");
|
|
25536
|
-
var
|
|
25761
|
+
var import_node_path31 = require("node:path");
|
|
25537
25762
|
var sleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
25538
25763
|
async function atomicWrite(target, contents) {
|
|
25539
25764
|
const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
@@ -25584,12 +25809,12 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
|
|
|
25584
25809
|
},
|
|
25585
25810
|
// Standalone atomic write — THROWS on failure (no best-effort swallow, #2846).
|
|
25586
25811
|
write: async (entries) => {
|
|
25587
|
-
await (0, import_promises7.mkdir)((0,
|
|
25812
|
+
await (0, import_promises7.mkdir)((0, import_node_path31.dirname)(registryPath), { recursive: true });
|
|
25588
25813
|
await atomicWrite(registryPath, serializeDeferredWorktrees(entries));
|
|
25589
25814
|
},
|
|
25590
25815
|
// Serialized read-modify-write under the repo-wide lock (#2846).
|
|
25591
25816
|
update: async (mutate) => {
|
|
25592
|
-
await (0, import_promises7.mkdir)((0,
|
|
25817
|
+
await (0, import_promises7.mkdir)((0, import_node_path31.dirname)(registryPath), { recursive: true });
|
|
25593
25818
|
const deadline = Date.now() + opts.maxWaitMs;
|
|
25594
25819
|
for (; ; ) {
|
|
25595
25820
|
const guard = await acquireLock(lockPath, opts, deadline);
|
|
@@ -25737,7 +25962,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
25737
25962
|
);
|
|
25738
25963
|
const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
25739
25964
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
25740
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
25965
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path32.dirname)((0, import_node_path32.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
25741
25966
|
const gcActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
|
|
25742
25967
|
const owners = readWorktreeOwners(primaryRepoRoot);
|
|
25743
25968
|
const removalNow = Date.now();
|
|
@@ -25882,8 +26107,8 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
|
|
|
25882
26107
|
const commits = JSON.parse(raw).commits ?? [];
|
|
25883
26108
|
const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
|
|
25884
26109
|
if (!body) return void 0;
|
|
25885
|
-
const dir = (0, import_node_fs33.mkdtempSync)((0,
|
|
25886
|
-
const path2 = (0,
|
|
26110
|
+
const dir = (0, import_node_fs33.mkdtempSync)((0, import_node_path32.join)((0, import_node_os12.tmpdir)(), "mmi-squash-body-"));
|
|
26111
|
+
const path2 = (0, import_node_path32.join)(dir, "body.txt");
|
|
25887
26112
|
(0, import_node_fs33.writeFileSync)(path2, `${body}
|
|
25888
26113
|
`, "utf8");
|
|
25889
26114
|
return { path: path2, cleanup: () => {
|
|
@@ -26403,7 +26628,7 @@ async function checkDocsIndexAtHead(opts, deps) {
|
|
|
26403
26628
|
// src/worktree-lifecycle-commands.ts
|
|
26404
26629
|
var import_node_fs34 = require("node:fs");
|
|
26405
26630
|
var import_promises9 = require("node:fs/promises");
|
|
26406
|
-
var
|
|
26631
|
+
var import_node_path33 = require("node:path");
|
|
26407
26632
|
var GH_TIMEOUT_MS = 2e4;
|
|
26408
26633
|
var DEFAULT_BASE = "origin/development";
|
|
26409
26634
|
var DEFAULT_REMOTE = "origin";
|
|
@@ -26549,7 +26774,7 @@ function classifyStaleLeaks(input) {
|
|
|
26549
26774
|
var defaultOrphanDirScanDeps = {
|
|
26550
26775
|
listDirs: (root) => {
|
|
26551
26776
|
try {
|
|
26552
|
-
return (0, import_node_fs34.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0,
|
|
26777
|
+
return (0, import_node_fs34.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path33.join)(root, e.name));
|
|
26553
26778
|
} catch {
|
|
26554
26779
|
return [];
|
|
26555
26780
|
}
|
|
@@ -26702,13 +26927,13 @@ function registerWorktreeCommands(program3) {
|
|
|
26702
26927
|
const detached = headBorn && !symbolicBranch;
|
|
26703
26928
|
const branch = symbolicBranch || (detached ? "HEAD" : "");
|
|
26704
26929
|
if (!wtPath || !branch) return fail("worktree land: not inside a git worktree");
|
|
26705
|
-
const gitFile = (0,
|
|
26930
|
+
const gitFile = (0, import_node_path33.join)(wtPath, ".git");
|
|
26706
26931
|
const isLinked = (0, import_node_fs34.existsSync)(gitFile) && (0, import_node_fs34.statSync)(gitFile).isFile();
|
|
26707
26932
|
if (apply && !isLinked) {
|
|
26708
26933
|
return fail("worktree land: run from inside the linked worktree you want to land (this is the primary checkout)");
|
|
26709
26934
|
}
|
|
26710
26935
|
const commonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
26711
|
-
const primaryCheckout = commonDir ? (0,
|
|
26936
|
+
const primaryCheckout = commonDir ? (0, import_node_path33.dirname)(commonDir) : wtPath;
|
|
26712
26937
|
const localBranchNames = await execFileP2("git", ["-C", primaryCheckout, "for-each-ref", "--format=%(refname:short)", "refs/heads"], { timeout: GIT_TIMEOUT_MS }).then(({ stdout }) => new Set((stdout || "").split("\n").map((l) => l.trim()).filter(Boolean))).catch(() => void 0);
|
|
26713
26938
|
const orphan = classifyOrphanedWorktree({
|
|
26714
26939
|
branch,
|
|
@@ -26919,7 +27144,7 @@ async function gatherWorktreeContext() {
|
|
|
26919
27144
|
if (s) stages.push({ path: wt.path, port: s.port });
|
|
26920
27145
|
}
|
|
26921
27146
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
26922
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
27147
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path33.dirname)((0, import_node_path33.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
26923
27148
|
const wtRoot = siblingMmiWorktreesRoot(primaryRepoRoot);
|
|
26924
27149
|
let orphanDirs = [];
|
|
26925
27150
|
if ((0, import_node_fs34.existsSync)(wtRoot)) {
|
|
@@ -27604,7 +27829,7 @@ ${lines}`, {
|
|
|
27604
27829
|
|
|
27605
27830
|
// src/train-commands.ts
|
|
27606
27831
|
var import_node_fs36 = require("node:fs");
|
|
27607
|
-
var
|
|
27832
|
+
var import_node_path34 = require("node:path");
|
|
27608
27833
|
|
|
27609
27834
|
// src/train-status.ts
|
|
27610
27835
|
function buildTrainStatusReport(input) {
|
|
@@ -27644,7 +27869,7 @@ function formatTrainStatus(r) {
|
|
|
27644
27869
|
// src/train-commands.ts
|
|
27645
27870
|
function readRepoVersion() {
|
|
27646
27871
|
try {
|
|
27647
|
-
return JSON.parse((0, import_node_fs36.readFileSync)((0,
|
|
27872
|
+
return JSON.parse((0, import_node_fs36.readFileSync)((0, import_node_path34.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
27648
27873
|
} catch {
|
|
27649
27874
|
return void 0;
|
|
27650
27875
|
}
|
|
@@ -27792,7 +28017,7 @@ function registerDeployCommands(program3) {
|
|
|
27792
28017
|
// src/discovery-commands.ts
|
|
27793
28018
|
var import_node_fs37 = require("node:fs");
|
|
27794
28019
|
var import_node_os13 = require("node:os");
|
|
27795
|
-
var
|
|
28020
|
+
var import_node_path35 = require("node:path");
|
|
27796
28021
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
27797
28022
|
async function collectStatus() {
|
|
27798
28023
|
const repo = await resolveRepo();
|
|
@@ -27982,8 +28207,8 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
27982
28207
|
}
|
|
27983
28208
|
const home = (0, import_node_os13.homedir)();
|
|
27984
28209
|
const plugin = onboardPluginGate({
|
|
27985
|
-
readKnown: () => readFileSyncSafe((0,
|
|
27986
|
-
readSettings: () => readFileSyncSafe((0,
|
|
28210
|
+
readKnown: () => readFileSyncSafe((0, import_node_path35.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs37.readFileSync),
|
|
28211
|
+
readSettings: () => readFileSyncSafe((0, import_node_path35.join)(home, ".claude", "settings.json"), import_node_fs37.readFileSync)
|
|
27987
28212
|
});
|
|
27988
28213
|
return { track, board, registry: registry2, secrets, plugin, estateCli, nextCommand };
|
|
27989
28214
|
}
|
|
@@ -29246,6 +29471,63 @@ var surfaces_default = {
|
|
|
29246
29471
|
"Some read, write, and web-fetch tool outputs are not rewriteable by the host.",
|
|
29247
29472
|
"Provisioned skills and agents become discoverable only after a fresh session."
|
|
29248
29473
|
]
|
|
29474
|
+
},
|
|
29475
|
+
{
|
|
29476
|
+
token: "jervcode",
|
|
29477
|
+
displayName: "JervCode",
|
|
29478
|
+
lifecycle: "active",
|
|
29479
|
+
artifactIds: ["mmi-pi-plugin", "mmi-skills", "mmi-cli"],
|
|
29480
|
+
plannedArtifactIds: [],
|
|
29481
|
+
assembly: {
|
|
29482
|
+
mode: "source-tree",
|
|
29483
|
+
rootPath: ".",
|
|
29484
|
+
sync: []
|
|
29485
|
+
},
|
|
29486
|
+
install: {
|
|
29487
|
+
mechanism: "managed-config",
|
|
29488
|
+
locator: "~/.pi/agent/settings.json"
|
|
29489
|
+
},
|
|
29490
|
+
upgrade: {
|
|
29491
|
+
mechanism: "reinstall",
|
|
29492
|
+
reload: "restart"
|
|
29493
|
+
},
|
|
29494
|
+
skills: {
|
|
29495
|
+
delivery: "manifest",
|
|
29496
|
+
sourceSurfaceId: "mmi-skills",
|
|
29497
|
+
artifactId: "mmi-skills",
|
|
29498
|
+
invocation: {
|
|
29499
|
+
entry: "/mmi",
|
|
29500
|
+
any: "/<skill>"
|
|
29501
|
+
}
|
|
29502
|
+
},
|
|
29503
|
+
hooks: {
|
|
29504
|
+
adapterPath: ".pi-plugin/package.json",
|
|
29505
|
+
sourceSurfaceId: "mmi-hook-policy",
|
|
29506
|
+
gates: [],
|
|
29507
|
+
probe: "unsupported",
|
|
29508
|
+
execution: "unsupported",
|
|
29509
|
+
preToolUse: "unsupported",
|
|
29510
|
+
postToolOutput: "unsupported",
|
|
29511
|
+
finalOutput: "unsupported"
|
|
29512
|
+
},
|
|
29513
|
+
cli: {
|
|
29514
|
+
delivery: "standalone",
|
|
29515
|
+
artifactId: "mmi-cli"
|
|
29516
|
+
},
|
|
29517
|
+
ownership: {
|
|
29518
|
+
trust: "host",
|
|
29519
|
+
cache: "host",
|
|
29520
|
+
repair: "mmi-cli"
|
|
29521
|
+
},
|
|
29522
|
+
certification: {
|
|
29523
|
+
hostCommand: "jervcode",
|
|
29524
|
+
versionArgs: ["--version"]
|
|
29525
|
+
},
|
|
29526
|
+
enforcementCeilings: [
|
|
29527
|
+
"Pi reads the user package set from ~/.pi/agent/settings.json at launch: a live seat keeps the packages it started with, the mmi-cli heal refuses to edit the file while PI_SESSION_ID is set, and a new registration reaches JervCode only on the next launch.",
|
|
29528
|
+
"No hook event is wired on this host \u2014 there is no PreToolUse deny gate, no vault-edit gate, and no secret-output redaction or detection; the hook policy artifact does not ship here.",
|
|
29529
|
+
"The settings entry points into a version-pinned plugin cache clone; a release that mints a new clone leaves the entry stale until `mmi-cli doctor` re-points it, and Pi keeps loading the dead path silently until then."
|
|
29530
|
+
]
|
|
29249
29531
|
}
|
|
29250
29532
|
],
|
|
29251
29533
|
surfaces: [
|
|
@@ -29556,12 +29838,38 @@ var surfaces_default = {
|
|
|
29556
29838
|
applicability: "master DM notifications only; chatops out by default",
|
|
29557
29839
|
versionCoordinated: false,
|
|
29558
29840
|
publishVisibility: "n/a"
|
|
29841
|
+
},
|
|
29842
|
+
{
|
|
29843
|
+
id: "mmi-pi-plugin",
|
|
29844
|
+
classification: "packaging",
|
|
29845
|
+
kind: "plugin",
|
|
29846
|
+
ownerPath: ".pi-plugin/package.json",
|
|
29847
|
+
deliveryPath: ".pi-plugin/package.json",
|
|
29848
|
+
delivery: "release",
|
|
29849
|
+
applicability: "JervCode (branded launcher of the pi coding agent). A generated pi package wrapper whose `pi` block references the canonical skills tree by relative path inside the shipped clone \u2014 content is never copied. mmi-cli registers the wrapper directory as a local-path entry in the `packages` array of ~/.pi/agent/settings.json.",
|
|
29850
|
+
versionCoordinated: true,
|
|
29851
|
+
surfaceToken: "jervcode",
|
|
29852
|
+
versionPaths: [
|
|
29853
|
+
{ path: ".pi-plugin/package.json", pointer: "version" }
|
|
29854
|
+
],
|
|
29855
|
+
artifactIdentity: {
|
|
29856
|
+
kind: "sha256-tree",
|
|
29857
|
+
paths: [".pi-plugin/package.json", "skills"]
|
|
29858
|
+
},
|
|
29859
|
+
verify: [
|
|
29860
|
+
{
|
|
29861
|
+
command: "node",
|
|
29862
|
+
args: ["scripts/check-jervcode-consumer.mjs"],
|
|
29863
|
+
expected: "JervCode consumer fitness: clean ({version})"
|
|
29864
|
+
}
|
|
29865
|
+
],
|
|
29866
|
+
publishVisibility: "public"
|
|
29559
29867
|
}
|
|
29560
29868
|
]
|
|
29561
29869
|
};
|
|
29562
29870
|
|
|
29563
29871
|
// src/surface-doctor.ts
|
|
29564
|
-
var TOKENS = /* @__PURE__ */ new Set(["claude", "codex", "kimi", "cursor", "kilo"]);
|
|
29872
|
+
var TOKENS = /* @__PURE__ */ new Set(["claude", "codex", "kimi", "cursor", "kilo", "jervcode"]);
|
|
29565
29873
|
function isActiveToken(value) {
|
|
29566
29874
|
return TOKENS.has(value);
|
|
29567
29875
|
}
|
|
@@ -30625,17 +30933,17 @@ function parseOriginRepo(remoteUrl) {
|
|
|
30625
30933
|
}
|
|
30626
30934
|
function ghHostsConfigPath(env, platform2) {
|
|
30627
30935
|
const sep3 = platform2 === "win32" ? "\\" : "/";
|
|
30628
|
-
const
|
|
30936
|
+
const join33 = (...parts) => parts.join(sep3);
|
|
30629
30937
|
const explicit = env.GH_CONFIG_DIR?.trim();
|
|
30630
|
-
if (explicit) return
|
|
30938
|
+
if (explicit) return join33(explicit, "hosts.yml");
|
|
30631
30939
|
if (platform2 === "win32") {
|
|
30632
30940
|
const appData = (env.AppData ?? env.APPDATA)?.trim();
|
|
30633
|
-
return appData ?
|
|
30941
|
+
return appData ? join33(appData, "GitHub CLI", "hosts.yml") : void 0;
|
|
30634
30942
|
}
|
|
30635
30943
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
30636
|
-
if (xdg) return
|
|
30944
|
+
if (xdg) return join33(xdg, "gh", "hosts.yml");
|
|
30637
30945
|
const home = env.HOME?.trim();
|
|
30638
|
-
return home ?
|
|
30946
|
+
return home ? join33(home, ".config", "gh", "hosts.yml") : void 0;
|
|
30639
30947
|
}
|
|
30640
30948
|
function parseGhHostsAccounts(yaml, host = "github.com") {
|
|
30641
30949
|
let hostIndent = null;
|
|
@@ -30687,7 +30995,7 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
30687
30995
|
// src/doctor-io.ts
|
|
30688
30996
|
var import_node_fs38 = require("node:fs");
|
|
30689
30997
|
var import_node_os14 = require("node:os");
|
|
30690
|
-
var
|
|
30998
|
+
var import_node_path36 = require("node:path");
|
|
30691
30999
|
var import_node_child_process17 = require("node:child_process");
|
|
30692
31000
|
var import_node_util8 = require("node:util");
|
|
30693
31001
|
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process17.execFile);
|
|
@@ -30695,7 +31003,7 @@ var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
|
30695
31003
|
function installedClaudePluginVersion() {
|
|
30696
31004
|
try {
|
|
30697
31005
|
const file = JSON.parse(
|
|
30698
|
-
(0, import_node_fs38.readFileSync)((0,
|
|
31006
|
+
(0, import_node_fs38.readFileSync)((0, import_node_path36.join)((0, import_node_os14.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
30699
31007
|
);
|
|
30700
31008
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
30701
31009
|
if (versions.length === 0) return void 0;
|
|
@@ -30716,17 +31024,22 @@ function installedSurfacePluginVersion(surface) {
|
|
|
30716
31024
|
const token = surfaceToken(surface);
|
|
30717
31025
|
if (token === "kilo") {
|
|
30718
31026
|
try {
|
|
30719
|
-
const stamp = (0, import_node_fs38.readFileSync)((0,
|
|
31027
|
+
const stamp = (0, import_node_fs38.readFileSync)((0, import_node_path36.join)((0, import_node_os14.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
|
|
30720
31028
|
return stamp || void 0;
|
|
30721
31029
|
} catch {
|
|
30722
31030
|
return void 0;
|
|
30723
31031
|
}
|
|
30724
31032
|
}
|
|
30725
31033
|
if (token === "cursor") {
|
|
30726
|
-
return manifestVersion((0,
|
|
31034
|
+
return manifestVersion((0, import_node_path36.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
|
|
31035
|
+
}
|
|
31036
|
+
if (token === "jervcode") {
|
|
31037
|
+
const entry = mmiPiWrapperEntry();
|
|
31038
|
+
if (!entry) return void 0;
|
|
31039
|
+
return manifestVersion((0, import_node_path36.join)(decodeURIComponent(entry.replace(/^file:\/\/\/?/, "")), "package.json"));
|
|
30727
31040
|
}
|
|
30728
31041
|
if (token === "kimi") {
|
|
30729
|
-
return manifestVersion((0,
|
|
31042
|
+
return manifestVersion((0, import_node_path36.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
|
|
30730
31043
|
}
|
|
30731
31044
|
if (token === "claude") return installedClaudePluginVersion();
|
|
30732
31045
|
if (token !== "codex") return void 0;
|
|
@@ -30764,7 +31077,7 @@ function worktreeRootSync() {
|
|
|
30764
31077
|
}
|
|
30765
31078
|
var gitignorePath = () => {
|
|
30766
31079
|
const root = worktreeRootSync();
|
|
30767
|
-
return root === null ? null : (0,
|
|
31080
|
+
return root === null ? null : (0, import_node_path36.join)(root, ".gitignore");
|
|
30768
31081
|
};
|
|
30769
31082
|
function readGitignore() {
|
|
30770
31083
|
const path2 = gitignorePath();
|
|
@@ -30803,7 +31116,7 @@ async function repoRoot() {
|
|
|
30803
31116
|
}
|
|
30804
31117
|
function hasRepoLocalWorktrees() {
|
|
30805
31118
|
const root = worktreeRootSync();
|
|
30806
|
-
return root !== null && (0, import_node_fs38.existsSync)((0,
|
|
31119
|
+
return root !== null && (0, import_node_fs38.existsSync)((0, import_node_path36.join)(root, ".worktrees"));
|
|
30807
31120
|
}
|
|
30808
31121
|
|
|
30809
31122
|
// src/index.ts
|
|
@@ -30848,7 +31161,7 @@ function ghMultiAccountCaveat(announcedLogin) {
|
|
|
30848
31161
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
30849
31162
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
30850
31163
|
function envHealLockPath(home) {
|
|
30851
|
-
return (0,
|
|
31164
|
+
return (0, import_node_path37.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
30852
31165
|
}
|
|
30853
31166
|
async function withEnvHealLock(what, run) {
|
|
30854
31167
|
try {
|
|
@@ -30974,14 +31287,14 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
30974
31287
|
const home = (0, import_node_os15.homedir)();
|
|
30975
31288
|
const rows = marketplaceRows(
|
|
30976
31289
|
MMI_MARKETPLACE_NAME,
|
|
30977
|
-
readFileSyncSafe((0,
|
|
30978
|
-
readFileSyncSafe((0,
|
|
31290
|
+
readFileSyncSafe((0, import_node_path37.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs39.readFileSync),
|
|
31291
|
+
readFileSyncSafe((0, import_node_path37.join)(home, ".claude", "settings.json"), import_node_fs39.readFileSync),
|
|
30979
31292
|
// #3974: this CLI heals these rows, so report mode names the doctor run rather than the hand
|
|
30980
31293
|
// edit. Unconditional ? it is a fact about mmi-cli, not about the lane this run is on.
|
|
30981
31294
|
true
|
|
30982
31295
|
);
|
|
30983
31296
|
const pending = readMarketplacePinPending(
|
|
30984
|
-
(0,
|
|
31297
|
+
(0, import_node_path37.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"),
|
|
30985
31298
|
MMI_MARKETPLACE_NAME
|
|
30986
31299
|
);
|
|
30987
31300
|
if (!pending) return rows;
|
|
@@ -31007,9 +31320,9 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
31007
31320
|
if (detectSurface(process.env) === "codex") return void 0;
|
|
31008
31321
|
const home = (0, import_node_os15.homedir)();
|
|
31009
31322
|
const names = [MMI_MARKETPLACE_NAME];
|
|
31010
|
-
const result = applyOrgMarketplacePins((0,
|
|
31323
|
+
const result = applyOrgMarketplacePins((0, import_node_path37.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), names);
|
|
31011
31324
|
if (result?.wrote) {
|
|
31012
|
-
writeMarketplacePinPending((0,
|
|
31325
|
+
writeMarketplacePinPending((0, import_node_path37.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"), names);
|
|
31013
31326
|
}
|
|
31014
31327
|
return result;
|
|
31015
31328
|
} catch {
|
|
@@ -31025,7 +31338,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
31025
31338
|
// adopted the generated routing index (MMG-Unlive: `docs/Archive/**` only, no index, no gate.yml) would
|
|
31026
31339
|
// get a permanent ? demanding an artifact it never asked for.
|
|
31027
31340
|
docsIndexState: (root) => {
|
|
31028
|
-
if (!(0, import_node_fs39.existsSync)((0,
|
|
31341
|
+
if (!(0, import_node_fs39.existsSync)((0, import_node_path37.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
31029
31342
|
const real = createDocsIndexDeps(root);
|
|
31030
31343
|
let docs2;
|
|
31031
31344
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -31034,7 +31347,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
31034
31347
|
},
|
|
31035
31348
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
31036
31349
|
healDocsIndex: (root) => {
|
|
31037
|
-
if (!(0, import_node_fs39.existsSync)((0,
|
|
31350
|
+
if (!(0, import_node_fs39.existsSync)((0, import_node_path37.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
31038
31351
|
const real = createDocsIndexDeps(root);
|
|
31039
31352
|
let docs2;
|
|
31040
31353
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -31368,7 +31681,7 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
31368
31681
|
});
|
|
31369
31682
|
var rules = program2.command("rules").description("org-managed .gitignore delivery");
|
|
31370
31683
|
rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").option("--json", "machine-readable output").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
|
|
31371
|
-
const path2 = (0,
|
|
31684
|
+
const path2 = (0, import_node_path37.join)(process.cwd(), ".gitignore");
|
|
31372
31685
|
const current = (0, import_node_fs39.existsSync)(path2) ? (0, import_node_fs39.readFileSync)(path2, "utf8") : null;
|
|
31373
31686
|
const plan = planManagedGitignore(current);
|
|
31374
31687
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
@@ -31537,7 +31850,7 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
|
|
|
31537
31850
|
if (!Number.isFinite(limit) || limit < 1) return fail("worktree gc: --limit must be a positive integer");
|
|
31538
31851
|
let root;
|
|
31539
31852
|
if (o.root !== void 0) {
|
|
31540
|
-
root = (0,
|
|
31853
|
+
root = (0, import_node_path37.resolve)(o.root);
|
|
31541
31854
|
if (!(0, import_node_fs39.existsSync)(root) || !(0, import_node_fs39.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
|
|
31542
31855
|
const gcRepoRoot = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
31543
31856
|
if (isPathUnderDirectory(gcRepoRoot, root)) {
|
|
@@ -31653,7 +31966,7 @@ function acquireWorktreeSetupLock(worktreeRoot) {
|
|
|
31653
31966
|
};
|
|
31654
31967
|
};
|
|
31655
31968
|
try {
|
|
31656
|
-
(0, import_node_fs39.mkdirSync)((0,
|
|
31969
|
+
(0, import_node_fs39.mkdirSync)((0, import_node_path37.dirname)(lockPath), { recursive: true });
|
|
31657
31970
|
return take();
|
|
31658
31971
|
} catch {
|
|
31659
31972
|
try {
|
|
@@ -31965,7 +32278,7 @@ repoIndex.command("publish").description("publish this checkout's projection to
|
|
|
31965
32278
|
return await failGraceful(e.message);
|
|
31966
32279
|
}
|
|
31967
32280
|
});
|
|
31968
|
-
|
|
32281
|
+
async function runRepoIndexSearchCommand(query, o, defaultMode) {
|
|
31969
32282
|
try {
|
|
31970
32283
|
const limit = Math.max(1, Math.min(100, Number(o.limit ?? 20) || 20));
|
|
31971
32284
|
const useLocal = o.local === true && o.cloud !== true;
|
|
@@ -31988,7 +32301,7 @@ repoIndex.command("search").description("search Hub cloud by default (lexical/hy
|
|
|
31988
32301
|
}
|
|
31989
32302
|
return;
|
|
31990
32303
|
}
|
|
31991
|
-
const mode = o.semantic ? "semantic" : o.lexical ? "lexical" :
|
|
32304
|
+
const mode = o.semantic ? "semantic" : o.lexical ? "lexical" : defaultMode;
|
|
31992
32305
|
const cfg = await loadConfig();
|
|
31993
32306
|
const res = await searchRepoIndexCloud(query, { mode, limit, repo: o.repo }, registryClientDeps(cfg));
|
|
31994
32307
|
if (!res.ok) return await failGraceful(res.error);
|
|
@@ -32007,7 +32320,13 @@ repoIndex.command("search").description("search Hub cloud by default (lexical/hy
|
|
|
32007
32320
|
} catch (e) {
|
|
32008
32321
|
return await failGraceful(e.message);
|
|
32009
32322
|
}
|
|
32010
|
-
}
|
|
32323
|
+
}
|
|
32324
|
+
var REPO_INDEX_SEARCH_WHEN = 'beats grep for cross-repo questions, "where does X happen", and unknown filenames; grep wins inside a known checkout';
|
|
32325
|
+
function repoIndexSearchOptions(cmd) {
|
|
32326
|
+
return cmd.option("--limit <n>", "max hits", "20").option("--json", "machine-readable hits").option("--local", "search only the local projection (rebuild if missing)").option("--cloud", "force Hub cloud search (default when not --local)").option("--semantic", "semantic mode (Titan cosine over path+symbols embeddings)").option("--lexical", "lexical-only mode (paths/symbols)").option("--repo <owner/name>", "limit cloud search to one repo");
|
|
32327
|
+
}
|
|
32328
|
+
repoIndexSearchOptions(repoIndex.command("search").description(`search Hub cloud by default (lexical/hybrid/semantic); use --local for checkout-only \u2014 ${REPO_INDEX_SEARCH_WHEN}`).argument("<query>", "path fragment, symbol, or meaning phrase")).action(async (query, o) => runRepoIndexSearchCommand(query, o, "hybrid"));
|
|
32329
|
+
repoIndexSearchOptions(program2.command("find").description(`estate code search across every registered repo (repo-index search, semantic by default) \u2014 ${REPO_INDEX_SEARCH_WHEN}`).argument("<query>", "path fragment, symbol, or meaning phrase")).action(async (query, o) => runRepoIndexSearchCommand(query, o, "semantic"));
|
|
32011
32330
|
repoIndex.command("status").description("show local and/or cloud repo-index status").option("--json", "machine-readable status").option("--cloud", "query Hub cloud status (estate or --repo)").option("--repo <owner/name>", "cloud status for one repo").action(async (o) => {
|
|
32012
32331
|
try {
|
|
32013
32332
|
if (o.cloud || o.repo) {
|
|
@@ -32059,11 +32378,10 @@ repoIndex.command("status").description("show local and/or cloud repo-index stat
|
|
|
32059
32378
|
return await failGraceful(e.message);
|
|
32060
32379
|
}
|
|
32061
32380
|
});
|
|
32062
|
-
repoIndex.command("health").description("post-deploy health gate: status + golden lexical/semantic queries (Hub#4149)").option("--live", "hit Hub cloud (status + searches); omit for offline golden-shape check").option("--golden <path>", "golden queries JSON (default:
|
|
32381
|
+
repoIndex.command("health").description("post-deploy health gate: status + golden lexical/semantic queries (Hub#4149)").option("--live", "hit Hub cloud (status + searches); omit for offline golden-shape check").option("--golden <path>", "golden queries JSON (default: the suite bundled with the CLI, so the gate runs from any repo)").option("--json", "machine-readable findings").action(async (o) => {
|
|
32063
32382
|
try {
|
|
32064
|
-
const
|
|
32065
|
-
const
|
|
32066
|
-
const golden = loadGoldenSuite(goldenPath);
|
|
32383
|
+
const goldenPath = o.golden || "bundled repo-index-golden-queries.json";
|
|
32384
|
+
const golden = o.golden ? loadGoldenSuite(o.golden) : defaultGoldenSuite();
|
|
32067
32385
|
const result = o.live ? await runRepoIndexHealth({
|
|
32068
32386
|
golden,
|
|
32069
32387
|
live: {
|
|
@@ -33266,11 +33584,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
|
|
|
33266
33584
|
}
|
|
33267
33585
|
});
|
|
33268
33586
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
33269
|
-
const wfDir = (0,
|
|
33587
|
+
const wfDir = (0, import_node_path37.join)(cwd, ".github", "workflows");
|
|
33270
33588
|
if (!(0, import_node_fs39.existsSync)(wfDir)) return [];
|
|
33271
33589
|
return (0, import_node_fs39.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
33272
33590
|
try {
|
|
33273
|
-
return workflowReportsPrChecks((0, import_node_fs39.readFileSync)((0,
|
|
33591
|
+
return workflowReportsPrChecks((0, import_node_fs39.readFileSync)((0, import_node_path37.join)(wfDir, name), "utf8"));
|
|
33274
33592
|
} catch {
|
|
33275
33593
|
return true;
|
|
33276
33594
|
}
|
|
@@ -33302,16 +33620,16 @@ function ciAuditDeps() {
|
|
|
33302
33620
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
33303
33621
|
readSeedFile: (path2) => {
|
|
33304
33622
|
if (!root) return null;
|
|
33305
|
-
const fullPath = (0,
|
|
33623
|
+
const fullPath = (0, import_node_path37.join)(root, path2);
|
|
33306
33624
|
return (0, import_node_fs39.existsSync)(fullPath) ? (0, import_node_fs39.readFileSync)(fullPath, "utf8") : null;
|
|
33307
33625
|
}
|
|
33308
33626
|
};
|
|
33309
33627
|
}
|
|
33310
33628
|
function hubRoot() {
|
|
33311
|
-
const fromPkg = (0,
|
|
33629
|
+
const fromPkg = (0, import_node_path37.join)(__dirname, "..", "..");
|
|
33312
33630
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
33313
|
-
if ((0, import_node_fs39.existsSync)((0,
|
|
33314
|
-
if ((0, import_node_fs39.existsSync)((0,
|
|
33631
|
+
if ((0, import_node_fs39.existsSync)((0, import_node_path37.join)(fromPkg, marker))) return fromPkg;
|
|
33632
|
+
if ((0, import_node_fs39.existsSync)((0, import_node_path37.join)(process.cwd(), marker))) return process.cwd();
|
|
33315
33633
|
return null;
|
|
33316
33634
|
}
|
|
33317
33635
|
pr.command("ci-policy").description("report merge CI policy: wait-for-checks vs no-ci (for grind/build agents)").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the current checkout)").action(async (o) => {
|
|
@@ -34158,7 +34476,7 @@ function directoryBytes(path2) {
|
|
|
34158
34476
|
return 0;
|
|
34159
34477
|
}
|
|
34160
34478
|
for (const entry of entries) {
|
|
34161
|
-
const child2 = (0,
|
|
34479
|
+
const child2 = (0, import_node_path37.join)(path2, entry.name);
|
|
34162
34480
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
34163
34481
|
else {
|
|
34164
34482
|
try {
|
|
@@ -34188,7 +34506,7 @@ function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
|
34188
34506
|
dirBytes,
|
|
34189
34507
|
listStagingDirs: (root) => (0, import_node_fs39.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
34190
34508
|
try {
|
|
34191
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
34509
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path37.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs39.statSync)(p).mtimeMs) };
|
|
34192
34510
|
} catch {
|
|
34193
34511
|
return { name: d.name, mtimeMs: Date.now() };
|
|
34194
34512
|
}
|
|
@@ -34202,7 +34520,7 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
34202
34520
|
return {
|
|
34203
34521
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
34204
34522
|
mtimeMs: (name) => {
|
|
34205
|
-
const p = (0,
|
|
34523
|
+
const p = (0, import_node_path37.join)(stagingRoot, name);
|
|
34206
34524
|
if (!(0, import_node_fs39.existsSync)(p)) return null;
|
|
34207
34525
|
try {
|
|
34208
34526
|
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs39.statSync)(q).mtimeMs);
|
package/package.json
CHANGED