@mutmutco/cli 2.50.1 → 2.52.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 +451 -196
- package/dist/saga.cjs +14 -7
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -5571,6 +5571,7 @@ var import_node_child_process13 = require("node:child_process");
|
|
|
5571
5571
|
// src/cli-shared.ts
|
|
5572
5572
|
var import_promises = require("node:fs/promises");
|
|
5573
5573
|
var import_node_fs7 = require("node:fs");
|
|
5574
|
+
var import_node_path7 = require("node:path");
|
|
5574
5575
|
var import_node_crypto2 = require("node:crypto");
|
|
5575
5576
|
var import_node_child_process4 = require("node:child_process");
|
|
5576
5577
|
var import_node_util4 = require("node:util");
|
|
@@ -6326,16 +6327,20 @@ async function hubHeaders(extra = {}) {
|
|
|
6326
6327
|
const base = { ...clientVersionHeaders(), ...extra };
|
|
6327
6328
|
return t ? { ...base, Authorization: `Bearer ${t}` } : base;
|
|
6328
6329
|
}
|
|
6330
|
+
var CONFIG_FILE = ".mmi/config.json";
|
|
6329
6331
|
async function loadConfig() {
|
|
6330
6332
|
let file = {};
|
|
6331
6333
|
try {
|
|
6332
|
-
file = JSON.parse(await (0, import_promises.readFile)(
|
|
6334
|
+
file = JSON.parse(await (0, import_promises.readFile)(CONFIG_FILE, "utf8"));
|
|
6333
6335
|
} catch {
|
|
6334
6336
|
file = {};
|
|
6335
6337
|
}
|
|
6336
6338
|
if (!file.sagaApiUrl) file.sagaApiUrl = defaultHubUrl();
|
|
6337
6339
|
return file;
|
|
6338
6340
|
}
|
|
6341
|
+
function isOrgRepoRoot(cwd = process.cwd(), exists = import_node_fs7.existsSync) {
|
|
6342
|
+
return exists((0, import_node_path7.join)(cwd, CONFIG_FILE));
|
|
6343
|
+
}
|
|
6339
6344
|
var SESSION_FILE = ".mmi/.session";
|
|
6340
6345
|
var gitOut = async (args) => {
|
|
6341
6346
|
try {
|
|
@@ -6568,8 +6573,8 @@ function memorySyncBanner(report) {
|
|
|
6568
6573
|
|
|
6569
6574
|
// src/continuity.ts
|
|
6570
6575
|
var import_node_fs8 = require("node:fs");
|
|
6571
|
-
var
|
|
6572
|
-
var CONTINUITY_FILE = (0,
|
|
6576
|
+
var import_node_path8 = require("node:path");
|
|
6577
|
+
var CONTINUITY_FILE = (0, import_node_path8.join)(".mmi", "continuity.json");
|
|
6573
6578
|
function parseContinuityStamp(raw) {
|
|
6574
6579
|
if (!raw) return {};
|
|
6575
6580
|
try {
|
|
@@ -6595,7 +6600,7 @@ function readContinuityStamp(path2 = CONTINUITY_FILE) {
|
|
|
6595
6600
|
function stampSagaNoteContinuity(now = (/* @__PURE__ */ new Date()).toISOString(), path2 = CONTINUITY_FILE) {
|
|
6596
6601
|
try {
|
|
6597
6602
|
const current = readContinuityStamp(path2);
|
|
6598
|
-
(0, import_node_fs8.mkdirSync)((0,
|
|
6603
|
+
(0, import_node_fs8.mkdirSync)((0, import_node_path8.dirname)(path2), { recursive: true });
|
|
6599
6604
|
(0, import_node_fs8.writeFileSync)(path2, serializeContinuityStamp({ ...current, lastSagaNoteAt: now }), "utf8");
|
|
6600
6605
|
} catch {
|
|
6601
6606
|
}
|
|
@@ -6954,7 +6959,7 @@ var import_node_fs10 = require("node:fs");
|
|
|
6954
6959
|
// src/stage-runner.ts
|
|
6955
6960
|
var import_node_child_process5 = require("node:child_process");
|
|
6956
6961
|
var import_node_fs9 = require("node:fs");
|
|
6957
|
-
var
|
|
6962
|
+
var import_node_path9 = require("node:path");
|
|
6958
6963
|
var import_node_net = require("node:net");
|
|
6959
6964
|
var import_node_util5 = require("node:util");
|
|
6960
6965
|
var execFileP3 = (0, import_node_util5.promisify)(import_node_child_process5.execFile);
|
|
@@ -7071,11 +7076,11 @@ function appendForceRecreate(up) {
|
|
|
7071
7076
|
return `${up.trimEnd()} --force-recreate`;
|
|
7072
7077
|
}
|
|
7073
7078
|
function stageStatePath(cwd = process.cwd()) {
|
|
7074
|
-
return (0,
|
|
7079
|
+
return (0, import_node_path9.join)(cwd, "tmp", "stage", "state.json");
|
|
7075
7080
|
}
|
|
7076
7081
|
function stageGlobalStatePath(cwd = process.cwd(), gitCommonDir = ".git") {
|
|
7077
|
-
const dir = (0,
|
|
7078
|
-
return (0,
|
|
7082
|
+
const dir = (0, import_node_path9.isAbsolute)(gitCommonDir) ? gitCommonDir : (0, import_node_path9.resolve)(cwd, gitCommonDir);
|
|
7083
|
+
return (0, import_node_path9.join)(dir, "mmi", "stage", "state.json");
|
|
7079
7084
|
}
|
|
7080
7085
|
function normPath2(path2) {
|
|
7081
7086
|
return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
@@ -7299,8 +7304,8 @@ function stageProcessEnv(stagePort, extraEnv) {
|
|
|
7299
7304
|
}
|
|
7300
7305
|
async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
7301
7306
|
if (!config.ensureEnv) return;
|
|
7302
|
-
const target = (0,
|
|
7303
|
-
const example = (0,
|
|
7307
|
+
const target = (0, import_node_path9.join)(cwd, config.ensureEnv.target);
|
|
7308
|
+
const example = (0, import_node_path9.join)(cwd, config.ensureEnv.example);
|
|
7304
7309
|
if (!(0, import_node_fs9.existsSync)(target) && (0, import_node_fs9.existsSync)(example)) {
|
|
7305
7310
|
(0, import_node_fs9.copyFileSync)(example, target);
|
|
7306
7311
|
} else if ((0, import_node_fs9.existsSync)(target) && (0, import_node_fs9.existsSync)(example)) {
|
|
@@ -7811,12 +7816,14 @@ function registerSagaCommands(program3) {
|
|
|
7811
7816
|
saga.command("flush").option("--json", "machine-readable {flushed, dropped, remaining}").option("--run", "detached worker: drain the queue silently (spawned by note/capture)").description("roll the local pending-note queue forward (re-POST queued saga writes); reports what landed").action((o) => runSagaFlush(o));
|
|
7812
7817
|
saga.command("show").option("--quiet", "no-op silently when unconfigured/unreachable (SessionStart hook)").option("--latest-anywhere", "resume the newest saga across all repos (default: current repo)").description("print your resume block \u2014 current repo HEAD + project memory (where you left off)").action((opts) => runSagaShow(opts));
|
|
7813
7818
|
saga.command("capture").option("--quiet", "capture silently (for the Stop hook)").description("per-turn deterministic capture (Stop hook): turn boundary + current sha + gated HEAD-update").action(async (opts) => {
|
|
7819
|
+
if (!isOrgRepoRoot()) return;
|
|
7814
7820
|
const hook = parseHookInput(await readStdin());
|
|
7815
7821
|
if (hook.session_id) persistSession(hook.session_id);
|
|
7816
7822
|
await postCapture({ event: "stop", id: (0, import_node_crypto3.randomUUID)(), source: "hook", sha: await gitOut(["rev-parse", "--short", "HEAD"]), surface: agentSurface() }, opts.quiet ?? false);
|
|
7817
7823
|
await maybeSpawnHeadUpdate();
|
|
7818
7824
|
});
|
|
7819
7825
|
saga.command("session").option("--quiet", "silent (for the SessionStart hook)").description("persist the harness session id for this repo (SessionStart hook)").action(async () => {
|
|
7826
|
+
if (!isOrgRepoRoot()) return;
|
|
7820
7827
|
const hook = parseHookInput(await readStdin());
|
|
7821
7828
|
if (hook.session_id) persistSession(hook.session_id);
|
|
7822
7829
|
});
|
|
@@ -8230,16 +8237,16 @@ var import_node_child_process7 = require("node:child_process");
|
|
|
8230
8237
|
|
|
8231
8238
|
// src/session-start.ts
|
|
8232
8239
|
var import_node_fs13 = require("node:fs");
|
|
8233
|
-
var
|
|
8240
|
+
var import_node_path12 = require("node:path");
|
|
8234
8241
|
|
|
8235
8242
|
// src/scratch-gc.ts
|
|
8236
8243
|
var import_node_child_process6 = require("node:child_process");
|
|
8237
8244
|
var import_node_fs12 = require("node:fs");
|
|
8238
|
-
var
|
|
8245
|
+
var import_node_path11 = require("node:path");
|
|
8239
8246
|
|
|
8240
8247
|
// src/plan.ts
|
|
8241
8248
|
var import_node_fs11 = require("node:fs");
|
|
8242
|
-
var
|
|
8249
|
+
var import_node_path10 = require("node:path");
|
|
8243
8250
|
|
|
8244
8251
|
// src/frontmatter.ts
|
|
8245
8252
|
function splitFrontmatter(content) {
|
|
@@ -8322,8 +8329,8 @@ function rankPlansByRelevance(plans, signals, opts = {}) {
|
|
|
8322
8329
|
|
|
8323
8330
|
// src/plan.ts
|
|
8324
8331
|
var PLANS_DIR = "plans";
|
|
8325
|
-
var META_FILE = (0,
|
|
8326
|
-
var planPath = (slug) => (0,
|
|
8332
|
+
var META_FILE = (0, import_node_path10.join)(PLANS_DIR, ".plan-meta.json");
|
|
8333
|
+
var planPath = (slug) => (0, import_node_path10.join)(PLANS_DIR, `${slug}.md`);
|
|
8327
8334
|
var metaKey = (project2, slug) => `${project2}/${slug}`;
|
|
8328
8335
|
function parseMeta(raw) {
|
|
8329
8336
|
if (!raw) return {};
|
|
@@ -8348,7 +8355,7 @@ function hashContent(s) {
|
|
|
8348
8355
|
function staleHint(slug) {
|
|
8349
8356
|
return `remote "${slug}" is newer \u2014 run \`mmi-cli northstar pull ${slug}\` first (your local is based on an older version), or re-push with \`--force\` to overwrite`;
|
|
8350
8357
|
}
|
|
8351
|
-
var INDEX_FILE = (0,
|
|
8358
|
+
var INDEX_FILE = (0, import_node_path10.join)(PLANS_DIR, ".index.json");
|
|
8352
8359
|
var INDEX_TTL_MS = 6e4;
|
|
8353
8360
|
function parseIndex(raw) {
|
|
8354
8361
|
if (!raw) return null;
|
|
@@ -8377,7 +8384,7 @@ function mergeIndex(idx, scope, plans, now) {
|
|
|
8377
8384
|
const mergedScope = idx.scope === null ? null : [.../* @__PURE__ */ new Set([...idx.scope, ...scope])];
|
|
8378
8385
|
return { fetchedAt: now, scope: mergedScope, plans: [...kept, ...plans] };
|
|
8379
8386
|
}
|
|
8380
|
-
var QUEUE_FILE = (0,
|
|
8387
|
+
var QUEUE_FILE = (0, import_node_path10.join)(PLANS_DIR, ".sync-queue.json");
|
|
8381
8388
|
var QUEUE_MAX_ATTEMPTS = 10;
|
|
8382
8389
|
function isValidQueueEntry(e) {
|
|
8383
8390
|
if (!e || typeof e !== "object") return false;
|
|
@@ -8498,8 +8505,8 @@ function dropQueued(deps, project2, slug) {
|
|
|
8498
8505
|
}
|
|
8499
8506
|
function parsePlanSlugFromPath(cwd, filePath) {
|
|
8500
8507
|
const norm = (s) => s.replace(/\\/g, "/");
|
|
8501
|
-
const cwdNorm = norm((0,
|
|
8502
|
-
const pathNorm = norm((0,
|
|
8508
|
+
const cwdNorm = norm((0, import_node_path10.resolve)(cwd)).replace(/\/+$/, "");
|
|
8509
|
+
const pathNorm = norm((0, import_node_path10.resolve)(filePath));
|
|
8503
8510
|
const rel = pathNorm.startsWith(`${cwdNorm}/`) ? pathNorm.slice(cwdNorm.length + 1) : norm(filePath);
|
|
8504
8511
|
const m = /^plans\/([A-Za-z0-9][A-Za-z0-9_-]*)\.md$/.exec(rel);
|
|
8505
8512
|
return m ? m[1] : null;
|
|
@@ -9082,7 +9089,7 @@ var PLAN_ADVISORY_AGE_MS = 30 * 24 * 36e5;
|
|
|
9082
9089
|
var ROOT_SCRATCH_STALE_MS = 24 * 36e5;
|
|
9083
9090
|
var SCRATCH_GC_THROTTLE_MS = 24 * 36e5;
|
|
9084
9091
|
function scratchGcThrottlePath(mmiRoot) {
|
|
9085
|
-
return (0,
|
|
9092
|
+
return (0, import_node_path11.join)(mmiRoot, "head-ts", ".scratch-gc-last");
|
|
9086
9093
|
}
|
|
9087
9094
|
function scratchGcDue(stampPath, now = Date.now(), read = import_node_fs12.readFileSync) {
|
|
9088
9095
|
try {
|
|
@@ -9093,7 +9100,7 @@ function scratchGcDue(stampPath, now = Date.now(), read = import_node_fs12.readF
|
|
|
9093
9100
|
}
|
|
9094
9101
|
function markScratchGcRun(stampPath, now = Date.now()) {
|
|
9095
9102
|
try {
|
|
9096
|
-
(0, import_node_fs12.mkdirSync)((0,
|
|
9103
|
+
(0, import_node_fs12.mkdirSync)((0, import_node_path11.dirname)(stampPath), { recursive: true });
|
|
9097
9104
|
(0, import_node_fs12.writeFileSync)(stampPath, String(now), "utf8");
|
|
9098
9105
|
} catch {
|
|
9099
9106
|
}
|
|
@@ -9153,7 +9160,7 @@ function isTmpSidecar(name) {
|
|
|
9153
9160
|
function planScratchGc(snap, now = Date.now()) {
|
|
9154
9161
|
const candidates = [];
|
|
9155
9162
|
const normalizePath = (p) => p.replace(/\\/g, "/");
|
|
9156
|
-
const repoRoot = normalizePath((snap.repoRoot ?? (0,
|
|
9163
|
+
const repoRoot = normalizePath((snap.repoRoot ?? (0, import_node_path11.dirname)(snap.mmiRoot)).replace(/[\\/]+$/, ""));
|
|
9157
9164
|
const mmiPaths = new Set(snap.mmiFiles.map((f) => normalizePath(f.path)));
|
|
9158
9165
|
const headTsPrefix = `${snap.mmiRoot.replace(/[\\/]+$/, "")}/head-ts/`.replace(/\\/g, "/");
|
|
9159
9166
|
const days = (ms) => `${Math.floor(ms / 864e5)}d`;
|
|
@@ -9202,7 +9209,7 @@ function planScratchGc(snap, now = Date.now()) {
|
|
|
9202
9209
|
continue;
|
|
9203
9210
|
}
|
|
9204
9211
|
const orig = conflictCopyOriginal(f.name);
|
|
9205
|
-
if (orig && CONFLICT_COPY_ALLOWLIST.has(orig) && mmiPaths.has(normalizePath((0,
|
|
9212
|
+
if (orig && CONFLICT_COPY_ALLOWLIST.has(orig) && mmiPaths.has(normalizePath((0, import_node_path11.join)(f.dir, orig))) && age > CONFLICT_COPY_STALE_MS) {
|
|
9206
9213
|
add("conflict-copy", `cloud-sync conflict copy of ${orig} (${days(age)} old)`);
|
|
9207
9214
|
}
|
|
9208
9215
|
}
|
|
@@ -9272,7 +9279,7 @@ function syncedPlanMetaEntry(meta, project2, slug, hash) {
|
|
|
9272
9279
|
}
|
|
9273
9280
|
function readProject(repoRoot) {
|
|
9274
9281
|
try {
|
|
9275
|
-
const cfg = JSON.parse((0, import_node_fs12.readFileSync)((0,
|
|
9282
|
+
const cfg = JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path11.join)(repoRoot, ".mmi", "config.json"), "utf8"));
|
|
9276
9283
|
if (typeof cfg.project === "string" && cfg.project.trim()) return cfg.project.trim();
|
|
9277
9284
|
} catch {
|
|
9278
9285
|
}
|
|
@@ -9280,7 +9287,7 @@ function readProject(repoRoot) {
|
|
|
9280
9287
|
}
|
|
9281
9288
|
function readPlanMeta(plansRoot) {
|
|
9282
9289
|
try {
|
|
9283
|
-
return parseMeta((0, import_node_fs12.readFileSync)((0,
|
|
9290
|
+
return parseMeta((0, import_node_fs12.readFileSync)((0, import_node_path11.join)(plansRoot, ".plan-meta.json"), "utf8"));
|
|
9284
9291
|
} catch {
|
|
9285
9292
|
return null;
|
|
9286
9293
|
}
|
|
@@ -9288,7 +9295,7 @@ function readPlanMeta(plansRoot) {
|
|
|
9288
9295
|
function readSyncQueueSlugs(plansRoot) {
|
|
9289
9296
|
let queueRaw;
|
|
9290
9297
|
try {
|
|
9291
|
-
queueRaw = (0, import_node_fs12.readFileSync)((0,
|
|
9298
|
+
queueRaw = (0, import_node_fs12.readFileSync)((0, import_node_path11.join)(plansRoot, ".sync-queue.json"), "utf8");
|
|
9292
9299
|
} catch (e) {
|
|
9293
9300
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
9294
9301
|
return code === "ENOENT" || code === "ENOTDIR" ? /* @__PURE__ */ new Set() : null;
|
|
@@ -9303,8 +9310,8 @@ function readSyncQueueSlugs(plansRoot) {
|
|
|
9303
9310
|
}
|
|
9304
9311
|
function physicalPlanCandidateStillAllowed(candidatePath, repoAnchor, lstat = import_node_fs12.lstatSync) {
|
|
9305
9312
|
const path2 = candidatePath.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
9306
|
-
const parent = (0,
|
|
9307
|
-
const name = (0,
|
|
9313
|
+
const parent = (0, import_node_path11.dirname)(path2).replace(/\\/g, "/").replace(/\/+$/, "");
|
|
9314
|
+
const name = (0, import_node_path11.basename)(path2);
|
|
9308
9315
|
const plansDir = `${repoAnchor}/plans`;
|
|
9309
9316
|
if (parent !== plansDir || !name.endsWith(".md")) return false;
|
|
9310
9317
|
try {
|
|
@@ -9352,7 +9359,7 @@ function treeOlderThan(root, now, floor) {
|
|
|
9352
9359
|
if (now - st.mtimeMs <= floor) return false;
|
|
9353
9360
|
if (!st.isDirectory()) continue;
|
|
9354
9361
|
for (const ent of (0, import_node_fs12.readdirSync)(current, { withFileTypes: true })) {
|
|
9355
|
-
const child = (0,
|
|
9362
|
+
const child = (0, import_node_path11.join)(current, ent.name);
|
|
9356
9363
|
if (isLinkLike(child, ent.isSymbolicLink())) return false;
|
|
9357
9364
|
stack.push(child);
|
|
9358
9365
|
}
|
|
@@ -9364,7 +9371,7 @@ function applyScratchGc(plan2, mmiRoot, now = Date.now()) {
|
|
|
9364
9371
|
let repoAnchor;
|
|
9365
9372
|
let anchor;
|
|
9366
9373
|
try {
|
|
9367
|
-
repoAnchor = (0, import_node_fs12.realpathSync)((0,
|
|
9374
|
+
repoAnchor = (0, import_node_fs12.realpathSync)((0, import_node_path11.dirname)(mmiRoot)).replace(/\\/g, "/").replace(/\/+$/, "");
|
|
9368
9375
|
} catch {
|
|
9369
9376
|
return result;
|
|
9370
9377
|
}
|
|
@@ -9406,7 +9413,7 @@ function applyScratchGc(plan2, mmiRoot, now = Date.now()) {
|
|
|
9406
9413
|
continue;
|
|
9407
9414
|
}
|
|
9408
9415
|
const plansRoot = `${repoAnchor}/plans`;
|
|
9409
|
-
const slug = (0,
|
|
9416
|
+
const slug = (0, import_node_path11.basename)(c.path).replace(/\.md$/, "");
|
|
9410
9417
|
const pending = readSyncQueueSlugs(plansRoot);
|
|
9411
9418
|
if (pending === null || pending.has(slug)) {
|
|
9412
9419
|
result.skipped += 1;
|
|
@@ -9448,15 +9455,15 @@ function pathContained(real, anchor) {
|
|
|
9448
9455
|
}
|
|
9449
9456
|
function rootCandidateStillAllowed(c, repoAnchor) {
|
|
9450
9457
|
const path2 = c.path.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
9451
|
-
const parent = (0,
|
|
9452
|
-
const name = (0,
|
|
9458
|
+
const parent = (0, import_node_path11.dirname)(path2).replace(/\\/g, "/").replace(/\/+$/, "");
|
|
9459
|
+
const name = (0, import_node_path11.basename)(path2);
|
|
9453
9460
|
if (c.family === "scratch-dir") return c.kind === "dir" && parent === repoAnchor && ROOT_SCRATCH_DIRS.has(name);
|
|
9454
9461
|
if (c.family === "scratch-file") return (c.kind ?? "file") === "file" && parent === repoAnchor && ROOT_SCRATCH_FILE_PREFIXES.some((prefix) => name.startsWith(prefix));
|
|
9455
9462
|
if (c.family === "plan") return physicalPlanCandidateStillAllowed(c.path, repoAnchor);
|
|
9456
9463
|
return false;
|
|
9457
9464
|
}
|
|
9458
9465
|
function trackedPathStatus(c, repoAnchor) {
|
|
9459
|
-
const rel = (0,
|
|
9466
|
+
const rel = (0, import_node_path11.relative)(repoAnchor, c.path).replace(/\\/g, "/");
|
|
9460
9467
|
if (!rel || rel.startsWith("../") || rel === "..") return null;
|
|
9461
9468
|
const gitPath = c.kind === "dir" ? `${rel.replace(/\/+$/, "")}/` : rel;
|
|
9462
9469
|
try {
|
|
@@ -9487,7 +9494,7 @@ function rootScratchDirSnapshot(root, readdir, stat) {
|
|
|
9487
9494
|
while (stack.length) {
|
|
9488
9495
|
const current = stack.pop();
|
|
9489
9496
|
for (const ent of readdir(current, { withFileTypes: true })) {
|
|
9490
|
-
const child = (0,
|
|
9497
|
+
const child = (0, import_node_path11.join)(current, ent.name);
|
|
9491
9498
|
if (ent.isSymbolicLink?.()) return null;
|
|
9492
9499
|
try {
|
|
9493
9500
|
(0, import_node_fs12.readlinkSync)(child);
|
|
@@ -9509,8 +9516,8 @@ function collectScratchSnapshot(repoRoot, deps = {}) {
|
|
|
9509
9516
|
const readdir = deps.readdir ?? import_node_fs12.readdirSync;
|
|
9510
9517
|
const stat = deps.stat ?? import_node_fs12.statSync;
|
|
9511
9518
|
const readFile7 = deps.readFile ?? import_node_fs12.readFileSync;
|
|
9512
|
-
const mmiRoot = (0,
|
|
9513
|
-
const plansRoot = (0,
|
|
9519
|
+
const mmiRoot = (0, import_node_path11.join)(repoRoot, ".mmi");
|
|
9520
|
+
const plansRoot = (0, import_node_path11.join)(repoRoot, "plans");
|
|
9514
9521
|
const rootScratchFiles = [];
|
|
9515
9522
|
try {
|
|
9516
9523
|
for (const ent of readdir(repoRoot, { withFileTypes: true })) {
|
|
@@ -9519,7 +9526,7 @@ function collectScratchSnapshot(repoRoot, deps = {}) {
|
|
|
9519
9526
|
if (!(isDir && ROOT_SCRATCH_DIRS.has(ent.name)) && !(isFile && ROOT_SCRATCH_FILE_PREFIXES.some((prefix) => ent.name.startsWith(prefix)))) {
|
|
9520
9527
|
continue;
|
|
9521
9528
|
}
|
|
9522
|
-
const full = (0,
|
|
9529
|
+
const full = (0, import_node_path11.join)(repoRoot, ent.name);
|
|
9523
9530
|
try {
|
|
9524
9531
|
if (isDir) {
|
|
9525
9532
|
const snap = rootScratchDirSnapshot(full, readdir, stat);
|
|
@@ -9538,7 +9545,7 @@ function collectScratchSnapshot(repoRoot, deps = {}) {
|
|
|
9538
9545
|
for (const ent of readdir(mmiRoot, { recursive: true, withFileTypes: true })) {
|
|
9539
9546
|
if (!ent.isFile()) continue;
|
|
9540
9547
|
const dir = ent.parentPath ?? ent.path ?? mmiRoot;
|
|
9541
|
-
const full = (0,
|
|
9548
|
+
const full = (0, import_node_path11.join)(dir, ent.name);
|
|
9542
9549
|
try {
|
|
9543
9550
|
const st = stat(full);
|
|
9544
9551
|
mmiFiles.push({ path: full, dir, name: ent.name, mtimeMs: st.mtimeMs, bytes: st.size });
|
|
@@ -9551,7 +9558,7 @@ function collectScratchSnapshot(repoRoot, deps = {}) {
|
|
|
9551
9558
|
try {
|
|
9552
9559
|
for (const ent of readdir(plansRoot, { withFileTypes: true })) {
|
|
9553
9560
|
if (!ent.isFile() || !ent.name.endsWith(".md")) continue;
|
|
9554
|
-
const full = (0,
|
|
9561
|
+
const full = (0, import_node_path11.join)(plansRoot, ent.name);
|
|
9555
9562
|
try {
|
|
9556
9563
|
const st = stat(full);
|
|
9557
9564
|
const raw = readFile7(full, "utf8");
|
|
@@ -9563,20 +9570,20 @@ function collectScratchSnapshot(repoRoot, deps = {}) {
|
|
|
9563
9570
|
}
|
|
9564
9571
|
let planMeta = {};
|
|
9565
9572
|
try {
|
|
9566
|
-
planMeta = parseMeta(readFile7((0,
|
|
9573
|
+
planMeta = parseMeta(readFile7((0, import_node_path11.join)(plansRoot, ".plan-meta.json"), "utf8"));
|
|
9567
9574
|
} catch {
|
|
9568
9575
|
planMeta = {};
|
|
9569
9576
|
}
|
|
9570
9577
|
let project2;
|
|
9571
9578
|
try {
|
|
9572
|
-
const cfg = JSON.parse(readFile7((0,
|
|
9579
|
+
const cfg = JSON.parse(readFile7((0, import_node_path11.join)(repoRoot, ".mmi", "config.json"), "utf8"));
|
|
9573
9580
|
if (typeof cfg.project === "string" && cfg.project.trim()) project2 = cfg.project.trim();
|
|
9574
9581
|
} catch {
|
|
9575
9582
|
}
|
|
9576
9583
|
const syncQueueSlugs = (() => {
|
|
9577
9584
|
let queueRaw;
|
|
9578
9585
|
try {
|
|
9579
|
-
queueRaw = readFile7((0,
|
|
9586
|
+
queueRaw = readFile7((0, import_node_path11.join)(plansRoot, ".sync-queue.json"), "utf8");
|
|
9580
9587
|
} catch (e) {
|
|
9581
9588
|
const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
|
|
9582
9589
|
return code === "ENOENT" || code === "ENOTDIR" ? /* @__PURE__ */ new Set() : null;
|
|
@@ -9649,23 +9656,23 @@ function spawnDetachedSelf(args, deps) {
|
|
|
9649
9656
|
}
|
|
9650
9657
|
}
|
|
9651
9658
|
function isInsideRepoSubdir(cwd, exists = import_node_fs13.existsSync) {
|
|
9652
|
-
if (exists((0,
|
|
9659
|
+
if (exists((0, import_node_path12.join)(cwd, ".git"))) return false;
|
|
9653
9660
|
let dir = cwd;
|
|
9654
9661
|
for (; ; ) {
|
|
9655
|
-
const parent = (0,
|
|
9662
|
+
const parent = (0, import_node_path12.dirname)(dir);
|
|
9656
9663
|
if (parent === dir) return false;
|
|
9657
|
-
if (exists((0,
|
|
9664
|
+
if (exists((0, import_node_path12.join)(parent, ".git"))) return true;
|
|
9658
9665
|
dir = parent;
|
|
9659
9666
|
}
|
|
9660
9667
|
}
|
|
9661
9668
|
function planStoreLines(cwd) {
|
|
9662
9669
|
const mdFiles = (dir, minSize = 0) => {
|
|
9663
|
-
const p = (0,
|
|
9670
|
+
const p = (0, import_node_path12.join)(cwd, dir);
|
|
9664
9671
|
if (!(0, import_node_fs13.existsSync)(p)) return [];
|
|
9665
9672
|
try {
|
|
9666
9673
|
return (0, import_node_fs13.readdirSync)(p).filter((f) => f.toLowerCase().endsWith(".md")).filter((f) => {
|
|
9667
9674
|
try {
|
|
9668
|
-
return (0, import_node_fs13.statSync)((0,
|
|
9675
|
+
return (0, import_node_fs13.statSync)((0, import_node_path12.join)(p, f)).size >= minSize;
|
|
9669
9676
|
} catch {
|
|
9670
9677
|
return false;
|
|
9671
9678
|
}
|
|
@@ -9686,7 +9693,7 @@ function planStoreLines(cwd) {
|
|
|
9686
9693
|
function scratchGcLines(cwd, env = process.env, now = Date.now()) {
|
|
9687
9694
|
if (env.MMI_NO_AUTO_GC) return [];
|
|
9688
9695
|
try {
|
|
9689
|
-
const stamp = scratchGcThrottlePath((0,
|
|
9696
|
+
const stamp = scratchGcThrottlePath((0, import_node_path12.join)(cwd, ".mmi"));
|
|
9690
9697
|
if (!scratchGcDue(stamp, now)) return [];
|
|
9691
9698
|
const run = executeScratchGc(cwd, { apply: true }, now);
|
|
9692
9699
|
markScratchGcRun(stamp, now);
|
|
@@ -9879,8 +9886,8 @@ function registerCoopCommands(program3) {
|
|
|
9879
9886
|
// src/throttle-commands.ts
|
|
9880
9887
|
var import_node_child_process8 = require("node:child_process");
|
|
9881
9888
|
var import_node_fs14 = require("node:fs");
|
|
9882
|
-
var
|
|
9883
|
-
var THROTTLE_TRACE_REL = (0,
|
|
9889
|
+
var import_node_path13 = require("node:path");
|
|
9890
|
+
var THROTTLE_TRACE_REL = (0, import_node_path13.join)(".mmi", "throttle", "trace.jsonl");
|
|
9884
9891
|
function resolveRepoGitRoot(cwd = process.cwd()) {
|
|
9885
9892
|
try {
|
|
9886
9893
|
const root = (0, import_node_child_process8.execFileSync)("git", ["-C", cwd, "rev-parse", "--show-toplevel"], {
|
|
@@ -9893,7 +9900,7 @@ function resolveRepoGitRoot(cwd = process.cwd()) {
|
|
|
9893
9900
|
}
|
|
9894
9901
|
}
|
|
9895
9902
|
function resolveThrottleTracePath(cwd = process.cwd()) {
|
|
9896
|
-
return (0,
|
|
9903
|
+
return (0, import_node_path13.join)(resolveRepoGitRoot(cwd), THROTTLE_TRACE_REL);
|
|
9897
9904
|
}
|
|
9898
9905
|
function resolveModeFromEnv() {
|
|
9899
9906
|
const v = String(process.env.MMI_THROTTLE_MODE ?? "block").trim().toLowerCase();
|
|
@@ -10950,11 +10957,11 @@ function ghError(e) {
|
|
|
10950
10957
|
|
|
10951
10958
|
// src/board-slice-cache.ts
|
|
10952
10959
|
var import_node_fs15 = require("node:fs");
|
|
10953
|
-
var
|
|
10954
|
-
var BOARD_SLICE_CACHE_FILE = (0,
|
|
10960
|
+
var import_node_path14 = require("node:path");
|
|
10961
|
+
var BOARD_SLICE_CACHE_FILE = (0, import_node_path14.join)(".mmi", "board-slice.json");
|
|
10955
10962
|
var BOARD_SLICE_CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
10956
10963
|
function boardSliceCachePath(cwd) {
|
|
10957
|
-
return (0,
|
|
10964
|
+
return (0, import_node_path14.join)(cwd, BOARD_SLICE_CACHE_FILE);
|
|
10958
10965
|
}
|
|
10959
10966
|
function readCachedBoardSlice(cwd) {
|
|
10960
10967
|
try {
|
|
@@ -10969,7 +10976,7 @@ function writeCachedBoardSlice(cwd, slice) {
|
|
|
10969
10976
|
const path2 = boardSliceCachePath(cwd);
|
|
10970
10977
|
const tmp = `${path2}.${process.pid}.tmp`;
|
|
10971
10978
|
try {
|
|
10972
|
-
(0, import_node_fs15.mkdirSync)((0,
|
|
10979
|
+
(0, import_node_fs15.mkdirSync)((0, import_node_path14.dirname)(path2), { recursive: true });
|
|
10973
10980
|
(0, import_node_fs15.writeFileSync)(tmp, JSON.stringify(slice));
|
|
10974
10981
|
(0, import_node_fs15.renameSync)(tmp, path2);
|
|
10975
10982
|
} catch {
|
|
@@ -11103,7 +11110,7 @@ async function refreshBoardSliceCache(deps) {
|
|
|
11103
11110
|
|
|
11104
11111
|
// src/worktree.ts
|
|
11105
11112
|
var import_node_fs16 = require("node:fs");
|
|
11106
|
-
var
|
|
11113
|
+
var import_node_path15 = require("node:path");
|
|
11107
11114
|
var LOCAL_ONLY_FILES = [".claude/settings.local.json"];
|
|
11108
11115
|
var PKG = "package.json";
|
|
11109
11116
|
var LOCKFILE = "package-lock.json";
|
|
@@ -11133,12 +11140,12 @@ var realFsProbe = {
|
|
|
11133
11140
|
};
|
|
11134
11141
|
function scanInstallDirs(root, fs2 = realFsProbe) {
|
|
11135
11142
|
const factsFor = (dir) => {
|
|
11136
|
-
const abs = dir ? (0,
|
|
11143
|
+
const abs = dir ? (0, import_node_path15.join)(root, dir) : root;
|
|
11137
11144
|
return {
|
|
11138
11145
|
dir,
|
|
11139
|
-
hasPackageJson: fs2.isFile((0,
|
|
11140
|
-
hasLockfile: fs2.isFile((0,
|
|
11141
|
-
hasNodeModules: fs2.isDir((0,
|
|
11146
|
+
hasPackageJson: fs2.isFile((0, import_node_path15.join)(abs, PKG)),
|
|
11147
|
+
hasLockfile: fs2.isFile((0, import_node_path15.join)(abs, LOCKFILE)),
|
|
11148
|
+
hasNodeModules: fs2.isDir((0, import_node_path15.join)(abs, NODE_MODULES))
|
|
11142
11149
|
};
|
|
11143
11150
|
};
|
|
11144
11151
|
const children = fs2.listDirs(root).filter((name) => name !== NODE_MODULES && name !== ".git");
|
|
@@ -11148,7 +11155,7 @@ function npmInstallTargets(dirs) {
|
|
|
11148
11155
|
return dirs.filter((d) => d.hasPackageJson && !d.hasNodeModules).map((d) => ({ dir: d.dir, command: d.hasLockfile ? "npm ci" : "npm install" }));
|
|
11149
11156
|
}
|
|
11150
11157
|
function isLinkedWorktree(root, fs2 = realFsProbe) {
|
|
11151
|
-
return fs2.isFile((0,
|
|
11158
|
+
return fs2.isFile((0, import_node_path15.join)(root, ".git"));
|
|
11152
11159
|
}
|
|
11153
11160
|
function worktreeAutoProvisionBanner(root, fs2 = realFsProbe) {
|
|
11154
11161
|
if (!isLinkedWorktree(root, fs2)) return null;
|
|
@@ -11158,7 +11165,7 @@ function worktreeAutoProvisionBanner(root, fs2 = realFsProbe) {
|
|
|
11158
11165
|
return `[worktree] provisioning tooling in the background (deps in ${where} + local config) \u2014 \`mmi-cli worktree setup\` to redo`;
|
|
11159
11166
|
}
|
|
11160
11167
|
function defaultCopyFile(from, to) {
|
|
11161
|
-
(0, import_node_fs16.mkdirSync)((0,
|
|
11168
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path15.dirname)(to), { recursive: true });
|
|
11162
11169
|
(0, import_node_fs16.copyFileSync)(from, to);
|
|
11163
11170
|
}
|
|
11164
11171
|
async function provisionWorktree(worktreeRoot, deps) {
|
|
@@ -11171,7 +11178,7 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
11171
11178
|
const skippedInstall = allDirs.filter((d) => d.hasPackageJson && d.hasNodeModules).map((d) => d.dir);
|
|
11172
11179
|
const installed = [];
|
|
11173
11180
|
for (const target of targets) {
|
|
11174
|
-
const cwd = target.dir ? (0,
|
|
11181
|
+
const cwd = target.dir ? (0, import_node_path15.join)(worktreeRoot, target.dir) : worktreeRoot;
|
|
11175
11182
|
log(`installing deps: ${target.command} in ${target.dir || "."}`);
|
|
11176
11183
|
await deps.runInstall(target.command, cwd);
|
|
11177
11184
|
installed.push(target);
|
|
@@ -11180,7 +11187,7 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
11180
11187
|
const copySkipped = [];
|
|
11181
11188
|
const primary = await deps.primaryCheckout();
|
|
11182
11189
|
for (const rel of LOCAL_ONLY_FILES) {
|
|
11183
|
-
const dest = (0,
|
|
11190
|
+
const dest = (0, import_node_path15.join)(worktreeRoot, rel);
|
|
11184
11191
|
if (fs2.isFile(dest)) {
|
|
11185
11192
|
copySkipped.push({ file: rel, reason: "already-present" });
|
|
11186
11193
|
continue;
|
|
@@ -11189,11 +11196,11 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
11189
11196
|
copySkipped.push({ file: rel, reason: "no-primary" });
|
|
11190
11197
|
continue;
|
|
11191
11198
|
}
|
|
11192
|
-
if (!fs2.isFile((0,
|
|
11199
|
+
if (!fs2.isFile((0, import_node_path15.join)(primary, rel))) {
|
|
11193
11200
|
copySkipped.push({ file: rel, reason: "absent-in-primary" });
|
|
11194
11201
|
continue;
|
|
11195
11202
|
}
|
|
11196
|
-
copyFile((0,
|
|
11203
|
+
copyFile((0, import_node_path15.join)(primary, rel), dest);
|
|
11197
11204
|
copied.push(rel);
|
|
11198
11205
|
log(`copied local config: ${rel}`);
|
|
11199
11206
|
}
|
|
@@ -11201,7 +11208,7 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
11201
11208
|
}
|
|
11202
11209
|
function defaultWorktreePath(repoRoot, branch) {
|
|
11203
11210
|
const safe = branch.replace(/[/\\]+/g, "-");
|
|
11204
|
-
return (0,
|
|
11211
|
+
return (0, import_node_path15.join)((0, import_node_path15.dirname)(repoRoot), "mmi-worktrees", safe);
|
|
11205
11212
|
}
|
|
11206
11213
|
|
|
11207
11214
|
// src/northstar-context.ts
|
|
@@ -11343,7 +11350,7 @@ function whoamiLine(report) {
|
|
|
11343
11350
|
}
|
|
11344
11351
|
|
|
11345
11352
|
// src/index.ts
|
|
11346
|
-
var
|
|
11353
|
+
var import_node_path24 = require("node:path");
|
|
11347
11354
|
|
|
11348
11355
|
// src/merge-ci-policy.ts
|
|
11349
11356
|
function resolveMergeCiPolicy(input) {
|
|
@@ -12526,7 +12533,7 @@ async function resolveAutoAddBoardAttach(client, cfg, selector, priority, warn =
|
|
|
12526
12533
|
// src/gh-create.ts
|
|
12527
12534
|
var import_promises5 = require("node:fs/promises");
|
|
12528
12535
|
var import_node_os3 = require("node:os");
|
|
12529
|
-
var
|
|
12536
|
+
var import_node_path16 = require("node:path");
|
|
12530
12537
|
var import_node_crypto5 = require("node:crypto");
|
|
12531
12538
|
var ISSUE_TYPES = ["bug", "feature", "task"];
|
|
12532
12539
|
var GH_MUTATION_TIMEOUT_MS = 12e4;
|
|
@@ -12567,7 +12574,7 @@ async function bodyArgsViaFile(args, deps = {}) {
|
|
|
12567
12574
|
} };
|
|
12568
12575
|
const write = deps.write ?? import_promises5.writeFile;
|
|
12569
12576
|
const remove = deps.remove ?? import_promises5.unlink;
|
|
12570
|
-
const file = (0,
|
|
12577
|
+
const file = (0, import_node_path16.join)(deps.dir ?? (0, import_node_os3.tmpdir)(), `mmi-gh-body-${process.pid}-${(0, import_node_crypto5.randomBytes)(4).toString("hex")}.md`);
|
|
12571
12578
|
await write(file, args[i + 1], "utf8");
|
|
12572
12579
|
return {
|
|
12573
12580
|
args: [...args.slice(0, i), "--body-file", file, ...args.slice(i + 2)],
|
|
@@ -13679,7 +13686,7 @@ async function runStageLiveDown(deps, t) {
|
|
|
13679
13686
|
|
|
13680
13687
|
// src/design-system.ts
|
|
13681
13688
|
var import_node_fs17 = require("node:fs");
|
|
13682
|
-
var
|
|
13689
|
+
var import_node_path17 = require("node:path");
|
|
13683
13690
|
var UI_PACKAGE_CANDIDATES = ["@mutmutco/ui-dashboard", "@mutmutco/ui", "@mutmutco/theme"];
|
|
13684
13691
|
var DESIGN_SYSTEM_VERSION_LABEL = "@mutmutco design-system npm package (vs @latest)";
|
|
13685
13692
|
function dashboardConsumerRegistryFix(error) {
|
|
@@ -13734,11 +13741,11 @@ function readJsonFile(path2) {
|
|
|
13734
13741
|
}
|
|
13735
13742
|
}
|
|
13736
13743
|
function isUiFactoryCheckout(root) {
|
|
13737
|
-
const pkg = readJsonFile((0,
|
|
13744
|
+
const pkg = readJsonFile((0, import_node_path17.join)(root, "package.json"));
|
|
13738
13745
|
return pkg?.name === "mmd-ui" && pkg?.private === true;
|
|
13739
13746
|
}
|
|
13740
13747
|
function resolveDeclaredUiPackage(root) {
|
|
13741
|
-
const pkg = readJsonFile((0,
|
|
13748
|
+
const pkg = readJsonFile((0, import_node_path17.join)(root, "package.json"));
|
|
13742
13749
|
if (!pkg) return void 0;
|
|
13743
13750
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
13744
13751
|
for (const name of UI_PACKAGE_CANDIDATES) {
|
|
@@ -13748,7 +13755,7 @@ function resolveDeclaredUiPackage(root) {
|
|
|
13748
13755
|
return void 0;
|
|
13749
13756
|
}
|
|
13750
13757
|
function readLockfileInstalledVersion(root, packageName) {
|
|
13751
|
-
const lockPath = (0,
|
|
13758
|
+
const lockPath = (0, import_node_path17.join)(root, "package-lock.json");
|
|
13752
13759
|
if (!(0, import_node_fs17.existsSync)(lockPath)) return void 0;
|
|
13753
13760
|
const lock = readJsonFile(lockPath);
|
|
13754
13761
|
const node = lock?.packages?.[`node_modules/${packageName}`];
|
|
@@ -13779,7 +13786,7 @@ function designSystemSnapshot(root) {
|
|
|
13779
13786
|
// src/design-system-registry.ts
|
|
13780
13787
|
var import_node_crypto6 = require("node:crypto");
|
|
13781
13788
|
var import_node_fs19 = require("node:fs");
|
|
13782
|
-
var
|
|
13789
|
+
var import_node_path18 = require("node:path");
|
|
13783
13790
|
|
|
13784
13791
|
// src/atomic-write.ts
|
|
13785
13792
|
var import_node_fs18 = require("node:fs");
|
|
@@ -13803,7 +13810,7 @@ function readJsonFile2(path2) {
|
|
|
13803
13810
|
}
|
|
13804
13811
|
}
|
|
13805
13812
|
function readComponentsJson(root) {
|
|
13806
|
-
return readJsonFile2((0,
|
|
13813
|
+
return readJsonFile2((0, import_node_path18.join)(root, "components.json"));
|
|
13807
13814
|
}
|
|
13808
13815
|
function hasMutmutcoRegistry(root) {
|
|
13809
13816
|
const url = readComponentsJson(root)?.registries?.["@mutmutco"];
|
|
@@ -13811,7 +13818,7 @@ function hasMutmutcoRegistry(root) {
|
|
|
13811
13818
|
}
|
|
13812
13819
|
function resolveCacheDir(root) {
|
|
13813
13820
|
const custom = readComponentsJson(root)?.mmi?.cacheDir;
|
|
13814
|
-
return (0,
|
|
13821
|
+
return (0, import_node_path18.join)(root, custom ?? DESIGN_SYSTEM_CACHE_DIR);
|
|
13815
13822
|
}
|
|
13816
13823
|
function resolveRegistryUrlTemplate(root) {
|
|
13817
13824
|
return readComponentsJson(root)?.registries?.["@mutmutco"];
|
|
@@ -13820,7 +13827,7 @@ function registryItemUrl(template, name) {
|
|
|
13820
13827
|
return template.replace("{name}", name);
|
|
13821
13828
|
}
|
|
13822
13829
|
function readDesignSystemManifest(root) {
|
|
13823
|
-
const raw = readJsonFile2((0,
|
|
13830
|
+
const raw = readJsonFile2((0, import_node_path18.join)(root, DESIGN_SYSTEM_MANIFEST_PATH));
|
|
13824
13831
|
if (!raw || !Array.isArray(raw.components)) return void 0;
|
|
13825
13832
|
return raw;
|
|
13826
13833
|
}
|
|
@@ -13836,7 +13843,7 @@ function scanCachedComponentNames(cacheDir) {
|
|
|
13836
13843
|
const names = /* @__PURE__ */ new Set();
|
|
13837
13844
|
const walk = (dir) => {
|
|
13838
13845
|
for (const ent of (0, import_node_fs19.readdirSync)(dir, { withFileTypes: true })) {
|
|
13839
|
-
const p = (0,
|
|
13846
|
+
const p = (0, import_node_path18.join)(dir, ent.name);
|
|
13840
13847
|
if (ent.isDirectory()) walk(p);
|
|
13841
13848
|
else if (ent.isFile() && /\.(tsx?|jsx?)$/.test(ent.name)) {
|
|
13842
13849
|
names.add(ent.name.replace(/\.(tsx|ts|jsx|js)$/, ""));
|
|
@@ -13925,7 +13932,7 @@ async function gatherRegistryComponentsState(root, targetVersion, deps) {
|
|
|
13925
13932
|
let componentStale = false;
|
|
13926
13933
|
for (const file of item.files) {
|
|
13927
13934
|
if (!file.target || file.content == null) continue;
|
|
13928
|
-
const cachePath = (0,
|
|
13935
|
+
const cachePath = (0, import_node_path18.join)(cacheDir, cacheRelativePath(file.target));
|
|
13929
13936
|
if (!(0, import_node_fs19.existsSync)(cachePath)) {
|
|
13930
13937
|
componentStale = true;
|
|
13931
13938
|
break;
|
|
@@ -13941,7 +13948,7 @@ async function gatherRegistryComponentsState(root, targetVersion, deps) {
|
|
|
13941
13948
|
}
|
|
13942
13949
|
}
|
|
13943
13950
|
if (componentStale) {
|
|
13944
|
-
if ((0, import_node_fs19.existsSync)((0,
|
|
13951
|
+
if ((0, import_node_fs19.existsSync)((0, import_node_path18.join)(cacheDir, "ui", `${name}.tsx`)) || (0, import_node_fs19.existsSync)((0, import_node_path18.join)(cacheDir, `${name}.tsx`))) {
|
|
13945
13952
|
stale.push(name);
|
|
13946
13953
|
} else {
|
|
13947
13954
|
missing.push(name);
|
|
@@ -13969,15 +13976,15 @@ async function applyRegistryComponentsSync(root, components, targetVersion, log,
|
|
|
13969
13976
|
if (!item) return { ok: false };
|
|
13970
13977
|
for (const file of item.files) {
|
|
13971
13978
|
if (!file.target || file.content == null) continue;
|
|
13972
|
-
const outPath = (0,
|
|
13973
|
-
deps.mkdir((0,
|
|
13979
|
+
const outPath = (0, import_node_path18.join)(cacheDir, cacheRelativePath(file.target));
|
|
13980
|
+
deps.mkdir((0, import_node_path18.dirname)(outPath));
|
|
13974
13981
|
const body = file.content.endsWith("\n") ? file.content : `${file.content}
|
|
13975
13982
|
`;
|
|
13976
13983
|
deps.writeFile(outPath, body);
|
|
13977
13984
|
}
|
|
13978
13985
|
}
|
|
13979
|
-
const manifestPath = (0,
|
|
13980
|
-
deps.mkdir((0,
|
|
13986
|
+
const manifestPath = (0, import_node_path18.join)(root, DESIGN_SYSTEM_MANIFEST_PATH);
|
|
13987
|
+
deps.mkdir((0, import_node_path18.dirname)(manifestPath));
|
|
13981
13988
|
const manifest = {
|
|
13982
13989
|
version: targetVersion,
|
|
13983
13990
|
syncedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -14746,7 +14753,7 @@ async function announceRelease(deps, args) {
|
|
|
14746
14753
|
|
|
14747
14754
|
// src/port-registry.ts
|
|
14748
14755
|
var import_node_fs20 = require("node:fs");
|
|
14749
|
-
var
|
|
14756
|
+
var import_node_path19 = require("node:path");
|
|
14750
14757
|
|
|
14751
14758
|
// ../infra/port-geometry.mjs
|
|
14752
14759
|
var PORT_BLOCK = 100;
|
|
@@ -14799,18 +14806,18 @@ function existingPortRange(repo, registry2) {
|
|
|
14799
14806
|
return registry2[repo] ?? null;
|
|
14800
14807
|
}
|
|
14801
14808
|
function portRangeInfraAt(root, source) {
|
|
14802
|
-
const registryPath = (0,
|
|
14803
|
-
const ddbScriptPath = (0,
|
|
14809
|
+
const registryPath = (0, import_node_path19.join)(root, "infra", "port-ranges.json");
|
|
14810
|
+
const ddbScriptPath = (0, import_node_path19.join)(root, "infra", "port-ddb.mjs");
|
|
14804
14811
|
if (!(0, import_node_fs20.existsSync)(registryPath) || !(0, import_node_fs20.existsSync)(ddbScriptPath)) return null;
|
|
14805
14812
|
return { root, source, registryPath, ddbScriptPath };
|
|
14806
14813
|
}
|
|
14807
14814
|
function resolvePortRangeInfra(cwd) {
|
|
14808
14815
|
const direct = portRangeInfraAt(cwd, "cwd");
|
|
14809
14816
|
if (direct) return direct;
|
|
14810
|
-
for (let dir = cwd; ; dir = (0,
|
|
14811
|
-
const sibling = portRangeInfraAt((0,
|
|
14817
|
+
for (let dir = cwd; ; dir = (0, import_node_path19.dirname)(dir)) {
|
|
14818
|
+
const sibling = portRangeInfraAt((0, import_node_path19.join)(dir, "MMI-Hub"), "sibling-hub");
|
|
14812
14819
|
if (sibling) return sibling;
|
|
14813
|
-
const parent = (0,
|
|
14820
|
+
const parent = (0, import_node_path19.dirname)(dir);
|
|
14814
14821
|
if (parent === dir) return null;
|
|
14815
14822
|
}
|
|
14816
14823
|
}
|
|
@@ -15749,6 +15756,22 @@ function stageRequiredSecrets(stage2, meta) {
|
|
|
15749
15756
|
function stageKey(stage2, key) {
|
|
15750
15757
|
return key.includes("/") ? key : `${stage2}/${key}`;
|
|
15751
15758
|
}
|
|
15759
|
+
function materializedRuntimeSecretName(entry) {
|
|
15760
|
+
return entry.includes(":") ? entry.split(":").pop() : entry;
|
|
15761
|
+
}
|
|
15762
|
+
function runtimeSecretStreamGap(stage2, meta, presentSecrets) {
|
|
15763
|
+
const streamed = new Set(
|
|
15764
|
+
(contractByStage(meta.requiredRuntimeSecrets)[stage2] ?? []).map(materializedRuntimeSecretName)
|
|
15765
|
+
);
|
|
15766
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15767
|
+
const gap = [];
|
|
15768
|
+
for (const name of stageRequiredSecrets(stage2, meta).map(materializedRuntimeSecretName)) {
|
|
15769
|
+
if (seen.has(name)) continue;
|
|
15770
|
+
seen.add(name);
|
|
15771
|
+
if (!streamed.has(name) && presentSecrets.has(stageKey(stage2, name))) gap.push(name);
|
|
15772
|
+
}
|
|
15773
|
+
return gap;
|
|
15774
|
+
}
|
|
15752
15775
|
function hasRuntimeSecretContract(contract) {
|
|
15753
15776
|
if (!contract || typeof contract !== "object" || Array.isArray(contract)) return false;
|
|
15754
15777
|
return ["dev", "rc", "main"].some((stage2) => Array.isArray(contract[stage2]));
|
|
@@ -15990,6 +16013,11 @@ async function buildV2Doctor(repoOrSlug, deps) {
|
|
|
15990
16013
|
const missing = required.filter((key) => !presentSecrets.has(key));
|
|
15991
16014
|
return [stage2, { required, present, missing }];
|
|
15992
16015
|
}));
|
|
16016
|
+
const runtimeSecretStreamWarnings = Object.fromEntries(STAGES.map((stage2) => [
|
|
16017
|
+
stage2,
|
|
16018
|
+
stageInTrack(meta, stage2) ? runtimeSecretStreamGap(stage2, meta, presentSecrets) : []
|
|
16019
|
+
]));
|
|
16020
|
+
const runtimeSecretStreamWarningRows = STAGES.map((stage2) => ({ stage: stage2, names: runtimeSecretStreamWarnings[stage2] })).filter((row) => row.names.length > 0);
|
|
15993
16021
|
const metaMissing = ["class", "projectType", "deployModel", "vaultPath", "kbPointer"].filter((key) => meta[key] === void 0).concat(boardRegistryGaps(meta));
|
|
15994
16022
|
const ok = !secretsError && metaMissing.length === 0 && Object.values(deployCoords).every((v) => v.ok) && Object.values(secrets).every((v) => v.missing.length === 0);
|
|
15995
16023
|
const edgeDomainWarnings = deps.resolveDns ? await probeEdgeDomains(meta, deps.resolveDns) : [];
|
|
@@ -16005,6 +16033,7 @@ async function buildV2Doctor(repoOrSlug, deps) {
|
|
|
16005
16033
|
autoHealAvailable: Object.keys(autoHeal.patch),
|
|
16006
16034
|
appOwnedGaps: autoHeal.appOwnedGaps,
|
|
16007
16035
|
...edgeDomainWarnings.length ? { edgeDomainWarnings } : {},
|
|
16036
|
+
...runtimeSecretStreamWarningRows.length ? { runtimeSecretStreamWarnings: runtimeSecretStreamWarningRows } : {},
|
|
16008
16037
|
appAttested: appAttestationOf(meta) ?? void 0
|
|
16009
16038
|
};
|
|
16010
16039
|
}
|
|
@@ -16035,6 +16064,9 @@ function renderReadinessIssueBody(existingBody, report, opts = {}) {
|
|
|
16035
16064
|
...(report.edgeDomainWarnings ?? []).map(
|
|
16036
16065
|
(w) => `- \u26A0 edge domain does not resolve in DNS (advisory): ${w.stage} \u2192 ${w.host}; verify the registry edgeDomains value against the live public host`
|
|
16037
16066
|
),
|
|
16067
|
+
...(report.runtimeSecretStreamWarnings ?? []).map(
|
|
16068
|
+
(w) => `- \u26A0 required secrets provisioned but not in requiredRuntimeSecrets (advisory): ${w.stage} \u2192 ${w.names.join(", ")}; add them to the registry stream list or they will not be materialized into tenant.env`
|
|
16069
|
+
),
|
|
16038
16070
|
"",
|
|
16039
16071
|
"### Auto-heal applied / available",
|
|
16040
16072
|
...opts.healed?.length ? opts.healed.map((x) => `- ${x}`) : report.autoHealAvailable.map((x) => `- ${x}`),
|
|
@@ -17549,14 +17581,14 @@ function authorizeBodyHasMismatch(body) {
|
|
|
17549
17581
|
// src/doctor-run.ts
|
|
17550
17582
|
var import_node_fs26 = require("node:fs");
|
|
17551
17583
|
var import_promises7 = require("node:fs/promises");
|
|
17552
|
-
var
|
|
17584
|
+
var import_node_path23 = require("node:path");
|
|
17553
17585
|
var import_node_os5 = require("node:os");
|
|
17554
17586
|
|
|
17555
17587
|
// src/cursor-plugin-seed.ts
|
|
17556
17588
|
var import_node_child_process12 = require("node:child_process");
|
|
17557
17589
|
var import_node_fs22 = require("node:fs");
|
|
17558
17590
|
var import_node_os4 = require("node:os");
|
|
17559
|
-
var
|
|
17591
|
+
var import_node_path20 = require("node:path");
|
|
17560
17592
|
var import_node_util7 = require("node:util");
|
|
17561
17593
|
function isSemverVersion(v) {
|
|
17562
17594
|
return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
|
|
@@ -17573,13 +17605,13 @@ function ghReleaseTarballApiArgs(tag) {
|
|
|
17573
17605
|
}
|
|
17574
17606
|
function cursorUserGlobalStatePath() {
|
|
17575
17607
|
if (process.platform === "win32") {
|
|
17576
|
-
const base = process.env.APPDATA || (0,
|
|
17577
|
-
return (0,
|
|
17608
|
+
const base = process.env.APPDATA || (0, import_node_path20.join)((0, import_node_os4.homedir)(), "AppData", "Roaming");
|
|
17609
|
+
return (0, import_node_path20.join)(base, "Cursor", "User", "globalStorage", "state.vscdb");
|
|
17578
17610
|
}
|
|
17579
17611
|
if (process.platform === "darwin") {
|
|
17580
|
-
return (0,
|
|
17612
|
+
return (0, import_node_path20.join)((0, import_node_os4.homedir)(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
17581
17613
|
}
|
|
17582
|
-
return (0,
|
|
17614
|
+
return (0, import_node_path20.join)((0, import_node_os4.homedir)(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
17583
17615
|
}
|
|
17584
17616
|
async function readCursorThirdPartyExtensibilityEnabled(execFileP5) {
|
|
17585
17617
|
const dbPath = cursorUserGlobalStatePath();
|
|
@@ -17599,7 +17631,7 @@ async function readCursorThirdPartyExtensibilityEnabled(execFileP5) {
|
|
|
17599
17631
|
function syncDirContents(src, dest) {
|
|
17600
17632
|
(0, import_node_fs22.mkdirSync)(dest, { recursive: true });
|
|
17601
17633
|
for (const name of (0, import_node_fs22.readdirSync)(dest)) {
|
|
17602
|
-
(0, import_node_fs22.rmSync)((0,
|
|
17634
|
+
(0, import_node_fs22.rmSync)((0, import_node_path20.join)(dest, name), { recursive: true, force: true });
|
|
17603
17635
|
}
|
|
17604
17636
|
(0, import_node_fs22.cpSync)(src, dest, { recursive: true });
|
|
17605
17637
|
}
|
|
@@ -17607,21 +17639,21 @@ function releaseTag(releasedVersion) {
|
|
|
17607
17639
|
return releasedVersion.startsWith("v") ? releasedVersion : `v${releasedVersion}`;
|
|
17608
17640
|
}
|
|
17609
17641
|
async function extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5) {
|
|
17610
|
-
const tarFile = (0,
|
|
17642
|
+
const tarFile = (0, import_node_path20.join)(tmpRoot, "archive.tar");
|
|
17611
17643
|
try {
|
|
17612
17644
|
await execFileP5("git", gitFetchReleaseTagArgs(hubCheckout, tag), { timeout: 6e4 });
|
|
17613
17645
|
await execFileP5("git", ["-C", hubCheckout, "archive", "--format=tar", `--output=${tarFile}`, tag, "plugins/mmi"], {
|
|
17614
17646
|
timeout: 6e4
|
|
17615
17647
|
});
|
|
17616
17648
|
await execFileP5("tar", ["-xf", tarFile, "-C", tmpRoot], { timeout: 6e4 });
|
|
17617
|
-
const pluginMmi = (0,
|
|
17618
|
-
return (0, import_node_fs22.existsSync)((0,
|
|
17649
|
+
const pluginMmi = (0, import_node_path20.join)(tmpRoot, "plugins", "mmi");
|
|
17650
|
+
return (0, import_node_fs22.existsSync)((0, import_node_path20.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
|
|
17619
17651
|
} catch {
|
|
17620
17652
|
return void 0;
|
|
17621
17653
|
}
|
|
17622
17654
|
}
|
|
17623
17655
|
async function downloadPluginMmiViaGh(tag, tmpRoot) {
|
|
17624
|
-
const tarPath = (0,
|
|
17656
|
+
const tarPath = (0, import_node_path20.join)(tmpRoot, "repo.tgz");
|
|
17625
17657
|
try {
|
|
17626
17658
|
(0, import_node_fs22.mkdirSync)(tmpRoot, { recursive: true });
|
|
17627
17659
|
const { stdout } = await execFileBuffer("gh", ghReleaseTarballApiArgs(tag), {
|
|
@@ -17634,8 +17666,8 @@ async function downloadPluginMmiViaGh(tag, tmpRoot) {
|
|
|
17634
17666
|
await execFileBuffer("tar", ["-xzf", tarPath, "-C", tmpRoot], { timeout: 12e4, windowsHide: true });
|
|
17635
17667
|
const top = (0, import_node_fs22.readdirSync)(tmpRoot).find((entry) => entry !== "repo.tgz");
|
|
17636
17668
|
if (!top) return void 0;
|
|
17637
|
-
const pluginMmi = (0,
|
|
17638
|
-
return (0, import_node_fs22.existsSync)((0,
|
|
17669
|
+
const pluginMmi = (0, import_node_path20.join)(tmpRoot, top, "plugins", "mmi");
|
|
17670
|
+
return (0, import_node_fs22.existsSync)((0, import_node_path20.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
|
|
17639
17671
|
} catch {
|
|
17640
17672
|
return void 0;
|
|
17641
17673
|
}
|
|
@@ -17647,7 +17679,7 @@ async function resolvePluginMmiSource(releasedVersion, hubCheckout, tmpRoot, exe
|
|
|
17647
17679
|
const fromHub = await extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5);
|
|
17648
17680
|
if (fromHub) return fromHub;
|
|
17649
17681
|
}
|
|
17650
|
-
return downloadPluginMmiViaGh(tag, (0,
|
|
17682
|
+
return downloadPluginMmiViaGh(tag, (0, import_node_path20.join)(tmpRoot, "gh"));
|
|
17651
17683
|
}
|
|
17652
17684
|
function cursorPluginPinsNeedingSeed(pins, releasedVersion) {
|
|
17653
17685
|
if (!isSemverVersion(releasedVersion)) return pins.filter((pin) => !pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty);
|
|
@@ -18044,8 +18076,45 @@ var CLAUDE_RECOVERY = `claude plugin marketplace remove ${LEGACY_MMI_MARKETPLACE
|
|
|
18044
18076
|
var CODEX_RECOVERY = `codex plugin marketplace remove ${LEGACY_MMI_MARKETPLACE} && codex plugin marketplace remove mutmutco && codex plugin marketplace add mutmutco/MMI-Hub --ref main && codex plugin add mmi@mutmutco`;
|
|
18045
18077
|
var OPENCODE_PLUGIN_PACKAGE = "@mutmutco/opencode-mmi";
|
|
18046
18078
|
var OPENCODE_PLUGIN_SPEC = `${OPENCODE_PLUGIN_PACKAGE}@latest`;
|
|
18047
|
-
var OPENCODE_PLUGIN_INSTALL_COMMAND = `
|
|
18079
|
+
var OPENCODE_PLUGIN_INSTALL_COMMAND = `mmi-cli doctor --apply`;
|
|
18048
18080
|
var OPENCODE_RECOVERY = `${OPENCODE_PLUGIN_INSTALL_COMMAND} # then restart OpenCode to load MMI commands`;
|
|
18081
|
+
var OPENCODE_WORKFLOW_COMMANDS = [
|
|
18082
|
+
"mmi",
|
|
18083
|
+
"secrets",
|
|
18084
|
+
"stage",
|
|
18085
|
+
"rcand",
|
|
18086
|
+
"release",
|
|
18087
|
+
"hotfix",
|
|
18088
|
+
"bootstrap",
|
|
18089
|
+
"grind",
|
|
18090
|
+
"build",
|
|
18091
|
+
"handoff",
|
|
18092
|
+
"coop",
|
|
18093
|
+
"browser-automation"
|
|
18094
|
+
];
|
|
18095
|
+
function opencodeCommandDescription(command) {
|
|
18096
|
+
if (command === "mmi") return "Run the MMI work-board workflow.";
|
|
18097
|
+
if (command === "browser-automation") return "Run the MMI browser automation workflow.";
|
|
18098
|
+
return `Run the MMI ${command} workflow.`;
|
|
18099
|
+
}
|
|
18100
|
+
function opencodeCommandTemplate(command) {
|
|
18101
|
+
return [
|
|
18102
|
+
`Use the \`${command}\` skill and follow it exactly.`,
|
|
18103
|
+
"",
|
|
18104
|
+
"$ARGUMENTS"
|
|
18105
|
+
].join("\n");
|
|
18106
|
+
}
|
|
18107
|
+
function opencodeCommandMarkdown(command) {
|
|
18108
|
+
return [
|
|
18109
|
+
"---",
|
|
18110
|
+
`description: ${opencodeCommandDescription(command)}`,
|
|
18111
|
+
"agent: build",
|
|
18112
|
+
"---",
|
|
18113
|
+
"",
|
|
18114
|
+
opencodeCommandTemplate(command),
|
|
18115
|
+
""
|
|
18116
|
+
].join("\n");
|
|
18117
|
+
}
|
|
18049
18118
|
var PLUGIN_SURFACE_HEAL = {
|
|
18050
18119
|
claude: {
|
|
18051
18120
|
delivery: "plugin-cli",
|
|
@@ -18239,6 +18308,7 @@ function buildOpencodeVersionCheck(input) {
|
|
|
18239
18308
|
return { ...base, ok: false, installedVersion: input.installedVersion, releasedVersion: input.releasedVersion };
|
|
18240
18309
|
}
|
|
18241
18310
|
var OPENCODE_CONFIG_PLUGIN_LABEL = "OpenCode MMI adapter config wiring";
|
|
18311
|
+
var OPENCODE_SURFACE_ASSETS_LABEL = "OpenCode MMI commands and skills";
|
|
18242
18312
|
function opencodePluginEntryMatches(entry) {
|
|
18243
18313
|
return entry === OPENCODE_PLUGIN_PACKAGE || entry === OPENCODE_PLUGIN_SPEC || Array.isArray(entry) && (entry[0] === OPENCODE_PLUGIN_PACKAGE || entry[0] === OPENCODE_PLUGIN_SPEC);
|
|
18244
18314
|
}
|
|
@@ -18326,6 +18396,23 @@ function buildOpencodeConfigPluginCheck(input) {
|
|
|
18326
18396
|
}
|
|
18327
18397
|
return { ...base, configPath: input.configPath };
|
|
18328
18398
|
}
|
|
18399
|
+
function buildOpencodeSurfaceAssetsCheck(input) {
|
|
18400
|
+
const base = {
|
|
18401
|
+
ok: true,
|
|
18402
|
+
label: OPENCODE_SURFACE_ASSETS_LABEL,
|
|
18403
|
+
fix: OPENCODE_RECOVERY,
|
|
18404
|
+
configPath: input.configPath,
|
|
18405
|
+
commandsDir: input.commandsDir,
|
|
18406
|
+
skillsPath: input.skillsPath
|
|
18407
|
+
};
|
|
18408
|
+
if (!input.isOrgRepo) return base;
|
|
18409
|
+
const existing = new Set(input.existingCommands.map((c) => c.toLowerCase()));
|
|
18410
|
+
const missingCommands = OPENCODE_WORKFLOW_COMMANDS.filter((c) => !existing.has(c));
|
|
18411
|
+
const normalizedSkillsPath = input.skillsPath?.replace(/\\/g, "/");
|
|
18412
|
+
const hasSkillsPath = Boolean(normalizedSkillsPath && input.configuredSkillsPaths?.some((p) => p.replace(/\\/g, "/") === normalizedSkillsPath));
|
|
18413
|
+
if (!missingCommands.length && hasSkillsPath) return { ...base, hasSkillsPath };
|
|
18414
|
+
return { ...base, ok: false, missingCommands, hasSkillsPath };
|
|
18415
|
+
}
|
|
18329
18416
|
var OPENCODE_DESKTOP_BOOTSTRAP_LABEL = "OpenCode Desktop stale project bootstrap";
|
|
18330
18417
|
var OPENCODE_DESKTOP_BOOTSTRAP_FIX = "OpenCode Desktop is bootstrapping a deleted MMI worktree; open an existing checkout in OpenCode, remove/select away from the stale project entry, then restart OpenCode";
|
|
18331
18418
|
function decodeLogUrlDirectory(value) {
|
|
@@ -18440,7 +18527,7 @@ function buildCursorPluginInstallCheck(input) {
|
|
|
18440
18527
|
};
|
|
18441
18528
|
}
|
|
18442
18529
|
}
|
|
18443
|
-
if (input.
|
|
18530
|
+
if (input.cacheRootExists && isSemverVersion2(input.releasedVersion) && input.pins.some((pin) => isSemverVersion2(pin.version) && compareVersions(pin.version, input.releasedVersion) < 0)) {
|
|
18444
18531
|
const stale = input.pins.filter((pin) => isSemverVersion2(pin.version) && compareVersions(pin.version, input.releasedVersion) < 0).map((pin) => `${pin.version} at ${pin.name}`).join(", ");
|
|
18445
18532
|
return {
|
|
18446
18533
|
...base,
|
|
@@ -18620,12 +18707,34 @@ function doctorHealPlan(input) {
|
|
|
18620
18707
|
const versionUpdate = versionAutoUpdateAction(input.versionReport, input.hasPluginRoot);
|
|
18621
18708
|
const pluginReinstall = input.isOrgRepo && !input.installedVersionCheck.ok && (input.surface === "claude-cli" || input.surface === "claude-vscode" || input.surface === "codex") && Boolean(input.installedVersionCheck.staleSurfaces?.length);
|
|
18622
18709
|
const legacyPluginMigration = input.isOrgRepo && !input.legacyPluginCheck.ok;
|
|
18623
|
-
const
|
|
18624
|
-
|
|
18710
|
+
const opencodeReinstall = input.isOrgRepo && Boolean(input.opencodeAdapterStale);
|
|
18711
|
+
const cursorReseed = input.isOrgRepo && Boolean(input.cursorCacheStale);
|
|
18712
|
+
const needsEagerHeal = versionUpdate !== "none" || pluginReinstall || legacyPluginMigration || opencodeReinstall || cursorReseed;
|
|
18713
|
+
return { needsEagerHeal, versionUpdate, pluginReinstall, legacyPluginMigration, opencodeReinstall, cursorReseed };
|
|
18625
18714
|
}
|
|
18626
18715
|
function doctorPreflightDoneLine(surface) {
|
|
18627
18716
|
return `\u21BB MMI tooling updated \u2014 ${reloadAction(surface)} to load changes`;
|
|
18628
18717
|
}
|
|
18718
|
+
function selfUpdateHaltLine(report) {
|
|
18719
|
+
const to = report.releasedVersion ?? "latest";
|
|
18720
|
+
return `\u21BB mmi-cli updated \u2192 ${to}. This process is still the previous version \u2014 rerun \`mmi-cli doctor --apply\` so the remaining surfaces reconcile under the new CLI.`;
|
|
18721
|
+
}
|
|
18722
|
+
function buildSelfUpdateHaltPayload(input) {
|
|
18723
|
+
return {
|
|
18724
|
+
ok: false,
|
|
18725
|
+
rerunRequired: true,
|
|
18726
|
+
reason: "mmi-cli self-updated; rerun doctor so the remaining surfaces reconcile under the new CLI",
|
|
18727
|
+
updatedTo: input.updatedTo ?? null,
|
|
18728
|
+
checks: input.checks
|
|
18729
|
+
};
|
|
18730
|
+
}
|
|
18731
|
+
function preflightOutcome(input) {
|
|
18732
|
+
if (input.gaps.length) {
|
|
18733
|
+
return { healed: false, line: `\u26A0 MMI preflight: ${input.gaps.length} item(s) still need attention \u2014 ${input.gaps.map((g) => g.fix).join(" \xB7 ")}` };
|
|
18734
|
+
}
|
|
18735
|
+
if (input.needsEagerHeal) return { healed: true, line: doctorPreflightDoneLine(input.surface) };
|
|
18736
|
+
return { healed: true };
|
|
18737
|
+
}
|
|
18629
18738
|
function pluginAutonomousHaltLine(reloadHint) {
|
|
18630
18739
|
return `\u26A0 PLUGIN RELOAD REQUIRED \u2014 mmi:* skills and agent types are unavailable until you ${reloadHint}. Halt autonomous /grind and /build until then.`;
|
|
18631
18740
|
}
|
|
@@ -18661,14 +18770,14 @@ function renderTerseDoctorReport(input) {
|
|
|
18661
18770
|
|
|
18662
18771
|
// src/kb-drift-report.ts
|
|
18663
18772
|
var import_node_fs23 = require("node:fs");
|
|
18664
|
-
var
|
|
18773
|
+
var import_node_path21 = require("node:path");
|
|
18665
18774
|
function yesterdayIso() {
|
|
18666
18775
|
const d = /* @__PURE__ */ new Date();
|
|
18667
18776
|
d.setUTCDate(d.getUTCDate() - 1);
|
|
18668
18777
|
return d.toISOString().slice(0, 10);
|
|
18669
18778
|
}
|
|
18670
18779
|
async function fetchLatestKbDriftReport(execFileP5, repoRoot) {
|
|
18671
|
-
const sagaIo = (0,
|
|
18780
|
+
const sagaIo = (0, import_node_path21.join)(repoRoot, "infra", "saga-io.mjs");
|
|
18672
18781
|
if (!(0, import_node_fs23.existsSync)(sagaIo)) return null;
|
|
18673
18782
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
18674
18783
|
for (const date of [today, yesterdayIso()]) {
|
|
@@ -18686,7 +18795,7 @@ async function fetchLatestKbDriftReport(execFileP5, repoRoot) {
|
|
|
18686
18795
|
|
|
18687
18796
|
// src/cli-doctor-shared.ts
|
|
18688
18797
|
var import_node_fs24 = require("node:fs");
|
|
18689
|
-
var
|
|
18798
|
+
var import_node_path22 = require("node:path");
|
|
18690
18799
|
var import_node_fs25 = require("node:fs");
|
|
18691
18800
|
var GC_GH_TIMEOUT_MS = 2e4;
|
|
18692
18801
|
async function awsCallerArn() {
|
|
@@ -18733,7 +18842,7 @@ async function localBranchHeads() {
|
|
|
18733
18842
|
}
|
|
18734
18843
|
async function currentRepoWorktreeGitRoot(repoRoot) {
|
|
18735
18844
|
const gitCommonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
18736
|
-
return gitCommonDir ? (0,
|
|
18845
|
+
return gitCommonDir ? (0, import_node_path22.resolve)(repoRoot, gitCommonDir, "worktrees") : "";
|
|
18737
18846
|
}
|
|
18738
18847
|
async function worktreeBranches() {
|
|
18739
18848
|
const { stdout } = await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS });
|
|
@@ -18753,7 +18862,7 @@ function resolveGitdirForWorktreeFile(worktreePath, content) {
|
|
|
18753
18862
|
const match = /^gitdir:\s*(.+)\s*$/im.exec(content);
|
|
18754
18863
|
if (!match?.[1]) return void 0;
|
|
18755
18864
|
const raw = match[1].trim();
|
|
18756
|
-
return (0,
|
|
18865
|
+
return (0, import_node_path22.isAbsolute)(raw) ? raw : (0, import_node_path22.resolve)(worktreePath, raw);
|
|
18757
18866
|
}
|
|
18758
18867
|
function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
18759
18868
|
if (!worktreeGitRoot) return false;
|
|
@@ -18762,9 +18871,9 @@ function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
|
18762
18871
|
for (const ent of entries) {
|
|
18763
18872
|
if (!ent.isDirectory()) continue;
|
|
18764
18873
|
try {
|
|
18765
|
-
const gitdirPath = (0, import_node_fs24.readFileSync)((0,
|
|
18766
|
-
const resolvedGitdir = (0,
|
|
18767
|
-
if (sameWorktreeMetadataPath((0,
|
|
18874
|
+
const gitdirPath = (0, import_node_fs24.readFileSync)((0, import_node_path22.join)(worktreeGitRoot, ent.name, "gitdir"), "utf8").trim();
|
|
18875
|
+
const resolvedGitdir = (0, import_node_path22.isAbsolute)(gitdirPath) ? gitdirPath : (0, import_node_path22.resolve)(worktreeGitRoot, ent.name, gitdirPath);
|
|
18876
|
+
if (sameWorktreeMetadataPath((0, import_node_path22.dirname)(resolvedGitdir), worktreePath)) return true;
|
|
18768
18877
|
} catch {
|
|
18769
18878
|
}
|
|
18770
18879
|
}
|
|
@@ -18783,7 +18892,7 @@ function pathExistsKnown(path2) {
|
|
|
18783
18892
|
}
|
|
18784
18893
|
}
|
|
18785
18894
|
function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
18786
|
-
const gitPath = (0,
|
|
18895
|
+
const gitPath = (0, import_node_path22.join)(path2, ".git");
|
|
18787
18896
|
let st;
|
|
18788
18897
|
try {
|
|
18789
18898
|
st = (0, import_node_fs25.lstatSync)(gitPath);
|
|
@@ -18840,7 +18949,7 @@ async function siblingWorktreeDirs() {
|
|
|
18840
18949
|
const siblingRoot = siblingMmiWorktreesRoot(repoRoot);
|
|
18841
18950
|
try {
|
|
18842
18951
|
const entries = (0, import_node_fs25.readdirSync)(siblingRoot, { withFileTypes: true });
|
|
18843
|
-
return entries.filter((ent) => ent.isDirectory()).map((ent) => inspectSiblingWorktreeDir((0,
|
|
18952
|
+
return entries.filter((ent) => ent.isDirectory()).map((ent) => inspectSiblingWorktreeDir((0, import_node_path22.join)(siblingRoot, ent.name), worktreeGitRoot)).filter((entry) => Boolean(entry));
|
|
18844
18953
|
} catch {
|
|
18845
18954
|
return [];
|
|
18846
18955
|
}
|
|
@@ -18890,7 +18999,7 @@ async function fetchHubVersionInfo(baseUrl) {
|
|
|
18890
18999
|
}
|
|
18891
19000
|
function readRepoVersion() {
|
|
18892
19001
|
try {
|
|
18893
|
-
return JSON.parse((0, import_node_fs26.readFileSync)((0,
|
|
19002
|
+
return JSON.parse((0, import_node_fs26.readFileSync)((0, import_node_path23.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
18894
19003
|
} catch {
|
|
18895
19004
|
return void 0;
|
|
18896
19005
|
}
|
|
@@ -18908,24 +19017,24 @@ var NPM_VIEW_TIMEOUT_MS = 15e3;
|
|
|
18908
19017
|
var PLUGIN_PULL_TIMEOUT_MS = 3e4;
|
|
18909
19018
|
async function applyVersionAutoUpdate(report, log) {
|
|
18910
19019
|
const action = versionAutoUpdateAction(report, Boolean(process.env.CLAUDE_PLUGIN_ROOT));
|
|
18911
|
-
if (action === "none") return report;
|
|
19020
|
+
if (action === "none") return { report, applied: "none" };
|
|
18912
19021
|
const target = report.releasedVersion ?? "latest";
|
|
18913
19022
|
if (action === "plugin-pull") {
|
|
18914
19023
|
try {
|
|
18915
19024
|
const root = (await execFileP2("git", ["-C", process.env.CLAUDE_PLUGIN_ROOT, "rev-parse", "--show-toplevel"], { timeout: PLUGIN_PULL_TIMEOUT_MS })).stdout.trim();
|
|
18916
19025
|
log(` \u21BB refreshing MMI plugin ${report.currentVersion} \u2192 ${target} (effective next session)\u2026`);
|
|
18917
19026
|
await execFileP2("git", ["-C", root, "pull", "--ff-only"], { timeout: PLUGIN_PULL_TIMEOUT_MS });
|
|
18918
|
-
return { ...report, ok: true };
|
|
19027
|
+
return { report: { ...report, ok: true }, applied: "plugin-pull" };
|
|
18919
19028
|
} catch {
|
|
18920
|
-
return report;
|
|
19029
|
+
return { report, applied: "none" };
|
|
18921
19030
|
}
|
|
18922
19031
|
}
|
|
18923
19032
|
try {
|
|
18924
19033
|
log(` \u21BB updating mmi-cli ${report.currentVersion} \u2192 ${target}\u2026`);
|
|
18925
19034
|
await runHostBin("npm", ["install", "-g", "@mutmutco/cli@latest"], { timeout: NPM_UPDATE_TIMEOUT_MS });
|
|
18926
|
-
return { ...report, ok: true };
|
|
19035
|
+
return { report: { ...report, ok: true }, applied: "npm" };
|
|
18927
19036
|
} catch {
|
|
18928
|
-
return report;
|
|
19037
|
+
return { report, applied: "none" };
|
|
18929
19038
|
}
|
|
18930
19039
|
}
|
|
18931
19040
|
async function fetchNpmReleasedVersion() {
|
|
@@ -18953,7 +19062,11 @@ async function applyDesignSystemUpdate(check, log) {
|
|
|
18953
19062
|
if (check.latestVersion && installedVersion && compareVersions(installedVersion, check.latestVersion) >= 0) {
|
|
18954
19063
|
return { ...check, ok: true, installedVersion };
|
|
18955
19064
|
}
|
|
18956
|
-
return {
|
|
19065
|
+
return {
|
|
19066
|
+
...check,
|
|
19067
|
+
installedVersion,
|
|
19068
|
+
fix: `${check.packageName} is held below ${check.latestVersion ?? "latest"} by this repo's package.json range \u2014 \`npm update\` cannot cross it. Bump the range or run \`npm install ${check.packageName}@latest\` to force the update.`
|
|
19069
|
+
};
|
|
18957
19070
|
} catch {
|
|
18958
19071
|
return check;
|
|
18959
19072
|
}
|
|
@@ -18987,6 +19100,9 @@ var CLAUDE_PLUGIN_TIMEOUT_MS = 12e4;
|
|
|
18987
19100
|
function runHostBin(bin, args, opts) {
|
|
18988
19101
|
return isWin ? execFileP2("cmd.exe", ["/c", bin, ...args], opts) : execFileP2(bin, args, opts);
|
|
18989
19102
|
}
|
|
19103
|
+
function hostBinAvailable(bin) {
|
|
19104
|
+
return execFileP2(isWin ? "where" : "which", [bin]).then(() => true).catch(() => false);
|
|
19105
|
+
}
|
|
18990
19106
|
async function runClaudePlugin(args) {
|
|
18991
19107
|
try {
|
|
18992
19108
|
await runHostBin("claude", args, { timeout: CLAUDE_PLUGIN_TIMEOUT_MS });
|
|
@@ -19019,7 +19135,7 @@ async function applyPluginHeal(token, surface, log, opts) {
|
|
|
19019
19135
|
}
|
|
19020
19136
|
var installedPluginsPath = (surface = detectSurface(process.env)) => {
|
|
19021
19137
|
const homeDir = surface === "codex" ? ".codex" : ".claude";
|
|
19022
|
-
return (0,
|
|
19138
|
+
return (0, import_node_path23.join)((0, import_node_os5.homedir)(), homeDir, "plugins", "installed_plugins.json");
|
|
19023
19139
|
};
|
|
19024
19140
|
function readInstalledPlugins() {
|
|
19025
19141
|
try {
|
|
@@ -19030,7 +19146,7 @@ function readInstalledPlugins() {
|
|
|
19030
19146
|
}
|
|
19031
19147
|
function installedPluginSources() {
|
|
19032
19148
|
return ["claude", "codex"].map((surface) => {
|
|
19033
|
-
const recordPath = (0,
|
|
19149
|
+
const recordPath = (0, import_node_path23.join)((0, import_node_os5.homedir)(), `.${surface}`, "plugins", "installed_plugins.json");
|
|
19034
19150
|
try {
|
|
19035
19151
|
return { surface, installed: JSON.parse((0, import_node_fs26.readFileSync)(recordPath, "utf8")), recordPath };
|
|
19036
19152
|
} catch {
|
|
@@ -19040,7 +19156,7 @@ function installedPluginSources() {
|
|
|
19040
19156
|
}
|
|
19041
19157
|
function readClaudeSettings() {
|
|
19042
19158
|
try {
|
|
19043
|
-
return JSON.parse((0, import_node_fs26.readFileSync)((0,
|
|
19159
|
+
return JSON.parse((0, import_node_fs26.readFileSync)((0, import_node_path23.join)(process.cwd(), ".claude", "settings.json"), "utf8"));
|
|
19044
19160
|
} catch {
|
|
19045
19161
|
return null;
|
|
19046
19162
|
}
|
|
@@ -19084,8 +19200,17 @@ function backupAndWriteInstalledPlugins(records, pluginId) {
|
|
|
19084
19200
|
return false;
|
|
19085
19201
|
}
|
|
19086
19202
|
}
|
|
19203
|
+
function opencodeConfigDir() {
|
|
19204
|
+
return (0, import_node_path23.join)((0, import_node_os5.homedir)(), ".config", "opencode");
|
|
19205
|
+
}
|
|
19087
19206
|
function opencodeConfigPath() {
|
|
19088
|
-
return (0,
|
|
19207
|
+
return (0, import_node_path23.join)(opencodeConfigDir(), "opencode.jsonc");
|
|
19208
|
+
}
|
|
19209
|
+
function opencodeCommandsDir() {
|
|
19210
|
+
return (0, import_node_path23.join)(opencodeConfigDir(), "commands");
|
|
19211
|
+
}
|
|
19212
|
+
function opencodeSkillsPath() {
|
|
19213
|
+
return (0, import_node_path23.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "skills");
|
|
19089
19214
|
}
|
|
19090
19215
|
function opencodeConfigSnapshot() {
|
|
19091
19216
|
const path2 = opencodeConfigPath();
|
|
@@ -19094,12 +19219,14 @@ function opencodeConfigSnapshot() {
|
|
|
19094
19219
|
const raw = (0, import_node_fs26.readFileSync)(path2, "utf8");
|
|
19095
19220
|
const parsed = JSON.parse(stripJsonc(raw));
|
|
19096
19221
|
const hasPluginField = Object.prototype.hasOwnProperty.call(parsed, "plugin");
|
|
19222
|
+
const skillsPaths = Array.isArray(parsed.skills?.paths) ? parsed.skills.paths.filter((p) => typeof p === "string") : void 0;
|
|
19097
19223
|
return {
|
|
19098
19224
|
path: path2,
|
|
19099
19225
|
hasConfig: true,
|
|
19100
19226
|
hasPluginField,
|
|
19101
19227
|
parseOk: true,
|
|
19102
19228
|
raw,
|
|
19229
|
+
...skillsPaths ? { skillsPaths } : {},
|
|
19103
19230
|
...Array.isArray(parsed.plugin) ? { pluginEntries: parsed.plugin } : parsed.plugin === void 0 ? {} : { pluginEntries: void 0 }
|
|
19104
19231
|
};
|
|
19105
19232
|
} catch {
|
|
@@ -19112,7 +19239,7 @@ function writeOpencodeConfigPlugin(snapshot) {
|
|
|
19112
19239
|
const plan2 = planOpencodeConfigWrite(snapshot.hasConfig ? snapshot.raw : void 0);
|
|
19113
19240
|
if (plan2.action === "already") return true;
|
|
19114
19241
|
if (!plan2.text || plan2.action === "unsafe") return false;
|
|
19115
|
-
(0, import_node_fs26.mkdirSync)((0,
|
|
19242
|
+
(0, import_node_fs26.mkdirSync)((0, import_node_path23.dirname)(path2), { recursive: true });
|
|
19116
19243
|
if (snapshot.hasConfig) (0, import_node_fs26.copyFileSync)(path2, `${path2}.bak`);
|
|
19117
19244
|
(0, import_node_fs26.writeFileSync)(path2, plan2.text, "utf8");
|
|
19118
19245
|
return true;
|
|
@@ -19120,23 +19247,96 @@ function writeOpencodeConfigPlugin(snapshot) {
|
|
|
19120
19247
|
return false;
|
|
19121
19248
|
}
|
|
19122
19249
|
}
|
|
19250
|
+
function writeOpencodeSkillsPath(snapshot, skillsPath) {
|
|
19251
|
+
try {
|
|
19252
|
+
const raw = snapshot.hasConfig ? snapshot.raw : void 0;
|
|
19253
|
+
const parsed = raw ? JSON.parse(stripJsonc(raw)) : { $schema: "https://opencode.ai/config.json" };
|
|
19254
|
+
const skills = parsed.skills && typeof parsed.skills === "object" && !Array.isArray(parsed.skills) ? parsed.skills : {};
|
|
19255
|
+
const paths = Array.isArray(skills.paths) ? skills.paths.filter((p) => typeof p === "string") : [];
|
|
19256
|
+
const normalized = skillsPath.replace(/\\/g, "/");
|
|
19257
|
+
if (!paths.some((p) => p.replace(/\\/g, "/") === normalized)) paths.push(skillsPath.replace(/\\/g, "/"));
|
|
19258
|
+
parsed.skills = { ...skills, paths };
|
|
19259
|
+
(0, import_node_fs26.mkdirSync)((0, import_node_path23.dirname)(snapshot.path), { recursive: true });
|
|
19260
|
+
if (snapshot.hasConfig && (0, import_node_fs26.existsSync)(snapshot.path)) (0, import_node_fs26.copyFileSync)(snapshot.path, `${snapshot.path}.bak`);
|
|
19261
|
+
(0, import_node_fs26.writeFileSync)(snapshot.path, `${JSON.stringify(parsed, null, 2)}
|
|
19262
|
+
`, "utf8");
|
|
19263
|
+
return true;
|
|
19264
|
+
} catch {
|
|
19265
|
+
return false;
|
|
19266
|
+
}
|
|
19267
|
+
}
|
|
19268
|
+
function opencodeExistingCommands() {
|
|
19269
|
+
try {
|
|
19270
|
+
return (0, import_node_fs26.readdirSync)(opencodeCommandsDir(), { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name.slice(0, -3).toLowerCase());
|
|
19271
|
+
} catch {
|
|
19272
|
+
return [];
|
|
19273
|
+
}
|
|
19274
|
+
}
|
|
19275
|
+
function writeOpencodeCommandFiles() {
|
|
19276
|
+
try {
|
|
19277
|
+
const dir = opencodeCommandsDir();
|
|
19278
|
+
(0, import_node_fs26.mkdirSync)(dir, { recursive: true });
|
|
19279
|
+
for (const command of OPENCODE_WORKFLOW_COMMANDS) {
|
|
19280
|
+
(0, import_node_fs26.writeFileSync)((0, import_node_path23.join)(dir, `${command}.md`), opencodeCommandMarkdown(command), "utf8");
|
|
19281
|
+
}
|
|
19282
|
+
return true;
|
|
19283
|
+
} catch {
|
|
19284
|
+
return false;
|
|
19285
|
+
}
|
|
19286
|
+
}
|
|
19287
|
+
function readOpencodeAdapterDiskVersion() {
|
|
19288
|
+
const candidates = [
|
|
19289
|
+
(0, import_node_path23.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "package.json"),
|
|
19290
|
+
(0, import_node_path23.join)((0, import_node_os5.homedir)(), ".cache", "opencode", "node_modules", "@mutmutco", "opencode-mmi", "package.json")
|
|
19291
|
+
];
|
|
19292
|
+
for (const path2 of candidates) {
|
|
19293
|
+
try {
|
|
19294
|
+
const parsed = JSON.parse((0, import_node_fs26.readFileSync)(path2, "utf8"));
|
|
19295
|
+
if (typeof parsed.version === "string" && parsed.version.trim()) return parsed.version.trim();
|
|
19296
|
+
} catch {
|
|
19297
|
+
continue;
|
|
19298
|
+
}
|
|
19299
|
+
}
|
|
19300
|
+
return void 0;
|
|
19301
|
+
}
|
|
19302
|
+
function opencodeMmiPluginSpecs(snapshot) {
|
|
19303
|
+
const specs = (snapshot.pluginEntries ?? []).map((entry) => Array.isArray(entry) ? entry[0] : entry).filter((entry) => typeof entry === "string").filter((entry) => entry === OPENCODE_PLUGIN_PACKAGE || entry === OPENCODE_PLUGIN_SPEC || entry.startsWith("@mutmutco/"));
|
|
19304
|
+
return Array.from(new Set(specs.length ? specs : [OPENCODE_PLUGIN_SPEC]));
|
|
19305
|
+
}
|
|
19306
|
+
async function forceInstallOpencodeMmiPlugins(snapshot, log) {
|
|
19307
|
+
try {
|
|
19308
|
+
const specs = opencodeMmiPluginSpecs(snapshot);
|
|
19309
|
+
log(` \u21BB force-refreshing OpenCode MMI npm plugin(s): ${specs.join(", ")}\u2026`);
|
|
19310
|
+
(0, import_node_fs26.mkdirSync)(opencodeConfigDir(), { recursive: true });
|
|
19311
|
+
await runHostBin("npm", ["install", "--prefix", opencodeConfigDir(), "--force", ...specs], { timeout: NPM_UPDATE_TIMEOUT_MS });
|
|
19312
|
+
return true;
|
|
19313
|
+
} catch {
|
|
19314
|
+
return false;
|
|
19315
|
+
}
|
|
19316
|
+
}
|
|
19317
|
+
function opencodeInstalledVersionForDoctor() {
|
|
19318
|
+
return process.env.MMI_OPENCODE_PLUGIN_VERSION || readOpencodeAdapterDiskVersion();
|
|
19319
|
+
}
|
|
19320
|
+
function opencodePluginVersionsForReport() {
|
|
19321
|
+
return [process.env.MMI_OPENCODE_PLUGIN_VERSION, readOpencodeAdapterDiskVersion()].filter((v) => Boolean(v));
|
|
19322
|
+
}
|
|
19123
19323
|
function opencodeDesktopLogsRoot() {
|
|
19124
19324
|
if (process.platform === "win32") {
|
|
19125
|
-
const base = process.env.APPDATA || (0,
|
|
19126
|
-
return (0,
|
|
19325
|
+
const base = process.env.APPDATA || (0, import_node_path23.join)((0, import_node_os5.homedir)(), "AppData", "Roaming");
|
|
19326
|
+
return (0, import_node_path23.join)(base, "ai.opencode.desktop", "logs");
|
|
19127
19327
|
}
|
|
19128
19328
|
if (process.platform === "darwin") {
|
|
19129
|
-
return (0,
|
|
19329
|
+
return (0, import_node_path23.join)((0, import_node_os5.homedir)(), "Library", "Application Support", "ai.opencode.desktop", "logs");
|
|
19130
19330
|
}
|
|
19131
|
-
return (0,
|
|
19331
|
+
return (0, import_node_path23.join)((0, import_node_os5.homedir)(), ".config", "ai.opencode.desktop", "logs");
|
|
19132
19332
|
}
|
|
19133
19333
|
function opencodeDesktopBootstrapSnapshot() {
|
|
19134
19334
|
const root = opencodeDesktopLogsRoot();
|
|
19135
19335
|
try {
|
|
19136
|
-
const sessionDirs = (0, import_node_fs26.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => (0,
|
|
19336
|
+
const sessionDirs = (0, import_node_fs26.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => (0, import_node_path23.join)(root, entry.name)).sort((a, b) => (0, import_node_fs26.statSync)(b).mtimeMs - (0, import_node_fs26.statSync)(a).mtimeMs);
|
|
19137
19337
|
const newest = sessionDirs[0];
|
|
19138
19338
|
if (!newest) return [];
|
|
19139
|
-
const logPath = (0,
|
|
19339
|
+
const logPath = (0, import_node_path23.join)(newest, "renderer.log");
|
|
19140
19340
|
const text = (0, import_node_fs26.readFileSync)(logPath, "utf8");
|
|
19141
19341
|
return opencodeAgentDirectoriesFromLog(text).filter((directory) => !(0, import_node_fs26.existsSync)(directory)).map((directory) => ({ directory, logPath }));
|
|
19142
19342
|
} catch {
|
|
@@ -19144,7 +19344,7 @@ function opencodeDesktopBootstrapSnapshot() {
|
|
|
19144
19344
|
}
|
|
19145
19345
|
}
|
|
19146
19346
|
function opencodeLegacyConfigSnapshot() {
|
|
19147
|
-
const legacyPath = (0,
|
|
19347
|
+
const legacyPath = (0, import_node_path23.join)((0, import_node_os5.homedir)(), ".opencode", "opencode.json");
|
|
19148
19348
|
if (!(0, import_node_fs26.existsSync)(legacyPath)) return {};
|
|
19149
19349
|
const content = readTextFile(legacyPath);
|
|
19150
19350
|
if (content == null) return {};
|
|
@@ -19165,16 +19365,16 @@ function quarantineOpencodeLegacyConfig(legacyPath) {
|
|
|
19165
19365
|
}
|
|
19166
19366
|
}
|
|
19167
19367
|
function cursorPluginCacheRoot() {
|
|
19168
|
-
return (0,
|
|
19368
|
+
return (0, import_node_path23.join)((0, import_node_os5.homedir)(), ".cursor", "plugins", "cache", "mutmutco", "mmi");
|
|
19169
19369
|
}
|
|
19170
19370
|
function cursorPluginCachePinSnapshots() {
|
|
19171
19371
|
const root = cursorPluginCacheRoot();
|
|
19172
19372
|
try {
|
|
19173
19373
|
return (0, import_node_fs26.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => {
|
|
19174
|
-
const path2 = (0,
|
|
19175
|
-
const pluginJson = (0,
|
|
19176
|
-
const hooksJson = (0,
|
|
19177
|
-
const cliBundle = (0,
|
|
19374
|
+
const path2 = (0, import_node_path23.join)(root, entry.name);
|
|
19375
|
+
const pluginJson = (0, import_node_path23.join)(path2, ".cursor-plugin", "plugin.json");
|
|
19376
|
+
const hooksJson = (0, import_node_path23.join)(path2, "hooks", "hooks.json");
|
|
19377
|
+
const cliBundle = (0, import_node_path23.join)(path2, "cli", "dist", "index.cjs");
|
|
19178
19378
|
let version;
|
|
19179
19379
|
try {
|
|
19180
19380
|
const raw = JSON.parse((0, import_node_fs26.readFileSync)(pluginJson, "utf8"));
|
|
@@ -19203,19 +19403,19 @@ function cursorPluginCachePinSnapshots() {
|
|
|
19203
19403
|
}
|
|
19204
19404
|
}
|
|
19205
19405
|
function hubCheckoutForCursorSeed() {
|
|
19206
|
-
const manifest = (0,
|
|
19406
|
+
const manifest = (0, import_node_path23.join)(process.cwd(), "plugins", "mmi", ".cursor-plugin", "plugin.json");
|
|
19207
19407
|
return (0, import_node_fs26.existsSync)(manifest) ? process.cwd() : void 0;
|
|
19208
19408
|
}
|
|
19209
19409
|
function mmiPluginCacheRootSnapshots() {
|
|
19210
19410
|
const roots = [
|
|
19211
|
-
{ surface: "claude", root: (0,
|
|
19212
|
-
{ surface: "codex", root: (0,
|
|
19411
|
+
{ surface: "claude", root: (0, import_node_path23.join)((0, import_node_os5.homedir)(), ".claude", "plugins", "cache", "mutmutco", "mmi") },
|
|
19412
|
+
{ surface: "codex", root: (0, import_node_path23.join)((0, import_node_os5.homedir)(), ".codex", "plugins", "cache", "mutmutco", "mmi") }
|
|
19213
19413
|
];
|
|
19214
19414
|
return roots.flatMap(({ surface, root }) => {
|
|
19215
19415
|
try {
|
|
19216
19416
|
const entries = (0, import_node_fs26.readdirSync)(root, { withFileTypes: true }).map((entry) => ({
|
|
19217
19417
|
name: entry.name,
|
|
19218
|
-
path: (0,
|
|
19418
|
+
path: (0, import_node_path23.join)(root, entry.name),
|
|
19219
19419
|
isDirectory: entry.isDirectory()
|
|
19220
19420
|
}));
|
|
19221
19421
|
return [{ surface, root, entries }];
|
|
@@ -19226,7 +19426,7 @@ function mmiPluginCacheRootSnapshots() {
|
|
|
19226
19426
|
}
|
|
19227
19427
|
function hasNestedMmiChild(versionDir) {
|
|
19228
19428
|
try {
|
|
19229
|
-
return (0, import_node_fs26.statSync)((0,
|
|
19429
|
+
return (0, import_node_fs26.statSync)((0, import_node_path23.join)(versionDir, "mmi")).isDirectory();
|
|
19230
19430
|
} catch {
|
|
19231
19431
|
return false;
|
|
19232
19432
|
}
|
|
@@ -19251,7 +19451,7 @@ function quarantinePluginCacheDirs(plan2) {
|
|
|
19251
19451
|
try {
|
|
19252
19452
|
if (!(0, import_node_fs26.existsSync)(move.from)) continue;
|
|
19253
19453
|
const target = uniqueQuarantineTarget(move.to);
|
|
19254
|
-
(0, import_node_fs26.mkdirSync)((0,
|
|
19454
|
+
(0, import_node_fs26.mkdirSync)((0, import_node_path23.dirname)(target), { recursive: true });
|
|
19255
19455
|
(0, import_node_fs26.renameSync)(move.from, target);
|
|
19256
19456
|
moved += 1;
|
|
19257
19457
|
} catch {
|
|
@@ -19273,7 +19473,7 @@ async function clearNestedPluginTreeDir(targetPath) {
|
|
|
19273
19473
|
try {
|
|
19274
19474
|
if (!(0, import_node_fs26.existsSync)(targetPath)) return true;
|
|
19275
19475
|
if (isWin) {
|
|
19276
|
-
const emptyDir = (0,
|
|
19476
|
+
const emptyDir = (0, import_node_path23.join)((0, import_node_os5.tmpdir)(), `mmi-empty-${Date.now()}`);
|
|
19277
19477
|
(0, import_node_fs26.mkdirSync)(emptyDir, { recursive: true });
|
|
19278
19478
|
try {
|
|
19279
19479
|
await robocopyMirrorEmpty(emptyDir, targetPath);
|
|
@@ -19300,7 +19500,7 @@ async function applyNestedPluginTreeCleanup(paths, log) {
|
|
|
19300
19500
|
}
|
|
19301
19501
|
return true;
|
|
19302
19502
|
}
|
|
19303
|
-
var gitignorePath = () => (0,
|
|
19503
|
+
var gitignorePath = () => (0, import_node_path23.join)(process.cwd(), ".gitignore");
|
|
19304
19504
|
function readTextFile(path2) {
|
|
19305
19505
|
try {
|
|
19306
19506
|
if (!(0, import_node_fs26.existsSync)(path2)) return null;
|
|
@@ -19313,10 +19513,10 @@ function playwrightMcpConfigSnapshots() {
|
|
|
19313
19513
|
const cwd = process.cwd();
|
|
19314
19514
|
const home = (0, import_node_os5.homedir)();
|
|
19315
19515
|
const candidates = [
|
|
19316
|
-
(0,
|
|
19317
|
-
(0,
|
|
19318
|
-
(0,
|
|
19319
|
-
(0,
|
|
19516
|
+
(0, import_node_path23.join)(cwd, ".mcp.json"),
|
|
19517
|
+
(0, import_node_path23.join)(cwd, ".cursor", "mcp.json"),
|
|
19518
|
+
(0, import_node_path23.join)(home, ".cursor", "mcp.json"),
|
|
19519
|
+
(0, import_node_path23.join)(home, ".codex", "config.toml")
|
|
19320
19520
|
];
|
|
19321
19521
|
const out = [];
|
|
19322
19522
|
for (const path2 of candidates) {
|
|
@@ -19329,7 +19529,7 @@ function strayBrowserArtifactPaths() {
|
|
|
19329
19529
|
const cwd = process.cwd();
|
|
19330
19530
|
return STRAY_BROWSER_ARTIFACT_DIRS.filter((rel) => {
|
|
19331
19531
|
try {
|
|
19332
|
-
return (0, import_node_fs26.existsSync)((0,
|
|
19532
|
+
return (0, import_node_fs26.existsSync)((0, import_node_path23.join)(cwd, rel));
|
|
19333
19533
|
} catch {
|
|
19334
19534
|
return false;
|
|
19335
19535
|
}
|
|
@@ -19348,8 +19548,8 @@ function latestIso(values) {
|
|
|
19348
19548
|
return best;
|
|
19349
19549
|
}
|
|
19350
19550
|
function latestNorthstarContinuityAt() {
|
|
19351
|
-
const meta = parseMeta(readTextFile((0,
|
|
19352
|
-
const queue = parseQueue(readTextFile((0,
|
|
19551
|
+
const meta = parseMeta(readTextFile((0, import_node_path23.join)(process.cwd(), META_FILE)));
|
|
19552
|
+
const queue = parseQueue(readTextFile((0, import_node_path23.join)(process.cwd(), QUEUE_FILE)));
|
|
19353
19553
|
return latestIso([
|
|
19354
19554
|
...Object.values(meta).map((entry) => entry.syncedAt),
|
|
19355
19555
|
...queue.map((entry) => entry.queuedAt)
|
|
@@ -19406,6 +19606,10 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19406
19606
|
});
|
|
19407
19607
|
const installedForHealPlan = readInstalledPlugins();
|
|
19408
19608
|
const sourcesForHealPlan = installedPluginSources();
|
|
19609
|
+
const semverPrefix = /^\d+\.\d+\.\d+/;
|
|
19610
|
+
const isBehind = (installed2, released) => Boolean(installed2 && released && semverPrefix.test(installed2) && semverPrefix.test(released) && compareVersions(installed2, released) < 0);
|
|
19611
|
+
const opencodeAdapterStale = isBehind(opencodeInstalledVersionForDoctor(), releasedVersion);
|
|
19612
|
+
const cursorCacheStale = (0, import_node_fs26.existsSync)(cursorPluginCacheRoot()) && (cursorPluginCachePinSnapshots() ?? []).some((p) => isBehind(p.version, releasedVersion));
|
|
19409
19613
|
const healPlan = doctorHealPlan({
|
|
19410
19614
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
19411
19615
|
surface,
|
|
@@ -19420,7 +19624,9 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19420
19624
|
legacyPluginCheck: buildLegacyPluginInstallCheck({
|
|
19421
19625
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
19422
19626
|
sources: sourcesForHealPlan
|
|
19423
|
-
})
|
|
19627
|
+
}),
|
|
19628
|
+
opencodeAdapterStale,
|
|
19629
|
+
cursorCacheStale
|
|
19424
19630
|
});
|
|
19425
19631
|
if (opts.preflight && !healPlan.needsEagerHeal) return;
|
|
19426
19632
|
const eagerHeal = Boolean(opts.preflight) || Boolean(opts.banner) && healPlan.needsEagerHeal;
|
|
@@ -19451,9 +19657,19 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19451
19657
|
repoVersion: readRepoVersion(),
|
|
19452
19658
|
releasedVersion
|
|
19453
19659
|
});
|
|
19454
|
-
|
|
19660
|
+
let selfUpdatedCli = false;
|
|
19661
|
+
if (repairFull) {
|
|
19662
|
+
const updated = await applyVersionAutoUpdate(versionReport, (m) => io.err(m));
|
|
19663
|
+
versionReport = updated.report;
|
|
19664
|
+
selfUpdatedCli = updated.applied === "npm";
|
|
19665
|
+
}
|
|
19455
19666
|
if (!versionReport.ok) versionReport = { ...versionReport, fix: pluginRecoveryFix(surface) };
|
|
19456
19667
|
checks.push(versionReport);
|
|
19668
|
+
if (selfUpdatedCli) {
|
|
19669
|
+
if (opts.json) io.log(JSON.stringify(buildSelfUpdateHaltPayload({ checks, updatedTo: versionReport.releasedVersion }), null, 2));
|
|
19670
|
+
else io.err(selfUpdateHaltLine(versionReport));
|
|
19671
|
+
return;
|
|
19672
|
+
}
|
|
19457
19673
|
checks.push({ ok: Boolean(cfg.sagaApiUrl), label: "Hub API URL configured", fix: "set MMI_HUB_URL or use a current MMI CLI/plugin build" });
|
|
19458
19674
|
const hubVersionInfo = await fetchHubVersionInfo(cfg.sagaApiUrl);
|
|
19459
19675
|
const installedVersion = resolveClientVersion();
|
|
@@ -19568,14 +19784,12 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19568
19784
|
surface
|
|
19569
19785
|
});
|
|
19570
19786
|
if (!installedVersionCheck.ok && (repairFull || repairLocal)) {
|
|
19787
|
+
const crossSurfaceRepair = Boolean(opts.apply) || !opts.json && !opts.banner && !opts.preflight;
|
|
19788
|
+
const canDriveSurface = async (token) => surfaceToken(surface) === token || crossSurfaceRepair && await hostBinAvailable(token);
|
|
19789
|
+
const rereadInstalled = () => buildInstalledPluginVersionCheck({ isOrgRepo: Boolean(cfg.sagaApiUrl), sources: installedPluginSources(), releasedVersion, surface });
|
|
19571
19790
|
const claudeStale = installedVersionCheck.staleSurfaces?.some((s) => s.surface === "claude") ?? false;
|
|
19572
|
-
if (claudeStale && await applyPluginHeal("claude", surface, (m) => io.err(m))) {
|
|
19573
|
-
const healed =
|
|
19574
|
-
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
19575
|
-
sources: installedPluginSources(),
|
|
19576
|
-
releasedVersion,
|
|
19577
|
-
surface
|
|
19578
|
-
});
|
|
19791
|
+
if (claudeStale && await canDriveSurface("claude") && await applyPluginHeal("claude", surface, (m) => io.err(m), { force: true })) {
|
|
19792
|
+
const healed = rereadInstalled();
|
|
19579
19793
|
installedVersionCheck = healed;
|
|
19580
19794
|
if (healed.ok) {
|
|
19581
19795
|
markPluginReloadRequired();
|
|
@@ -19583,13 +19797,8 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19583
19797
|
}
|
|
19584
19798
|
}
|
|
19585
19799
|
const codexStale = installedVersionCheck.staleSurfaces?.some((s) => s.surface === "codex") ?? false;
|
|
19586
|
-
if (!installedVersionCheck.ok && codexStale && await applyPluginHeal("codex", surface, (m) => io.err(m))) {
|
|
19587
|
-
const healed =
|
|
19588
|
-
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
19589
|
-
sources: installedPluginSources(),
|
|
19590
|
-
releasedVersion,
|
|
19591
|
-
surface
|
|
19592
|
-
});
|
|
19800
|
+
if (!installedVersionCheck.ok && codexStale && await canDriveSurface("codex") && await applyPluginHeal("codex", surface, (m) => io.err(m), { force: true })) {
|
|
19801
|
+
const healed = rereadInstalled();
|
|
19593
19802
|
installedVersionCheck = healed;
|
|
19594
19803
|
if (healed.ok) {
|
|
19595
19804
|
markPluginReloadRequired();
|
|
@@ -19598,7 +19807,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19598
19807
|
}
|
|
19599
19808
|
}
|
|
19600
19809
|
checks.push(installedVersionCheck);
|
|
19601
|
-
|
|
19810
|
+
let openCodeConfigSnapshot = opencodeConfigSnapshot();
|
|
19602
19811
|
const inspectOpenCode = surface === "opencode" || openCodeConfigSnapshot.hasConfig || runExtended;
|
|
19603
19812
|
if (inspectOpenCode) {
|
|
19604
19813
|
let opencodeConfigCheck = buildOpencodeConfigPluginCheck({
|
|
@@ -19611,14 +19820,14 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19611
19820
|
});
|
|
19612
19821
|
if (!opencodeConfigCheck.ok && opencodeConfigCheck.reason !== "unreadable-config" && opencodeConfigCheck.reason !== "invalid-plugin-shape" && repairLocal) {
|
|
19613
19822
|
if (writeOpencodeConfigPlugin(openCodeConfigSnapshot)) {
|
|
19614
|
-
|
|
19823
|
+
openCodeConfigSnapshot = opencodeConfigSnapshot();
|
|
19615
19824
|
opencodeConfigCheck = buildOpencodeConfigPluginCheck({
|
|
19616
19825
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
19617
|
-
configPath:
|
|
19618
|
-
hasConfig:
|
|
19619
|
-
hasPluginField:
|
|
19620
|
-
pluginEntries:
|
|
19621
|
-
parseOk:
|
|
19826
|
+
configPath: openCodeConfigSnapshot.path,
|
|
19827
|
+
hasConfig: openCodeConfigSnapshot.hasConfig,
|
|
19828
|
+
hasPluginField: openCodeConfigSnapshot.hasPluginField,
|
|
19829
|
+
pluginEntries: openCodeConfigSnapshot.pluginEntries,
|
|
19830
|
+
parseOk: openCodeConfigSnapshot.parseOk
|
|
19622
19831
|
});
|
|
19623
19832
|
if (opencodeConfigCheck.ok) {
|
|
19624
19833
|
markPluginReloadRequired();
|
|
@@ -19627,11 +19836,55 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19627
19836
|
}
|
|
19628
19837
|
}
|
|
19629
19838
|
checks.push(opencodeConfigCheck);
|
|
19630
|
-
|
|
19839
|
+
let opencodeInstalledVersion = opencodeInstalledVersionForDoctor();
|
|
19840
|
+
let opencodeVersionCheck = buildOpencodeVersionCheck({
|
|
19631
19841
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
19632
|
-
installedVersion:
|
|
19842
|
+
installedVersion: opencodeInstalledVersion,
|
|
19633
19843
|
releasedVersion
|
|
19634
|
-
})
|
|
19844
|
+
});
|
|
19845
|
+
if (!opencodeVersionCheck.ok && repairFull) {
|
|
19846
|
+
if (await forceInstallOpencodeMmiPlugins(openCodeConfigSnapshot, (m) => io.err(m))) {
|
|
19847
|
+
opencodeInstalledVersion = readOpencodeAdapterDiskVersion() ?? opencodeInstalledVersion;
|
|
19848
|
+
opencodeVersionCheck = buildOpencodeVersionCheck({
|
|
19849
|
+
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
19850
|
+
installedVersion: opencodeInstalledVersion,
|
|
19851
|
+
releasedVersion
|
|
19852
|
+
});
|
|
19853
|
+
if (opencodeVersionCheck.ok) {
|
|
19854
|
+
markPluginReloadRequired();
|
|
19855
|
+
io.err(` \u21BB force-refreshed OpenCode MMI plugin \u2192 ${opencodeInstalledVersion ?? releasedVersion ?? "latest"} \u2014 ${reloadAction("opencode")} to load it`);
|
|
19856
|
+
}
|
|
19857
|
+
}
|
|
19858
|
+
}
|
|
19859
|
+
checks.push(opencodeVersionCheck);
|
|
19860
|
+
let surfaceAssetsCheck = buildOpencodeSurfaceAssetsCheck({
|
|
19861
|
+
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
19862
|
+
configPath: openCodeConfigSnapshot.path,
|
|
19863
|
+
commandsDir: opencodeCommandsDir(),
|
|
19864
|
+
existingCommands: opencodeExistingCommands(),
|
|
19865
|
+
skillsPath: opencodeSkillsPath(),
|
|
19866
|
+
configuredSkillsPaths: openCodeConfigSnapshot.skillsPaths
|
|
19867
|
+
});
|
|
19868
|
+
if (!surfaceAssetsCheck.ok && repairLocal) {
|
|
19869
|
+
const wroteCommands = surfaceAssetsCheck.missingCommands?.length ? writeOpencodeCommandFiles() : true;
|
|
19870
|
+
const wroteSkills = surfaceAssetsCheck.hasSkillsPath ? true : writeOpencodeSkillsPath(openCodeConfigSnapshot, opencodeSkillsPath());
|
|
19871
|
+
if (wroteCommands || wroteSkills) {
|
|
19872
|
+
openCodeConfigSnapshot = opencodeConfigSnapshot();
|
|
19873
|
+
surfaceAssetsCheck = buildOpencodeSurfaceAssetsCheck({
|
|
19874
|
+
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
19875
|
+
configPath: openCodeConfigSnapshot.path,
|
|
19876
|
+
commandsDir: opencodeCommandsDir(),
|
|
19877
|
+
existingCommands: opencodeExistingCommands(),
|
|
19878
|
+
skillsPath: opencodeSkillsPath(),
|
|
19879
|
+
configuredSkillsPaths: openCodeConfigSnapshot.skillsPaths
|
|
19880
|
+
});
|
|
19881
|
+
if (surfaceAssetsCheck.ok) {
|
|
19882
|
+
markPluginReloadRequired();
|
|
19883
|
+
io.err(` \u21BB materialized OpenCode MMI commands + skills path \u2014 ${reloadAction("opencode")} to load them`);
|
|
19884
|
+
}
|
|
19885
|
+
}
|
|
19886
|
+
}
|
|
19887
|
+
checks.push(surfaceAssetsCheck);
|
|
19635
19888
|
checks.push(buildOpencodeDesktopBootstrapCheck({
|
|
19636
19889
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
19637
19890
|
surface,
|
|
@@ -19729,7 +19982,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19729
19982
|
releasedVersion,
|
|
19730
19983
|
hubCheckout: hubCheckoutForCursorSeed(),
|
|
19731
19984
|
execFileP: execFileP2,
|
|
19732
|
-
mkdtemp: (prefix) => (0, import_promises7.mkdtemp)((0,
|
|
19985
|
+
mkdtemp: (prefix) => (0, import_promises7.mkdtemp)((0, import_node_path23.join)((0, import_node_os5.tmpdir)(), prefix)),
|
|
19733
19986
|
log: (m) => io.err(m)
|
|
19734
19987
|
});
|
|
19735
19988
|
if (seeded) {
|
|
@@ -19867,7 +20120,8 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19867
20120
|
}
|
|
19868
20121
|
const gaps = checks.filter((c) => !c.ok);
|
|
19869
20122
|
if (opts.preflight) {
|
|
19870
|
-
|
|
20123
|
+
const outcome = preflightOutcome({ gaps, needsEagerHeal: healPlan.needsEagerHeal, surface });
|
|
20124
|
+
if (outcome.line) io.err(outcome.line);
|
|
19871
20125
|
if (pluginReloadRequired) io.err(pluginAutonomousHaltLine(reloadHint));
|
|
19872
20126
|
return;
|
|
19873
20127
|
}
|
|
@@ -19885,7 +20139,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19885
20139
|
claudePluginVersions: sourceVersions("claude"),
|
|
19886
20140
|
codexPluginVersions: sourceVersions("codex"),
|
|
19887
20141
|
codexCacheVersions: cacheVersionsFor("codex"),
|
|
19888
|
-
opencodePluginVersions:
|
|
20142
|
+
opencodePluginVersions: opencodePluginVersionsForReport(),
|
|
19889
20143
|
releasedVersion
|
|
19890
20144
|
});
|
|
19891
20145
|
const resources = doctorResourcesForGaps(gaps);
|
|
@@ -20119,7 +20373,7 @@ rules.command("sync").option("--quiet", "stay silent unless something changed or
|
|
|
20119
20373
|
if (!await runRulesSync(opts)) process.exitCode = 1;
|
|
20120
20374
|
});
|
|
20121
20375
|
rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
|
|
20122
|
-
const path2 = (0,
|
|
20376
|
+
const path2 = (0, import_node_path24.join)(process.cwd(), ".gitignore");
|
|
20123
20377
|
const current = (0, import_node_fs27.existsSync)(path2) ? (0, import_node_fs27.readFileSync)(path2, "utf8") : null;
|
|
20124
20378
|
const plan2 = planManagedGitignore(current);
|
|
20125
20379
|
const drift = [...plan2.added.map((l) => `+${l}`), ...plan2.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
@@ -20326,7 +20580,7 @@ function runWorktreeInstall(command, cwd, quiet) {
|
|
|
20326
20580
|
async function primaryCheckoutRoot(worktreeRoot) {
|
|
20327
20581
|
try {
|
|
20328
20582
|
const out = (await execFileP2("git", ["-C", worktreeRoot, "rev-parse", "--path-format=absolute", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
|
|
20329
|
-
return out ? (0,
|
|
20583
|
+
return out ? (0, import_node_path24.dirname)(out) : void 0;
|
|
20330
20584
|
} catch {
|
|
20331
20585
|
return void 0;
|
|
20332
20586
|
}
|
|
@@ -20339,7 +20593,7 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
|
|
|
20339
20593
|
};
|
|
20340
20594
|
}
|
|
20341
20595
|
function acquireWorktreeSetupLock(worktreeRoot) {
|
|
20342
|
-
const lockPath = (0,
|
|
20596
|
+
const lockPath = (0, import_node_path24.join)(worktreeRoot, ".mmi", "worktree-setup.lock");
|
|
20343
20597
|
const take = () => {
|
|
20344
20598
|
const fd = (0, import_node_fs27.openSync)(lockPath, "wx");
|
|
20345
20599
|
try {
|
|
@@ -20355,7 +20609,7 @@ function acquireWorktreeSetupLock(worktreeRoot) {
|
|
|
20355
20609
|
};
|
|
20356
20610
|
};
|
|
20357
20611
|
try {
|
|
20358
|
-
(0, import_node_fs27.mkdirSync)((0,
|
|
20612
|
+
(0, import_node_fs27.mkdirSync)((0, import_node_path24.dirname)(lockPath), { recursive: true });
|
|
20359
20613
|
return take();
|
|
20360
20614
|
} catch {
|
|
20361
20615
|
try {
|
|
@@ -21361,7 +21615,7 @@ pr.command("create").description("create a PR and print {number,url} JSON").opti
|
|
|
21361
21615
|
console.log(JSON.stringify(created));
|
|
21362
21616
|
});
|
|
21363
21617
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
21364
|
-
const wfDir = (0,
|
|
21618
|
+
const wfDir = (0, import_node_path24.join)(cwd, ".github", "workflows");
|
|
21365
21619
|
if (!(0, import_node_fs27.existsSync)(wfDir)) return [];
|
|
21366
21620
|
return (0, import_node_fs27.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).map((name) => `.github/workflows/${name}`);
|
|
21367
21621
|
}
|
|
@@ -21535,7 +21789,7 @@ async function createDeferredWorktreeStore() {
|
|
|
21535
21789
|
},
|
|
21536
21790
|
write: async (entries) => {
|
|
21537
21791
|
try {
|
|
21538
|
-
await (0, import_promises8.mkdir)((0,
|
|
21792
|
+
await (0, import_promises8.mkdir)((0, import_node_path24.dirname)(registryPath), { recursive: true });
|
|
21539
21793
|
await (0, import_promises8.writeFile)(registryPath, serializeDeferredWorktrees(entries), "utf8");
|
|
21540
21794
|
} catch {
|
|
21541
21795
|
}
|
|
@@ -21893,8 +22147,8 @@ async function resolveStage() {
|
|
|
21893
22147
|
local,
|
|
21894
22148
|
shell: shellFor(),
|
|
21895
22149
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
21896
|
-
hasCompose: (0, import_node_fs27.existsSync)((0,
|
|
21897
|
-
hasEnvExample: (0, import_node_fs27.existsSync)((0,
|
|
22150
|
+
hasCompose: (0, import_node_fs27.existsSync)((0, import_node_path24.join)(process.cwd(), "docker-compose.yml")),
|
|
22151
|
+
hasEnvExample: (0, import_node_fs27.existsSync)((0, import_node_path24.join)(process.cwd(), ".env.example"))
|
|
21898
22152
|
});
|
|
21899
22153
|
}
|
|
21900
22154
|
async function fetchStageVaultEnvMerge() {
|
|
@@ -22749,6 +23003,7 @@ program2.command("session-start").description("run the SessionStart verbs (rules
|
|
|
22749
23003
|
console.error("[mmi-hook] session-start: cwd is a repository SUBDIRECTORY \u2014 skipping the SessionStart hook (spine/docs/plan/saga delivery); run it from the repo root.");
|
|
22750
23004
|
return;
|
|
22751
23005
|
}
|
|
23006
|
+
if (!isOrgRepoRoot(process.cwd())) return;
|
|
22752
23007
|
try {
|
|
22753
23008
|
const hook = parseHookInput(await readStdin());
|
|
22754
23009
|
if (hook.session_id) persistSession(hook.session_id);
|