@mutmutco/cli 2.51.0 → 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 +238 -191
- 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
|
}
|
|
@@ -17574,14 +17581,14 @@ function authorizeBodyHasMismatch(body) {
|
|
|
17574
17581
|
// src/doctor-run.ts
|
|
17575
17582
|
var import_node_fs26 = require("node:fs");
|
|
17576
17583
|
var import_promises7 = require("node:fs/promises");
|
|
17577
|
-
var
|
|
17584
|
+
var import_node_path23 = require("node:path");
|
|
17578
17585
|
var import_node_os5 = require("node:os");
|
|
17579
17586
|
|
|
17580
17587
|
// src/cursor-plugin-seed.ts
|
|
17581
17588
|
var import_node_child_process12 = require("node:child_process");
|
|
17582
17589
|
var import_node_fs22 = require("node:fs");
|
|
17583
17590
|
var import_node_os4 = require("node:os");
|
|
17584
|
-
var
|
|
17591
|
+
var import_node_path20 = require("node:path");
|
|
17585
17592
|
var import_node_util7 = require("node:util");
|
|
17586
17593
|
function isSemverVersion(v) {
|
|
17587
17594
|
return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
|
|
@@ -17598,13 +17605,13 @@ function ghReleaseTarballApiArgs(tag) {
|
|
|
17598
17605
|
}
|
|
17599
17606
|
function cursorUserGlobalStatePath() {
|
|
17600
17607
|
if (process.platform === "win32") {
|
|
17601
|
-
const base = process.env.APPDATA || (0,
|
|
17602
|
-
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");
|
|
17603
17610
|
}
|
|
17604
17611
|
if (process.platform === "darwin") {
|
|
17605
|
-
return (0,
|
|
17612
|
+
return (0, import_node_path20.join)((0, import_node_os4.homedir)(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
17606
17613
|
}
|
|
17607
|
-
return (0,
|
|
17614
|
+
return (0, import_node_path20.join)((0, import_node_os4.homedir)(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
|
|
17608
17615
|
}
|
|
17609
17616
|
async function readCursorThirdPartyExtensibilityEnabled(execFileP5) {
|
|
17610
17617
|
const dbPath = cursorUserGlobalStatePath();
|
|
@@ -17624,7 +17631,7 @@ async function readCursorThirdPartyExtensibilityEnabled(execFileP5) {
|
|
|
17624
17631
|
function syncDirContents(src, dest) {
|
|
17625
17632
|
(0, import_node_fs22.mkdirSync)(dest, { recursive: true });
|
|
17626
17633
|
for (const name of (0, import_node_fs22.readdirSync)(dest)) {
|
|
17627
|
-
(0, import_node_fs22.rmSync)((0,
|
|
17634
|
+
(0, import_node_fs22.rmSync)((0, import_node_path20.join)(dest, name), { recursive: true, force: true });
|
|
17628
17635
|
}
|
|
17629
17636
|
(0, import_node_fs22.cpSync)(src, dest, { recursive: true });
|
|
17630
17637
|
}
|
|
@@ -17632,21 +17639,21 @@ function releaseTag(releasedVersion) {
|
|
|
17632
17639
|
return releasedVersion.startsWith("v") ? releasedVersion : `v${releasedVersion}`;
|
|
17633
17640
|
}
|
|
17634
17641
|
async function extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5) {
|
|
17635
|
-
const tarFile = (0,
|
|
17642
|
+
const tarFile = (0, import_node_path20.join)(tmpRoot, "archive.tar");
|
|
17636
17643
|
try {
|
|
17637
17644
|
await execFileP5("git", gitFetchReleaseTagArgs(hubCheckout, tag), { timeout: 6e4 });
|
|
17638
17645
|
await execFileP5("git", ["-C", hubCheckout, "archive", "--format=tar", `--output=${tarFile}`, tag, "plugins/mmi"], {
|
|
17639
17646
|
timeout: 6e4
|
|
17640
17647
|
});
|
|
17641
17648
|
await execFileP5("tar", ["-xf", tarFile, "-C", tmpRoot], { timeout: 6e4 });
|
|
17642
|
-
const pluginMmi = (0,
|
|
17643
|
-
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;
|
|
17644
17651
|
} catch {
|
|
17645
17652
|
return void 0;
|
|
17646
17653
|
}
|
|
17647
17654
|
}
|
|
17648
17655
|
async function downloadPluginMmiViaGh(tag, tmpRoot) {
|
|
17649
|
-
const tarPath = (0,
|
|
17656
|
+
const tarPath = (0, import_node_path20.join)(tmpRoot, "repo.tgz");
|
|
17650
17657
|
try {
|
|
17651
17658
|
(0, import_node_fs22.mkdirSync)(tmpRoot, { recursive: true });
|
|
17652
17659
|
const { stdout } = await execFileBuffer("gh", ghReleaseTarballApiArgs(tag), {
|
|
@@ -17659,8 +17666,8 @@ async function downloadPluginMmiViaGh(tag, tmpRoot) {
|
|
|
17659
17666
|
await execFileBuffer("tar", ["-xzf", tarPath, "-C", tmpRoot], { timeout: 12e4, windowsHide: true });
|
|
17660
17667
|
const top = (0, import_node_fs22.readdirSync)(tmpRoot).find((entry) => entry !== "repo.tgz");
|
|
17661
17668
|
if (!top) return void 0;
|
|
17662
|
-
const pluginMmi = (0,
|
|
17663
|
-
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;
|
|
17664
17671
|
} catch {
|
|
17665
17672
|
return void 0;
|
|
17666
17673
|
}
|
|
@@ -17672,7 +17679,7 @@ async function resolvePluginMmiSource(releasedVersion, hubCheckout, tmpRoot, exe
|
|
|
17672
17679
|
const fromHub = await extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5);
|
|
17673
17680
|
if (fromHub) return fromHub;
|
|
17674
17681
|
}
|
|
17675
|
-
return downloadPluginMmiViaGh(tag, (0,
|
|
17682
|
+
return downloadPluginMmiViaGh(tag, (0, import_node_path20.join)(tmpRoot, "gh"));
|
|
17676
17683
|
}
|
|
17677
17684
|
function cursorPluginPinsNeedingSeed(pins, releasedVersion) {
|
|
17678
17685
|
if (!isSemverVersion(releasedVersion)) return pins.filter((pin) => !pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty);
|
|
@@ -18520,7 +18527,7 @@ function buildCursorPluginInstallCheck(input) {
|
|
|
18520
18527
|
};
|
|
18521
18528
|
}
|
|
18522
18529
|
}
|
|
18523
|
-
if (input.
|
|
18530
|
+
if (input.cacheRootExists && isSemverVersion2(input.releasedVersion) && input.pins.some((pin) => isSemverVersion2(pin.version) && compareVersions(pin.version, input.releasedVersion) < 0)) {
|
|
18524
18531
|
const stale = input.pins.filter((pin) => isSemverVersion2(pin.version) && compareVersions(pin.version, input.releasedVersion) < 0).map((pin) => `${pin.version} at ${pin.name}`).join(", ");
|
|
18525
18532
|
return {
|
|
18526
18533
|
...base,
|
|
@@ -18700,12 +18707,34 @@ function doctorHealPlan(input) {
|
|
|
18700
18707
|
const versionUpdate = versionAutoUpdateAction(input.versionReport, input.hasPluginRoot);
|
|
18701
18708
|
const pluginReinstall = input.isOrgRepo && !input.installedVersionCheck.ok && (input.surface === "claude-cli" || input.surface === "claude-vscode" || input.surface === "codex") && Boolean(input.installedVersionCheck.staleSurfaces?.length);
|
|
18702
18709
|
const legacyPluginMigration = input.isOrgRepo && !input.legacyPluginCheck.ok;
|
|
18703
|
-
const
|
|
18704
|
-
|
|
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 };
|
|
18705
18714
|
}
|
|
18706
18715
|
function doctorPreflightDoneLine(surface) {
|
|
18707
18716
|
return `\u21BB MMI tooling updated \u2014 ${reloadAction(surface)} to load changes`;
|
|
18708
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
|
+
}
|
|
18709
18738
|
function pluginAutonomousHaltLine(reloadHint) {
|
|
18710
18739
|
return `\u26A0 PLUGIN RELOAD REQUIRED \u2014 mmi:* skills and agent types are unavailable until you ${reloadHint}. Halt autonomous /grind and /build until then.`;
|
|
18711
18740
|
}
|
|
@@ -18741,14 +18770,14 @@ function renderTerseDoctorReport(input) {
|
|
|
18741
18770
|
|
|
18742
18771
|
// src/kb-drift-report.ts
|
|
18743
18772
|
var import_node_fs23 = require("node:fs");
|
|
18744
|
-
var
|
|
18773
|
+
var import_node_path21 = require("node:path");
|
|
18745
18774
|
function yesterdayIso() {
|
|
18746
18775
|
const d = /* @__PURE__ */ new Date();
|
|
18747
18776
|
d.setUTCDate(d.getUTCDate() - 1);
|
|
18748
18777
|
return d.toISOString().slice(0, 10);
|
|
18749
18778
|
}
|
|
18750
18779
|
async function fetchLatestKbDriftReport(execFileP5, repoRoot) {
|
|
18751
|
-
const sagaIo = (0,
|
|
18780
|
+
const sagaIo = (0, import_node_path21.join)(repoRoot, "infra", "saga-io.mjs");
|
|
18752
18781
|
if (!(0, import_node_fs23.existsSync)(sagaIo)) return null;
|
|
18753
18782
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
18754
18783
|
for (const date of [today, yesterdayIso()]) {
|
|
@@ -18766,7 +18795,7 @@ async function fetchLatestKbDriftReport(execFileP5, repoRoot) {
|
|
|
18766
18795
|
|
|
18767
18796
|
// src/cli-doctor-shared.ts
|
|
18768
18797
|
var import_node_fs24 = require("node:fs");
|
|
18769
|
-
var
|
|
18798
|
+
var import_node_path22 = require("node:path");
|
|
18770
18799
|
var import_node_fs25 = require("node:fs");
|
|
18771
18800
|
var GC_GH_TIMEOUT_MS = 2e4;
|
|
18772
18801
|
async function awsCallerArn() {
|
|
@@ -18813,7 +18842,7 @@ async function localBranchHeads() {
|
|
|
18813
18842
|
}
|
|
18814
18843
|
async function currentRepoWorktreeGitRoot(repoRoot) {
|
|
18815
18844
|
const gitCommonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
18816
|
-
return gitCommonDir ? (0,
|
|
18845
|
+
return gitCommonDir ? (0, import_node_path22.resolve)(repoRoot, gitCommonDir, "worktrees") : "";
|
|
18817
18846
|
}
|
|
18818
18847
|
async function worktreeBranches() {
|
|
18819
18848
|
const { stdout } = await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS });
|
|
@@ -18833,7 +18862,7 @@ function resolveGitdirForWorktreeFile(worktreePath, content) {
|
|
|
18833
18862
|
const match = /^gitdir:\s*(.+)\s*$/im.exec(content);
|
|
18834
18863
|
if (!match?.[1]) return void 0;
|
|
18835
18864
|
const raw = match[1].trim();
|
|
18836
|
-
return (0,
|
|
18865
|
+
return (0, import_node_path22.isAbsolute)(raw) ? raw : (0, import_node_path22.resolve)(worktreePath, raw);
|
|
18837
18866
|
}
|
|
18838
18867
|
function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
18839
18868
|
if (!worktreeGitRoot) return false;
|
|
@@ -18842,9 +18871,9 @@ function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
|
18842
18871
|
for (const ent of entries) {
|
|
18843
18872
|
if (!ent.isDirectory()) continue;
|
|
18844
18873
|
try {
|
|
18845
|
-
const gitdirPath = (0, import_node_fs24.readFileSync)((0,
|
|
18846
|
-
const resolvedGitdir = (0,
|
|
18847
|
-
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;
|
|
18848
18877
|
} catch {
|
|
18849
18878
|
}
|
|
18850
18879
|
}
|
|
@@ -18863,7 +18892,7 @@ function pathExistsKnown(path2) {
|
|
|
18863
18892
|
}
|
|
18864
18893
|
}
|
|
18865
18894
|
function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
18866
|
-
const gitPath = (0,
|
|
18895
|
+
const gitPath = (0, import_node_path22.join)(path2, ".git");
|
|
18867
18896
|
let st;
|
|
18868
18897
|
try {
|
|
18869
18898
|
st = (0, import_node_fs25.lstatSync)(gitPath);
|
|
@@ -18920,7 +18949,7 @@ async function siblingWorktreeDirs() {
|
|
|
18920
18949
|
const siblingRoot = siblingMmiWorktreesRoot(repoRoot);
|
|
18921
18950
|
try {
|
|
18922
18951
|
const entries = (0, import_node_fs25.readdirSync)(siblingRoot, { withFileTypes: true });
|
|
18923
|
-
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));
|
|
18924
18953
|
} catch {
|
|
18925
18954
|
return [];
|
|
18926
18955
|
}
|
|
@@ -18970,7 +18999,7 @@ async function fetchHubVersionInfo(baseUrl) {
|
|
|
18970
18999
|
}
|
|
18971
19000
|
function readRepoVersion() {
|
|
18972
19001
|
try {
|
|
18973
|
-
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;
|
|
18974
19003
|
} catch {
|
|
18975
19004
|
return void 0;
|
|
18976
19005
|
}
|
|
@@ -18988,24 +19017,24 @@ var NPM_VIEW_TIMEOUT_MS = 15e3;
|
|
|
18988
19017
|
var PLUGIN_PULL_TIMEOUT_MS = 3e4;
|
|
18989
19018
|
async function applyVersionAutoUpdate(report, log) {
|
|
18990
19019
|
const action = versionAutoUpdateAction(report, Boolean(process.env.CLAUDE_PLUGIN_ROOT));
|
|
18991
|
-
if (action === "none") return report;
|
|
19020
|
+
if (action === "none") return { report, applied: "none" };
|
|
18992
19021
|
const target = report.releasedVersion ?? "latest";
|
|
18993
19022
|
if (action === "plugin-pull") {
|
|
18994
19023
|
try {
|
|
18995
19024
|
const root = (await execFileP2("git", ["-C", process.env.CLAUDE_PLUGIN_ROOT, "rev-parse", "--show-toplevel"], { timeout: PLUGIN_PULL_TIMEOUT_MS })).stdout.trim();
|
|
18996
19025
|
log(` \u21BB refreshing MMI plugin ${report.currentVersion} \u2192 ${target} (effective next session)\u2026`);
|
|
18997
19026
|
await execFileP2("git", ["-C", root, "pull", "--ff-only"], { timeout: PLUGIN_PULL_TIMEOUT_MS });
|
|
18998
|
-
return { ...report, ok: true };
|
|
19027
|
+
return { report: { ...report, ok: true }, applied: "plugin-pull" };
|
|
18999
19028
|
} catch {
|
|
19000
|
-
return report;
|
|
19029
|
+
return { report, applied: "none" };
|
|
19001
19030
|
}
|
|
19002
19031
|
}
|
|
19003
19032
|
try {
|
|
19004
19033
|
log(` \u21BB updating mmi-cli ${report.currentVersion} \u2192 ${target}\u2026`);
|
|
19005
19034
|
await runHostBin("npm", ["install", "-g", "@mutmutco/cli@latest"], { timeout: NPM_UPDATE_TIMEOUT_MS });
|
|
19006
|
-
return { ...report, ok: true };
|
|
19035
|
+
return { report: { ...report, ok: true }, applied: "npm" };
|
|
19007
19036
|
} catch {
|
|
19008
|
-
return report;
|
|
19037
|
+
return { report, applied: "none" };
|
|
19009
19038
|
}
|
|
19010
19039
|
}
|
|
19011
19040
|
async function fetchNpmReleasedVersion() {
|
|
@@ -19033,7 +19062,11 @@ async function applyDesignSystemUpdate(check, log) {
|
|
|
19033
19062
|
if (check.latestVersion && installedVersion && compareVersions(installedVersion, check.latestVersion) >= 0) {
|
|
19034
19063
|
return { ...check, ok: true, installedVersion };
|
|
19035
19064
|
}
|
|
19036
|
-
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
|
+
};
|
|
19037
19070
|
} catch {
|
|
19038
19071
|
return check;
|
|
19039
19072
|
}
|
|
@@ -19067,6 +19100,9 @@ var CLAUDE_PLUGIN_TIMEOUT_MS = 12e4;
|
|
|
19067
19100
|
function runHostBin(bin, args, opts) {
|
|
19068
19101
|
return isWin ? execFileP2("cmd.exe", ["/c", bin, ...args], opts) : execFileP2(bin, args, opts);
|
|
19069
19102
|
}
|
|
19103
|
+
function hostBinAvailable(bin) {
|
|
19104
|
+
return execFileP2(isWin ? "where" : "which", [bin]).then(() => true).catch(() => false);
|
|
19105
|
+
}
|
|
19070
19106
|
async function runClaudePlugin(args) {
|
|
19071
19107
|
try {
|
|
19072
19108
|
await runHostBin("claude", args, { timeout: CLAUDE_PLUGIN_TIMEOUT_MS });
|
|
@@ -19099,7 +19135,7 @@ async function applyPluginHeal(token, surface, log, opts) {
|
|
|
19099
19135
|
}
|
|
19100
19136
|
var installedPluginsPath = (surface = detectSurface(process.env)) => {
|
|
19101
19137
|
const homeDir = surface === "codex" ? ".codex" : ".claude";
|
|
19102
|
-
return (0,
|
|
19138
|
+
return (0, import_node_path23.join)((0, import_node_os5.homedir)(), homeDir, "plugins", "installed_plugins.json");
|
|
19103
19139
|
};
|
|
19104
19140
|
function readInstalledPlugins() {
|
|
19105
19141
|
try {
|
|
@@ -19110,7 +19146,7 @@ function readInstalledPlugins() {
|
|
|
19110
19146
|
}
|
|
19111
19147
|
function installedPluginSources() {
|
|
19112
19148
|
return ["claude", "codex"].map((surface) => {
|
|
19113
|
-
const recordPath = (0,
|
|
19149
|
+
const recordPath = (0, import_node_path23.join)((0, import_node_os5.homedir)(), `.${surface}`, "plugins", "installed_plugins.json");
|
|
19114
19150
|
try {
|
|
19115
19151
|
return { surface, installed: JSON.parse((0, import_node_fs26.readFileSync)(recordPath, "utf8")), recordPath };
|
|
19116
19152
|
} catch {
|
|
@@ -19120,7 +19156,7 @@ function installedPluginSources() {
|
|
|
19120
19156
|
}
|
|
19121
19157
|
function readClaudeSettings() {
|
|
19122
19158
|
try {
|
|
19123
|
-
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"));
|
|
19124
19160
|
} catch {
|
|
19125
19161
|
return null;
|
|
19126
19162
|
}
|
|
@@ -19165,16 +19201,16 @@ function backupAndWriteInstalledPlugins(records, pluginId) {
|
|
|
19165
19201
|
}
|
|
19166
19202
|
}
|
|
19167
19203
|
function opencodeConfigDir() {
|
|
19168
|
-
return (0,
|
|
19204
|
+
return (0, import_node_path23.join)((0, import_node_os5.homedir)(), ".config", "opencode");
|
|
19169
19205
|
}
|
|
19170
19206
|
function opencodeConfigPath() {
|
|
19171
|
-
return (0,
|
|
19207
|
+
return (0, import_node_path23.join)(opencodeConfigDir(), "opencode.jsonc");
|
|
19172
19208
|
}
|
|
19173
19209
|
function opencodeCommandsDir() {
|
|
19174
|
-
return (0,
|
|
19210
|
+
return (0, import_node_path23.join)(opencodeConfigDir(), "commands");
|
|
19175
19211
|
}
|
|
19176
19212
|
function opencodeSkillsPath() {
|
|
19177
|
-
return (0,
|
|
19213
|
+
return (0, import_node_path23.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "skills");
|
|
19178
19214
|
}
|
|
19179
19215
|
function opencodeConfigSnapshot() {
|
|
19180
19216
|
const path2 = opencodeConfigPath();
|
|
@@ -19203,7 +19239,7 @@ function writeOpencodeConfigPlugin(snapshot) {
|
|
|
19203
19239
|
const plan2 = planOpencodeConfigWrite(snapshot.hasConfig ? snapshot.raw : void 0);
|
|
19204
19240
|
if (plan2.action === "already") return true;
|
|
19205
19241
|
if (!plan2.text || plan2.action === "unsafe") return false;
|
|
19206
|
-
(0, import_node_fs26.mkdirSync)((0,
|
|
19242
|
+
(0, import_node_fs26.mkdirSync)((0, import_node_path23.dirname)(path2), { recursive: true });
|
|
19207
19243
|
if (snapshot.hasConfig) (0, import_node_fs26.copyFileSync)(path2, `${path2}.bak`);
|
|
19208
19244
|
(0, import_node_fs26.writeFileSync)(path2, plan2.text, "utf8");
|
|
19209
19245
|
return true;
|
|
@@ -19220,7 +19256,7 @@ function writeOpencodeSkillsPath(snapshot, skillsPath) {
|
|
|
19220
19256
|
const normalized = skillsPath.replace(/\\/g, "/");
|
|
19221
19257
|
if (!paths.some((p) => p.replace(/\\/g, "/") === normalized)) paths.push(skillsPath.replace(/\\/g, "/"));
|
|
19222
19258
|
parsed.skills = { ...skills, paths };
|
|
19223
|
-
(0, import_node_fs26.mkdirSync)((0,
|
|
19259
|
+
(0, import_node_fs26.mkdirSync)((0, import_node_path23.dirname)(snapshot.path), { recursive: true });
|
|
19224
19260
|
if (snapshot.hasConfig && (0, import_node_fs26.existsSync)(snapshot.path)) (0, import_node_fs26.copyFileSync)(snapshot.path, `${snapshot.path}.bak`);
|
|
19225
19261
|
(0, import_node_fs26.writeFileSync)(snapshot.path, `${JSON.stringify(parsed, null, 2)}
|
|
19226
19262
|
`, "utf8");
|
|
@@ -19241,7 +19277,7 @@ function writeOpencodeCommandFiles() {
|
|
|
19241
19277
|
const dir = opencodeCommandsDir();
|
|
19242
19278
|
(0, import_node_fs26.mkdirSync)(dir, { recursive: true });
|
|
19243
19279
|
for (const command of OPENCODE_WORKFLOW_COMMANDS) {
|
|
19244
|
-
(0, import_node_fs26.writeFileSync)((0,
|
|
19280
|
+
(0, import_node_fs26.writeFileSync)((0, import_node_path23.join)(dir, `${command}.md`), opencodeCommandMarkdown(command), "utf8");
|
|
19245
19281
|
}
|
|
19246
19282
|
return true;
|
|
19247
19283
|
} catch {
|
|
@@ -19250,8 +19286,8 @@ function writeOpencodeCommandFiles() {
|
|
|
19250
19286
|
}
|
|
19251
19287
|
function readOpencodeAdapterDiskVersion() {
|
|
19252
19288
|
const candidates = [
|
|
19253
|
-
(0,
|
|
19254
|
-
(0,
|
|
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")
|
|
19255
19291
|
];
|
|
19256
19292
|
for (const path2 of candidates) {
|
|
19257
19293
|
try {
|
|
@@ -19286,21 +19322,21 @@ function opencodePluginVersionsForReport() {
|
|
|
19286
19322
|
}
|
|
19287
19323
|
function opencodeDesktopLogsRoot() {
|
|
19288
19324
|
if (process.platform === "win32") {
|
|
19289
|
-
const base = process.env.APPDATA || (0,
|
|
19290
|
-
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");
|
|
19291
19327
|
}
|
|
19292
19328
|
if (process.platform === "darwin") {
|
|
19293
|
-
return (0,
|
|
19329
|
+
return (0, import_node_path23.join)((0, import_node_os5.homedir)(), "Library", "Application Support", "ai.opencode.desktop", "logs");
|
|
19294
19330
|
}
|
|
19295
|
-
return (0,
|
|
19331
|
+
return (0, import_node_path23.join)((0, import_node_os5.homedir)(), ".config", "ai.opencode.desktop", "logs");
|
|
19296
19332
|
}
|
|
19297
19333
|
function opencodeDesktopBootstrapSnapshot() {
|
|
19298
19334
|
const root = opencodeDesktopLogsRoot();
|
|
19299
19335
|
try {
|
|
19300
|
-
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);
|
|
19301
19337
|
const newest = sessionDirs[0];
|
|
19302
19338
|
if (!newest) return [];
|
|
19303
|
-
const logPath = (0,
|
|
19339
|
+
const logPath = (0, import_node_path23.join)(newest, "renderer.log");
|
|
19304
19340
|
const text = (0, import_node_fs26.readFileSync)(logPath, "utf8");
|
|
19305
19341
|
return opencodeAgentDirectoriesFromLog(text).filter((directory) => !(0, import_node_fs26.existsSync)(directory)).map((directory) => ({ directory, logPath }));
|
|
19306
19342
|
} catch {
|
|
@@ -19308,7 +19344,7 @@ function opencodeDesktopBootstrapSnapshot() {
|
|
|
19308
19344
|
}
|
|
19309
19345
|
}
|
|
19310
19346
|
function opencodeLegacyConfigSnapshot() {
|
|
19311
|
-
const legacyPath = (0,
|
|
19347
|
+
const legacyPath = (0, import_node_path23.join)((0, import_node_os5.homedir)(), ".opencode", "opencode.json");
|
|
19312
19348
|
if (!(0, import_node_fs26.existsSync)(legacyPath)) return {};
|
|
19313
19349
|
const content = readTextFile(legacyPath);
|
|
19314
19350
|
if (content == null) return {};
|
|
@@ -19329,16 +19365,16 @@ function quarantineOpencodeLegacyConfig(legacyPath) {
|
|
|
19329
19365
|
}
|
|
19330
19366
|
}
|
|
19331
19367
|
function cursorPluginCacheRoot() {
|
|
19332
|
-
return (0,
|
|
19368
|
+
return (0, import_node_path23.join)((0, import_node_os5.homedir)(), ".cursor", "plugins", "cache", "mutmutco", "mmi");
|
|
19333
19369
|
}
|
|
19334
19370
|
function cursorPluginCachePinSnapshots() {
|
|
19335
19371
|
const root = cursorPluginCacheRoot();
|
|
19336
19372
|
try {
|
|
19337
19373
|
return (0, import_node_fs26.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => {
|
|
19338
|
-
const path2 = (0,
|
|
19339
|
-
const pluginJson = (0,
|
|
19340
|
-
const hooksJson = (0,
|
|
19341
|
-
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");
|
|
19342
19378
|
let version;
|
|
19343
19379
|
try {
|
|
19344
19380
|
const raw = JSON.parse((0, import_node_fs26.readFileSync)(pluginJson, "utf8"));
|
|
@@ -19367,19 +19403,19 @@ function cursorPluginCachePinSnapshots() {
|
|
|
19367
19403
|
}
|
|
19368
19404
|
}
|
|
19369
19405
|
function hubCheckoutForCursorSeed() {
|
|
19370
|
-
const manifest = (0,
|
|
19406
|
+
const manifest = (0, import_node_path23.join)(process.cwd(), "plugins", "mmi", ".cursor-plugin", "plugin.json");
|
|
19371
19407
|
return (0, import_node_fs26.existsSync)(manifest) ? process.cwd() : void 0;
|
|
19372
19408
|
}
|
|
19373
19409
|
function mmiPluginCacheRootSnapshots() {
|
|
19374
19410
|
const roots = [
|
|
19375
|
-
{ surface: "claude", root: (0,
|
|
19376
|
-
{ 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") }
|
|
19377
19413
|
];
|
|
19378
19414
|
return roots.flatMap(({ surface, root }) => {
|
|
19379
19415
|
try {
|
|
19380
19416
|
const entries = (0, import_node_fs26.readdirSync)(root, { withFileTypes: true }).map((entry) => ({
|
|
19381
19417
|
name: entry.name,
|
|
19382
|
-
path: (0,
|
|
19418
|
+
path: (0, import_node_path23.join)(root, entry.name),
|
|
19383
19419
|
isDirectory: entry.isDirectory()
|
|
19384
19420
|
}));
|
|
19385
19421
|
return [{ surface, root, entries }];
|
|
@@ -19390,7 +19426,7 @@ function mmiPluginCacheRootSnapshots() {
|
|
|
19390
19426
|
}
|
|
19391
19427
|
function hasNestedMmiChild(versionDir) {
|
|
19392
19428
|
try {
|
|
19393
|
-
return (0, import_node_fs26.statSync)((0,
|
|
19429
|
+
return (0, import_node_fs26.statSync)((0, import_node_path23.join)(versionDir, "mmi")).isDirectory();
|
|
19394
19430
|
} catch {
|
|
19395
19431
|
return false;
|
|
19396
19432
|
}
|
|
@@ -19415,7 +19451,7 @@ function quarantinePluginCacheDirs(plan2) {
|
|
|
19415
19451
|
try {
|
|
19416
19452
|
if (!(0, import_node_fs26.existsSync)(move.from)) continue;
|
|
19417
19453
|
const target = uniqueQuarantineTarget(move.to);
|
|
19418
|
-
(0, import_node_fs26.mkdirSync)((0,
|
|
19454
|
+
(0, import_node_fs26.mkdirSync)((0, import_node_path23.dirname)(target), { recursive: true });
|
|
19419
19455
|
(0, import_node_fs26.renameSync)(move.from, target);
|
|
19420
19456
|
moved += 1;
|
|
19421
19457
|
} catch {
|
|
@@ -19437,7 +19473,7 @@ async function clearNestedPluginTreeDir(targetPath) {
|
|
|
19437
19473
|
try {
|
|
19438
19474
|
if (!(0, import_node_fs26.existsSync)(targetPath)) return true;
|
|
19439
19475
|
if (isWin) {
|
|
19440
|
-
const emptyDir = (0,
|
|
19476
|
+
const emptyDir = (0, import_node_path23.join)((0, import_node_os5.tmpdir)(), `mmi-empty-${Date.now()}`);
|
|
19441
19477
|
(0, import_node_fs26.mkdirSync)(emptyDir, { recursive: true });
|
|
19442
19478
|
try {
|
|
19443
19479
|
await robocopyMirrorEmpty(emptyDir, targetPath);
|
|
@@ -19464,7 +19500,7 @@ async function applyNestedPluginTreeCleanup(paths, log) {
|
|
|
19464
19500
|
}
|
|
19465
19501
|
return true;
|
|
19466
19502
|
}
|
|
19467
|
-
var gitignorePath = () => (0,
|
|
19503
|
+
var gitignorePath = () => (0, import_node_path23.join)(process.cwd(), ".gitignore");
|
|
19468
19504
|
function readTextFile(path2) {
|
|
19469
19505
|
try {
|
|
19470
19506
|
if (!(0, import_node_fs26.existsSync)(path2)) return null;
|
|
@@ -19477,10 +19513,10 @@ function playwrightMcpConfigSnapshots() {
|
|
|
19477
19513
|
const cwd = process.cwd();
|
|
19478
19514
|
const home = (0, import_node_os5.homedir)();
|
|
19479
19515
|
const candidates = [
|
|
19480
|
-
(0,
|
|
19481
|
-
(0,
|
|
19482
|
-
(0,
|
|
19483
|
-
(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")
|
|
19484
19520
|
];
|
|
19485
19521
|
const out = [];
|
|
19486
19522
|
for (const path2 of candidates) {
|
|
@@ -19493,7 +19529,7 @@ function strayBrowserArtifactPaths() {
|
|
|
19493
19529
|
const cwd = process.cwd();
|
|
19494
19530
|
return STRAY_BROWSER_ARTIFACT_DIRS.filter((rel) => {
|
|
19495
19531
|
try {
|
|
19496
|
-
return (0, import_node_fs26.existsSync)((0,
|
|
19532
|
+
return (0, import_node_fs26.existsSync)((0, import_node_path23.join)(cwd, rel));
|
|
19497
19533
|
} catch {
|
|
19498
19534
|
return false;
|
|
19499
19535
|
}
|
|
@@ -19512,8 +19548,8 @@ function latestIso(values) {
|
|
|
19512
19548
|
return best;
|
|
19513
19549
|
}
|
|
19514
19550
|
function latestNorthstarContinuityAt() {
|
|
19515
|
-
const meta = parseMeta(readTextFile((0,
|
|
19516
|
-
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)));
|
|
19517
19553
|
return latestIso([
|
|
19518
19554
|
...Object.values(meta).map((entry) => entry.syncedAt),
|
|
19519
19555
|
...queue.map((entry) => entry.queuedAt)
|
|
@@ -19570,6 +19606,10 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19570
19606
|
});
|
|
19571
19607
|
const installedForHealPlan = readInstalledPlugins();
|
|
19572
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));
|
|
19573
19613
|
const healPlan = doctorHealPlan({
|
|
19574
19614
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
19575
19615
|
surface,
|
|
@@ -19584,7 +19624,9 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19584
19624
|
legacyPluginCheck: buildLegacyPluginInstallCheck({
|
|
19585
19625
|
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
19586
19626
|
sources: sourcesForHealPlan
|
|
19587
|
-
})
|
|
19627
|
+
}),
|
|
19628
|
+
opencodeAdapterStale,
|
|
19629
|
+
cursorCacheStale
|
|
19588
19630
|
});
|
|
19589
19631
|
if (opts.preflight && !healPlan.needsEagerHeal) return;
|
|
19590
19632
|
const eagerHeal = Boolean(opts.preflight) || Boolean(opts.banner) && healPlan.needsEagerHeal;
|
|
@@ -19615,9 +19657,19 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19615
19657
|
repoVersion: readRepoVersion(),
|
|
19616
19658
|
releasedVersion
|
|
19617
19659
|
});
|
|
19618
|
-
|
|
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
|
+
}
|
|
19619
19666
|
if (!versionReport.ok) versionReport = { ...versionReport, fix: pluginRecoveryFix(surface) };
|
|
19620
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
|
+
}
|
|
19621
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" });
|
|
19622
19674
|
const hubVersionInfo = await fetchHubVersionInfo(cfg.sagaApiUrl);
|
|
19623
19675
|
const installedVersion = resolveClientVersion();
|
|
@@ -19732,14 +19784,12 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19732
19784
|
surface
|
|
19733
19785
|
});
|
|
19734
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 });
|
|
19735
19790
|
const claudeStale = installedVersionCheck.staleSurfaces?.some((s) => s.surface === "claude") ?? false;
|
|
19736
|
-
if (claudeStale && await applyPluginHeal("claude", surface, (m) => io.err(m))) {
|
|
19737
|
-
const healed =
|
|
19738
|
-
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
19739
|
-
sources: installedPluginSources(),
|
|
19740
|
-
releasedVersion,
|
|
19741
|
-
surface
|
|
19742
|
-
});
|
|
19791
|
+
if (claudeStale && await canDriveSurface("claude") && await applyPluginHeal("claude", surface, (m) => io.err(m), { force: true })) {
|
|
19792
|
+
const healed = rereadInstalled();
|
|
19743
19793
|
installedVersionCheck = healed;
|
|
19744
19794
|
if (healed.ok) {
|
|
19745
19795
|
markPluginReloadRequired();
|
|
@@ -19747,13 +19797,8 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19747
19797
|
}
|
|
19748
19798
|
}
|
|
19749
19799
|
const codexStale = installedVersionCheck.staleSurfaces?.some((s) => s.surface === "codex") ?? false;
|
|
19750
|
-
if (!installedVersionCheck.ok && codexStale && await applyPluginHeal("codex", surface, (m) => io.err(m))) {
|
|
19751
|
-
const healed =
|
|
19752
|
-
isOrgRepo: Boolean(cfg.sagaApiUrl),
|
|
19753
|
-
sources: installedPluginSources(),
|
|
19754
|
-
releasedVersion,
|
|
19755
|
-
surface
|
|
19756
|
-
});
|
|
19800
|
+
if (!installedVersionCheck.ok && codexStale && await canDriveSurface("codex") && await applyPluginHeal("codex", surface, (m) => io.err(m), { force: true })) {
|
|
19801
|
+
const healed = rereadInstalled();
|
|
19757
19802
|
installedVersionCheck = healed;
|
|
19758
19803
|
if (healed.ok) {
|
|
19759
19804
|
markPluginReloadRequired();
|
|
@@ -19937,7 +19982,7 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
19937
19982
|
releasedVersion,
|
|
19938
19983
|
hubCheckout: hubCheckoutForCursorSeed(),
|
|
19939
19984
|
execFileP: execFileP2,
|
|
19940
|
-
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)),
|
|
19941
19986
|
log: (m) => io.err(m)
|
|
19942
19987
|
});
|
|
19943
19988
|
if (seeded) {
|
|
@@ -20075,7 +20120,8 @@ async function runDoctor(opts, io = consoleIo) {
|
|
|
20075
20120
|
}
|
|
20076
20121
|
const gaps = checks.filter((c) => !c.ok);
|
|
20077
20122
|
if (opts.preflight) {
|
|
20078
|
-
|
|
20123
|
+
const outcome = preflightOutcome({ gaps, needsEagerHeal: healPlan.needsEagerHeal, surface });
|
|
20124
|
+
if (outcome.line) io.err(outcome.line);
|
|
20079
20125
|
if (pluginReloadRequired) io.err(pluginAutonomousHaltLine(reloadHint));
|
|
20080
20126
|
return;
|
|
20081
20127
|
}
|
|
@@ -20327,7 +20373,7 @@ rules.command("sync").option("--quiet", "stay silent unless something changed or
|
|
|
20327
20373
|
if (!await runRulesSync(opts)) process.exitCode = 1;
|
|
20328
20374
|
});
|
|
20329
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) => {
|
|
20330
|
-
const path2 = (0,
|
|
20376
|
+
const path2 = (0, import_node_path24.join)(process.cwd(), ".gitignore");
|
|
20331
20377
|
const current = (0, import_node_fs27.existsSync)(path2) ? (0, import_node_fs27.readFileSync)(path2, "utf8") : null;
|
|
20332
20378
|
const plan2 = planManagedGitignore(current);
|
|
20333
20379
|
const drift = [...plan2.added.map((l) => `+${l}`), ...plan2.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
@@ -20534,7 +20580,7 @@ function runWorktreeInstall(command, cwd, quiet) {
|
|
|
20534
20580
|
async function primaryCheckoutRoot(worktreeRoot) {
|
|
20535
20581
|
try {
|
|
20536
20582
|
const out = (await execFileP2("git", ["-C", worktreeRoot, "rev-parse", "--path-format=absolute", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
|
|
20537
|
-
return out ? (0,
|
|
20583
|
+
return out ? (0, import_node_path24.dirname)(out) : void 0;
|
|
20538
20584
|
} catch {
|
|
20539
20585
|
return void 0;
|
|
20540
20586
|
}
|
|
@@ -20547,7 +20593,7 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
|
|
|
20547
20593
|
};
|
|
20548
20594
|
}
|
|
20549
20595
|
function acquireWorktreeSetupLock(worktreeRoot) {
|
|
20550
|
-
const lockPath = (0,
|
|
20596
|
+
const lockPath = (0, import_node_path24.join)(worktreeRoot, ".mmi", "worktree-setup.lock");
|
|
20551
20597
|
const take = () => {
|
|
20552
20598
|
const fd = (0, import_node_fs27.openSync)(lockPath, "wx");
|
|
20553
20599
|
try {
|
|
@@ -20563,7 +20609,7 @@ function acquireWorktreeSetupLock(worktreeRoot) {
|
|
|
20563
20609
|
};
|
|
20564
20610
|
};
|
|
20565
20611
|
try {
|
|
20566
|
-
(0, import_node_fs27.mkdirSync)((0,
|
|
20612
|
+
(0, import_node_fs27.mkdirSync)((0, import_node_path24.dirname)(lockPath), { recursive: true });
|
|
20567
20613
|
return take();
|
|
20568
20614
|
} catch {
|
|
20569
20615
|
try {
|
|
@@ -21569,7 +21615,7 @@ pr.command("create").description("create a PR and print {number,url} JSON").opti
|
|
|
21569
21615
|
console.log(JSON.stringify(created));
|
|
21570
21616
|
});
|
|
21571
21617
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
21572
|
-
const wfDir = (0,
|
|
21618
|
+
const wfDir = (0, import_node_path24.join)(cwd, ".github", "workflows");
|
|
21573
21619
|
if (!(0, import_node_fs27.existsSync)(wfDir)) return [];
|
|
21574
21620
|
return (0, import_node_fs27.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).map((name) => `.github/workflows/${name}`);
|
|
21575
21621
|
}
|
|
@@ -21743,7 +21789,7 @@ async function createDeferredWorktreeStore() {
|
|
|
21743
21789
|
},
|
|
21744
21790
|
write: async (entries) => {
|
|
21745
21791
|
try {
|
|
21746
|
-
await (0, import_promises8.mkdir)((0,
|
|
21792
|
+
await (0, import_promises8.mkdir)((0, import_node_path24.dirname)(registryPath), { recursive: true });
|
|
21747
21793
|
await (0, import_promises8.writeFile)(registryPath, serializeDeferredWorktrees(entries), "utf8");
|
|
21748
21794
|
} catch {
|
|
21749
21795
|
}
|
|
@@ -22101,8 +22147,8 @@ async function resolveStage() {
|
|
|
22101
22147
|
local,
|
|
22102
22148
|
shell: shellFor(),
|
|
22103
22149
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
22104
|
-
hasCompose: (0, import_node_fs27.existsSync)((0,
|
|
22105
|
-
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"))
|
|
22106
22152
|
});
|
|
22107
22153
|
}
|
|
22108
22154
|
async function fetchStageVaultEnvMerge() {
|
|
@@ -22957,6 +23003,7 @@ program2.command("session-start").description("run the SessionStart verbs (rules
|
|
|
22957
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.");
|
|
22958
23004
|
return;
|
|
22959
23005
|
}
|
|
23006
|
+
if (!isOrgRepoRoot(process.cwd())) return;
|
|
22960
23007
|
try {
|
|
22961
23008
|
const hook = parseHookInput(await readStdin());
|
|
22962
23009
|
if (hook.session_id) persistSession(hook.session_id);
|