@mutmutco/cli 3.126.0 → 3.128.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 +1136 -604
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -523,7 +523,7 @@ var require_graceful_fs = __commonJS({
|
|
|
523
523
|
function patch(fs3) {
|
|
524
524
|
polyfills(fs3);
|
|
525
525
|
fs3.gracefulify = patch;
|
|
526
|
-
fs3.createReadStream =
|
|
526
|
+
fs3.createReadStream = createReadStream2;
|
|
527
527
|
fs3.createWriteStream = createWriteStream;
|
|
528
528
|
var fs$readFile = fs3.readFile;
|
|
529
529
|
fs3.readFile = readFile9;
|
|
@@ -733,7 +733,7 @@ var require_graceful_fs = __commonJS({
|
|
|
733
733
|
}
|
|
734
734
|
});
|
|
735
735
|
}
|
|
736
|
-
function
|
|
736
|
+
function createReadStream2(path2, options) {
|
|
737
737
|
return new fs3.ReadStream(path2, options);
|
|
738
738
|
}
|
|
739
739
|
function createWriteStream(path2, options) {
|
|
@@ -1239,11 +1239,11 @@ var require_mtime_precision = __commonJS({
|
|
|
1239
1239
|
function probe(file, fs2, callback) {
|
|
1240
1240
|
const cachedPrecision = fs2[cacheSymbol];
|
|
1241
1241
|
if (cachedPrecision) {
|
|
1242
|
-
return fs2.stat(file, (err,
|
|
1242
|
+
return fs2.stat(file, (err, stat4) => {
|
|
1243
1243
|
if (err) {
|
|
1244
1244
|
return callback(err);
|
|
1245
1245
|
}
|
|
1246
|
-
callback(null,
|
|
1246
|
+
callback(null, stat4.mtime, cachedPrecision);
|
|
1247
1247
|
});
|
|
1248
1248
|
}
|
|
1249
1249
|
const mtime = new Date(Math.ceil(Date.now() / 1e3) * 1e3 + 5);
|
|
@@ -1251,13 +1251,13 @@ var require_mtime_precision = __commonJS({
|
|
|
1251
1251
|
if (err) {
|
|
1252
1252
|
return callback(err);
|
|
1253
1253
|
}
|
|
1254
|
-
fs2.stat(file, (err2,
|
|
1254
|
+
fs2.stat(file, (err2, stat4) => {
|
|
1255
1255
|
if (err2) {
|
|
1256
1256
|
return callback(err2);
|
|
1257
1257
|
}
|
|
1258
|
-
const precision =
|
|
1258
|
+
const precision = stat4.mtime.getTime() % 1e3 === 0 ? "s" : "ms";
|
|
1259
1259
|
Object.defineProperty(fs2, cacheSymbol, { value: precision });
|
|
1260
|
-
callback(null,
|
|
1260
|
+
callback(null, stat4.mtime, precision);
|
|
1261
1261
|
});
|
|
1262
1262
|
});
|
|
1263
1263
|
}
|
|
@@ -1311,14 +1311,14 @@ var require_lockfile = __commonJS({
|
|
|
1311
1311
|
if (options.stale <= 0) {
|
|
1312
1312
|
return callback(Object.assign(new Error("Lock file is already being held"), { code: "ELOCKED", file }));
|
|
1313
1313
|
}
|
|
1314
|
-
options.fs.stat(lockfilePath, (err2,
|
|
1314
|
+
options.fs.stat(lockfilePath, (err2, stat4) => {
|
|
1315
1315
|
if (err2) {
|
|
1316
1316
|
if (err2.code === "ENOENT") {
|
|
1317
1317
|
return acquireLock2(file, { ...options, stale: 0 }, callback);
|
|
1318
1318
|
}
|
|
1319
1319
|
return callback(err2);
|
|
1320
1320
|
}
|
|
1321
|
-
if (!isLockStale(
|
|
1321
|
+
if (!isLockStale(stat4, options)) {
|
|
1322
1322
|
return callback(Object.assign(new Error("Lock file is already being held"), { code: "ELOCKED", file }));
|
|
1323
1323
|
}
|
|
1324
1324
|
removeLock(file, options, (err3) => {
|
|
@@ -1330,8 +1330,8 @@ var require_lockfile = __commonJS({
|
|
|
1330
1330
|
});
|
|
1331
1331
|
});
|
|
1332
1332
|
}
|
|
1333
|
-
function isLockStale(
|
|
1334
|
-
return
|
|
1333
|
+
function isLockStale(stat4, options) {
|
|
1334
|
+
return stat4.mtime.getTime() < Date.now() - options.stale;
|
|
1335
1335
|
}
|
|
1336
1336
|
function removeLock(file, options, callback) {
|
|
1337
1337
|
options.fs.rmdir(getLockFile(file, options), (err) => {
|
|
@@ -1349,7 +1349,7 @@ var require_lockfile = __commonJS({
|
|
|
1349
1349
|
lock2.updateDelay = lock2.updateDelay || options.update;
|
|
1350
1350
|
lock2.updateTimeout = setTimeout(() => {
|
|
1351
1351
|
lock2.updateTimeout = null;
|
|
1352
|
-
options.fs.stat(lock2.lockfilePath, (err,
|
|
1352
|
+
options.fs.stat(lock2.lockfilePath, (err, stat4) => {
|
|
1353
1353
|
const isOverThreshold = lock2.lastUpdate + options.stale < Date.now();
|
|
1354
1354
|
if (err) {
|
|
1355
1355
|
if (err.code === "ENOENT" || isOverThreshold) {
|
|
@@ -1358,7 +1358,7 @@ var require_lockfile = __commonJS({
|
|
|
1358
1358
|
lock2.updateDelay = 1e3;
|
|
1359
1359
|
return updateLock(file, options);
|
|
1360
1360
|
}
|
|
1361
|
-
const isMtimeOurs = lock2.mtime.getTime() ===
|
|
1361
|
+
const isMtimeOurs = lock2.mtime.getTime() === stat4.mtime.getTime();
|
|
1362
1362
|
if (!isMtimeOurs) {
|
|
1363
1363
|
return setLockAsCompromised(
|
|
1364
1364
|
file,
|
|
@@ -1483,11 +1483,11 @@ var require_lockfile = __commonJS({
|
|
|
1483
1483
|
if (err) {
|
|
1484
1484
|
return callback(err);
|
|
1485
1485
|
}
|
|
1486
|
-
options.fs.stat(getLockFile(file2, options), (err2,
|
|
1486
|
+
options.fs.stat(getLockFile(file2, options), (err2, stat4) => {
|
|
1487
1487
|
if (err2) {
|
|
1488
1488
|
return err2.code === "ENOENT" ? callback(null, false) : callback(err2);
|
|
1489
1489
|
}
|
|
1490
|
-
return callback(null, !isLockStale(
|
|
1490
|
+
return callback(null, !isLockStale(stat4, options));
|
|
1491
1491
|
});
|
|
1492
1492
|
});
|
|
1493
1493
|
}
|
|
@@ -1533,12 +1533,12 @@ var require_adapter = __commonJS({
|
|
|
1533
1533
|
return newFs;
|
|
1534
1534
|
}
|
|
1535
1535
|
function toPromise(method) {
|
|
1536
|
-
return (...args) => new Promise((
|
|
1536
|
+
return (...args) => new Promise((resolve6, reject) => {
|
|
1537
1537
|
args.push((err, result) => {
|
|
1538
1538
|
if (err) {
|
|
1539
1539
|
reject(err);
|
|
1540
1540
|
} else {
|
|
1541
|
-
|
|
1541
|
+
resolve6(result);
|
|
1542
1542
|
}
|
|
1543
1543
|
});
|
|
1544
1544
|
method(...args);
|
|
@@ -4999,8 +4999,8 @@ function useColor() {
|
|
|
4999
4999
|
var program = new Command();
|
|
5000
5000
|
|
|
5001
5001
|
// src/index.ts
|
|
5002
|
-
var
|
|
5003
|
-
var
|
|
5002
|
+
var import_promises12 = require("node:fs/promises");
|
|
5003
|
+
var import_node_fs48 = require("node:fs");
|
|
5004
5004
|
var import_node_child_process20 = require("node:child_process");
|
|
5005
5005
|
|
|
5006
5006
|
// src/cli-shared.ts
|
|
@@ -5112,19 +5112,19 @@ function hardExit(code) {
|
|
|
5112
5112
|
}
|
|
5113
5113
|
var STDIO_FLUSH_TIMEOUT_MS = 2e3;
|
|
5114
5114
|
function flushStream(stream) {
|
|
5115
|
-
return new Promise((
|
|
5115
|
+
return new Promise((resolve6) => {
|
|
5116
5116
|
try {
|
|
5117
|
-
stream.write("", () =>
|
|
5117
|
+
stream.write("", () => resolve6());
|
|
5118
5118
|
} catch {
|
|
5119
|
-
|
|
5119
|
+
resolve6();
|
|
5120
5120
|
}
|
|
5121
5121
|
});
|
|
5122
5122
|
}
|
|
5123
5123
|
async function flushStdio(timeoutMs = STDIO_FLUSH_TIMEOUT_MS) {
|
|
5124
5124
|
await Promise.race([
|
|
5125
5125
|
Promise.all([flushStream(process.stdout), flushStream(process.stderr)]),
|
|
5126
|
-
new Promise((
|
|
5127
|
-
setTimeout(
|
|
5126
|
+
new Promise((resolve6) => {
|
|
5127
|
+
setTimeout(resolve6, timeoutMs).unref?.();
|
|
5128
5128
|
})
|
|
5129
5129
|
]);
|
|
5130
5130
|
}
|
|
@@ -5132,7 +5132,7 @@ async function cleanExit(code) {
|
|
|
5132
5132
|
process.exitCode = code;
|
|
5133
5133
|
await closeHttpPool();
|
|
5134
5134
|
await flushStdio();
|
|
5135
|
-
await new Promise((
|
|
5135
|
+
await new Promise((resolve6) => setImmediate(resolve6));
|
|
5136
5136
|
return void 0;
|
|
5137
5137
|
}
|
|
5138
5138
|
var CLI_EXIT_WATCHDOG_MS = (() => {
|
|
@@ -5519,7 +5519,7 @@ async function fetchWithRetry(fetchImpl, url, init, opts = {}) {
|
|
|
5519
5519
|
const attempts = opts.attempts ?? 3;
|
|
5520
5520
|
const baseDelayMs = opts.baseDelayMs ?? 250;
|
|
5521
5521
|
const retryOn = opts.retryOn ?? ((res) => res.status >= 500);
|
|
5522
|
-
const sleep3 = opts.sleep ?? ((ms) => new Promise((
|
|
5522
|
+
const sleep3 = opts.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
|
|
5523
5523
|
let lastErr;
|
|
5524
5524
|
for (let i = 0; i < attempts; i++) {
|
|
5525
5525
|
const isLast = i === attempts - 1;
|
|
@@ -5646,10 +5646,10 @@ var import_node_fs4 = require("node:fs");
|
|
|
5646
5646
|
var injectedStdin;
|
|
5647
5647
|
function stdinHasPipedInput(statFd = () => (0, import_node_fs4.fstatSync)(0), getIsTTY = () => process.stdin.isTTY) {
|
|
5648
5648
|
try {
|
|
5649
|
-
const
|
|
5650
|
-
if (
|
|
5651
|
-
if (
|
|
5652
|
-
if (
|
|
5649
|
+
const stat4 = statFd();
|
|
5650
|
+
if (stat4.isFIFO() || stat4.isFile()) return true;
|
|
5651
|
+
if (stat4.isCharacterDevice()) return false;
|
|
5652
|
+
if (stat4.isSocket()) return false;
|
|
5653
5653
|
if (getIsTTY() === true) return false;
|
|
5654
5654
|
return true;
|
|
5655
5655
|
} catch {
|
|
@@ -5680,8 +5680,8 @@ async function readStdin(opts = {}) {
|
|
|
5680
5680
|
})().catch(() => {
|
|
5681
5681
|
});
|
|
5682
5682
|
let timer;
|
|
5683
|
-
const timeout = new Promise((
|
|
5684
|
-
timer = setTimeout(
|
|
5683
|
+
const timeout = new Promise((resolve6) => {
|
|
5684
|
+
timer = setTimeout(resolve6, timeoutMs);
|
|
5685
5685
|
});
|
|
5686
5686
|
try {
|
|
5687
5687
|
await Promise.race([drain, timeout]);
|
|
@@ -5735,12 +5735,12 @@ function killProcessTree(pid) {
|
|
|
5735
5735
|
function execFileHard(file, args, options) {
|
|
5736
5736
|
const { timeout, step, ...rest } = options;
|
|
5737
5737
|
const started = Date.now();
|
|
5738
|
-
return new Promise((
|
|
5738
|
+
return new Promise((resolve6, reject) => {
|
|
5739
5739
|
const child2 = (0, import_node_child_process3.execFile)(file, args, { encoding: "utf8", windowsHide: true, ...rest, timeout: 0 }, (error, stdout, stderr) => {
|
|
5740
5740
|
clearTimeout(timer);
|
|
5741
5741
|
if (expired) return;
|
|
5742
5742
|
if (error) reject(error);
|
|
5743
|
-
else
|
|
5743
|
+
else resolve6({ stdout: String(stdout), stderr: String(stderr) });
|
|
5744
5744
|
});
|
|
5745
5745
|
let expired = false;
|
|
5746
5746
|
const timer = setTimeout(() => {
|
|
@@ -6234,9 +6234,9 @@ function trackedPathStatus(c, realPath, repoAnchor) {
|
|
|
6234
6234
|
return null;
|
|
6235
6235
|
}
|
|
6236
6236
|
}
|
|
6237
|
-
function rootScratchDirSnapshot(root, readdir2,
|
|
6237
|
+
function rootScratchDirSnapshot(root, readdir2, stat4) {
|
|
6238
6238
|
try {
|
|
6239
|
-
const rootStat =
|
|
6239
|
+
const rootStat = stat4(root);
|
|
6240
6240
|
let mtimeMs = rootStat.mtimeMs;
|
|
6241
6241
|
let bytes = 0;
|
|
6242
6242
|
const stack = [root];
|
|
@@ -6250,7 +6250,7 @@ function rootScratchDirSnapshot(root, readdir2, stat3) {
|
|
|
6250
6250
|
return null;
|
|
6251
6251
|
} catch {
|
|
6252
6252
|
}
|
|
6253
|
-
const st =
|
|
6253
|
+
const st = stat4(child2);
|
|
6254
6254
|
mtimeMs = Math.max(mtimeMs, st.mtimeMs);
|
|
6255
6255
|
if (ent.isDirectory()) stack.push(child2);
|
|
6256
6256
|
else if (ent.isFile()) bytes += st.size;
|
|
@@ -6263,7 +6263,7 @@ function rootScratchDirSnapshot(root, readdir2, stat3) {
|
|
|
6263
6263
|
}
|
|
6264
6264
|
function collectScratchSnapshot(repoRoot2, deps = {}) {
|
|
6265
6265
|
const readdir2 = deps.readdir ?? import_node_fs6.readdirSync;
|
|
6266
|
-
const
|
|
6266
|
+
const stat4 = deps.stat ?? import_node_fs6.statSync;
|
|
6267
6267
|
const plansRoot = (0, import_node_path4.join)(repoRoot2, "plans");
|
|
6268
6268
|
const rootScratchFiles = [];
|
|
6269
6269
|
try {
|
|
@@ -6276,10 +6276,10 @@ function collectScratchSnapshot(repoRoot2, deps = {}) {
|
|
|
6276
6276
|
const full = (0, import_node_path4.join)(repoRoot2, ent.name);
|
|
6277
6277
|
try {
|
|
6278
6278
|
if (isDir) {
|
|
6279
|
-
const snap = rootScratchDirSnapshot(full, readdir2,
|
|
6279
|
+
const snap = rootScratchDirSnapshot(full, readdir2, stat4);
|
|
6280
6280
|
if (snap) rootScratchFiles.push({ path: full, dir: repoRoot2, name: ent.name, mtimeMs: snap.mtimeMs, bytes: snap.bytes, kind: "dir" });
|
|
6281
6281
|
} else {
|
|
6282
|
-
const st =
|
|
6282
|
+
const st = stat4(full);
|
|
6283
6283
|
rootScratchFiles.push({ path: full, dir: repoRoot2, name: ent.name, mtimeMs: st.mtimeMs, bytes: st.size, kind: "file" });
|
|
6284
6284
|
}
|
|
6285
6285
|
} catch {
|
|
@@ -6293,7 +6293,7 @@ function collectScratchSnapshot(repoRoot2, deps = {}) {
|
|
|
6293
6293
|
if (!ent.isFile() || !ent.name.endsWith(".md")) continue;
|
|
6294
6294
|
const full = (0, import_node_path4.join)(plansRoot, ent.name);
|
|
6295
6295
|
try {
|
|
6296
|
-
const st =
|
|
6296
|
+
const st = stat4(full);
|
|
6297
6297
|
planMdFiles.push({ path: full, dir: plansRoot, name: ent.name, mtimeMs: st.mtimeMs, bytes: st.size, kind: "file" });
|
|
6298
6298
|
} catch {
|
|
6299
6299
|
}
|
|
@@ -6744,7 +6744,7 @@ function localTrainSyncBannerLine(result) {
|
|
|
6744
6744
|
// src/gc.ts
|
|
6745
6745
|
var import_node_fs11 = require("node:fs");
|
|
6746
6746
|
var import_promises = require("node:fs/promises");
|
|
6747
|
-
var
|
|
6747
|
+
var import_node_path10 = require("node:path");
|
|
6748
6748
|
|
|
6749
6749
|
// src/active-workspace-root.ts
|
|
6750
6750
|
var import_node_fs9 = require("node:fs");
|
|
@@ -6762,6 +6762,12 @@ function isPathUnderDirectory(childPath, parentPath, platform2 = process.platfor
|
|
|
6762
6762
|
if (child2 === parent) return true;
|
|
6763
6763
|
return child2.startsWith(`${parent}/`);
|
|
6764
6764
|
}
|
|
6765
|
+
function isCursorAgentHost(env = process.env) {
|
|
6766
|
+
return env.CURSOR_AGENT === "1" || Boolean(env.CURSOR_EXTENSION_HOST_ROLE?.trim()) || Boolean(env.AGENT_TRANSCRIPTS?.trim());
|
|
6767
|
+
}
|
|
6768
|
+
function unresolvedWorkspaceRefusalMessage() {
|
|
6769
|
+
return "refusing to remove a worktree: Cursor agent host cannot resolve the active workspace root. Open the primary checkout in Cursor first (keep the workspace root on the primary; edit the worktree without move_agent_to_root), or set MMI_ACTIVE_WORKSPACE_ROOT to the primary path, then retry `mmi-cli worktree gc sweep-deferred` / `worktree gc --apply` / `worktree land --apply`.";
|
|
6770
|
+
}
|
|
6765
6771
|
function normalizeActiveWorkspaceRoot(value, opts = {}) {
|
|
6766
6772
|
const raw = value?.trim();
|
|
6767
6773
|
if (!raw) return void 0;
|
|
@@ -6779,8 +6785,13 @@ function removalTouchesActiveWorkspace(targetPath, activeWorkspaceRoot, platform
|
|
|
6779
6785
|
function activeWorkspaceRefusalMessage(targetPath, activeWorkspaceRoot) {
|
|
6780
6786
|
return `refusing to remove active Cursor workspace ${activeWorkspaceRoot} (target ${targetPath}). Open the primary checkout in Cursor first, then retry \`mmi-cli worktree gc sweep-deferred\` / \`worktree gc --apply\`.`;
|
|
6781
6787
|
}
|
|
6782
|
-
function decideActiveWorkspaceGuard(targetPath, activeWorkspaceRoot, platform2 = process.platform) {
|
|
6783
|
-
if (!activeWorkspaceRoot)
|
|
6788
|
+
function decideActiveWorkspaceGuard(targetPath, activeWorkspaceRoot, platform2 = process.platform, opts = {}) {
|
|
6789
|
+
if (!activeWorkspaceRoot) {
|
|
6790
|
+
if (opts.cursorAgentHost) {
|
|
6791
|
+
return { action: "refuse", reason: "unresolved-workspace", message: unresolvedWorkspaceRefusalMessage() };
|
|
6792
|
+
}
|
|
6793
|
+
return { action: "proceed" };
|
|
6794
|
+
}
|
|
6784
6795
|
if (!removalTouchesActiveWorkspace(targetPath, activeWorkspaceRoot, platform2)) {
|
|
6785
6796
|
return { action: "proceed" };
|
|
6786
6797
|
}
|
|
@@ -6882,10 +6893,137 @@ function resolveActiveWorkspaceRoot(deps = {}) {
|
|
|
6882
6893
|
return resolveCursorAgentWorkspaceRoot(deps);
|
|
6883
6894
|
}
|
|
6884
6895
|
|
|
6896
|
+
// src/estate-hygiene.ts
|
|
6897
|
+
var import_node_path8 = require("node:path");
|
|
6898
|
+
function win32LongPath(p, platform2 = process.platform) {
|
|
6899
|
+
if (platform2 !== "win32") return p;
|
|
6900
|
+
if (p.startsWith("\\\\?\\")) return p;
|
|
6901
|
+
if (p.startsWith("\\\\")) return `\\\\?\\UNC\\${p.slice(2)}`;
|
|
6902
|
+
return `\\\\?\\${p}`;
|
|
6903
|
+
}
|
|
6904
|
+
function normalizeGithubRemote(url) {
|
|
6905
|
+
const raw = url.trim();
|
|
6906
|
+
if (!raw) return void 0;
|
|
6907
|
+
const ssh = raw.match(/^git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/i);
|
|
6908
|
+
if (ssh) return `${ssh[1]}/${ssh[2]}`.toLowerCase();
|
|
6909
|
+
const https = raw.match(/^https?:\/\/github\.com\/([^/]+)\/(.+?)(?:\.git)?$/i);
|
|
6910
|
+
if (https) return `${https[1]}/${https[2]}`.toLowerCase();
|
|
6911
|
+
return void 0;
|
|
6912
|
+
}
|
|
6913
|
+
function remotesOverlap(a, b) {
|
|
6914
|
+
const left = new Set(a.map(normalizeGithubRemote).filter((x) => Boolean(x)));
|
|
6915
|
+
if (!left.size) return false;
|
|
6916
|
+
return b.some((url) => {
|
|
6917
|
+
const key = normalizeGithubRemote(url);
|
|
6918
|
+
return Boolean(key && left.has(key));
|
|
6919
|
+
});
|
|
6920
|
+
}
|
|
6921
|
+
function worktreesRootOf(primaryCheckout) {
|
|
6922
|
+
return (0, import_node_path8.join)((0, import_node_path8.dirname)(primaryCheckout), "mmi-worktrees", (0, import_node_path8.basename)(primaryCheckout));
|
|
6923
|
+
}
|
|
6924
|
+
function helperWorktreeRoots(primaryCheckout) {
|
|
6925
|
+
return [
|
|
6926
|
+
(0, import_node_path8.join)(primaryCheckout, ".claude", "worktrees"),
|
|
6927
|
+
(0, import_node_path8.join)(primaryCheckout, ".worktrees")
|
|
6928
|
+
];
|
|
6929
|
+
}
|
|
6930
|
+
function pathProvesRepoContainerOwnership(dir, repoContainer, platform2 = process.platform) {
|
|
6931
|
+
const norm = (p) => {
|
|
6932
|
+
const unified = p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
6933
|
+
return platform2 === "win32" || platform2 === "darwin" ? unified.toLowerCase() : unified;
|
|
6934
|
+
};
|
|
6935
|
+
return norm((0, import_node_path8.dirname)(dir)) === norm(repoContainer);
|
|
6936
|
+
}
|
|
6937
|
+
function worktreesRootFromLeaseRef(ref) {
|
|
6938
|
+
const unified = ref.replace(/\\/g, "/");
|
|
6939
|
+
const marker = "/mmi-worktrees/";
|
|
6940
|
+
const idx = unified.toLowerCase().lastIndexOf(marker);
|
|
6941
|
+
if (idx < 0) return void 0;
|
|
6942
|
+
const after = unified.slice(idx + marker.length);
|
|
6943
|
+
const repo = after.split("/").filter(Boolean)[0];
|
|
6944
|
+
if (!repo) return void 0;
|
|
6945
|
+
return unified.slice(0, idx + marker.length + repo.length);
|
|
6946
|
+
}
|
|
6947
|
+
function classifyEstateAudit(input) {
|
|
6948
|
+
const otherPrimaries = input.otherCheckouts.filter((c) => remotesOverlap(input.remoteUrls, c.remoteUrls)).map((c) => c.path);
|
|
6949
|
+
const thisRoot = input.thisWorktreesRoot.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
6950
|
+
const foreignLeaseRoots = [...new Set(
|
|
6951
|
+
(input.leaseRefs ?? []).map(worktreesRootFromLeaseRef).filter((root) => Boolean(root)).filter((root) => root.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase() !== thisRoot)
|
|
6952
|
+
)];
|
|
6953
|
+
const reasons = [];
|
|
6954
|
+
if (otherPrimaries.length) {
|
|
6955
|
+
reasons.push(
|
|
6956
|
+
`another primary checkout of the same remotes exists: ${otherPrimaries.join(", ")}`
|
|
6957
|
+
);
|
|
6958
|
+
}
|
|
6959
|
+
if (foreignLeaseRoots.length) {
|
|
6960
|
+
reasons.push(
|
|
6961
|
+
`jerv lease ledger points at another worktrees root: ${foreignLeaseRoots.join(", ")}`
|
|
6962
|
+
);
|
|
6963
|
+
}
|
|
6964
|
+
return {
|
|
6965
|
+
auditedClone: input.auditedClone,
|
|
6966
|
+
otherPrimaries,
|
|
6967
|
+
foreignLeaseRoots,
|
|
6968
|
+
blocksGreen: reasons.length > 0,
|
|
6969
|
+
reasons
|
|
6970
|
+
};
|
|
6971
|
+
}
|
|
6972
|
+
function formatEstateAuditLines(audit) {
|
|
6973
|
+
const lines = [`audited clone: ${audit.auditedClone}`];
|
|
6974
|
+
if (!audit.blocksGreen) return lines;
|
|
6975
|
+
lines.push('refusing a clean "no leaks" report \u2014 this clone is not the whole estate:');
|
|
6976
|
+
for (const reason of audit.reasons) lines.push(` ${reason}`);
|
|
6977
|
+
lines.push(" fix: run doctor / worktree list --stale / worktree gc from the canonical primary (E:\\AI Projects\\Mutatis Mutandis\\\u2026), not a second clone");
|
|
6978
|
+
return lines;
|
|
6979
|
+
}
|
|
6980
|
+
function classifyOriginLeftovers(input) {
|
|
6981
|
+
const local = new Set(input.localBranches ?? []);
|
|
6982
|
+
const leftovers = [];
|
|
6983
|
+
for (const branch of input.remoteBranches) {
|
|
6984
|
+
const name = branch.replace(/^origin\//, "").trim();
|
|
6985
|
+
if (!name || name === "HEAD" || input.protectedBranches.has(name)) continue;
|
|
6986
|
+
if (input.openPrBranches.has(name)) {
|
|
6987
|
+
leftovers.push({
|
|
6988
|
+
branch: name,
|
|
6989
|
+
kind: "open-pr",
|
|
6990
|
+
autoReap: false,
|
|
6991
|
+
detail: `origin/${name} has an open PR \u2014 gc will not delete it`
|
|
6992
|
+
});
|
|
6993
|
+
continue;
|
|
6994
|
+
}
|
|
6995
|
+
if (input.mergedPrBranches.has(name)) {
|
|
6996
|
+
leftovers.push({
|
|
6997
|
+
branch: name,
|
|
6998
|
+
kind: "merged-missed",
|
|
6999
|
+
autoReap: true,
|
|
7000
|
+
detail: local.has(name) ? `origin/${name} is merged; this clone still has the head` : `origin/${name} is merged; this clone has no local branch (missed reap)`
|
|
7001
|
+
});
|
|
7002
|
+
continue;
|
|
7003
|
+
}
|
|
7004
|
+
if (input.closedUnmergedBranches.has(name)) {
|
|
7005
|
+
leftovers.push({
|
|
7006
|
+
branch: name,
|
|
7007
|
+
kind: "closed-not-merged",
|
|
7008
|
+
autoReap: Boolean(input.trainOnly),
|
|
7009
|
+
detail: `origin/${name} is closed-not-merged \u2014 gc will not auto-delete it${input.trainOnly ? " unless --train-only --apply" : ""}`
|
|
7010
|
+
});
|
|
7011
|
+
continue;
|
|
7012
|
+
}
|
|
7013
|
+
leftovers.push({
|
|
7014
|
+
branch: name,
|
|
7015
|
+
kind: local.has(name) ? "no-pr" : "other-clone",
|
|
7016
|
+
autoReap: Boolean(input.trainOnly),
|
|
7017
|
+
detail: local.has(name) ? `origin/${name} has no PR \u2014 gc will not auto-delete it` : `origin/${name} is an other-clone leftover with no PR this clone can see`
|
|
7018
|
+
});
|
|
7019
|
+
}
|
|
7020
|
+
return leftovers;
|
|
7021
|
+
}
|
|
7022
|
+
|
|
6885
7023
|
// src/worktree-ownership.ts
|
|
6886
7024
|
var import_node_fs10 = require("node:fs");
|
|
6887
7025
|
var import_node_os4 = require("node:os");
|
|
6888
|
-
var
|
|
7026
|
+
var import_node_path9 = require("node:path");
|
|
6889
7027
|
var OWNERS_FILE = "worktree-owners.json";
|
|
6890
7028
|
var EVENTS_FILE = "worktree-events.jsonl";
|
|
6891
7029
|
var WORKTREE_ACTIVITY_WINDOW_MS = 30 * 6e4;
|
|
@@ -6990,7 +7128,7 @@ function decideWorktreeRemoval(input) {
|
|
|
6990
7128
|
}
|
|
6991
7129
|
function readFreshWorktreeLease(path2, now) {
|
|
6992
7130
|
try {
|
|
6993
|
-
const candidate = JSON.parse((0, import_node_fs10.readFileSync)((0,
|
|
7131
|
+
const candidate = JSON.parse((0, import_node_fs10.readFileSync)((0, import_node_path9.join)(path2, WORKTREE_LEASE_MARKER), "utf8"));
|
|
6994
7132
|
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return void 0;
|
|
6995
7133
|
const lease = candidate;
|
|
6996
7134
|
if (lease.kind !== "worktree" || lease.state !== "active" || typeof lease.agent !== "string" || !lease.agent.trim() || typeof lease.ref !== "string" || !sameWorktreePath(lease.ref, path2) || typeof lease.createdAt !== "string" || typeof lease.ttlHours !== "number" || !Number.isFinite(lease.ttlHours) || lease.ttlHours <= 0) return void 0;
|
|
@@ -7017,7 +7155,7 @@ function readOwners(primaryRoot) {
|
|
|
7017
7155
|
function writeOwners(primaryRoot, entries) {
|
|
7018
7156
|
try {
|
|
7019
7157
|
const path2 = worktreeOwnersPath(primaryRoot);
|
|
7020
|
-
(0, import_node_fs10.mkdirSync)((0,
|
|
7158
|
+
(0, import_node_fs10.mkdirSync)((0, import_node_path9.dirname)(path2), { recursive: true });
|
|
7021
7159
|
(0, import_node_fs10.writeFileSync)(path2, serializeWorktreeOwners(entries), "utf8");
|
|
7022
7160
|
} catch {
|
|
7023
7161
|
}
|
|
@@ -7053,7 +7191,7 @@ function dropWorktreeOwner(primaryRoot, path2, expectedCreatedAt) {
|
|
|
7053
7191
|
function appendWorktreeEvent(primaryRoot, event) {
|
|
7054
7192
|
try {
|
|
7055
7193
|
const path2 = worktreeEventsPath(primaryRoot);
|
|
7056
|
-
(0, import_node_fs10.mkdirSync)((0,
|
|
7194
|
+
(0, import_node_fs10.mkdirSync)((0, import_node_path9.dirname)(path2), { recursive: true });
|
|
7057
7195
|
(0, import_node_fs10.appendFileSync)(path2, `${JSON.stringify({ at: event.at ?? (/* @__PURE__ */ new Date()).toISOString(), ...event })}
|
|
7058
7196
|
`, "utf8");
|
|
7059
7197
|
} catch {
|
|
@@ -7132,7 +7270,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
|
7132
7270
|
const resolveRoot = deps.resolveArchiveRoot ?? repoRuntimeStatePath;
|
|
7133
7271
|
if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
|
|
7134
7272
|
if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
|
|
7135
|
-
const source = (0,
|
|
7273
|
+
const source = (0, import_node_path10.join)(args.worktreePath, ".jerv");
|
|
7136
7274
|
if (!exists(source)) return { status: "absent" };
|
|
7137
7275
|
if (!isDirectory(source)) return { status: "skipped", reason: "jerv-not-a-directory" };
|
|
7138
7276
|
const runScope = resolveJervArtifactRunScope(env);
|
|
@@ -7140,7 +7278,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
|
|
|
7140
7278
|
const stamp = now().toISOString().replace(/[:.]/g, "-");
|
|
7141
7279
|
const dest = resolveRoot(args.primaryRoot, "jerv-artifacts", runScope, branchSlug, stamp, ".jerv");
|
|
7142
7280
|
try {
|
|
7143
|
-
mkdirp((0,
|
|
7281
|
+
mkdirp((0, import_node_path10.dirname)(dest));
|
|
7144
7282
|
copyDir(source, dest);
|
|
7145
7283
|
if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
|
|
7146
7284
|
return { status: "archived", path: dest, runScope };
|
|
@@ -7294,7 +7432,9 @@ async function sweepDeferredWorktrees(store, deps, removalContext) {
|
|
|
7294
7432
|
const owner = removalContext ? lookupWorktreeOwner(removalContext.primaryRoot, entry.path) : void 0;
|
|
7295
7433
|
if (removalContext) {
|
|
7296
7434
|
const activeRoot = removalContext.activeWorkspaceRoot ?? resolveActiveWorkspaceRoot();
|
|
7297
|
-
const activeGuard = decideActiveWorkspaceGuard(entry.path, activeRoot
|
|
7435
|
+
const activeGuard = decideActiveWorkspaceGuard(entry.path, activeRoot, process.platform, {
|
|
7436
|
+
cursorAgentHost: removalContext.cursorAgentHost ?? false
|
|
7437
|
+
});
|
|
7298
7438
|
if (activeGuard.action === "refuse") {
|
|
7299
7439
|
stillDeferred.push({ ...entry, reason: "active-workspace" });
|
|
7300
7440
|
recordWorktreeRemoval(removalContext.primaryRoot, {
|
|
@@ -7405,7 +7545,7 @@ async function runWithSweepWatchdog(run, timeoutMs, onTimeout) {
|
|
|
7405
7545
|
clearTimeout(timer);
|
|
7406
7546
|
}
|
|
7407
7547
|
}
|
|
7408
|
-
var defaultSleep = (ms) => new Promise((
|
|
7548
|
+
var defaultSleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
7409
7549
|
async function removeWorktreeWithRecovery(wtPath, deps) {
|
|
7410
7550
|
const maxAttempts = deps.maxAttempts ?? 3;
|
|
7411
7551
|
const backoff = deps.backoffMs ?? [250, 1e3];
|
|
@@ -7639,23 +7779,23 @@ function resolveSafeSiblingWorktreeCleanupTarget(worktreePath, siblingRoots, dep
|
|
|
7639
7779
|
return { ok: false, reason: reasons.join("; ") || "no worktrees root to check against" };
|
|
7640
7780
|
}
|
|
7641
7781
|
function siblingMmiWorktreesRoot(repoRoot2) {
|
|
7642
|
-
const parent = (0,
|
|
7643
|
-
if ((0,
|
|
7644
|
-
const grandparent = (0,
|
|
7645
|
-
if ((0,
|
|
7646
|
-
return (0,
|
|
7782
|
+
const parent = (0, import_node_path10.dirname)(repoRoot2);
|
|
7783
|
+
if ((0, import_node_path10.basename)(parent).toLowerCase() === "mmi-worktrees") return parent;
|
|
7784
|
+
const grandparent = (0, import_node_path10.dirname)(parent);
|
|
7785
|
+
if ((0, import_node_path10.basename)(grandparent).toLowerCase() === "mmi-worktrees") return grandparent;
|
|
7786
|
+
return (0, import_node_path10.join)(parent, "mmi-worktrees");
|
|
7647
7787
|
}
|
|
7648
7788
|
function agentWorktreesRoot(repoRoot2) {
|
|
7649
|
-
return (0,
|
|
7789
|
+
return (0, import_node_path10.join)(repoRoot2, ".claude", "worktrees");
|
|
7650
7790
|
}
|
|
7651
7791
|
function worktreeScanDirs(root, repoRoot2, listDirs, isRepoCheckout) {
|
|
7652
|
-
const projectsDir = (0,
|
|
7653
|
-
const ownName = (0,
|
|
7792
|
+
const projectsDir = (0, import_node_path10.dirname)(root);
|
|
7793
|
+
const ownName = (0, import_node_path10.basename)(repoRoot2).toLowerCase();
|
|
7654
7794
|
const flat = [];
|
|
7655
7795
|
let ownContainer = null;
|
|
7656
7796
|
for (const dir of listDirs(root)) {
|
|
7657
|
-
const name = (0,
|
|
7658
|
-
if (isRepoCheckout((0,
|
|
7797
|
+
const name = (0, import_node_path10.basename)(dir);
|
|
7798
|
+
if (isRepoCheckout((0, import_node_path10.join)(projectsDir, name))) {
|
|
7659
7799
|
if (name.toLowerCase() === ownName) ownContainer = dir;
|
|
7660
7800
|
continue;
|
|
7661
7801
|
}
|
|
@@ -7664,9 +7804,9 @@ function worktreeScanDirs(root, repoRoot2, listDirs, isRepoCheckout) {
|
|
|
7664
7804
|
return ownContainer ? [...flat, ...listDirs(ownContainer)] : flat;
|
|
7665
7805
|
}
|
|
7666
7806
|
function explicitRepoWorktreesRoot(root, repoRoot2, rootDirs) {
|
|
7667
|
-
const repoName2 = (0,
|
|
7807
|
+
const repoName2 = (0, import_node_path10.basename)(repoRoot2);
|
|
7668
7808
|
const repoDir = rootDirs.find((name) => name.toLowerCase() === repoName2.toLowerCase());
|
|
7669
|
-
return repoDir ? (0,
|
|
7809
|
+
return repoDir ? (0, import_node_path10.join)(root, repoDir) : root;
|
|
7670
7810
|
}
|
|
7671
7811
|
function classifySiblingWorktreeDir(entry) {
|
|
7672
7812
|
if (!entry.ownedByCurrentRepo) {
|
|
@@ -7914,7 +8054,33 @@ function buildGcPlan(inputs) {
|
|
|
7914
8054
|
const state = closedState(prSet);
|
|
7915
8055
|
return state ? { ref, branch, prState: state.state, prNumbers: state.numbers } : null;
|
|
7916
8056
|
}).filter((r) => Boolean(r));
|
|
7917
|
-
|
|
8057
|
+
const originLeftovers = classifyOriginLeftovers({
|
|
8058
|
+
remoteBranches: inputs.originBranches ?? [],
|
|
8059
|
+
localBranches: inputs.localBranches,
|
|
8060
|
+
protectedBranches,
|
|
8061
|
+
openPrBranches: new Set(
|
|
8062
|
+
[...prs.entries()].filter(([, set]) => set.some((pr2) => pr2.state === "OPEN")).map(([b]) => b)
|
|
8063
|
+
),
|
|
8064
|
+
mergedPrBranches: new Set(
|
|
8065
|
+
[...prs.entries()].filter(([, set]) => set.some((pr2) => pr2.state === "MERGED")).map(([b]) => b)
|
|
8066
|
+
),
|
|
8067
|
+
closedUnmergedBranches: new Set(
|
|
8068
|
+
[...prs.entries()].filter(([, set]) => set.some((pr2) => pr2.state === "CLOSED") && !set.some((pr2) => pr2.state === "MERGED")).map(([b]) => b)
|
|
8069
|
+
),
|
|
8070
|
+
trainOnly: inputs.trainOnly
|
|
8071
|
+
});
|
|
8072
|
+
const plannedBranches = new Set(branches.map((b) => b.branch));
|
|
8073
|
+
const reapOriginHeads = originLeftovers.filter((l) => l.autoReap && !plannedBranches.has(l.branch)).map((l) => {
|
|
8074
|
+
const prSet = prs.get(l.branch);
|
|
8075
|
+
const state = closedState(prSet);
|
|
8076
|
+
return {
|
|
8077
|
+
branch: l.branch,
|
|
8078
|
+
prState: state?.state ?? "MERGED",
|
|
8079
|
+
prNumbers: state?.numbers ?? [],
|
|
8080
|
+
...state?.headOids.length ? { reviewedHeadOids: state.headOids } : {}
|
|
8081
|
+
};
|
|
8082
|
+
});
|
|
8083
|
+
return { branches, trackingRefs, worktreeDirs, skippedWorktreeDirs, skipped, originLeftovers, reapOriginHeads };
|
|
7918
8084
|
}
|
|
7919
8085
|
function parseRemotePruneDryRun(stdout) {
|
|
7920
8086
|
const refs = [];
|
|
@@ -8229,7 +8395,9 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
8229
8395
|
report.worktree = { path: wtPath, status: "not-attempted", reason: "main-worktree" };
|
|
8230
8396
|
} else if (wtPath) {
|
|
8231
8397
|
const activeRoot = options.removalContext?.activeWorkspaceRoot ?? resolveActiveWorkspaceRoot();
|
|
8232
|
-
const activeGuard = decideActiveWorkspaceGuard(wtPath, activeRoot
|
|
8398
|
+
const activeGuard = decideActiveWorkspaceGuard(wtPath, activeRoot, process.platform, {
|
|
8399
|
+
cursorAgentHost: options.removalContext?.cursorAgentHost ?? false
|
|
8400
|
+
});
|
|
8233
8401
|
if (activeGuard.action === "refuse") {
|
|
8234
8402
|
if (options.deferredStore) {
|
|
8235
8403
|
try {
|
|
@@ -8463,9 +8631,13 @@ async function cleanupPrMergeLocalBranch(branch, options) {
|
|
|
8463
8631
|
if (wtPath) await git2(["worktree", "prune"]).catch(() => "");
|
|
8464
8632
|
return report;
|
|
8465
8633
|
}
|
|
8466
|
-
function formatGcPlan(plan, apply) {
|
|
8634
|
+
function formatGcPlan(plan, apply, auditClone) {
|
|
8467
8635
|
const lines = [`mmi-cli worktree gc: ${apply ? "apply" : "dry-run"}`];
|
|
8468
|
-
if (
|
|
8636
|
+
if (auditClone) lines.push(`audited clone: ${auditClone}`);
|
|
8637
|
+
const hasOriginNames = Boolean(plan.originLeftovers?.length);
|
|
8638
|
+
if (!plan.branches.length && !plan.trackingRefs.length && !plan.worktreeDirs.length && !plan.reapOriginHeads?.length) {
|
|
8639
|
+
lines.push(hasOriginNames ? "nothing this clone will auto-delete" : "nothing to clean");
|
|
8640
|
+
}
|
|
8469
8641
|
if (plan.branches.length) {
|
|
8470
8642
|
lines.push("local branches:");
|
|
8471
8643
|
for (const b of plan.branches) {
|
|
@@ -8507,6 +8679,12 @@ function formatGcPlan(plan, apply) {
|
|
|
8507
8679
|
lines.push(` - ${s.path}: ${s.reason}${s.detail ? ` (${s.detail})` : ""}`);
|
|
8508
8680
|
}
|
|
8509
8681
|
}
|
|
8682
|
+
if (plan.originLeftovers?.length) {
|
|
8683
|
+
lines.push("origin leftovers gc will not silently delete (use --train-only for closed-not-merged / no-PR):");
|
|
8684
|
+
for (const leftover of plan.originLeftovers) {
|
|
8685
|
+
lines.push(` - ${leftover.branch}: ${leftover.kind}${leftover.autoReap ? " (will reap)" : ""} \u2014 ${leftover.detail}`);
|
|
8686
|
+
}
|
|
8687
|
+
}
|
|
8510
8688
|
if (!apply && (plan.branches.length || plan.trackingRefs.length || plan.worktreeDirs.length)) lines.push("rerun with --apply to delete only the listed local, remote, tracking, and directory items");
|
|
8511
8689
|
return lines.join("\n");
|
|
8512
8690
|
}
|
|
@@ -8592,7 +8770,7 @@ async function gatherStaleWorktreeWarning(gitRun = defaultGitRun) {
|
|
|
8592
8770
|
|
|
8593
8771
|
// src/released-version-cache.ts
|
|
8594
8772
|
var import_node_fs12 = require("node:fs");
|
|
8595
|
-
var
|
|
8773
|
+
var import_node_path11 = require("node:path");
|
|
8596
8774
|
|
|
8597
8775
|
// src/version-lag.ts
|
|
8598
8776
|
var VERSION_LABEL = "installed plugin/adapter cache freshness";
|
|
@@ -8688,7 +8866,7 @@ function versionAutoUpdateAction(report, releasedSource) {
|
|
|
8688
8866
|
// src/released-version-cache.ts
|
|
8689
8867
|
var RELEASED_VERSION_CACHE_MS = 24 * 36e5;
|
|
8690
8868
|
function releasedVersionCachePath(runtimeRoot) {
|
|
8691
|
-
return (0,
|
|
8869
|
+
return (0, import_node_path11.join)(runtimeRoot, "head-ts", ".released-version");
|
|
8692
8870
|
}
|
|
8693
8871
|
function readReleasedVersionCache(cachePath, now = Date.now(), read = import_node_fs12.readFileSync, currentVersion = resolveClientVersion()) {
|
|
8694
8872
|
let parsed;
|
|
@@ -8708,7 +8886,7 @@ function readReleasedVersionCache(cachePath, now = Date.now(), read = import_nod
|
|
|
8708
8886
|
}
|
|
8709
8887
|
function writeReleasedVersionCache(cachePath, version, now = Date.now()) {
|
|
8710
8888
|
try {
|
|
8711
|
-
(0, import_node_fs12.mkdirSync)((0,
|
|
8889
|
+
(0, import_node_fs12.mkdirSync)((0, import_node_path11.dirname)(cachePath), { recursive: true });
|
|
8712
8890
|
(0, import_node_fs12.writeFileSync)(cachePath, JSON.stringify({ version, at: now }), "utf8");
|
|
8713
8891
|
} catch {
|
|
8714
8892
|
}
|
|
@@ -8723,10 +8901,10 @@ function cachedReadNote(cachedAt, now = Date.now()) {
|
|
|
8723
8901
|
|
|
8724
8902
|
// src/schedules-drift-cache.ts
|
|
8725
8903
|
var import_node_fs13 = require("node:fs");
|
|
8726
|
-
var
|
|
8904
|
+
var import_node_path12 = require("node:path");
|
|
8727
8905
|
var SCHEDULES_DRIFT_CACHE_MS = 6 * 36e5;
|
|
8728
8906
|
function schedulesDriftCachePath(runtimeRoot) {
|
|
8729
|
-
return (0,
|
|
8907
|
+
return (0, import_node_path12.join)(runtimeRoot, "head-ts", ".schedules-drift");
|
|
8730
8908
|
}
|
|
8731
8909
|
function readSchedulesDriftCache(cachePath, now = Date.now(), read = import_node_fs13.readFileSync) {
|
|
8732
8910
|
let parsed;
|
|
@@ -8745,7 +8923,7 @@ function readSchedulesDriftCache(cachePath, now = Date.now(), read = import_node
|
|
|
8745
8923
|
}
|
|
8746
8924
|
function writeSchedulesDriftCache(cachePath, driftLines, now = Date.now()) {
|
|
8747
8925
|
try {
|
|
8748
|
-
(0, import_node_fs13.mkdirSync)((0,
|
|
8926
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path12.dirname)(cachePath), { recursive: true });
|
|
8749
8927
|
(0, import_node_fs13.writeFileSync)(cachePath, JSON.stringify({ driftLines, at: now }), "utf8");
|
|
8750
8928
|
} catch {
|
|
8751
8929
|
}
|
|
@@ -8901,7 +9079,7 @@ function resolveCatalogRef(probe) {
|
|
|
8901
9079
|
// src/plugin-guard-io.ts
|
|
8902
9080
|
var import_node_fs14 = require("node:fs");
|
|
8903
9081
|
var import_node_child_process6 = require("node:child_process");
|
|
8904
|
-
var
|
|
9082
|
+
var import_node_path13 = require("node:path");
|
|
8905
9083
|
var import_node_os5 = require("node:os");
|
|
8906
9084
|
var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
|
|
8907
9085
|
|
|
@@ -9204,17 +9382,17 @@ function runHostBin(bin, args, opts) {
|
|
|
9204
9382
|
return step ? execFileHard(file, argv, { ...shared, step }) : execFileP2(file, argv, shared);
|
|
9205
9383
|
}
|
|
9206
9384
|
function surfaceConfigRoot(surface, env = process.env, home = (0, import_node_os5.homedir)()) {
|
|
9207
|
-
if (surface === "codex") return env.CODEX_HOME?.trim() || (0,
|
|
9208
|
-
if (surface === "kimi") return env.KIMI_CODE_HOME?.trim() || (0,
|
|
9209
|
-
if (surface === "kilo") return env.KILO_CONFIG_DIR?.trim() || (0,
|
|
9210
|
-
if (surface === "cursor") return (0,
|
|
9385
|
+
if (surface === "codex") return env.CODEX_HOME?.trim() || (0, import_node_path13.join)(home, ".codex");
|
|
9386
|
+
if (surface === "kimi") return env.KIMI_CODE_HOME?.trim() || (0, import_node_path13.join)(home, ".kimi-code");
|
|
9387
|
+
if (surface === "kilo") return env.KILO_CONFIG_DIR?.trim() || (0, import_node_path13.join)(home, ".config", "kilo");
|
|
9388
|
+
if (surface === "cursor") return (0, import_node_path13.join)(home, ".cursor");
|
|
9211
9389
|
if (surface === "jervcode") {
|
|
9212
|
-
return env.JERVCODE_CODING_AGENT_DIR?.trim() || env.PI_CODING_AGENT_DIR?.trim() || (0,
|
|
9390
|
+
return env.JERVCODE_CODING_AGENT_DIR?.trim() || env.PI_CODING_AGENT_DIR?.trim() || (0, import_node_path13.join)(home, ".jerv", "agent");
|
|
9213
9391
|
}
|
|
9214
|
-
return (0,
|
|
9392
|
+
return (0, import_node_path13.join)(home, ".claude");
|
|
9215
9393
|
}
|
|
9216
9394
|
var installedPluginsPath = (surface = detectSurface(process.env)) => {
|
|
9217
|
-
return (0,
|
|
9395
|
+
return (0, import_node_path13.join)(surfaceConfigRoot(surface), "plugins", "installed_plugins.json");
|
|
9218
9396
|
};
|
|
9219
9397
|
function readInstalledPlugins(surface = detectSurface(process.env)) {
|
|
9220
9398
|
try {
|
|
@@ -9227,15 +9405,15 @@ function marketplaceCloneCandidates(surface, home, env = process.env) {
|
|
|
9227
9405
|
if (surface === "codex") {
|
|
9228
9406
|
const root = surfaceConfigRoot(surface, env, home);
|
|
9229
9407
|
return [
|
|
9230
|
-
(0,
|
|
9231
|
-
(0,
|
|
9408
|
+
(0, import_node_path13.join)(root, ".tmp", "marketplaces", CODEX_MARKETPLACE),
|
|
9409
|
+
(0, import_node_path13.join)(root, "plugins", "marketplaces", CODEX_MARKETPLACE)
|
|
9232
9410
|
];
|
|
9233
9411
|
}
|
|
9234
9412
|
if (surface === "kimi") return [];
|
|
9235
9413
|
if (surface === "kilo") return [];
|
|
9236
9414
|
if (surface === "cursor") return [];
|
|
9237
9415
|
if (surface === "jervcode") return [];
|
|
9238
|
-
return [(0,
|
|
9416
|
+
return [(0, import_node_path13.join)(home, ".claude", "plugins", "marketplaces", "mutmutco")];
|
|
9239
9417
|
}
|
|
9240
9418
|
function marketplaceClonePresent(surface, home, exists = import_node_fs14.existsSync, env = process.env) {
|
|
9241
9419
|
return marketplaceCloneCandidates(surface, home, env).some(exists);
|
|
@@ -9286,11 +9464,11 @@ function codexHookTrustState(status = codexPluginStatus()) {
|
|
|
9286
9464
|
return { applicable: false, trusted: false, trustedCount: 0, requiredCount: 0 };
|
|
9287
9465
|
}
|
|
9288
9466
|
const root = surfaceConfigRoot("codex");
|
|
9289
|
-
const hooksPath = (0,
|
|
9467
|
+
const hooksPath = (0, import_node_path13.join)(root, "plugins", "cache", CODEX_MARKETPLACE, "mmi", status.version, "hooks", "codex-hooks.json");
|
|
9290
9468
|
const requiredCount = countCodexHookCommands(hooksPath);
|
|
9291
9469
|
let config = "";
|
|
9292
9470
|
try {
|
|
9293
|
-
config = (0, import_node_fs14.readFileSync)((0,
|
|
9471
|
+
config = (0, import_node_fs14.readFileSync)((0, import_node_path13.join)(root, "config.toml"), "utf8");
|
|
9294
9472
|
} catch {
|
|
9295
9473
|
return { applicable: true, trusted: false, trustedCount: 0, requiredCount };
|
|
9296
9474
|
}
|
|
@@ -9317,7 +9495,7 @@ var NPM_INSTALL_TIMEOUT_MS = 12e4;
|
|
|
9317
9495
|
var CLI_VERSION_PROBE_TIMEOUT_MS = 3e4;
|
|
9318
9496
|
function resolveNpmSpawn(args) {
|
|
9319
9497
|
if (!isWin) return { file: "npm", args };
|
|
9320
|
-
const cli = (0,
|
|
9498
|
+
const cli = (0, import_node_path13.join)((0, import_node_path13.dirname)(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
|
|
9321
9499
|
if ((0, import_node_fs14.existsSync)(cli)) return { file: process.execPath, args: [cli, ...args] };
|
|
9322
9500
|
return { file: "cmd.exe", args: ["/c", "npm", ...args] };
|
|
9323
9501
|
}
|
|
@@ -9346,9 +9524,9 @@ async function npmSelfUpdateCli(target, onStep, deps = {}) {
|
|
|
9346
9524
|
}
|
|
9347
9525
|
function kiloConfigListsPlugin(configRoot, home = (0, import_node_os5.homedir)(), read = (p) => (0, import_node_fs14.readFileSync)(p, "utf8"), exists = import_node_fs14.existsSync) {
|
|
9348
9526
|
const candidates = ["kilo.json", "kilo.jsonc", "opencode.json", "opencode.jsonc", "config.json"];
|
|
9349
|
-
for (const dir of [configRoot, (0,
|
|
9527
|
+
for (const dir of [configRoot, (0, import_node_path13.join)(home, ".kilo")]) {
|
|
9350
9528
|
for (const file of candidates) {
|
|
9351
|
-
const path2 = (0,
|
|
9529
|
+
const path2 = (0, import_node_path13.join)(dir, file);
|
|
9352
9530
|
if (!exists(path2)) continue;
|
|
9353
9531
|
try {
|
|
9354
9532
|
const stripped = read(path2).replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "");
|
|
@@ -9365,7 +9543,7 @@ function kiloConfigListsPlugin(configRoot, home = (0, import_node_os5.homedir)()
|
|
|
9365
9543
|
return false;
|
|
9366
9544
|
}
|
|
9367
9545
|
function cursorLocalPluginRoot(env = process.env, home = (0, import_node_os5.homedir)()) {
|
|
9368
|
-
return (0,
|
|
9546
|
+
return (0, import_node_path13.join)(surfaceConfigRoot("cursor", env, home), "plugins", "local", "mmi");
|
|
9369
9547
|
}
|
|
9370
9548
|
function cursorPluginTreeHealthy(root, exists = import_node_fs14.existsSync) {
|
|
9371
9549
|
return [
|
|
@@ -9374,14 +9552,14 @@ function cursorPluginTreeHealthy(root, exists = import_node_fs14.existsSync) {
|
|
|
9374
9552
|
"hooks/cursor-hooks.json",
|
|
9375
9553
|
"scripts/hook-run.mjs",
|
|
9376
9554
|
"scripts/hook-policy.mjs"
|
|
9377
|
-
].every((path2) => exists((0,
|
|
9555
|
+
].every((path2) => exists((0, import_node_path13.join)(root, ...path2.split("/"))));
|
|
9378
9556
|
}
|
|
9379
9557
|
function kimiPluginTreeHealthy(root, exists = import_node_fs14.existsSync) {
|
|
9380
9558
|
return [
|
|
9381
9559
|
".kimi-plugin/plugin.json",
|
|
9382
9560
|
"skills/mmi/SKILL.md",
|
|
9383
9561
|
"scripts/hook-run.mjs"
|
|
9384
|
-
].every((path2) => exists((0,
|
|
9562
|
+
].every((path2) => exists((0, import_node_path13.join)(root, ...path2.split("/"))));
|
|
9385
9563
|
}
|
|
9386
9564
|
var JERVCODE_WRAPPER_DIR = ".pi-plugin";
|
|
9387
9565
|
function normalizePiEntry(value) {
|
|
@@ -9404,7 +9582,7 @@ function jervcodePackageFamily(entry) {
|
|
|
9404
9582
|
function isMmiOwnedPiEntry(entry) {
|
|
9405
9583
|
if (typeof entry !== "string") return false;
|
|
9406
9584
|
try {
|
|
9407
|
-
const pkg = JSON.parse((0, import_node_fs14.readFileSync)((0,
|
|
9585
|
+
const pkg = JSON.parse((0, import_node_fs14.readFileSync)((0, import_node_path13.join)(piEntryFsPath(entry), "package.json"), "utf8"));
|
|
9408
9586
|
return pkg.name === "mmi";
|
|
9409
9587
|
} catch {
|
|
9410
9588
|
return false;
|
|
@@ -9436,8 +9614,8 @@ function readPiSettings(path2) {
|
|
|
9436
9614
|
}
|
|
9437
9615
|
}
|
|
9438
9616
|
function jervcodeSettingsCandidates(env = process.env, home = (0, import_node_os5.homedir)()) {
|
|
9439
|
-
const primary = (0,
|
|
9440
|
-
const legacy = (0,
|
|
9617
|
+
const primary = (0, import_node_path13.join)(surfaceConfigRoot("jervcode", env, home), "settings.json");
|
|
9618
|
+
const legacy = (0, import_node_path13.join)(home, ".pi", "agent", "settings.json");
|
|
9441
9619
|
return legacy === primary ? [primary] : [primary, legacy];
|
|
9442
9620
|
}
|
|
9443
9621
|
function mmiPiWrapperEntry(env = process.env, home = (0, import_node_os5.homedir)()) {
|
|
@@ -9456,17 +9634,17 @@ function mmiPiWrapperEntry(env = process.env, home = (0, import_node_os5.homedir
|
|
|
9456
9634
|
function mmiPiWrapperHealthy(entry) {
|
|
9457
9635
|
if (!entry) return false;
|
|
9458
9636
|
const wrapper = piEntryFsPath(entry);
|
|
9459
|
-
return isMmiOwnedPiEntry(entry) && (0, import_node_fs14.existsSync)((0,
|
|
9637
|
+
return isMmiOwnedPiEntry(entry) && (0, import_node_fs14.existsSync)((0, import_node_path13.join)((0, import_node_path13.dirname)(wrapper), "skills", "mmi", "SKILL.md"));
|
|
9460
9638
|
}
|
|
9461
9639
|
function findMmiPiSourceClone(home = (0, import_node_os5.homedir)()) {
|
|
9462
|
-
const cacheRoot = (0,
|
|
9640
|
+
const cacheRoot = (0, import_node_path13.join)(home, ".claude", "plugins", "cache", "mutmutco", "mmi");
|
|
9463
9641
|
let best = null;
|
|
9464
9642
|
try {
|
|
9465
9643
|
for (const entry of (0, import_node_fs14.readdirSync)(cacheRoot, { withFileTypes: true })) {
|
|
9466
9644
|
if (!entry.isDirectory() || !/^\d+\.\d+\.\d+$/.test(entry.name)) continue;
|
|
9467
|
-
if (!(0, import_node_fs14.existsSync)((0,
|
|
9645
|
+
if (!(0, import_node_fs14.existsSync)((0, import_node_path13.join)(cacheRoot, entry.name, JERVCODE_WRAPPER_DIR, "package.json"))) continue;
|
|
9468
9646
|
if (!best || compareVersions(entry.name, best.version) > 0) {
|
|
9469
|
-
best = { path: (0,
|
|
9647
|
+
best = { path: (0, import_node_path13.join)(cacheRoot, entry.name), version: entry.name };
|
|
9470
9648
|
}
|
|
9471
9649
|
}
|
|
9472
9650
|
} catch {
|
|
@@ -9520,7 +9698,7 @@ function healOneJervCodeSettingsFile(settingsPath2, packagePath, version, nextLa
|
|
|
9520
9698
|
}
|
|
9521
9699
|
current.packages = merged.next;
|
|
9522
9700
|
try {
|
|
9523
|
-
(0, import_node_fs14.mkdirSync)((0,
|
|
9701
|
+
(0, import_node_fs14.mkdirSync)((0, import_node_path13.dirname)(settingsPath2), { recursive: true });
|
|
9524
9702
|
const tmp = `${settingsPath2}.tmp-${process.pid}`;
|
|
9525
9703
|
(0, import_node_fs14.writeFileSync)(tmp, `${JSON.stringify(current, null, 2)}
|
|
9526
9704
|
`, "utf8");
|
|
@@ -9551,7 +9729,7 @@ function healJervCodePackageRegistration(opts = {}) {
|
|
|
9551
9729
|
const nextLaunch = inSeat ? " \u2014 takes effect on the next seat launch" : "";
|
|
9552
9730
|
const home = opts.home ?? (0, import_node_os5.homedir)();
|
|
9553
9731
|
const agentDir = surfaceConfigRoot("jervcode", env, home);
|
|
9554
|
-
const legacyPi = (0,
|
|
9732
|
+
const legacyPi = (0, import_node_path13.join)(home, ".pi", "agent");
|
|
9555
9733
|
if (!(0, import_node_fs14.existsSync)(agentDir) && !(0, import_node_fs14.existsSync)(legacyPi)) {
|
|
9556
9734
|
return { available: false, ok: true, changed: false, version: null, detail: "skipped \u2014 no Pi/JervCode install (no agent config dir)" };
|
|
9557
9735
|
}
|
|
@@ -9560,8 +9738,8 @@ function healJervCodePackageRegistration(opts = {}) {
|
|
|
9560
9738
|
return { available: true, ok: true, changed: false, version: null, detail: "skipped \u2014 no installed MMI clone carries the pi wrapper (install the Claude plugin first)" };
|
|
9561
9739
|
}
|
|
9562
9740
|
const packagePath = `${clone.path.replace(/\\/g, "/").replace(/\/+$/, "")}/${JERVCODE_WRAPPER_DIR}`;
|
|
9563
|
-
const targets = [(0,
|
|
9564
|
-
const legacySettings = (0,
|
|
9741
|
+
const targets = [(0, import_node_path13.join)(agentDir, "settings.json")];
|
|
9742
|
+
const legacySettings = (0, import_node_path13.join)(legacyPi, "settings.json");
|
|
9565
9743
|
if ((0, import_node_fs14.existsSync)(legacyPi) && legacySettings !== targets[0]) targets.push(legacySettings);
|
|
9566
9744
|
const details = [];
|
|
9567
9745
|
let anyChanged = false;
|
|
@@ -9590,7 +9768,7 @@ function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRep
|
|
|
9590
9768
|
return {
|
|
9591
9769
|
isOrgRepo,
|
|
9592
9770
|
installRecordPresent: hasUserInstallRecord(installed, MMI_PLUGIN_ID) || hasProjectInstallRecord(installed, MMI_PLUGIN_ID, process.cwd()) || // Kimi's managed plugin directory is its native install record; it has no Claude-style ledger.
|
|
9593
|
-
surface === "kimi" && (0, import_node_fs14.existsSync)((0,
|
|
9771
|
+
surface === "kimi" && (0, import_node_fs14.existsSync)((0, import_node_path13.join)(root, "plugins", "managed", "mmi")) || // kilo-p1: the install record is the config file itself.
|
|
9594
9772
|
surface === "kilo" && kiloConfigListsPlugin(root) || // #4188: jervcode's install record is the settings-file packages[] entry itself.
|
|
9595
9773
|
surface === "jervcode" && piEntry !== null || surface === "cursor" && (0, import_node_fs14.existsSync)(cursorLocalPluginRoot()),
|
|
9596
9774
|
// Kilo has no marketplace to clone — the config file IS the install record, so this dimension of
|
|
@@ -9599,9 +9777,9 @@ function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRep
|
|
|
9599
9777
|
// Kimi keeps no plugin cache dir — installs are copied to plugins/managed/<id> and run from there.
|
|
9600
9778
|
// Kilo (kilo-p1) keeps no cache dir either: the plugin's server() provisions ~/.kilo behind the
|
|
9601
9779
|
// version stamp, so the stamp's presence is the cache signal.
|
|
9602
|
-
pluginCachePresent: surface === "jervcode" ? mmiPiWrapperHealthy(piEntry) : surface === "kilo" ? (0, import_node_fs14.existsSync)((0,
|
|
9603
|
-
codexStatus?.installed && codexStatus.enabled && codexStatus.version && (0, import_node_fs14.existsSync)((0,
|
|
9604
|
-
) : (0, import_node_fs14.existsSync)((0,
|
|
9780
|
+
pluginCachePresent: surface === "jervcode" ? mmiPiWrapperHealthy(piEntry) : surface === "kilo" ? (0, import_node_fs14.existsSync)((0, import_node_path13.join)((0, import_node_os5.homedir)(), ".kilo", ".mmi-kilo-version")) : surface === "kimi" ? kimiPluginTreeHealthy((0, import_node_path13.join)(root, "plugins", "managed", "mmi")) : surface === "cursor" ? cursorPluginTreeHealthy(cursorLocalPluginRoot()) : surface === "codex" ? Boolean(
|
|
9781
|
+
codexStatus?.installed && codexStatus.enabled && codexStatus.version && (0, import_node_fs14.existsSync)((0, import_node_path13.join)(root, "plugins", "cache", CODEX_MARKETPLACE, "mmi", codexStatus.version))
|
|
9782
|
+
) : (0, import_node_fs14.existsSync)((0, import_node_path13.join)(root, "plugins", "cache", "mutmutco", "mmi"))
|
|
9605
9783
|
};
|
|
9606
9784
|
}
|
|
9607
9785
|
async function runHostBinLogged(bin, args, opts) {
|
|
@@ -9621,9 +9799,9 @@ async function runPluginCli(bin, args, log) {
|
|
|
9621
9799
|
function captureCodexHookLauncher() {
|
|
9622
9800
|
const status = codexPluginStatus();
|
|
9623
9801
|
if (!status.installed || !status.enabled || !status.version) return void 0;
|
|
9624
|
-
const root = (0,
|
|
9802
|
+
const root = (0, import_node_path13.join)(surfaceConfigRoot("codex"), "plugins", "cache", CODEX_MARKETPLACE, "mmi", status.version);
|
|
9625
9803
|
const files = ["mmi-hook", "mmi-hook.exe"].flatMap((name) => {
|
|
9626
|
-
const path2 = (0,
|
|
9804
|
+
const path2 = (0, import_node_path13.join)(root, "bin", name);
|
|
9627
9805
|
try {
|
|
9628
9806
|
return [{ name, content: (0, import_node_fs14.readFileSync)(path2) }];
|
|
9629
9807
|
} catch {
|
|
@@ -9633,11 +9811,11 @@ function captureCodexHookLauncher() {
|
|
|
9633
9811
|
return files.length === 2 ? { root, files } : void 0;
|
|
9634
9812
|
}
|
|
9635
9813
|
function restoreCodexHookLauncher(snapshot) {
|
|
9636
|
-
if (!snapshot || (0, import_node_fs14.existsSync)((0,
|
|
9637
|
-
const bin = (0,
|
|
9814
|
+
if (!snapshot || (0, import_node_fs14.existsSync)((0, import_node_path13.join)(snapshot.root, "scripts", "hook-run.mjs"))) return false;
|
|
9815
|
+
const bin = (0, import_node_path13.join)(snapshot.root, "bin");
|
|
9638
9816
|
(0, import_node_fs14.mkdirSync)(bin, { recursive: true });
|
|
9639
9817
|
for (const file of snapshot.files) {
|
|
9640
|
-
const path2 = (0,
|
|
9818
|
+
const path2 = (0, import_node_path13.join)(bin, file.name);
|
|
9641
9819
|
(0, import_node_fs14.writeFileSync)(path2, file.content);
|
|
9642
9820
|
if (file.name === "mmi-hook") (0, import_node_fs14.chmodSync)(path2, 493);
|
|
9643
9821
|
}
|
|
@@ -9648,8 +9826,8 @@ function canonicalCursorRemote(remote) {
|
|
|
9648
9826
|
}
|
|
9649
9827
|
async function installCursorPluginCheckout(env = process.env) {
|
|
9650
9828
|
const configRoot = surfaceConfigRoot("cursor", env);
|
|
9651
|
-
const pluginsRoot = (0,
|
|
9652
|
-
const target = (0,
|
|
9829
|
+
const pluginsRoot = (0, import_node_path13.join)(configRoot, "plugins");
|
|
9830
|
+
const target = (0, import_node_path13.join)(pluginsRoot, "local", "mmi");
|
|
9653
9831
|
const source = env.MMI_CURSOR_PLUGIN_SOURCE?.trim();
|
|
9654
9832
|
if ((0, import_node_fs14.existsSync)(target) && !source) {
|
|
9655
9833
|
try {
|
|
@@ -9661,12 +9839,12 @@ async function installCursorPluginCheckout(env = process.env) {
|
|
|
9661
9839
|
return { ok: false, detail: `refused to replace unmanaged Cursor plugin directory at ${target}` };
|
|
9662
9840
|
}
|
|
9663
9841
|
}
|
|
9664
|
-
(0, import_node_fs14.mkdirSync)((0,
|
|
9665
|
-
(0, import_node_fs14.mkdirSync)((0,
|
|
9666
|
-
(0, import_node_fs14.mkdirSync)((0,
|
|
9842
|
+
(0, import_node_fs14.mkdirSync)((0, import_node_path13.join)(pluginsRoot, "local"), { recursive: true });
|
|
9843
|
+
(0, import_node_fs14.mkdirSync)((0, import_node_path13.join)(pluginsRoot, "staging"), { recursive: true });
|
|
9844
|
+
(0, import_node_fs14.mkdirSync)((0, import_node_path13.join)(pluginsRoot, "quarantine"), { recursive: true });
|
|
9667
9845
|
const suffix = `${Date.now()}-${process.pid}`;
|
|
9668
|
-
const staged = (0,
|
|
9669
|
-
const quarantined = (0,
|
|
9846
|
+
const staged = (0, import_node_path13.join)(pluginsRoot, "staging", `mmi-${suffix}`);
|
|
9847
|
+
const quarantined = (0, import_node_path13.join)(pluginsRoot, "quarantine", `mmi-${suffix}`);
|
|
9670
9848
|
try {
|
|
9671
9849
|
if (source) {
|
|
9672
9850
|
(0, import_node_fs14.cpSync)(source, staged, {
|
|
@@ -9737,7 +9915,7 @@ async function runHealSteps(host, tableSteps, deps) {
|
|
|
9737
9915
|
const refSupported = needsRefProbe ? await marketplaceAddRefSupported(host) : true;
|
|
9738
9916
|
const { steps } = adaptHealStepsForRefSupport(tableSteps, refSupported);
|
|
9739
9917
|
if (deps.banner) log(deps.banner(refSupported));
|
|
9740
|
-
const pinsPath = (0,
|
|
9918
|
+
const pinsPath = (0, import_node_path13.join)((0, import_node_os5.homedir)(), ...KNOWN_MARKETPLACES_RELATIVE);
|
|
9741
9919
|
const pins = host === "claude" ? captureMarketplacePins(readKnownMarketplacesFile(pinsPath), [MMI_MARKETPLACE_NAME, JERV_MARKETPLACE_NAME]) : /* @__PURE__ */ new Map();
|
|
9742
9920
|
let failure;
|
|
9743
9921
|
try {
|
|
@@ -10048,7 +10226,7 @@ function describeUpdatePlan(hasBinary) {
|
|
|
10048
10226
|
|
|
10049
10227
|
// src/hook-activity.ts
|
|
10050
10228
|
var import_node_fs15 = require("node:fs");
|
|
10051
|
-
var
|
|
10229
|
+
var import_node_path14 = require("node:path");
|
|
10052
10230
|
var DEFAULT_SURFACE = "claude";
|
|
10053
10231
|
function activityLogPath(cwd) {
|
|
10054
10232
|
return repoRuntimeStatePath(cwd, "hooks", "activity.jsonl");
|
|
@@ -10061,7 +10239,7 @@ function appendHookActivity(cwd, entry) {
|
|
|
10061
10239
|
surface: DEFAULT_SURFACE,
|
|
10062
10240
|
...entry
|
|
10063
10241
|
};
|
|
10064
|
-
(0, import_node_fs15.mkdirSync)((0,
|
|
10242
|
+
(0, import_node_fs15.mkdirSync)((0, import_node_path14.dirname)(path2), { recursive: true });
|
|
10065
10243
|
(0, import_node_fs15.appendFileSync)(path2, `${JSON.stringify(line)}
|
|
10066
10244
|
`, "utf8");
|
|
10067
10245
|
} catch {
|
|
@@ -10070,12 +10248,12 @@ function appendHookActivity(cwd, entry) {
|
|
|
10070
10248
|
|
|
10071
10249
|
// src/worktree.ts
|
|
10072
10250
|
var import_node_fs16 = require("node:fs");
|
|
10073
|
-
var
|
|
10251
|
+
var import_node_path16 = require("node:path");
|
|
10074
10252
|
|
|
10075
10253
|
// src/file-lock.ts
|
|
10076
10254
|
var import_promises2 = require("node:fs/promises");
|
|
10077
|
-
var
|
|
10078
|
-
var sleep = (ms) => new Promise((
|
|
10255
|
+
var import_node_path15 = require("node:path");
|
|
10256
|
+
var sleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
10079
10257
|
var IMMEDIATE_RETRY_BUDGET = 3;
|
|
10080
10258
|
var FileLockBusyError = class extends Error {
|
|
10081
10259
|
lockPath;
|
|
@@ -10159,7 +10337,7 @@ async function releaseFileLock(lockPath, guard) {
|
|
|
10159
10337
|
}
|
|
10160
10338
|
async function withFileLock(lockPath, opts, fn) {
|
|
10161
10339
|
const resolved = resolveFileLockOpts(opts);
|
|
10162
|
-
await (0, import_promises2.mkdir)((0,
|
|
10340
|
+
await (0, import_promises2.mkdir)((0, import_node_path15.dirname)(lockPath), { recursive: true }).catch(() => void 0);
|
|
10163
10341
|
const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
|
|
10164
10342
|
try {
|
|
10165
10343
|
return await fn();
|
|
@@ -10214,7 +10392,7 @@ var realFsProbe = {
|
|
|
10214
10392
|
}
|
|
10215
10393
|
};
|
|
10216
10394
|
function declaredProvision(fs2, abs) {
|
|
10217
|
-
const raw = fs2.readFile?.((0,
|
|
10395
|
+
const raw = fs2.readFile?.((0, import_node_path16.join)(abs, PKG));
|
|
10218
10396
|
if (raw === void 0) return void 0;
|
|
10219
10397
|
try {
|
|
10220
10398
|
const scripts = JSON.parse(raw).scripts;
|
|
@@ -10226,13 +10404,13 @@ function declaredProvision(fs2, abs) {
|
|
|
10226
10404
|
}
|
|
10227
10405
|
function scanInstallDirs(root, fs2 = realFsProbe) {
|
|
10228
10406
|
const factsFor = (dir) => {
|
|
10229
|
-
const abs = dir ? (0,
|
|
10230
|
-
const match = LOCKFILE_INSTALLS.find((c) => fs2.isFile((0,
|
|
10231
|
-
const hasPackageJson = fs2.isFile((0,
|
|
10407
|
+
const abs = dir ? (0, import_node_path16.join)(root, dir) : root;
|
|
10408
|
+
const match = LOCKFILE_INSTALLS.find((c) => fs2.isFile((0, import_node_path16.join)(abs, c.lockfile)));
|
|
10409
|
+
const hasPackageJson = fs2.isFile((0, import_node_path16.join)(abs, PKG));
|
|
10232
10410
|
return {
|
|
10233
10411
|
dir,
|
|
10234
10412
|
hasPackageJson,
|
|
10235
|
-
hasNodeModules: fs2.isDir((0,
|
|
10413
|
+
hasNodeModules: fs2.isDir((0, import_node_path16.join)(abs, NODE_MODULES)),
|
|
10236
10414
|
install: match?.command,
|
|
10237
10415
|
provision: hasPackageJson ? declaredProvision(fs2, abs) : void 0
|
|
10238
10416
|
};
|
|
@@ -10248,7 +10426,7 @@ function npmInstallTargets(dirs) {
|
|
|
10248
10426
|
}));
|
|
10249
10427
|
}
|
|
10250
10428
|
function isLinkedWorktree(root, fs2 = realFsProbe) {
|
|
10251
|
-
return fs2.isFile((0,
|
|
10429
|
+
return fs2.isFile((0, import_node_path16.join)(root, ".git"));
|
|
10252
10430
|
}
|
|
10253
10431
|
function worktreeAutoProvisionBanner(root, fs2 = realFsProbe) {
|
|
10254
10432
|
if (!isLinkedWorktree(root, fs2)) return null;
|
|
@@ -10258,7 +10436,7 @@ function worktreeAutoProvisionBanner(root, fs2 = realFsProbe) {
|
|
|
10258
10436
|
return `[worktree] provisioning tooling in the background (deps in ${where} + local config) \u2014 \`mmi-cli worktree setup\` to redo`;
|
|
10259
10437
|
}
|
|
10260
10438
|
function defaultCopyFile(from, to) {
|
|
10261
|
-
(0, import_node_fs16.mkdirSync)((0,
|
|
10439
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path16.dirname)(to), { recursive: true });
|
|
10262
10440
|
(0, import_node_fs16.copyFileSync)(from, to);
|
|
10263
10441
|
}
|
|
10264
10442
|
async function runDeclaredProvision(target, cwd, runInstall) {
|
|
@@ -10289,7 +10467,7 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
10289
10467
|
const targets = npmInstallTargets(allDirs);
|
|
10290
10468
|
if (deps.validateInstall) {
|
|
10291
10469
|
for (const dir of allDirs.filter((d) => d.hasPackageJson && (d.provision ?? d.install) && d.hasNodeModules)) {
|
|
10292
|
-
const cwd = dir.dir ? (0,
|
|
10470
|
+
const cwd = dir.dir ? (0, import_node_path16.join)(worktreeRoot, dir.dir) : worktreeRoot;
|
|
10293
10471
|
if (!await deps.validateInstall(cwd)) {
|
|
10294
10472
|
targets.push({
|
|
10295
10473
|
dir: dir.dir,
|
|
@@ -10303,7 +10481,7 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
10303
10481
|
const skippedInstall = allDirs.filter((d) => d.hasPackageJson && d.hasNodeModules && !targetDirs.has(d.dir)).map((d) => d.dir);
|
|
10304
10482
|
const installed = [];
|
|
10305
10483
|
for (const target of targets) {
|
|
10306
|
-
const cwd = target.dir ? (0,
|
|
10484
|
+
const cwd = target.dir ? (0, import_node_path16.join)(worktreeRoot, target.dir) : worktreeRoot;
|
|
10307
10485
|
log(`installing deps: ${target.command} in ${target.dir || "."}`);
|
|
10308
10486
|
if (target.declared) await runDeclaredProvision(target, cwd, deps.runInstall);
|
|
10309
10487
|
else await deps.runInstall(target.command, cwd);
|
|
@@ -10313,7 +10491,7 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
10313
10491
|
const copySkipped = [];
|
|
10314
10492
|
const primary = await deps.primaryCheckout();
|
|
10315
10493
|
for (const rel of LOCAL_ONLY_FILES) {
|
|
10316
|
-
const dest = (0,
|
|
10494
|
+
const dest = (0, import_node_path16.join)(worktreeRoot, rel);
|
|
10317
10495
|
if (fs2.isFile(dest)) {
|
|
10318
10496
|
copySkipped.push({ file: rel, reason: "already-present" });
|
|
10319
10497
|
continue;
|
|
@@ -10322,11 +10500,11 @@ async function provisionWorktree(worktreeRoot, deps) {
|
|
|
10322
10500
|
copySkipped.push({ file: rel, reason: "no-primary" });
|
|
10323
10501
|
continue;
|
|
10324
10502
|
}
|
|
10325
|
-
if (!fs2.isFile((0,
|
|
10503
|
+
if (!fs2.isFile((0, import_node_path16.join)(primary, rel))) {
|
|
10326
10504
|
copySkipped.push({ file: rel, reason: "absent-in-primary" });
|
|
10327
10505
|
continue;
|
|
10328
10506
|
}
|
|
10329
|
-
copyFile((0,
|
|
10507
|
+
copyFile((0, import_node_path16.join)(primary, rel), dest);
|
|
10330
10508
|
copied.push(rel);
|
|
10331
10509
|
log(`copied local config: ${rel}`);
|
|
10332
10510
|
}
|
|
@@ -10340,12 +10518,12 @@ function capWorktreeDirName(name, max = 40) {
|
|
|
10340
10518
|
}
|
|
10341
10519
|
function defaultWorktreePath(repoRoot2, branch) {
|
|
10342
10520
|
const safe = capWorktreeDirName(branch.replace(/[/\\]+/g, "-"));
|
|
10343
|
-
return (0,
|
|
10521
|
+
return (0, import_node_path16.join)((0, import_node_path16.dirname)(repoRoot2), "mmi-worktrees", (0, import_node_path16.basename)(repoRoot2), safe);
|
|
10344
10522
|
}
|
|
10345
10523
|
async function primaryCheckoutRootOf(git2) {
|
|
10346
10524
|
try {
|
|
10347
10525
|
const out = (await git2(["rev-parse", "--path-format=absolute", "--git-common-dir"])).trim();
|
|
10348
|
-
return out ? (0,
|
|
10526
|
+
return out ? (0, import_node_path16.dirname)(out) : void 0;
|
|
10349
10527
|
} catch {
|
|
10350
10528
|
return void 0;
|
|
10351
10529
|
}
|
|
@@ -10488,7 +10666,7 @@ function commandLadderHint() {
|
|
|
10488
10666
|
}
|
|
10489
10667
|
|
|
10490
10668
|
// src/index.ts
|
|
10491
|
-
var
|
|
10669
|
+
var import_node_path46 = require("node:path");
|
|
10492
10670
|
|
|
10493
10671
|
// src/merge-ci-policy.ts
|
|
10494
10672
|
function resolveMergeCiPolicy(input) {
|
|
@@ -11162,12 +11340,12 @@ function planManagedGitignore(current) {
|
|
|
11162
11340
|
|
|
11163
11341
|
// src/docs-index-command.ts
|
|
11164
11342
|
var import_node_fs18 = require("node:fs");
|
|
11165
|
-
var
|
|
11343
|
+
var import_node_path18 = require("node:path");
|
|
11166
11344
|
|
|
11167
11345
|
// src/doc-refs-core.ts
|
|
11168
11346
|
var import_node_child_process7 = require("node:child_process");
|
|
11169
11347
|
var import_node_fs17 = require("node:fs");
|
|
11170
|
-
var
|
|
11348
|
+
var import_node_path17 = require("node:path");
|
|
11171
11349
|
var PIN_RE = /<!--\s*pinned by\s+([^:]+?)\s*:\s*"([^"]+)"[^"]*-->/;
|
|
11172
11350
|
var PIN_MENTION_RE = /<!--\s*pinned by\b/;
|
|
11173
11351
|
var FWD_RE = /<!--\s*forward-ref:\s*(\S+?)\s*-->/;
|
|
@@ -11226,7 +11404,7 @@ function checkPins(root, readFile9, docs2) {
|
|
|
11226
11404
|
findings.push({ kind: "malformed-pin", doc, line: pin.line, detail: pin.text });
|
|
11227
11405
|
continue;
|
|
11228
11406
|
}
|
|
11229
|
-
const source = readFile9((0,
|
|
11407
|
+
const source = readFile9((0, import_node_path17.join)(root, pin.file));
|
|
11230
11408
|
if (source == null) {
|
|
11231
11409
|
findings.push({ kind: "missing-test", doc, line: pin.line, detail: pin.file });
|
|
11232
11410
|
continue;
|
|
@@ -11288,11 +11466,11 @@ function checkRefs(root, deps, docs2) {
|
|
|
11288
11466
|
for (const { ref } of extractRefs(markdown)) allFirstSegments.add(refFirstSegment(ref));
|
|
11289
11467
|
}
|
|
11290
11468
|
const tracked = allFirstSegments.size ? deps.trackedFirstSegments?.([...allFirstSegments]) ?? null : null;
|
|
11291
|
-
const firstVerifiable = (first) => tracked ? tracked.has(first) : exists((0,
|
|
11469
|
+
const firstVerifiable = (first) => tracked ? tracked.has(first) : exists((0, import_node_path17.join)(root, first));
|
|
11292
11470
|
const candidates = [];
|
|
11293
11471
|
const direct = [];
|
|
11294
11472
|
for (const [doc, markdown] of Object.entries(docs2)) {
|
|
11295
|
-
const docDir =
|
|
11473
|
+
const docDir = import_node_path17.posix.dirname(doc);
|
|
11296
11474
|
const base = docDir === "." ? "" : docDir;
|
|
11297
11475
|
const covered = /* @__PURE__ */ new Set();
|
|
11298
11476
|
const markers = [];
|
|
@@ -11301,21 +11479,21 @@ function checkRefs(root, deps, docs2) {
|
|
|
11301
11479
|
direct.push({ kind: "malformed-forward-ref", doc, line: fwd.line, detail: fwd.text });
|
|
11302
11480
|
continue;
|
|
11303
11481
|
}
|
|
11304
|
-
const docRel =
|
|
11305
|
-
const rootRel =
|
|
11482
|
+
const docRel = import_node_path17.posix.normalize(import_node_path17.posix.join(base, fwd.target));
|
|
11483
|
+
const rootRel = import_node_path17.posix.normalize(fwd.target.replace(/^\/+/, ""));
|
|
11306
11484
|
markers.push({ target: fwd.target, line: fwd.line, docRel, rootRel });
|
|
11307
11485
|
covered.add(docRel);
|
|
11308
11486
|
covered.add(rootRel);
|
|
11309
11487
|
}
|
|
11310
11488
|
const links = extractLinks(markdown).map(({ target, line }) => {
|
|
11311
|
-
const resolved =
|
|
11312
|
-
return { target, line, resolved, missing: !exists((0,
|
|
11489
|
+
const resolved = import_node_path17.posix.normalize(import_node_path17.posix.join(base, target));
|
|
11490
|
+
return { target, line, resolved, missing: !exists((0, import_node_path17.join)(root, resolved)) };
|
|
11313
11491
|
});
|
|
11314
11492
|
for (const marker of markers) {
|
|
11315
11493
|
const coversMissing = links.some(
|
|
11316
11494
|
(l) => l.missing && (l.resolved === marker.docRel || l.resolved === marker.rootRel)
|
|
11317
11495
|
);
|
|
11318
|
-
if (!coversMissing && (exists((0,
|
|
11496
|
+
if (!coversMissing && (exists((0, import_node_path17.join)(root, marker.docRel)) || exists((0, import_node_path17.join)(root, marker.rootRel)))) {
|
|
11319
11497
|
direct.push({
|
|
11320
11498
|
kind: "stale-forward-ref",
|
|
11321
11499
|
doc,
|
|
@@ -11326,7 +11504,7 @@ function checkRefs(root, deps, docs2) {
|
|
|
11326
11504
|
}
|
|
11327
11505
|
for (const { ref, line } of extractRefs(markdown)) {
|
|
11328
11506
|
if (!firstVerifiable(refFirstSegment(ref))) continue;
|
|
11329
|
-
if (!exists((0,
|
|
11507
|
+
if (!exists((0, import_node_path17.join)(root, ref))) candidates.push({ kind: "missing-path", doc, line, detail: ref });
|
|
11330
11508
|
}
|
|
11331
11509
|
for (const { target, line, resolved, missing } of links) {
|
|
11332
11510
|
if (resolved.startsWith("..")) {
|
|
@@ -11378,18 +11556,18 @@ function readFileOrNull(path2) {
|
|
|
11378
11556
|
}
|
|
11379
11557
|
function walk(dir, root, out) {
|
|
11380
11558
|
for (const entry of (0, import_node_fs17.readdirSync)(dir)) {
|
|
11381
|
-
const full = (0,
|
|
11559
|
+
const full = (0, import_node_path17.join)(dir, entry);
|
|
11382
11560
|
if ((0, import_node_fs17.statSync)(full).isDirectory()) walk(full, root, out);
|
|
11383
11561
|
else if (entry.endsWith(".md")) out.push(full.slice(root.length + 1).replaceAll("\\", "/"));
|
|
11384
11562
|
}
|
|
11385
11563
|
return out;
|
|
11386
11564
|
}
|
|
11387
11565
|
function defaultListDocs(root) {
|
|
11388
|
-
const docsDir = (0,
|
|
11566
|
+
const docsDir = (0, import_node_path17.join)(root, "docs");
|
|
11389
11567
|
const docs2 = ((0, import_node_fs17.existsSync)(docsDir) ? walk(docsDir, root, []) : []).filter(
|
|
11390
11568
|
(rel) => !SKIP_WALK.some((skip) => rel.startsWith(skip))
|
|
11391
11569
|
);
|
|
11392
|
-
return [...ROOT_DOCS.filter((rel) => (0, import_node_fs17.existsSync)((0,
|
|
11570
|
+
return [...ROOT_DOCS.filter((rel) => (0, import_node_fs17.existsSync)((0, import_node_path17.join)(root, rel))), ...docs2];
|
|
11393
11571
|
}
|
|
11394
11572
|
var CHECK_IGNORE_MAX_BUFFER = 32 * 1024 * 1024;
|
|
11395
11573
|
function defaultIsIgnored(root, relPaths, exec = import_node_child_process7.execFileSync) {
|
|
@@ -11449,7 +11627,7 @@ function runDocRefs(root, deps = {}) {
|
|
|
11449
11627
|
const walked = listDocs(root);
|
|
11450
11628
|
const ignoredDocs = walked.length ? isIgnored(walked) : /* @__PURE__ */ new Set();
|
|
11451
11629
|
const docs2 = Object.fromEntries(
|
|
11452
|
-
walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile9((0,
|
|
11630
|
+
walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile9((0, import_node_path17.join)(root, rel))]).filter(([, body]) => body != null)
|
|
11453
11631
|
);
|
|
11454
11632
|
const refResult = checkRefs(root, { exists, isIgnored, trackedFirstSegments }, docs2);
|
|
11455
11633
|
const findings = [
|
|
@@ -11557,19 +11735,19 @@ function walkMarkdown(dir) {
|
|
|
11557
11735
|
while (stack.length) {
|
|
11558
11736
|
const current = stack.pop();
|
|
11559
11737
|
for (const entry of (0, import_node_fs18.readdirSync)(current, { withFileTypes: true })) {
|
|
11560
|
-
const full = (0,
|
|
11738
|
+
const full = (0, import_node_path18.join)(current, entry.name);
|
|
11561
11739
|
if (entry.isDirectory()) {
|
|
11562
11740
|
stack.push(full);
|
|
11563
11741
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
11564
|
-
out.push((0,
|
|
11742
|
+
out.push((0, import_node_path18.relative)(dir, full).split(import_node_path18.sep).join("/"));
|
|
11565
11743
|
}
|
|
11566
11744
|
}
|
|
11567
11745
|
}
|
|
11568
11746
|
return out;
|
|
11569
11747
|
}
|
|
11570
11748
|
function createDocsIndexDeps(repoRoot2) {
|
|
11571
|
-
const docsDir = (0,
|
|
11572
|
-
const indexPath = (0,
|
|
11749
|
+
const docsDir = (0, import_node_path18.join)(repoRoot2, "docs");
|
|
11750
|
+
const indexPath = (0, import_node_path18.join)(repoRoot2, DOCS_INDEX_PATH);
|
|
11573
11751
|
return {
|
|
11574
11752
|
listDocs: () => {
|
|
11575
11753
|
if (!(0, import_node_fs18.existsSync)(docsDir)) return [];
|
|
@@ -11578,7 +11756,7 @@ function createDocsIndexDeps(repoRoot2) {
|
|
|
11578
11756
|
const ignored = defaultIsIgnored(repoRoot2, walked.map((rel) => `docs/${rel}`));
|
|
11579
11757
|
return walked.filter((rel) => !ignored.has(`docs/${rel}`)).sort();
|
|
11580
11758
|
},
|
|
11581
|
-
readDoc: (relPath) => (0, import_node_fs18.readFileSync)((0,
|
|
11759
|
+
readDoc: (relPath) => (0, import_node_fs18.readFileSync)((0, import_node_path18.join)(docsDir, relPath), "utf8"),
|
|
11582
11760
|
readIndex: () => (0, import_node_fs18.existsSync)(indexPath) ? (0, import_node_fs18.readFileSync)(indexPath, "utf8") : null,
|
|
11583
11761
|
writeIndex: (content) => (0, import_node_fs18.writeFileSync)(indexPath, content, "utf8")
|
|
11584
11762
|
};
|
|
@@ -12338,6 +12516,12 @@ function extractControlOutputFromLog(log) {
|
|
|
12338
12516
|
const slice = end < 0 ? lines.slice(start + 1) : lines.slice(start + 1, end);
|
|
12339
12517
|
return slice.map(logLinePayload).join("\n").trim();
|
|
12340
12518
|
}
|
|
12519
|
+
function clampTaskOutput(output, maxChars = 16384) {
|
|
12520
|
+
if (output.length <= maxChars) return output;
|
|
12521
|
+
const kept = output.slice(output.length - maxChars);
|
|
12522
|
+
return `[task output truncated by mmi-cli \u2014 last ${maxChars} of ${output.length} chars]
|
|
12523
|
+
${kept}`;
|
|
12524
|
+
}
|
|
12341
12525
|
function parseStatusSnippet(stdout) {
|
|
12342
12526
|
const t = stdout.toLowerCase();
|
|
12343
12527
|
const m = t.match(/service[:=]\s*(running|stopped|missing|up|down|absent)/);
|
|
@@ -12367,7 +12551,7 @@ function parseVerifyBroker(stdout) {
|
|
|
12367
12551
|
// src/train-apply.ts
|
|
12368
12552
|
var import_node_fs21 = require("node:fs");
|
|
12369
12553
|
var import_promises4 = require("node:fs/promises");
|
|
12370
|
-
var
|
|
12554
|
+
var import_node_path21 = require("node:path");
|
|
12371
12555
|
|
|
12372
12556
|
// src/bootstrap-org-ruleset.ts
|
|
12373
12557
|
var ORG_NO_AGENT_FILES_RULESET_NAME = "mmi-no-agent-files-org";
|
|
@@ -12840,7 +13024,7 @@ function renderAccessReport(report) {
|
|
|
12840
13024
|
|
|
12841
13025
|
// src/cli-doctor-shared.ts
|
|
12842
13026
|
var import_node_fs19 = require("node:fs");
|
|
12843
|
-
var
|
|
13027
|
+
var import_node_path20 = require("node:path");
|
|
12844
13028
|
var import_node_fs20 = require("node:fs");
|
|
12845
13029
|
|
|
12846
13030
|
// ../infra/registry-endpoints.mjs
|
|
@@ -12970,9 +13154,9 @@ function extractWorkflowCrons(yamlText) {
|
|
|
12970
13154
|
function workflowEntry(repo, workflowPath, yamlText) {
|
|
12971
13155
|
const crons = extractWorkflowCrons(yamlText);
|
|
12972
13156
|
if (!crons.length) return null;
|
|
12973
|
-
const
|
|
13157
|
+
const basename8 = workflowPath.split("/").pop() ?? workflowPath;
|
|
12974
13158
|
return {
|
|
12975
|
-
name: `${repo}/${
|
|
13159
|
+
name: `${repo}/${basename8.replace(/\.ya?ml$/, "")}`,
|
|
12976
13160
|
cadence: crons.join(" + "),
|
|
12977
13161
|
executor: "github-actions",
|
|
12978
13162
|
llm: llmFromHeader(yamlText),
|
|
@@ -13382,8 +13566,8 @@ function scheduleRecordFromWorkflow(repo, workflowPath, yamlText) {
|
|
|
13382
13566
|
const crons = extractWorkflowCrons(yamlText);
|
|
13383
13567
|
const header = parseScheduleHeader(yamlText);
|
|
13384
13568
|
if (!crons.length && !header.schedule) return null;
|
|
13385
|
-
const
|
|
13386
|
-
const expectedId = `${repo}/${
|
|
13569
|
+
const basename8 = workflowPath.split("/").pop() ?? workflowPath;
|
|
13570
|
+
const expectedId = `${repo}/${basename8.replace(/\.ya?ml$/, "")}`;
|
|
13387
13571
|
if (isHarbourUnmanaged(yamlText)) return null;
|
|
13388
13572
|
const missing = SCHEDULE_HEADER_FIELDS.filter((f) => !header[f]);
|
|
13389
13573
|
if (missing.length) {
|
|
@@ -13628,6 +13812,9 @@ async function postSchedulesUnpark(id, deps) {
|
|
|
13628
13812
|
async function tenantControl(payload, deps) {
|
|
13629
13813
|
return postJson("/tenant-control", payload, deps, "POST", { noRetry: true });
|
|
13630
13814
|
}
|
|
13815
|
+
async function tenantArtifactUpload(payload, deps) {
|
|
13816
|
+
return postJson("/tenant-control", { ...payload, action: "artifact-upload" }, deps, "POST", { noRetry: true });
|
|
13817
|
+
}
|
|
13631
13818
|
async function tenantReconcile(payload, deps) {
|
|
13632
13819
|
return postJson("/tenant-reconcile", payload, deps, "POST", { noRetry: true });
|
|
13633
13820
|
}
|
|
@@ -13755,7 +13942,7 @@ var import_node_os7 = require("node:os");
|
|
|
13755
13942
|
// src/gh-create.ts
|
|
13756
13943
|
var import_promises3 = require("node:fs/promises");
|
|
13757
13944
|
var import_node_os6 = require("node:os");
|
|
13758
|
-
var
|
|
13945
|
+
var import_node_path19 = require("node:path");
|
|
13759
13946
|
var import_node_crypto3 = require("node:crypto");
|
|
13760
13947
|
|
|
13761
13948
|
// src/board-priority.ts
|
|
@@ -13838,8 +14025,8 @@ async function bodyArgsViaFile(args, deps = {}) {
|
|
|
13838
14025
|
const remove2 = deps.remove ?? import_promises3.unlink;
|
|
13839
14026
|
const ensureDir = deps.ensureDir ?? import_promises3.mkdir;
|
|
13840
14027
|
const dir = deps.dir ?? (0, import_node_os6.tmpdir)();
|
|
13841
|
-
const file = (0,
|
|
13842
|
-
await ensureDir((0,
|
|
14028
|
+
const file = (0, import_node_path19.join)(dir, `mmi-gh-body-${process.pid}-${(0, import_node_crypto3.randomBytes)(4).toString("hex")}.md`);
|
|
14029
|
+
await ensureDir((0, import_node_path19.dirname)(file), { recursive: true }).catch(() => {
|
|
13843
14030
|
});
|
|
13844
14031
|
await write(file, args[i + 1], "utf8");
|
|
13845
14032
|
return {
|
|
@@ -13930,7 +14117,7 @@ function upstreamFaultMessage(verb, stderr) {
|
|
|
13930
14117
|
}
|
|
13931
14118
|
async function ghCreate(args, deps = {}) {
|
|
13932
14119
|
const exec = deps.exec ?? execFileP2;
|
|
13933
|
-
const sleep3 = deps.sleep ?? ((ms) => new Promise((
|
|
14120
|
+
const sleep3 = deps.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
|
|
13934
14121
|
const swapped = await bodyArgsViaFile(args);
|
|
13935
14122
|
try {
|
|
13936
14123
|
for (let attempt = 1; attempt <= GH_CREATE_UPSTREAM_RETRIES; attempt++) {
|
|
@@ -15136,8 +15323,8 @@ var PRE_SPAWN_DRAIN_TIMEOUT_MS = 2e3;
|
|
|
15136
15323
|
async function drainHttpPoolBeforeSpawn() {
|
|
15137
15324
|
const drained = await Promise.race([
|
|
15138
15325
|
closeHttpPool().then(() => true),
|
|
15139
|
-
new Promise((
|
|
15140
|
-
setTimeout(() =>
|
|
15326
|
+
new Promise((resolve6) => {
|
|
15327
|
+
setTimeout(() => resolve6(false), PRE_SPAWN_DRAIN_TIMEOUT_MS).unref?.();
|
|
15141
15328
|
})
|
|
15142
15329
|
]);
|
|
15143
15330
|
if (!drained) destroyHttpPool();
|
|
@@ -15382,7 +15569,7 @@ async function localBranchHeads() {
|
|
|
15382
15569
|
}
|
|
15383
15570
|
async function currentRepoWorktreeGitRoot(repoRoot2) {
|
|
15384
15571
|
const gitCommonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
15385
|
-
return gitCommonDir ? (0,
|
|
15572
|
+
return gitCommonDir ? (0, import_node_path20.resolve)(repoRoot2, gitCommonDir, "worktrees") : "";
|
|
15386
15573
|
}
|
|
15387
15574
|
async function worktreeBranches() {
|
|
15388
15575
|
const { stdout } = await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS });
|
|
@@ -15402,7 +15589,7 @@ function resolveGitdirForWorktreeFile(worktreePath, content) {
|
|
|
15402
15589
|
const match = /^gitdir:\s*(.+)\s*$/im.exec(content);
|
|
15403
15590
|
if (!match?.[1]) return void 0;
|
|
15404
15591
|
const raw = match[1].trim();
|
|
15405
|
-
return (0,
|
|
15592
|
+
return (0, import_node_path20.isAbsolute)(raw) ? raw : (0, import_node_path20.resolve)(worktreePath, raw);
|
|
15406
15593
|
}
|
|
15407
15594
|
function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
15408
15595
|
if (!worktreeGitRoot) return false;
|
|
@@ -15411,9 +15598,9 @@ function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
|
|
|
15411
15598
|
for (const ent of entries) {
|
|
15412
15599
|
if (!ent.isDirectory()) continue;
|
|
15413
15600
|
try {
|
|
15414
|
-
const gitdirPath = (0, import_node_fs19.readFileSync)((0,
|
|
15415
|
-
const resolvedGitdir = (0,
|
|
15416
|
-
if (sameWorktreeMetadataPath((0,
|
|
15601
|
+
const gitdirPath = (0, import_node_fs19.readFileSync)((0, import_node_path20.join)(worktreeGitRoot, ent.name, "gitdir"), "utf8").trim();
|
|
15602
|
+
const resolvedGitdir = (0, import_node_path20.isAbsolute)(gitdirPath) ? gitdirPath : (0, import_node_path20.resolve)(worktreeGitRoot, ent.name, gitdirPath);
|
|
15603
|
+
if (sameWorktreeMetadataPath((0, import_node_path20.dirname)(resolvedGitdir), worktreePath)) return true;
|
|
15417
15604
|
} catch {
|
|
15418
15605
|
}
|
|
15419
15606
|
}
|
|
@@ -15432,7 +15619,7 @@ function pathExistsKnown(path2) {
|
|
|
15432
15619
|
}
|
|
15433
15620
|
}
|
|
15434
15621
|
function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
|
|
15435
|
-
const gitPath = (0,
|
|
15622
|
+
const gitPath = (0, import_node_path20.join)(path2, ".git");
|
|
15436
15623
|
let st;
|
|
15437
15624
|
try {
|
|
15438
15625
|
st = (0, import_node_fs20.lstatSync)(gitPath);
|
|
@@ -15486,24 +15673,25 @@ async function preservedBranches() {
|
|
|
15486
15673
|
async function siblingWorktreeDirs(explicitRoot) {
|
|
15487
15674
|
const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
15488
15675
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
15489
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
15676
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path20.dirname)((0, import_node_path20.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
15490
15677
|
try {
|
|
15491
15678
|
const dirs = explicitRoot ? listDirsIn(resolveExplicitScanRoot(explicitRoot, primaryRepoRoot)) : worktreeScanDirs(siblingMmiWorktreesRoot(primaryRepoRoot), primaryRepoRoot, listDirsIn, isRepoCheckoutDir);
|
|
15492
15679
|
const agentDirs2 = listDirsIn(agentWorktreesRoot(primaryRepoRoot));
|
|
15493
|
-
|
|
15680
|
+
const repoLocalWorktrees = listDirsIn((0, import_node_path20.join)(primaryRepoRoot, ".worktrees"));
|
|
15681
|
+
return [...dirs, ...agentDirs2, ...repoLocalWorktrees].map((dir) => inspectSiblingWorktreeDir(dir, worktreeGitRoot)).filter((entry) => Boolean(entry));
|
|
15494
15682
|
} catch {
|
|
15495
15683
|
return [];
|
|
15496
15684
|
}
|
|
15497
15685
|
}
|
|
15498
15686
|
function listDirsIn(dir) {
|
|
15499
15687
|
try {
|
|
15500
|
-
return (0, import_node_fs20.readdirSync)(dir, { withFileTypes: true }).filter((ent) => ent.isDirectory()).map((ent) => (0,
|
|
15688
|
+
return (0, import_node_fs20.readdirSync)(dir, { withFileTypes: true }).filter((ent) => ent.isDirectory()).map((ent) => (0, import_node_path20.join)(dir, ent.name));
|
|
15501
15689
|
} catch {
|
|
15502
15690
|
return [];
|
|
15503
15691
|
}
|
|
15504
15692
|
}
|
|
15505
15693
|
function isRepoCheckoutDir(dir) {
|
|
15506
|
-
return (0, import_node_fs20.existsSync)((0,
|
|
15694
|
+
return (0, import_node_fs20.existsSync)((0, import_node_path20.join)(dir, ".git"));
|
|
15507
15695
|
}
|
|
15508
15696
|
function resolveExplicitScanRoot(explicitRoot, repoRoot2) {
|
|
15509
15697
|
let rootDirs;
|
|
@@ -15515,7 +15703,7 @@ function resolveExplicitScanRoot(explicitRoot, repoRoot2) {
|
|
|
15515
15703
|
return explicitRepoWorktreesRoot(explicitRoot, repoRoot2, rootDirs);
|
|
15516
15704
|
}
|
|
15517
15705
|
async function gcPlan(remote, limit, opts = {}) {
|
|
15518
|
-
const [branches, heads, current, stale, worktrees, siblingDirs, preserved, mergedIntoBase] = await Promise.all([
|
|
15706
|
+
const [branches, heads, current, stale, worktrees, siblingDirs, preserved, mergedIntoBase, originListed] = await Promise.all([
|
|
15519
15707
|
gitOut(["branch", "--format=%(refname:short)"]),
|
|
15520
15708
|
localBranchHeads(),
|
|
15521
15709
|
gitOut(["rev-parse", "--abbrev-ref", "HEAD"]),
|
|
@@ -15525,11 +15713,18 @@ async function gcPlan(remote, limit, opts = {}) {
|
|
|
15525
15713
|
worktreeBranches(),
|
|
15526
15714
|
siblingWorktreeDirs(opts.root),
|
|
15527
15715
|
preservedBranches(),
|
|
15528
|
-
branchesMergedIntoBase(remote)
|
|
15716
|
+
branchesMergedIntoBase(remote),
|
|
15717
|
+
gitOut(["branch", "-r", "--format=%(refname:short)"]).catch(() => "")
|
|
15529
15718
|
]);
|
|
15530
15719
|
const localBranches = branches.split(/\r?\n/).map((b) => b.trim()).filter(Boolean);
|
|
15720
|
+
const originBranches = originListed.split(/\r?\n/).map((b) => b.trim()).filter((b) => b.startsWith(`${remote}/`) && b !== `${remote}/HEAD`);
|
|
15721
|
+
const originNames = originBranches.map((b) => b.slice(remote.length + 1));
|
|
15531
15722
|
const { prs, failures } = await resolveBranchPrs(
|
|
15532
|
-
[
|
|
15723
|
+
[.../* @__PURE__ */ new Set([
|
|
15724
|
+
...localBranches,
|
|
15725
|
+
...stale.map((ref) => branchForTrackingRef(ref, remote)).filter((b) => Boolean(b)),
|
|
15726
|
+
...originNames
|
|
15727
|
+
])].filter((b) => !isProtectedBranch(b)),
|
|
15533
15728
|
limit
|
|
15534
15729
|
);
|
|
15535
15730
|
return buildGcPlan({
|
|
@@ -15543,7 +15738,9 @@ async function gcPlan(remote, limit, opts = {}) {
|
|
|
15543
15738
|
worktrees,
|
|
15544
15739
|
remote,
|
|
15545
15740
|
preservedBranches: preserved,
|
|
15546
|
-
mergedIntoBase
|
|
15741
|
+
mergedIntoBase,
|
|
15742
|
+
originBranches,
|
|
15743
|
+
trainOnly: opts.trainOnly
|
|
15547
15744
|
});
|
|
15548
15745
|
}
|
|
15549
15746
|
async function branchesMergedIntoBase(remote) {
|
|
@@ -15840,10 +16037,10 @@ var rollout_plan_default = {
|
|
|
15840
16037
|
note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
|
|
15841
16038
|
},
|
|
15842
16039
|
baseline: {
|
|
15843
|
-
version: "3.
|
|
15844
|
-
tag: "v3.
|
|
15845
|
-
commit: "
|
|
15846
|
-
npm: "@mutmutco/cli@3.
|
|
16040
|
+
version: "3.128.0",
|
|
16041
|
+
tag: "v3.128.0",
|
|
16042
|
+
commit: "95241fa22be9",
|
|
16043
|
+
npm: "@mutmutco/cli@3.128.0"
|
|
15847
16044
|
},
|
|
15848
16045
|
exitCriterion: "fleet-n-of-n",
|
|
15849
16046
|
hubOnlyShortcut: "forbidden",
|
|
@@ -15860,14 +16057,14 @@ var rollout_plan_default = {
|
|
|
15860
16057
|
repo: "mutmutco/mmi-hub",
|
|
15861
16058
|
role: "canary",
|
|
15862
16059
|
schedule: "train",
|
|
15863
|
-
v3Target: "v3.
|
|
16060
|
+
v3Target: "v3.128.0"
|
|
15864
16061
|
}
|
|
15865
16062
|
],
|
|
15866
16063
|
rollbackTrigger: "Any red inside the post-cut soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a v3 client refused while the compat window must still admit it (SUPPORTED_MINOR_WINDOW=2, MIN_CLIENT_VERSION 0.0.0 \u2014 D6a), or npm consumer install/doctor failure on the v4 dist.",
|
|
15867
16064
|
rollback: {
|
|
15868
16065
|
independent: true,
|
|
15869
|
-
mechanism: "npm dist-tag latest -> 3.
|
|
15870
|
-
v3Target: "v3.
|
|
16066
|
+
mechanism: "npm dist-tag latest -> 3.128.0 and redeploy the Hub Lambda from tag v3.128.0 (95241fa22be9); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
16067
|
+
v3Target: "v3.128.0 (@mutmutco/cli@3.128.0, tag commit 95241fa22be9 \u2014 the preserved latest-v3 distribution, D6b)"
|
|
15871
16068
|
}
|
|
15872
16069
|
},
|
|
15873
16070
|
{
|
|
@@ -18116,7 +18313,7 @@ function publishVisibilityFor(surfaceId) {
|
|
|
18116
18313
|
}
|
|
18117
18314
|
function npmPackArtifactName(packagePath) {
|
|
18118
18315
|
try {
|
|
18119
|
-
const pkg = JSON.parse((0, import_node_fs21.readFileSync)((0,
|
|
18316
|
+
const pkg = JSON.parse((0, import_node_fs21.readFileSync)((0, import_node_path21.join)(packagePath, "package.json"), "utf8"));
|
|
18120
18317
|
return pkg.name || void 0;
|
|
18121
18318
|
} catch {
|
|
18122
18319
|
return void 0;
|
|
@@ -18274,7 +18471,7 @@ async function runMergeTreePreflight(deps, ours, theirs) {
|
|
|
18274
18471
|
async function predictMergeConflicts(deps, ours, theirs) {
|
|
18275
18472
|
return runMergeTreePreflight(deps, ours, theirs);
|
|
18276
18473
|
}
|
|
18277
|
-
async function mergeWithToleratedResolution(deps, sourceRef, label,
|
|
18474
|
+
async function mergeWithToleratedResolution(deps, sourceRef, label, resolve6, extraTolerated = []) {
|
|
18278
18475
|
try {
|
|
18279
18476
|
await deps.run("git", ["merge", sourceRef, "--no-edit"]);
|
|
18280
18477
|
return;
|
|
@@ -18288,7 +18485,7 @@ async function mergeWithToleratedResolution(deps, sourceRef, label, resolve5, ex
|
|
|
18288
18485
|
unmerged.length === 0 ? `${label} merge failed without conflicted paths \u2014 merge aborted; inspect the repo state and rerun` : `${label} merge conflicts on untolerated path(s): ${blocking.join(", ")} \u2014 merge aborted (the train is misaligned; reconcile the branches via an approved alignment PR, then rerun)`
|
|
18289
18486
|
);
|
|
18290
18487
|
}
|
|
18291
|
-
await deps.run("git", ["checkout", `--${
|
|
18488
|
+
await deps.run("git", ["checkout", `--${resolve6}`, "--", ...unmerged]);
|
|
18292
18489
|
await deps.run("git", ["add", "--", ...unmerged]);
|
|
18293
18490
|
await deps.run("git", ["commit", "--no-edit"]);
|
|
18294
18491
|
}
|
|
@@ -18499,7 +18696,7 @@ var CORRELATE_SKEW_SLACK_MS = 1e4;
|
|
|
18499
18696
|
var CORRELATE_PAGE_LIMIT = 50;
|
|
18500
18697
|
var RUN_CONFIRM_ATTEMPTS = 3;
|
|
18501
18698
|
var RUN_CONFIRM_DELAY_MS = 1e3;
|
|
18502
|
-
var defaultSleep2 = (ms) => new Promise((
|
|
18699
|
+
var defaultSleep2 = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
18503
18700
|
function resolveSleep(deps) {
|
|
18504
18701
|
return deps.sleep ?? defaultSleep2;
|
|
18505
18702
|
}
|
|
@@ -19287,7 +19484,7 @@ async function dispatchDeploy(deps, ctx, stage, ref, model, watch, autoRunSince,
|
|
|
19287
19484
|
return { note: `no manual dispatch: ${model} repo deploys via its own push-triggered workflow`, deployStatus: "pending" };
|
|
19288
19485
|
}
|
|
19289
19486
|
function readLocalGateWorkflows() {
|
|
19290
|
-
const dir = (0,
|
|
19487
|
+
const dir = (0, import_node_path21.join)(".github", "workflows");
|
|
19291
19488
|
let names;
|
|
19292
19489
|
try {
|
|
19293
19490
|
names = (0, import_node_fs21.readdirSync)(dir);
|
|
@@ -19297,7 +19494,7 @@ function readLocalGateWorkflows() {
|
|
|
19297
19494
|
const files = [];
|
|
19298
19495
|
for (const name of names.filter(isGateWorkflowPath)) {
|
|
19299
19496
|
try {
|
|
19300
|
-
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs21.readFileSync)((0,
|
|
19497
|
+
files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs21.readFileSync)((0, import_node_path21.join)(dir, name), "utf8") });
|
|
19301
19498
|
} catch {
|
|
19302
19499
|
}
|
|
19303
19500
|
}
|
|
@@ -20420,14 +20617,14 @@ async function runTenantReconcile(deps, options) {
|
|
|
20420
20617
|
};
|
|
20421
20618
|
}
|
|
20422
20619
|
function tenantControlWatches(action) {
|
|
20423
|
-
return action === "status" || action === "retire" || action === "verify-secrets" || action === "verify-broker" || action === "logs";
|
|
20620
|
+
return action === "status" || action === "retire" || action === "verify-secrets" || action === "verify-broker" || action === "logs" || action === "run-task";
|
|
20424
20621
|
}
|
|
20425
20622
|
async function runTenantControl(deps, options) {
|
|
20426
20623
|
const { repo, stage, action } = options;
|
|
20427
20624
|
const watch = options.watch ?? tenantControlWatches(action);
|
|
20428
20625
|
const base = { command: "tenant-control", repo, stage, action };
|
|
20429
20626
|
const since = (deps.now ?? Date.now)();
|
|
20430
|
-
const d = await deps.dispatchTenantControl({ repo, stage, action, lines: options.lines });
|
|
20627
|
+
const d = await deps.dispatchTenantControl({ repo, stage, action, lines: options.lines, task: options.task, artifact: options.artifact });
|
|
20431
20628
|
if (!d.ok) {
|
|
20432
20629
|
const transport = d.category === "transport-failed";
|
|
20433
20630
|
return {
|
|
@@ -20438,7 +20635,7 @@ async function runTenantControl(deps, options) {
|
|
|
20438
20635
|
note: transport ? `runtime tenant control ${action} dispatch failed (transport) \u2014 safe to retry` : `runtime tenant control ${action} rejected: ${d.error ?? "request rejected by the Hub"}`
|
|
20439
20636
|
};
|
|
20440
20637
|
}
|
|
20441
|
-
const correlated = await correlateControlRun(deps, since, [stage, action]);
|
|
20638
|
+
const correlated = await correlateControlRun(deps, since, [stage, action, ...action === "run-task" && options.task ? [options.task] : []]);
|
|
20442
20639
|
if (correlated.read === "failed") {
|
|
20443
20640
|
return {
|
|
20444
20641
|
...base,
|
|
@@ -20474,6 +20671,13 @@ async function runTenantControl(deps, options) {
|
|
|
20474
20671
|
result.brokerRaw = output;
|
|
20475
20672
|
}
|
|
20476
20673
|
}
|
|
20674
|
+
if (watch && runId != null && action === "run-task" && (conclusion === "success" || conclusion === "failure")) {
|
|
20675
|
+
const fetched = await fetchControlRunLog(deps, runId);
|
|
20676
|
+
if (fetched.ok) {
|
|
20677
|
+
const output = extractControlOutputFromLog(fetched.log);
|
|
20678
|
+
if (output) result.taskOutput = clampTaskOutput(output);
|
|
20679
|
+
}
|
|
20680
|
+
}
|
|
20477
20681
|
result.note = conclusion === "success" ? `tenant-control ${action} run succeeded` : conclusion === "failure" ? `tenant-control ${action} run failed \u2014 inspect the run` : runId == null ? `dispatched tenant-control.yml (${action}) \u2014 run not correlated; check the Actions tab` : `dispatched tenant-control.yml (${action}) \u2014 not watched`;
|
|
20478
20682
|
return result;
|
|
20479
20683
|
}
|
|
@@ -20485,6 +20689,10 @@ function renderTenantControl(r) {
|
|
|
20485
20689
|
if (r.secrets?.length) {
|
|
20486
20690
|
for (const s of r.secrets) lines.push(` ${s.key}: ${s.status}`);
|
|
20487
20691
|
}
|
|
20692
|
+
if (r.taskOutput) {
|
|
20693
|
+
lines.push(" task output:");
|
|
20694
|
+
for (const l of r.taskOutput.split("\n")) lines.push(` ${l}`);
|
|
20695
|
+
}
|
|
20488
20696
|
lines.push(` ${r.note}`);
|
|
20489
20697
|
return lines.join("\n");
|
|
20490
20698
|
}
|
|
@@ -21417,7 +21625,7 @@ async function mergeAutoWithTransientRetry(prNumber, repo, deps) {
|
|
|
21417
21625
|
if (first.mergeStatus !== "failed") return first;
|
|
21418
21626
|
const ready = await deps.probeMergeReady(prNumber, repo).catch(() => ({ open: false, mergeable: false, checksPassing: false }));
|
|
21419
21627
|
if (!ready.open || !ready.mergeable || !ready.checksPassing) return first;
|
|
21420
|
-
const sleep3 = deps.sleep ?? ((ms) => new Promise((
|
|
21628
|
+
const sleep3 = deps.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
|
|
21421
21629
|
await sleep3(PR_LAND_MERGE_RETRY_DELAY_MS);
|
|
21422
21630
|
const retried = await deps.mergeAuto(prNumber, repo);
|
|
21423
21631
|
if (retried.mergeStatus !== "failed") return retried;
|
|
@@ -21428,7 +21636,7 @@ var AUTO_MERGE_CONFIRM_DELAY_MS = 3e3;
|
|
|
21428
21636
|
async function confirmAutoMergeEnqueued(deps, options) {
|
|
21429
21637
|
const retries = options?.retries ?? AUTO_MERGE_CONFIRM_RETRIES;
|
|
21430
21638
|
const delayMs = options?.delayMs ?? AUTO_MERGE_CONFIRM_DELAY_MS;
|
|
21431
|
-
const sleep3 = deps.sleep ?? ((ms) => new Promise((
|
|
21639
|
+
const sleep3 = deps.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
|
|
21432
21640
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
21433
21641
|
if (await deps.readMerged().catch(() => false)) return "merged";
|
|
21434
21642
|
const stuck = await deps.readAutoMergeRequest().then((s) => s.trim()).catch(() => "");
|
|
@@ -21445,7 +21653,7 @@ async function confirmAutoMergeEnqueued(deps, options) {
|
|
|
21445
21653
|
async function readGhPrStateWithRetry(fetchState, options) {
|
|
21446
21654
|
const retries = options?.retries ?? PR_LAND_STATE_READ_RETRIES;
|
|
21447
21655
|
const delayMs = options?.delayMs ?? PR_LAND_STATE_READ_DELAY_MS;
|
|
21448
|
-
const sleep3 = options?.sleep ?? ((ms) => new Promise((
|
|
21656
|
+
const sleep3 = options?.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
|
|
21449
21657
|
let lastError = "empty state";
|
|
21450
21658
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
21451
21659
|
try {
|
|
@@ -21523,7 +21731,7 @@ var import_node_fs23 = require("node:fs");
|
|
|
21523
21731
|
// src/stage-runner.ts
|
|
21524
21732
|
var import_node_child_process9 = require("node:child_process");
|
|
21525
21733
|
var import_node_fs22 = require("node:fs");
|
|
21526
|
-
var
|
|
21734
|
+
var import_node_path22 = require("node:path");
|
|
21527
21735
|
var import_node_net = require("node:net");
|
|
21528
21736
|
var import_node_util5 = require("node:util");
|
|
21529
21737
|
|
|
@@ -21552,7 +21760,7 @@ function healthPollIntervalMs() {
|
|
|
21552
21760
|
return HEALTH_POLL_INTERVAL_MS;
|
|
21553
21761
|
}
|
|
21554
21762
|
function waitForProcessStability(child2, graceMs = earlyExitGraceMs()) {
|
|
21555
|
-
return new Promise((
|
|
21763
|
+
return new Promise((resolve6, reject) => {
|
|
21556
21764
|
let settled = false;
|
|
21557
21765
|
const finish = (fn) => {
|
|
21558
21766
|
if (settled) return;
|
|
@@ -21562,7 +21770,7 @@ function waitForProcessStability(child2, graceMs = earlyExitGraceMs()) {
|
|
|
21562
21770
|
child2.removeAllListeners("exit");
|
|
21563
21771
|
fn();
|
|
21564
21772
|
};
|
|
21565
|
-
const timer = setTimeout(() => finish(
|
|
21773
|
+
const timer = setTimeout(() => finish(resolve6), graceMs);
|
|
21566
21774
|
child2.on("error", (err) => finish(() => reject(new Error(`stage process failed to start: ${err.message}`))));
|
|
21567
21775
|
child2.on("exit", (code, signal) => {
|
|
21568
21776
|
const detail = code != null ? `code ${code}` : signal ? `signal ${signal}` : "unknown reason";
|
|
@@ -21662,11 +21870,11 @@ function appendForceRecreate(up) {
|
|
|
21662
21870
|
return `${up.trimEnd()} --force-recreate`;
|
|
21663
21871
|
}
|
|
21664
21872
|
function stageStatePath(cwd = process.cwd()) {
|
|
21665
|
-
return (0,
|
|
21873
|
+
return (0, import_node_path22.join)(cwd, "tmp", "stage", "state.json");
|
|
21666
21874
|
}
|
|
21667
21875
|
function stageGlobalStatePath(cwd = process.cwd(), gitCommonDir = ".git") {
|
|
21668
|
-
const dir = (0,
|
|
21669
|
-
return (0,
|
|
21876
|
+
const dir = (0, import_node_path22.isAbsolute)(gitCommonDir) ? gitCommonDir : (0, import_node_path22.resolve)(cwd, gitCommonDir);
|
|
21877
|
+
return (0, import_node_path22.join)(dir, "mmi", "stage", "state.json");
|
|
21670
21878
|
}
|
|
21671
21879
|
function normPath3(path2) {
|
|
21672
21880
|
return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
@@ -21772,10 +21980,10 @@ function pickStagePort(range, isFree) {
|
|
|
21772
21980
|
throw new Error(`no free stage port in range ${start}-${end} \u2014 every port is in use`);
|
|
21773
21981
|
}
|
|
21774
21982
|
function isPortFree(port) {
|
|
21775
|
-
return new Promise((
|
|
21983
|
+
return new Promise((resolve6) => {
|
|
21776
21984
|
const srv = (0, import_node_net.createServer)();
|
|
21777
|
-
srv.once("error", () =>
|
|
21778
|
-
srv.once("listening", () => srv.close(() =>
|
|
21985
|
+
srv.once("error", () => resolve6(false));
|
|
21986
|
+
srv.once("listening", () => srv.close(() => resolve6(true)));
|
|
21779
21987
|
srv.listen(port, "127.0.0.1");
|
|
21780
21988
|
});
|
|
21781
21989
|
}
|
|
@@ -21890,8 +22098,8 @@ function stageProcessEnv(stagePort, extraEnv) {
|
|
|
21890
22098
|
}
|
|
21891
22099
|
async function ensureStageRuntimeEnv(config, opts, cwd) {
|
|
21892
22100
|
if (!config.ensureEnv) return;
|
|
21893
|
-
const target = (0,
|
|
21894
|
-
const example = (0,
|
|
22101
|
+
const target = (0, import_node_path22.join)(cwd, config.ensureEnv.target);
|
|
22102
|
+
const example = (0, import_node_path22.join)(cwd, config.ensureEnv.example);
|
|
21895
22103
|
if (!(0, import_node_fs22.existsSync)(target) && (0, import_node_fs22.existsSync)(example)) {
|
|
21896
22104
|
(0, import_node_fs22.copyFileSync)(example, target);
|
|
21897
22105
|
} else if ((0, import_node_fs22.existsSync)(target) && (0, import_node_fs22.existsSync)(example)) {
|
|
@@ -21986,7 +22194,7 @@ async function killTree(pid) {
|
|
|
21986
22194
|
} catch {
|
|
21987
22195
|
}
|
|
21988
22196
|
}
|
|
21989
|
-
await new Promise((
|
|
22197
|
+
await new Promise((resolve6) => setTimeout(resolve6, 500));
|
|
21990
22198
|
try {
|
|
21991
22199
|
process.kill(-pid, "SIGKILL");
|
|
21992
22200
|
} catch {
|
|
@@ -22007,7 +22215,7 @@ async function waitForHealth(url, timeoutMs, anyStatus = false) {
|
|
|
22007
22215
|
} catch (e) {
|
|
22008
22216
|
last = e.message;
|
|
22009
22217
|
}
|
|
22010
|
-
await new Promise((
|
|
22218
|
+
await new Promise((resolve6) => setTimeout(resolve6, healthPollIntervalMs()));
|
|
22011
22219
|
}
|
|
22012
22220
|
throw new Error(`stage health check timed out for ${url}${last ? ` (${last})` : ""}`);
|
|
22013
22221
|
}
|
|
@@ -22256,13 +22464,13 @@ async function executeWaveLand(plan, deps, opts = { preserveWorktree: true }) {
|
|
|
22256
22464
|
}
|
|
22257
22465
|
|
|
22258
22466
|
// src/index.ts
|
|
22259
|
-
var
|
|
22467
|
+
var import_node_os20 = require("node:os");
|
|
22260
22468
|
|
|
22261
22469
|
// src/board.ts
|
|
22262
22470
|
var import_node_child_process10 = require("node:child_process");
|
|
22263
22471
|
var import_node_fs24 = require("node:fs");
|
|
22264
22472
|
var import_node_os8 = require("node:os");
|
|
22265
|
-
var
|
|
22473
|
+
var import_node_path23 = require("node:path");
|
|
22266
22474
|
var import_node_util6 = require("node:util");
|
|
22267
22475
|
|
|
22268
22476
|
// src/board-dependency.ts
|
|
@@ -23542,7 +23750,7 @@ async function setBoardItemPriority(client, cfg, itemId, priority) {
|
|
|
23542
23750
|
await updateItemSingleSelect(client, cfg.projectId, itemId, cfg.priorityFieldId, optionId);
|
|
23543
23751
|
return cliPriorityToFieldName(priority);
|
|
23544
23752
|
}
|
|
23545
|
-
var defaultRetrySleep = (ms) => new Promise((
|
|
23753
|
+
var defaultRetrySleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
23546
23754
|
async function resolveProjectItemIdWithRetry(client, cfg, selector, opts = {}) {
|
|
23547
23755
|
const attempts = Math.max(1, opts.attempts ?? 5);
|
|
23548
23756
|
const delayMs = opts.delayMs ?? 300;
|
|
@@ -24005,14 +24213,14 @@ function probeLocalClaimSession(marker, now = Date.now()) {
|
|
|
24005
24213
|
claimSessionProbeCache.set(cacheKey, { checkedAt: now, state });
|
|
24006
24214
|
return state;
|
|
24007
24215
|
};
|
|
24008
|
-
const root = (0,
|
|
24216
|
+
const root = (0, import_node_path23.join)((0, import_node_os8.homedir)(), ".claude", "projects");
|
|
24009
24217
|
try {
|
|
24010
24218
|
const wanted = `${marker.session}.jsonl`.toLowerCase();
|
|
24011
24219
|
const pending = [root];
|
|
24012
24220
|
while (pending.length) {
|
|
24013
24221
|
const dir = pending.pop();
|
|
24014
24222
|
for (const entry of (0, import_node_fs24.readdirSync)(dir, { withFileTypes: true })) {
|
|
24015
|
-
const path2 = (0,
|
|
24223
|
+
const path2 = (0, import_node_path23.join)(dir, entry.name);
|
|
24016
24224
|
if (entry.isDirectory()) pending.push(path2);
|
|
24017
24225
|
else if (entry.isFile() && entry.name.toLowerCase() === wanted) {
|
|
24018
24226
|
return remember(now - (0, import_node_fs24.statSync)(path2).mtimeMs <= CLAIM_SESSION_ACTIVITY_MS ? "live" : "dead");
|
|
@@ -24653,7 +24861,7 @@ function buildOption(opt) {
|
|
|
24653
24861
|
if (opt.hidden) out.discovery = "all-only";
|
|
24654
24862
|
return out;
|
|
24655
24863
|
}
|
|
24656
|
-
function buildCommand(cmd,
|
|
24864
|
+
function buildCommand(cmd, flatPath) {
|
|
24657
24865
|
const metadata = commandMetadata(cmd) ?? {
|
|
24658
24866
|
category: "core",
|
|
24659
24867
|
discovery: "primary",
|
|
@@ -24661,29 +24869,31 @@ function buildCommand(cmd, path2) {
|
|
|
24661
24869
|
module_owner: "unclassified",
|
|
24662
24870
|
consumer: "unclassified"
|
|
24663
24871
|
};
|
|
24664
|
-
const house = houseForPath(
|
|
24872
|
+
const house = houseForPath(flatPath);
|
|
24665
24873
|
if (!house) {
|
|
24666
24874
|
throw new Error(
|
|
24667
|
-
`command-manifest: command '${
|
|
24875
|
+
`command-manifest: command '${flatPath}' has no house assignment \u2014 add it to cli/src/house-map.ts (the six-house taxonomy; every top-level command maps to exactly one house).`
|
|
24668
24876
|
);
|
|
24669
24877
|
}
|
|
24670
24878
|
const aliases = cmd.aliases();
|
|
24879
|
+
const canonical = canonicalPathFor(flatPath) ?? flatPath;
|
|
24671
24880
|
const out = {
|
|
24672
24881
|
name: cmd.name(),
|
|
24673
|
-
path:
|
|
24882
|
+
path: canonical,
|
|
24883
|
+
flat_path: flatPath,
|
|
24674
24884
|
...aliases.length ? { aliases: [...aliases] } : {},
|
|
24675
24885
|
house,
|
|
24676
|
-
canonical
|
|
24886
|
+
canonical,
|
|
24677
24887
|
arguments: cmd.registeredArguments.map(buildArgument),
|
|
24678
24888
|
// A hand-parsed command registers no Commander options, so its declared set is merged in (#3682).
|
|
24679
24889
|
options: [...cmd.options.map(buildOption), ...readDeclaredOptions(cmd)],
|
|
24680
24890
|
subcommands: cmd.commands.map(
|
|
24681
|
-
(child2) => buildCommand(child2,
|
|
24891
|
+
(child2) => buildCommand(child2, flatPath ? `${flatPath} ${child2.name()}` : child2.name())
|
|
24682
24892
|
),
|
|
24683
24893
|
...metadata
|
|
24684
24894
|
};
|
|
24685
24895
|
if (cmd._allowUnknownOption) out.parses_own_argv = true;
|
|
24686
|
-
if (
|
|
24896
|
+
if (flatPath && house !== "core") out.flat_removed = true;
|
|
24687
24897
|
const description = cmd.description();
|
|
24688
24898
|
if (description) out.description = description;
|
|
24689
24899
|
const examples = readExamples(cmd);
|
|
@@ -24712,7 +24922,7 @@ function collectLeaves(node, acc) {
|
|
|
24712
24922
|
function buildHouses(tree) {
|
|
24713
24923
|
const collect = (node, parentHouse, root, out) => {
|
|
24714
24924
|
if (node.house === root && parentHouse !== root) {
|
|
24715
|
-
const entry = { path: node.
|
|
24925
|
+
const entry = { path: node.flat_path, canonical: node.canonical };
|
|
24716
24926
|
if (node.description) entry.description = node.description;
|
|
24717
24927
|
out.push(entry);
|
|
24718
24928
|
}
|
|
@@ -24916,7 +25126,7 @@ function consolidateCommandNamespaces(program3) {
|
|
|
24916
25126
|
|
|
24917
25127
|
// src/pi-plugin-registration.ts
|
|
24918
25128
|
var import_node_fs25 = require("node:fs");
|
|
24919
|
-
var
|
|
25129
|
+
var import_node_path24 = require("node:path");
|
|
24920
25130
|
var import_proper_lockfile2 = __toESM(require_proper_lockfile(), 1);
|
|
24921
25131
|
|
|
24922
25132
|
// src/plugin-cache-prune.ts
|
|
@@ -25179,17 +25389,17 @@ function newestExistingPiPlugin(home) {
|
|
|
25179
25389
|
return void 0;
|
|
25180
25390
|
}
|
|
25181
25391
|
for (const version of names.filter(isVersionDirName).sort((a, b) => compareVersions(b, a))) {
|
|
25182
|
-
const candidate = (0,
|
|
25392
|
+
const candidate = (0, import_node_path24.join)(cacheRoot, version, ".pi-plugin");
|
|
25183
25393
|
if ((0, import_node_fs25.existsSync)(candidate)) return candidate;
|
|
25184
25394
|
}
|
|
25185
25395
|
return void 0;
|
|
25186
25396
|
}
|
|
25187
25397
|
function expectedPiPluginPath(home, env, installedVersion) {
|
|
25188
25398
|
const root = env.CLAUDE_PLUGIN_ROOT?.trim();
|
|
25189
|
-
if (root && /[\\/]mutmutco[\\/]mmi[\\/]/.test(root)) return (0,
|
|
25399
|
+
if (root && /[\\/]mutmutco[\\/]mmi[\\/]/.test(root)) return (0, import_node_path24.join)(root, ".pi-plugin");
|
|
25190
25400
|
const version = installedVersion ?? runningPluginVersion(env);
|
|
25191
25401
|
if (version) {
|
|
25192
|
-
const pinned = (0,
|
|
25402
|
+
const pinned = (0, import_node_path24.join)(home, ".claude", "plugins", "cache", "mutmutco", "mmi", version, ".pi-plugin");
|
|
25193
25403
|
if ((0, import_node_fs25.existsSync)(pinned)) return pinned;
|
|
25194
25404
|
}
|
|
25195
25405
|
return newestExistingPiPlugin(home);
|
|
@@ -25197,15 +25407,15 @@ function expectedPiPluginPath(home, env, installedVersion) {
|
|
|
25197
25407
|
function agentDirs(home, env = process.env) {
|
|
25198
25408
|
const override = env.JERVCODE_CODING_AGENT_DIR?.trim() || env.PI_CODING_AGENT_DIR?.trim();
|
|
25199
25409
|
if (override) return [override];
|
|
25200
|
-
const jerv = (0,
|
|
25201
|
-
const pi = (0,
|
|
25410
|
+
const jerv = (0, import_node_path24.join)(home, ".jerv", "agent");
|
|
25411
|
+
const pi = (0, import_node_path24.join)(home, ".pi", "agent");
|
|
25202
25412
|
if (!(0, import_node_fs25.existsSync)(jerv) && !(0, import_node_fs25.existsSync)(pi)) return [];
|
|
25203
25413
|
const dirs = [jerv];
|
|
25204
25414
|
if ((0, import_node_fs25.existsSync)(pi) && pi !== jerv) dirs.push(pi);
|
|
25205
25415
|
return dirs;
|
|
25206
25416
|
}
|
|
25207
25417
|
function settingsPath(agentDir) {
|
|
25208
|
-
return (0,
|
|
25418
|
+
return (0, import_node_path24.join)(agentDir, "settings.json");
|
|
25209
25419
|
}
|
|
25210
25420
|
function readPiPluginState(home, env, installedVersion) {
|
|
25211
25421
|
const dirs = agentDirs(home, env);
|
|
@@ -25239,7 +25449,7 @@ function acquirePiSettingsLock2(settingsFile) {
|
|
|
25239
25449
|
return void 0;
|
|
25240
25450
|
}
|
|
25241
25451
|
function atomicWriteSettings(file, body) {
|
|
25242
|
-
(0, import_node_fs25.mkdirSync)((0,
|
|
25452
|
+
(0, import_node_fs25.mkdirSync)((0, import_node_path24.dirname)(file), { recursive: true });
|
|
25243
25453
|
const tmp = `${file}.tmp-${process.pid}`;
|
|
25244
25454
|
(0, import_node_fs25.writeFileSync)(tmp, body, "utf8");
|
|
25245
25455
|
try {
|
|
@@ -25317,18 +25527,18 @@ function healPiPluginRegistration(home, env, installedVersion) {
|
|
|
25317
25527
|
// src/claude-binary-doctor.ts
|
|
25318
25528
|
var import_node_fs27 = require("node:fs");
|
|
25319
25529
|
var import_node_os11 = require("node:os");
|
|
25320
|
-
var
|
|
25530
|
+
var import_node_path26 = require("node:path");
|
|
25321
25531
|
|
|
25322
25532
|
// src/jerv-cli-spawn.ts
|
|
25323
25533
|
var import_node_fs26 = require("node:fs");
|
|
25324
25534
|
var import_node_os10 = require("node:os");
|
|
25325
|
-
var
|
|
25535
|
+
var import_node_path25 = require("node:path");
|
|
25326
25536
|
var WIN_NAMES = ["jerv-cli.cmd", "jerv-cli.exe", "jerv-cli"];
|
|
25327
25537
|
var POSIX_NAMES = ["jerv-cli"];
|
|
25328
|
-
var JERV_CLI_ENTRY = (0,
|
|
25538
|
+
var JERV_CLI_ENTRY = (0, import_node_path25.join)("node_modules", "@jervaise", "jerv-cli", "dist", "index.cjs");
|
|
25329
25539
|
function pathEnvEntries(pathEnv, platform2 = process.platform) {
|
|
25330
25540
|
if (platform2 !== "win32") {
|
|
25331
|
-
return pathEnv.split(
|
|
25541
|
+
return pathEnv.split(import_node_path25.delimiter).map((e) => e.trim()).filter(Boolean);
|
|
25332
25542
|
}
|
|
25333
25543
|
if (pathEnv.includes(";")) {
|
|
25334
25544
|
return pathEnv.split(";").map((e) => e.trim()).filter(Boolean);
|
|
@@ -25361,10 +25571,10 @@ function jervCliCandidateDirs(env = process.env, home = (0, import_node_os10.hom
|
|
|
25361
25571
|
push(normalizeSpawnPathEntry(entry, platform2));
|
|
25362
25572
|
}
|
|
25363
25573
|
if (platform2 === "win32") {
|
|
25364
|
-
if (env.APPDATA) push((0,
|
|
25365
|
-
if (env.LOCALAPPDATA) push((0,
|
|
25574
|
+
if (env.APPDATA) push((0, import_node_path25.join)(env.APPDATA, "npm"));
|
|
25575
|
+
if (env.LOCALAPPDATA) push((0, import_node_path25.join)(env.LOCALAPPDATA, "npm"));
|
|
25366
25576
|
} else {
|
|
25367
|
-
push((0,
|
|
25577
|
+
push((0, import_node_path25.join)(home, ".local", "bin"));
|
|
25368
25578
|
}
|
|
25369
25579
|
return out;
|
|
25370
25580
|
}
|
|
@@ -25372,7 +25582,7 @@ function jervCliCandidatePaths(env = process.env, home = (0, import_node_os10.ho
|
|
|
25372
25582
|
const names = platform2 === "win32" ? WIN_NAMES : POSIX_NAMES;
|
|
25373
25583
|
const out = [];
|
|
25374
25584
|
for (const dir of jervCliCandidateDirs(env, home, platform2)) {
|
|
25375
|
-
for (const name of names) out.push((0,
|
|
25585
|
+
for (const name of names) out.push((0, import_node_path25.join)(dir, name));
|
|
25376
25586
|
}
|
|
25377
25587
|
return out;
|
|
25378
25588
|
}
|
|
@@ -25383,7 +25593,7 @@ function resolveJervCliPath(env = process.env, home = (0, import_node_os10.homed
|
|
|
25383
25593
|
return void 0;
|
|
25384
25594
|
}
|
|
25385
25595
|
function resolveJervCliNodeEntry(shimPath, exists = import_node_fs26.existsSync) {
|
|
25386
|
-
const entry = (0,
|
|
25596
|
+
const entry = (0, import_node_path25.join)((0, import_node_path25.dirname)(shimPath), JERV_CLI_ENTRY);
|
|
25387
25597
|
return exists(entry) ? entry : void 0;
|
|
25388
25598
|
}
|
|
25389
25599
|
function jervCliExecFileArgs(args, opts = {}) {
|
|
@@ -25478,10 +25688,10 @@ function globalNodeModulesRoots(host) {
|
|
|
25478
25688
|
out.push(dir);
|
|
25479
25689
|
};
|
|
25480
25690
|
const prefix = env.npm_config_prefix?.trim();
|
|
25481
|
-
if (prefix) push(platform2 === "win32" ? (0,
|
|
25691
|
+
if (prefix) push(platform2 === "win32" ? (0, import_node_path26.join)(prefix, "node_modules") : (0, import_node_path26.join)(prefix, "lib", "node_modules"));
|
|
25482
25692
|
for (const dir of jervCliCandidateDirs(env, host.home ?? (0, import_node_os11.homedir)(), platform2)) {
|
|
25483
|
-
push((0,
|
|
25484
|
-
push((0,
|
|
25693
|
+
push((0, import_node_path26.join)(dir, "node_modules"));
|
|
25694
|
+
push((0, import_node_path26.join)((0, import_node_path26.dirname)(dir), "lib", "node_modules"));
|
|
25485
25695
|
}
|
|
25486
25696
|
return out;
|
|
25487
25697
|
}
|
|
@@ -25519,17 +25729,17 @@ function readClaudeBinaryState(host = {}) {
|
|
|
25519
25729
|
const arch = host.arch ?? process.arch;
|
|
25520
25730
|
const magic = EXECUTABLE_MAGIC[platform2];
|
|
25521
25731
|
if (!magic) return void 0;
|
|
25522
|
-
const packageRoot = globalNodeModulesRoots(host).map((root) => (0,
|
|
25732
|
+
const packageRoot = globalNodeModulesRoots(host).map((root) => (0, import_node_path26.join)(root, ...PACKAGE.split("/"))).find((dir) => (0, import_node_fs27.existsSync)((0, import_node_path26.join)(dir, "package.json")));
|
|
25523
25733
|
if (!packageRoot) return void 0;
|
|
25524
25734
|
const keys = platformPackageKeys(platform2, arch);
|
|
25525
25735
|
const fallbackPackage = `${PACKAGE}-${keys[0]}`;
|
|
25526
25736
|
let manifest;
|
|
25527
25737
|
try {
|
|
25528
|
-
manifest = JSON.parse((0, import_node_fs27.readFileSync)((0,
|
|
25738
|
+
manifest = JSON.parse((0, import_node_fs27.readFileSync)((0, import_node_path26.join)(packageRoot, "package.json"), "utf8"));
|
|
25529
25739
|
} catch (e) {
|
|
25530
25740
|
return {
|
|
25531
25741
|
state: "unreadable",
|
|
25532
|
-
binPath: (0,
|
|
25742
|
+
binPath: (0, import_node_path26.join)(packageRoot, "package.json"),
|
|
25533
25743
|
expectedMagic: magic.name,
|
|
25534
25744
|
platformPackage: fallbackPackage,
|
|
25535
25745
|
error: `package.json could not be read \u2014 ${e.message}`
|
|
@@ -25546,15 +25756,15 @@ function readClaudeBinaryState(host = {}) {
|
|
|
25546
25756
|
error: "package.json declares no `bin` entry \u2014 the executed file cannot be resolved"
|
|
25547
25757
|
};
|
|
25548
25758
|
}
|
|
25549
|
-
const binPath = (0,
|
|
25550
|
-
const binName = (0,
|
|
25759
|
+
const binPath = (0, import_node_path26.join)(packageRoot, binRelative);
|
|
25760
|
+
const binName = (0, import_node_path26.basename)(binRelative);
|
|
25551
25761
|
const optional = manifest.optionalDependencies;
|
|
25552
25762
|
const publishes = (name) => typeof optional === "object" && optional !== null && Object.prototype.hasOwnProperty.call(optional, name);
|
|
25553
25763
|
const published = keys.map((key) => `${PACKAGE}-${key}`).filter(publishes);
|
|
25554
25764
|
if (published.length === 0) return void 0;
|
|
25555
25765
|
const binIn = (name) => [
|
|
25556
|
-
(0,
|
|
25557
|
-
(0,
|
|
25766
|
+
(0, import_node_path26.join)(packageRoot, "node_modules", ...name.split("/"), binName),
|
|
25767
|
+
(0, import_node_path26.join)((0, import_node_path26.dirname)((0, import_node_path26.dirname)(packageRoot)), ...name.split("/"), binName)
|
|
25558
25768
|
];
|
|
25559
25769
|
const found = published.map((name) => ({ name, path: binIn(name).find((file) => (0, import_node_fs27.existsSync)(file)) })).find((c) => c.path);
|
|
25560
25770
|
const platformPackage = found?.name ?? published[0];
|
|
@@ -25875,6 +26085,57 @@ function renderVerifyBroker(input) {
|
|
|
25875
26085
|
};
|
|
25876
26086
|
}
|
|
25877
26087
|
|
|
26088
|
+
// src/tenant-artifact.ts
|
|
26089
|
+
var import_node_crypto4 = require("node:crypto");
|
|
26090
|
+
var import_node_fs28 = require("node:fs");
|
|
26091
|
+
var import_promises5 = require("node:fs/promises");
|
|
26092
|
+
var import_node_path27 = require("node:path");
|
|
26093
|
+
var ARTIFACT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
26094
|
+
var MAX_BYTES = 5 * 1024 * 1024 * 1024;
|
|
26095
|
+
async function sha256File(path2) {
|
|
26096
|
+
const hash = (0, import_node_crypto4.createHash)("sha256");
|
|
26097
|
+
for await (const chunk of (0, import_node_fs28.createReadStream)(path2)) hash.update(chunk);
|
|
26098
|
+
return hash.digest("hex");
|
|
26099
|
+
}
|
|
26100
|
+
async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
26101
|
+
if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
|
|
26102
|
+
const path2 = (0, import_node_path27.resolve)(inputPath);
|
|
26103
|
+
const info = await (0, import_promises5.stat)(path2);
|
|
26104
|
+
if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
|
|
26105
|
+
if (!Number.isSafeInteger(info.size) || info.size < 1 || info.size > MAX_BYTES) throw new Error(`tenant artifact put: file must be 1..${MAX_BYTES} bytes`);
|
|
26106
|
+
const sha256 = await sha256File(path2);
|
|
26107
|
+
const prepared = await tenantArtifactUpload({ repo, stage, size: info.size, sha256 }, deps);
|
|
26108
|
+
if (!prepared.ok) {
|
|
26109
|
+
const detail = prepared.body?.error ?? prepared.error ?? `HTTP ${prepared.status}`;
|
|
26110
|
+
throw new Error(`tenant artifact put: ${detail}`);
|
|
26111
|
+
}
|
|
26112
|
+
const body = prepared.body;
|
|
26113
|
+
if (!ARTIFACT_ID_RE.test(body.artifactId ?? "") || typeof body.uploadUrl !== "string" || !body.uploadUrl.startsWith("https://")) throw new Error("tenant artifact put: Hub returned an invalid upload grant");
|
|
26114
|
+
if (!body.headers || typeof body.headers !== "object" || Array.isArray(body.headers)) throw new Error("tenant artifact put: Hub returned invalid signed headers");
|
|
26115
|
+
const headers = Object.fromEntries(Object.entries(body.headers).map(([key, value]) => {
|
|
26116
|
+
if (typeof value !== "string" || /[\r\n]/.test(key + value)) throw new Error("tenant artifact put: Hub returned an unsafe signed header");
|
|
26117
|
+
return [key, value];
|
|
26118
|
+
}));
|
|
26119
|
+
headers["content-length"] = String(info.size);
|
|
26120
|
+
const stream = (0, import_node_fs28.createReadStream)(path2);
|
|
26121
|
+
let uploaded;
|
|
26122
|
+
try {
|
|
26123
|
+
uploaded = await fetch(body.uploadUrl, {
|
|
26124
|
+
method: "PUT",
|
|
26125
|
+
headers,
|
|
26126
|
+
body: stream,
|
|
26127
|
+
duplex: "half",
|
|
26128
|
+
signal: AbortSignal.timeout(60 * 60 * 1e3)
|
|
26129
|
+
});
|
|
26130
|
+
} catch (error) {
|
|
26131
|
+
stream.destroy();
|
|
26132
|
+
throw error;
|
|
26133
|
+
}
|
|
26134
|
+
if (!uploaded.ok) throw new Error(`tenant artifact put: object upload failed (HTTP ${uploaded.status})`);
|
|
26135
|
+
if (body.size !== info.size || body.sha256 !== sha256 || typeof body.expiresAt !== "string" || !Number.isFinite(Date.parse(body.expiresAt))) throw new Error("tenant artifact put: Hub returned inconsistent artifact metadata");
|
|
26136
|
+
return { artifactId: body.artifactId, repo, stage, size: info.size, sha256, expiresAt: body.expiresAt };
|
|
26137
|
+
}
|
|
26138
|
+
|
|
25878
26139
|
// src/hotfix-coverage.ts
|
|
25879
26140
|
var import_node_child_process12 = require("node:child_process");
|
|
25880
26141
|
var CHERRY_TRAILER = /\(cherry picked from commit ([0-9a-f]{7,40})\)/g;
|
|
@@ -26058,7 +26319,7 @@ function clean3(out) {
|
|
|
26058
26319
|
return out.trim();
|
|
26059
26320
|
}
|
|
26060
26321
|
function sleeper(deps) {
|
|
26061
|
-
return deps.sleep ?? ((ms) => new Promise((
|
|
26322
|
+
return deps.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
|
|
26062
26323
|
}
|
|
26063
26324
|
function normalizeHotfixVersion(input) {
|
|
26064
26325
|
const m = /^v?(\d+\.\d+\.\d+)$/.exec(input.trim());
|
|
@@ -26972,10 +27233,10 @@ async function announceRelease(deps, args) {
|
|
|
26972
27233
|
}
|
|
26973
27234
|
|
|
26974
27235
|
// src/repo-index.ts
|
|
26975
|
-
var
|
|
27236
|
+
var import_node_crypto5 = require("node:crypto");
|
|
26976
27237
|
var import_node_child_process13 = require("node:child_process");
|
|
26977
|
-
var
|
|
26978
|
-
var
|
|
27238
|
+
var import_node_fs29 = require("node:fs");
|
|
27239
|
+
var import_node_path28 = require("node:path");
|
|
26979
27240
|
var REPO_INDEX_SCHEMA = 1;
|
|
26980
27241
|
var HARD_DENY = [
|
|
26981
27242
|
/(^|\/)\.env(\.|$)/i,
|
|
@@ -27100,11 +27361,11 @@ function loadReadmeHints(cwd, candidatePaths) {
|
|
|
27100
27361
|
}
|
|
27101
27362
|
for (const rel of readmes) {
|
|
27102
27363
|
if (isHardDeniedPath(rel)) continue;
|
|
27103
|
-
const abs = (0,
|
|
27104
|
-
if (!(0,
|
|
27364
|
+
const abs = (0, import_node_path28.join)(cwd, ...rel.split("/"));
|
|
27365
|
+
if (!(0, import_node_fs29.existsSync)(abs)) continue;
|
|
27105
27366
|
let text;
|
|
27106
27367
|
try {
|
|
27107
|
-
text = (0,
|
|
27368
|
+
text = (0, import_node_fs29.readFileSync)(abs, "utf8");
|
|
27108
27369
|
} catch {
|
|
27109
27370
|
continue;
|
|
27110
27371
|
}
|
|
@@ -27117,7 +27378,7 @@ function loadReadmeHints(cwd, candidatePaths) {
|
|
|
27117
27378
|
return hints;
|
|
27118
27379
|
}
|
|
27119
27380
|
function toPosix(p) {
|
|
27120
|
-
return p.split(
|
|
27381
|
+
return p.split(import_node_path28.sep).join("/");
|
|
27121
27382
|
}
|
|
27122
27383
|
function listCandidatePaths(cwd, exec = import_node_child_process13.execFileSync) {
|
|
27123
27384
|
try {
|
|
@@ -27139,16 +27400,16 @@ function rebuildRepoIndex(cwd, repoSlug2) {
|
|
|
27139
27400
|
for (const rel of candidates) {
|
|
27140
27401
|
if (ignored.has(rel)) continue;
|
|
27141
27402
|
if (isHardDeniedPath(rel)) continue;
|
|
27142
|
-
const abs = (0,
|
|
27143
|
-
if (!(0,
|
|
27403
|
+
const abs = (0, import_node_path28.join)(cwd, ...rel.split("/"));
|
|
27404
|
+
if (!(0, import_node_fs29.existsSync)(abs)) continue;
|
|
27144
27405
|
let text;
|
|
27145
27406
|
try {
|
|
27146
|
-
text = (0,
|
|
27407
|
+
text = (0, import_node_fs29.readFileSync)(abs, "utf8");
|
|
27147
27408
|
} catch {
|
|
27148
27409
|
continue;
|
|
27149
27410
|
}
|
|
27150
27411
|
if (text.length > 15e5) continue;
|
|
27151
|
-
const hash = (0,
|
|
27412
|
+
const hash = (0, import_node_crypto5.createHash)("sha256").update(text).digest("hex").slice(0, 16);
|
|
27152
27413
|
const symbols = extractSymbols(text);
|
|
27153
27414
|
const docBlurb = extractModuleBlurb(text);
|
|
27154
27415
|
const top = rel.includes("/") ? rel.split("/")[0] : "";
|
|
@@ -27168,16 +27429,16 @@ function rebuildRepoIndex(cwd, repoSlug2) {
|
|
|
27168
27429
|
entries
|
|
27169
27430
|
};
|
|
27170
27431
|
const store = repoIndexStorePath(cwd);
|
|
27171
|
-
(0,
|
|
27172
|
-
(0,
|
|
27432
|
+
(0, import_node_fs29.mkdirSync)((0, import_node_path28.dirname)(store), { recursive: true });
|
|
27433
|
+
(0, import_node_fs29.writeFileSync)(store, `${JSON.stringify(projection, null, 2)}
|
|
27173
27434
|
`, "utf8");
|
|
27174
27435
|
return projection;
|
|
27175
27436
|
}
|
|
27176
27437
|
function loadRepoIndex(cwd) {
|
|
27177
27438
|
const store = repoIndexStorePath(cwd);
|
|
27178
|
-
if (!(0,
|
|
27439
|
+
if (!(0, import_node_fs29.existsSync)(store)) return null;
|
|
27179
27440
|
try {
|
|
27180
|
-
const raw = JSON.parse((0,
|
|
27441
|
+
const raw = JSON.parse((0, import_node_fs29.readFileSync)(store, "utf8"));
|
|
27181
27442
|
if (raw?.schema !== REPO_INDEX_SCHEMA || !Array.isArray(raw.entries)) return null;
|
|
27182
27443
|
return raw;
|
|
27183
27444
|
} catch {
|
|
@@ -27249,7 +27510,7 @@ function inferRepoSlug(cwd, exec = import_node_child_process13.execFileSync) {
|
|
|
27249
27510
|
if (m?.[1]) return m[1].toLowerCase();
|
|
27250
27511
|
} catch {
|
|
27251
27512
|
}
|
|
27252
|
-
return ((0,
|
|
27513
|
+
return ((0, import_node_path28.basename)(cwd) || "local").toLowerCase();
|
|
27253
27514
|
}
|
|
27254
27515
|
|
|
27255
27516
|
// src/repo-index-cloud-client.ts
|
|
@@ -27389,9 +27650,9 @@ async function gcRepoIndexCloud(deps) {
|
|
|
27389
27650
|
}
|
|
27390
27651
|
|
|
27391
27652
|
// src/repo-index-sync.ts
|
|
27392
|
-
var
|
|
27653
|
+
var import_node_fs30 = require("node:fs");
|
|
27393
27654
|
var import_node_os12 = require("node:os");
|
|
27394
|
-
var
|
|
27655
|
+
var import_node_path29 = require("node:path");
|
|
27395
27656
|
var import_node_child_process14 = require("node:child_process");
|
|
27396
27657
|
var MAX_EMBED_BACKFILL_ROUNDS = 40;
|
|
27397
27658
|
function normalizeRepo(raw) {
|
|
@@ -27435,7 +27696,7 @@ async function syncEstateRepoIndex(opts) {
|
|
|
27435
27696
|
const failed = [];
|
|
27436
27697
|
const skipped = [];
|
|
27437
27698
|
for (const repo of repos) {
|
|
27438
|
-
const dir = (0,
|
|
27699
|
+
const dir = (0, import_node_fs30.mkdtempSync)((0, import_node_path29.join)((0, import_node_os12.tmpdir)(), "mmi-repo-index-"));
|
|
27439
27700
|
try {
|
|
27440
27701
|
shallowClone(repo, dir, opts.githubToken);
|
|
27441
27702
|
const built = rebuildRepoIndex(dir, repo);
|
|
@@ -27485,7 +27746,7 @@ async function syncEstateRepoIndex(opts) {
|
|
|
27485
27746
|
failed.push({ repo, error: e.message });
|
|
27486
27747
|
} finally {
|
|
27487
27748
|
try {
|
|
27488
|
-
(0,
|
|
27749
|
+
(0, import_node_fs30.rmSync)(dir, { recursive: true, force: true });
|
|
27489
27750
|
} catch {
|
|
27490
27751
|
}
|
|
27491
27752
|
}
|
|
@@ -27494,7 +27755,7 @@ async function syncEstateRepoIndex(opts) {
|
|
|
27494
27755
|
}
|
|
27495
27756
|
|
|
27496
27757
|
// src/repo-index-health.ts
|
|
27497
|
-
var
|
|
27758
|
+
var import_node_fs31 = require("node:fs");
|
|
27498
27759
|
|
|
27499
27760
|
// testdata/repo-index-golden-queries.json
|
|
27500
27761
|
var repo_index_golden_queries_default = {
|
|
@@ -27536,7 +27797,7 @@ function assertGoldenSuite(raw, source) {
|
|
|
27536
27797
|
function loadGoldenSuite(path2) {
|
|
27537
27798
|
let text;
|
|
27538
27799
|
try {
|
|
27539
|
-
text = (0,
|
|
27800
|
+
text = (0, import_node_fs31.readFileSync)(path2, "utf8");
|
|
27540
27801
|
} catch (e) {
|
|
27541
27802
|
throw new Error(`golden suite unreadable at ${path2}: ${e.message}`);
|
|
27542
27803
|
}
|
|
@@ -27690,8 +27951,8 @@ async function runRepoIndexHealth(opts) {
|
|
|
27690
27951
|
|
|
27691
27952
|
// src/spawn-policy-core.ts
|
|
27692
27953
|
var import_node_child_process15 = require("node:child_process");
|
|
27693
|
-
var
|
|
27694
|
-
var
|
|
27954
|
+
var import_node_fs32 = require("node:fs");
|
|
27955
|
+
var import_node_path30 = require("node:path");
|
|
27695
27956
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
27696
27957
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
27697
27958
|
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
@@ -27777,7 +28038,7 @@ function runSpawnPolicy(root) {
|
|
|
27777
28038
|
for (const file of files) {
|
|
27778
28039
|
let raw;
|
|
27779
28040
|
try {
|
|
27780
|
-
raw = (0,
|
|
28041
|
+
raw = (0, import_node_fs32.readFileSync)((0, import_node_path30.join)(root, file), "utf8");
|
|
27781
28042
|
} catch {
|
|
27782
28043
|
continue;
|
|
27783
28044
|
}
|
|
@@ -27795,8 +28056,8 @@ function runSpawnPolicy(root) {
|
|
|
27795
28056
|
|
|
27796
28057
|
// src/test-policy-core.ts
|
|
27797
28058
|
var import_node_child_process16 = require("node:child_process");
|
|
27798
|
-
var
|
|
27799
|
-
var
|
|
28059
|
+
var import_node_fs33 = require("node:fs");
|
|
28060
|
+
var import_node_path31 = require("node:path");
|
|
27800
28061
|
var POLICY_FILE = "test-policy.json";
|
|
27801
28062
|
var TEST_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
|
27802
28063
|
var PY_TEST_RE = /(?:^|\/)test_[^/]*\.py$|_test\.py$/;
|
|
@@ -27849,7 +28110,7 @@ function isTestPath(path2) {
|
|
|
27849
28110
|
return TEST_RE.test(path2) || PY_TEST_RE.test(path2);
|
|
27850
28111
|
}
|
|
27851
28112
|
function loadPolicy(root, readFile9 = readFileOrNull2) {
|
|
27852
|
-
const raw = readFile9((0,
|
|
28113
|
+
const raw = readFile9((0, import_node_path31.join)(root, POLICY_FILE));
|
|
27853
28114
|
if (raw == null) return { mandatory: [], declared: false };
|
|
27854
28115
|
try {
|
|
27855
28116
|
return { ...JSON.parse(raw), declared: true };
|
|
@@ -27859,7 +28120,7 @@ function loadPolicy(root, readFile9 = readFileOrNull2) {
|
|
|
27859
28120
|
}
|
|
27860
28121
|
function readFileOrNull2(path2) {
|
|
27861
28122
|
try {
|
|
27862
|
-
return (0,
|
|
28123
|
+
return (0, import_node_fs33.readFileSync)(path2, "utf8");
|
|
27863
28124
|
} catch {
|
|
27864
28125
|
return null;
|
|
27865
28126
|
}
|
|
@@ -27886,12 +28147,12 @@ function classify(changed, policy, present = () => false) {
|
|
|
27886
28147
|
const removedProtected = [...removed].filter((p) => protectedBy.has(p)).map((p) => ({ path: p, why: protectedBy.get(p) ?? "" }));
|
|
27887
28148
|
return { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected };
|
|
27888
28149
|
}
|
|
27889
|
-
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0,
|
|
27890
|
-
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0,
|
|
28150
|
+
function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs33.existsSync)(path2)) {
|
|
28151
|
+
return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path31.join)(root, p)));
|
|
27891
28152
|
}
|
|
27892
|
-
function unresolvedSatisfiers(policy, root, exists = (path2) => (0,
|
|
28153
|
+
function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs33.existsSync)(path2)) {
|
|
27893
28154
|
const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
|
|
27894
|
-
return [...new Set(declared)].filter((p) => !exists((0,
|
|
28155
|
+
return [...new Set(declared)].filter((p) => !exists((0, import_node_path31.join)(root, p)));
|
|
27895
28156
|
}
|
|
27896
28157
|
function evaluate(changed, policy, present = () => false) {
|
|
27897
28158
|
const { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected } = classify(changed, policy, present);
|
|
@@ -28073,13 +28334,13 @@ function changedFilesSince(base, cwd) {
|
|
|
28073
28334
|
}
|
|
28074
28335
|
function runTestPolicy(root, deps = {}) {
|
|
28075
28336
|
const policy = deps.policy ?? loadPolicy(root);
|
|
28076
|
-
const exists = deps.exists ?? ((path2) => (0,
|
|
28337
|
+
const exists = deps.exists ?? ((path2) => (0, import_node_fs33.existsSync)(path2));
|
|
28077
28338
|
const counts = { mandatoryCount: (policy.mandatory ?? []).length, protectedCount: (policy.protected ?? []).length };
|
|
28078
28339
|
const base = deps.changed ? "(injected)" : resolveBase(root, deps.base);
|
|
28079
28340
|
const refusal = deps.changed ? null : untrustworthyRange(root, base);
|
|
28080
28341
|
const changed = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
|
|
28081
28342
|
const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride(base, root) : { override: null, refusals: [] };
|
|
28082
|
-
const present = (path2) => exists((0,
|
|
28343
|
+
const present = (path2) => exists((0, import_node_path31.join)(root, path2));
|
|
28083
28344
|
const removedByThisDiff = removedPaths(changed);
|
|
28084
28345
|
const staleFindings = [];
|
|
28085
28346
|
const unresolved = unresolvedProtectedEntries(policy, root, exists).filter((p) => !removedByThisDiff.has(p));
|
|
@@ -28117,8 +28378,8 @@ function runTestPolicy(root, deps = {}) {
|
|
|
28117
28378
|
}
|
|
28118
28379
|
|
|
28119
28380
|
// src/project-info-sync.ts
|
|
28120
|
-
var
|
|
28121
|
-
var
|
|
28381
|
+
var import_node_fs34 = require("node:fs");
|
|
28382
|
+
var import_node_path32 = require("node:path");
|
|
28122
28383
|
var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
|
|
28123
28384
|
updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
|
|
28124
28385
|
projectV2 { id }
|
|
@@ -28163,14 +28424,14 @@ function sharedName(entries, fallback) {
|
|
|
28163
28424
|
}
|
|
28164
28425
|
function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
28165
28426
|
if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
|
|
28166
|
-
const readmePath = (0,
|
|
28167
|
-
if (!(0,
|
|
28427
|
+
const readmePath = (0, import_node_path32.join)(repoRoot2, "README.md");
|
|
28428
|
+
if (!(0, import_node_fs34.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
|
|
28168
28429
|
const entries = entriesFor(project2, projects);
|
|
28169
28430
|
const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
|
|
28170
28431
|
const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
|
|
28171
28432
|
if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
|
|
28172
28433
|
const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
|
|
28173
|
-
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0,
|
|
28434
|
+
const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs34.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
|
|
28174
28435
|
const lines = [
|
|
28175
28436
|
`# ${projectName}`,
|
|
28176
28437
|
"",
|
|
@@ -28189,8 +28450,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
|
|
|
28189
28450
|
const targetBase = `https://github.com/${targetRepo2}`;
|
|
28190
28451
|
const targetBranch = branchFor(targetRepo2, projects);
|
|
28191
28452
|
const orgDocs = [
|
|
28192
|
-
(0,
|
|
28193
|
-
(0,
|
|
28453
|
+
(0, import_node_fs34.existsSync)((0, import_node_path32.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
|
|
28454
|
+
(0, import_node_fs34.existsSync)((0, import_node_path32.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
|
|
28194
28455
|
].filter(Boolean);
|
|
28195
28456
|
if (orgDocs.length) lines.push("", "## Organisation docs", "", ...orgDocs);
|
|
28196
28457
|
return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines.join("\n")}
|
|
@@ -28212,7 +28473,7 @@ async function syncProjectInfo(plan, client, apply) {
|
|
|
28212
28473
|
}
|
|
28213
28474
|
|
|
28214
28475
|
// src/project-set.ts
|
|
28215
|
-
var UNSET_KEYS = ["oauth", "requiredRuntimeSecrets", "requiredBuildSecrets", "secrets", "edgeDomains", "requiredGcpApis", "publishRequired", "publishDir", "dsManifestPath", "fofuEnabled", "consumesDesignSystem", "ci", "requiredChecks", "ciExemptReason", "gate", "seedCanary"];
|
|
28476
|
+
var UNSET_KEYS = ["oauth", "requiredRuntimeSecrets", "requiredBuildSecrets", "tenantTasks", "secrets", "edgeDomains", "requiredGcpApis", "publishRequired", "publishDir", "dsManifestPath", "fofuEnabled", "consumesDesignSystem", "ci", "requiredChecks", "ciExemptReason", "gate", "seedCanary"];
|
|
28216
28477
|
var UNSET_KEY_SET = new Set(UNSET_KEYS);
|
|
28217
28478
|
var RUNTIME_SECRET_STAGES = ["dev", "rc", "main"];
|
|
28218
28479
|
var SECRET_CONSUMERS = ["runtime", "build", "lambda", "actions", "agent", "box"];
|
|
@@ -28537,6 +28798,43 @@ function parseGateVar(raw) {
|
|
|
28537
28798
|
}
|
|
28538
28799
|
return out;
|
|
28539
28800
|
}
|
|
28801
|
+
function parseTenantTasksVar(raw) {
|
|
28802
|
+
let parsed;
|
|
28803
|
+
try {
|
|
28804
|
+
parsed = JSON.parse(raw);
|
|
28805
|
+
} catch {
|
|
28806
|
+
throw new Error("org project set: tenantTasks must be valid JSON");
|
|
28807
|
+
}
|
|
28808
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("org project set: tenantTasks must be a JSON object");
|
|
28809
|
+
const entries = Object.entries(parsed);
|
|
28810
|
+
if (entries.length > 20) throw new Error("org project set: tenantTasks supports at most 20 declarations");
|
|
28811
|
+
const out = {};
|
|
28812
|
+
const nameRe = /^[a-z0-9][a-z0-9-]{0,62}$/;
|
|
28813
|
+
const serviceRe = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,62}$/;
|
|
28814
|
+
const validStages = /* @__PURE__ */ new Set(["dev", "rc", "main"]);
|
|
28815
|
+
for (const [name, rawTask] of entries) {
|
|
28816
|
+
if (!nameRe.test(name)) throw new Error(`org project set: tenantTasks name ${JSON.stringify(name)} must be lowercase kebab-case`);
|
|
28817
|
+
if (!rawTask || typeof rawTask !== "object" || Array.isArray(rawTask)) throw new Error(`org project set: tenantTasks.${name} must be an object`);
|
|
28818
|
+
const task = rawTask;
|
|
28819
|
+
if (Object.keys(task).some((key) => !["service", "command", "stages", "timeoutSeconds", "artifact"].includes(key))) throw new Error(`org project set: tenantTasks.${name} has an unknown field`);
|
|
28820
|
+
if (typeof task.service !== "string" || !serviceRe.test(task.service)) throw new Error(`org project set: tenantTasks.${name}.service has an unsafe shape`);
|
|
28821
|
+
if (!Array.isArray(task.command) || task.command.length < 1 || task.command.length > 32 || task.command.some((arg) => typeof arg !== "string" || arg.length < 1 || arg.length > 256 || /[\u0000\r\n]/.test(arg))) throw new Error(`org project set: tenantTasks.${name}.command must be 1-32 bounded argv strings`);
|
|
28822
|
+
if (!Array.isArray(task.stages) || task.stages.length < 1 || task.stages.length > 3 || task.stages.some((stage) => typeof stage !== "string" || !validStages.has(stage)) || new Set(task.stages).size !== task.stages.length) throw new Error(`org project set: tenantTasks.${name}.stages must be unique dev/rc/main values`);
|
|
28823
|
+
if (task.timeoutSeconds !== void 0 && (!Number.isInteger(task.timeoutSeconds) || task.timeoutSeconds < 1 || task.timeoutSeconds > 3600)) throw new Error(`org project set: tenantTasks.${name}.timeoutSeconds must be an integer 1..3600`);
|
|
28824
|
+
const artifact = task.artifact ?? "none";
|
|
28825
|
+
if (artifact !== "none" && artifact !== "required") throw new Error(`org project set: tenantTasks.${name}.artifact must be none or required`);
|
|
28826
|
+
const carriesPlaceholder = task.command.some((arg) => arg.includes("{artifact}"));
|
|
28827
|
+
if (artifact === "required" !== carriesPlaceholder) throw new Error(`org project set: tenantTasks.${name} must use {artifact} exactly when artifact is required`);
|
|
28828
|
+
out[name] = {
|
|
28829
|
+
service: task.service,
|
|
28830
|
+
command: task.command,
|
|
28831
|
+
stages: task.stages,
|
|
28832
|
+
...task.timeoutSeconds !== void 0 ? { timeoutSeconds: task.timeoutSeconds } : {},
|
|
28833
|
+
...artifact !== "none" ? { artifact } : {}
|
|
28834
|
+
};
|
|
28835
|
+
}
|
|
28836
|
+
return out;
|
|
28837
|
+
}
|
|
28540
28838
|
var SETTABLE_VAR_KEYS = [
|
|
28541
28839
|
"name",
|
|
28542
28840
|
"division",
|
|
@@ -28556,6 +28854,7 @@ var SETTABLE_VAR_KEYS = [
|
|
|
28556
28854
|
"requiredGcpApis",
|
|
28557
28855
|
"requiredRuntimeSecrets",
|
|
28558
28856
|
"requiredBuildSecrets",
|
|
28857
|
+
"tenantTasks",
|
|
28559
28858
|
"edgeDomains",
|
|
28560
28859
|
"statusFieldId",
|
|
28561
28860
|
"statusOptions",
|
|
@@ -28584,6 +28883,7 @@ var SETTABLE_VAR_HINTS = {
|
|
|
28584
28883
|
requiredGcpApis: "comma-string",
|
|
28585
28884
|
requiredRuntimeSecrets: 'JSON stage map, e.g. {"dev":["KEY"],"rc":["KEY"],"main":["KEY"]}',
|
|
28586
28885
|
requiredBuildSecrets: 'JSON flat array, e.g. ["NODE_AUTH_TOKEN=@github-packages-token"]',
|
|
28886
|
+
tenantTasks: 'JSON map {name:{service,command[],stages[],timeoutSeconds?,artifact?:"required"}}; use {artifact} in argv when required',
|
|
28587
28887
|
secrets: "JSON catalog map keyed by KEY {key,purpose,group,owner,stages[],consumers[]} \u2014 merged per entry; clear with --unset secrets; prefer --secrets-file",
|
|
28588
28888
|
edgeDomains: "JSON {dev,rc,main} domain map",
|
|
28589
28889
|
statusOptions: "JSON name\u2192id map",
|
|
@@ -28656,6 +28956,8 @@ function buildProjectSetPatch(input) {
|
|
|
28656
28956
|
patch[key] = parseRuntimeSecretsVar(raw);
|
|
28657
28957
|
} else if (key === "requiredBuildSecrets") {
|
|
28658
28958
|
patch[key] = parseBuildSecretsVar(raw);
|
|
28959
|
+
} else if (key === "tenantTasks") {
|
|
28960
|
+
patch[key] = parseTenantTasksVar(raw);
|
|
28659
28961
|
} else if (key === "secrets") {
|
|
28660
28962
|
patch[key] = parseSecretsCatalogVar(raw);
|
|
28661
28963
|
} else if (key === "edgeDomains") {
|
|
@@ -28975,8 +29277,8 @@ function writeError(res) {
|
|
|
28975
29277
|
}
|
|
28976
29278
|
|
|
28977
29279
|
// src/secrets-commands.ts
|
|
28978
|
-
var
|
|
28979
|
-
var
|
|
29280
|
+
var import_node_fs35 = require("node:fs");
|
|
29281
|
+
var import_node_path33 = require("node:path");
|
|
28980
29282
|
var import_node_os13 = require("node:os");
|
|
28981
29283
|
|
|
28982
29284
|
// src/secrets-diff.ts
|
|
@@ -29079,18 +29381,18 @@ function collectMap(value, previous = []) {
|
|
|
29079
29381
|
return [...previous, value];
|
|
29080
29382
|
}
|
|
29081
29383
|
async function decryptRailsCredentials(input) {
|
|
29082
|
-
const appDir = (0,
|
|
29384
|
+
const appDir = (0, import_node_path33.resolve)(input.appDir ?? process.cwd());
|
|
29083
29385
|
const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
|
|
29084
29386
|
const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
|
|
29085
|
-
const credentialsPath = (0,
|
|
29086
|
-
const masterKeyPath = (0,
|
|
29387
|
+
const credentialsPath = (0, import_node_path33.resolve)(appDir, credentialsFile);
|
|
29388
|
+
const masterKeyPath = (0, import_node_path33.resolve)(appDir, masterKeyFile);
|
|
29087
29389
|
const env = {
|
|
29088
29390
|
...process.env,
|
|
29089
29391
|
MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
|
|
29090
29392
|
MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
|
|
29091
29393
|
};
|
|
29092
|
-
if ((0,
|
|
29093
|
-
env.RAILS_MASTER_KEY = (0,
|
|
29394
|
+
if ((0, import_node_fs35.existsSync)(masterKeyPath)) {
|
|
29395
|
+
env.RAILS_MASTER_KEY = (0, import_node_fs35.readFileSync)(masterKeyPath, "utf8").trim();
|
|
29094
29396
|
}
|
|
29095
29397
|
const script = [
|
|
29096
29398
|
'require "json"',
|
|
@@ -29100,9 +29402,9 @@ async function decryptRailsCredentials(input) {
|
|
|
29100
29402
|
'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
|
|
29101
29403
|
"puts JSON.generate(config.config)"
|
|
29102
29404
|
].join("\n");
|
|
29103
|
-
const scriptDir = (0,
|
|
29104
|
-
const scriptPath = (0,
|
|
29105
|
-
(0,
|
|
29405
|
+
const scriptDir = (0, import_node_fs35.mkdtempSync)((0, import_node_path33.join)((0, import_node_os13.tmpdir)(), "mmi-rails-decrypt-"));
|
|
29406
|
+
const scriptPath = (0, import_node_path33.join)(scriptDir, "decrypt.rb");
|
|
29407
|
+
(0, import_node_fs35.writeFileSync)(scriptPath, script, "utf8");
|
|
29106
29408
|
try {
|
|
29107
29409
|
const args = ["exec", "ruby", scriptPath];
|
|
29108
29410
|
const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
|
|
@@ -29114,7 +29416,7 @@ async function decryptRailsCredentials(input) {
|
|
|
29114
29416
|
});
|
|
29115
29417
|
return JSON.parse(stdout);
|
|
29116
29418
|
} finally {
|
|
29117
|
-
(0,
|
|
29419
|
+
(0, import_node_fs35.rmSync)(scriptDir, { recursive: true, force: true });
|
|
29118
29420
|
}
|
|
29119
29421
|
}
|
|
29120
29422
|
async function readSecretStdin() {
|
|
@@ -29204,7 +29506,7 @@ function registerSecretsCommands(program3) {
|
|
|
29204
29506
|
let body;
|
|
29205
29507
|
if (o.file) {
|
|
29206
29508
|
try {
|
|
29207
|
-
body = (0,
|
|
29509
|
+
body = (0, import_node_fs35.readFileSync)((0, import_node_path33.resolve)(o.file), "utf8");
|
|
29208
29510
|
} catch (e) {
|
|
29209
29511
|
return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
|
|
29210
29512
|
}
|
|
@@ -29309,7 +29611,7 @@ function registerSecretsCommands(program3) {
|
|
|
29309
29611
|
{
|
|
29310
29612
|
...d,
|
|
29311
29613
|
decryptRailsCredentials,
|
|
29312
|
-
removeFile: (path2) => (0,
|
|
29614
|
+
removeFile: (path2) => (0, import_node_fs35.unlinkSync)((0, import_node_path33.resolve)(o.appDir ?? process.cwd(), path2))
|
|
29313
29615
|
},
|
|
29314
29616
|
{
|
|
29315
29617
|
repo: o.repo,
|
|
@@ -29354,7 +29656,7 @@ function registerSecretsCommands(program3) {
|
|
|
29354
29656
|
}
|
|
29355
29657
|
|
|
29356
29658
|
// src/app-actor.ts
|
|
29357
|
-
var
|
|
29659
|
+
var import_node_crypto6 = require("node:crypto");
|
|
29358
29660
|
var APP_ACTOR_ENV = "MMI_ACTOR";
|
|
29359
29661
|
var APP_VAULT_REPO = "mutmutco/MMI-Hub";
|
|
29360
29662
|
var APP_VAULT_KEYS = ["GITHUB_APP_ID", "GITHUB_APP_INSTALLATION_ID", "GITHUB_APP_PRIVATE_KEY"];
|
|
@@ -29398,7 +29700,7 @@ function mintAppJwt(appId, privateKeyPem, nowSec) {
|
|
|
29398
29700
|
exp: now + APP_JWT_TTL_S,
|
|
29399
29701
|
iss: appId
|
|
29400
29702
|
}));
|
|
29401
|
-
const signer = (0,
|
|
29703
|
+
const signer = (0, import_node_crypto6.createSign)("RSA-SHA256");
|
|
29402
29704
|
signer.update(`${header}.${payload}`);
|
|
29403
29705
|
return `${header}.${payload}.${signer.sign(privateKeyPem, "base64url")}`;
|
|
29404
29706
|
}
|
|
@@ -29514,7 +29816,7 @@ function emitCliCallTelemetry(command) {
|
|
|
29514
29816
|
}
|
|
29515
29817
|
|
|
29516
29818
|
// src/box-commands.ts
|
|
29517
|
-
var
|
|
29819
|
+
var import_node_fs36 = require("node:fs");
|
|
29518
29820
|
|
|
29519
29821
|
// src/box.ts
|
|
29520
29822
|
var BOX_KEYS = {
|
|
@@ -29593,7 +29895,7 @@ function formatBoxTable(boxes) {
|
|
|
29593
29895
|
const addrW = w((b) => b.address || "(no public ipv4)", "ADDRESS");
|
|
29594
29896
|
const projW = w((b) => b.project, "PROJECT");
|
|
29595
29897
|
const statW = w((b) => b.status, "STATUS");
|
|
29596
|
-
const row = (name, addr, proj,
|
|
29898
|
+
const row = (name, addr, proj, stat4, key) => `${name.padEnd(nameW)} ${addr.padEnd(addrW)} ${proj.padEnd(projW)} ${stat4.padEnd(statW)} ${key}`;
|
|
29597
29899
|
const lines = [row("BOX", "ADDRESS", "PROJECT", "STATUS", "SSH KEY (vault path)")];
|
|
29598
29900
|
for (const b of boxes) {
|
|
29599
29901
|
lines.push(row(b.name, b.address || "(no public ipv4)", b.project, b.status, b.sshKeyPath));
|
|
@@ -29717,7 +30019,7 @@ function registerBoxCommands(program3) {
|
|
|
29717
30019
|
}
|
|
29718
30020
|
if (o.json) console.log(JSON.stringify({ box: found, incomplete }, null, 2));
|
|
29719
30021
|
else if (o.ssh && o.script) {
|
|
29720
|
-
(0,
|
|
30022
|
+
(0, import_node_fs36.writeFileSync)(o.script, sshRecipeScript(found), "utf8");
|
|
29721
30023
|
console.log(`wrote ${o.script} \u2014 run: bash "${o.script}"`);
|
|
29722
30024
|
} else if (o.ssh) console.log(`${formatSshRecipe(found)}
|
|
29723
30025
|
${SSH_RECIPE_AGENT_NOTE}`);
|
|
@@ -29731,7 +30033,7 @@ ${SSH_RECIPE_AGENT_NOTE}`);
|
|
|
29731
30033
|
}
|
|
29732
30034
|
|
|
29733
30035
|
// src/schedules-commands.ts
|
|
29734
|
-
var
|
|
30036
|
+
var import_promises6 = require("node:fs/promises");
|
|
29735
30037
|
var import_node_child_process17 = require("node:child_process");
|
|
29736
30038
|
var import_node_util7 = require("node:util");
|
|
29737
30039
|
var execFileP5 = (0, import_node_util7.promisify)(import_node_child_process17.execFile);
|
|
@@ -29763,8 +30065,8 @@ async function repoWorkflowEntries(client, repo) {
|
|
|
29763
30065
|
for (const wf of workflowsList) {
|
|
29764
30066
|
if (typeof wf?.path !== "string" || !wf.path) continue;
|
|
29765
30067
|
if (!wf.path.startsWith(".github/workflows/")) continue;
|
|
29766
|
-
const
|
|
29767
|
-
const name = `${repo}/${
|
|
30068
|
+
const basename8 = wf.path.split("/").pop() ?? wf.path;
|
|
30069
|
+
const name = `${repo}/${basename8.replace(/\.ya?ml$/, "")}`;
|
|
29768
30070
|
if (wf.state !== "active") {
|
|
29769
30071
|
disabled.push(name);
|
|
29770
30072
|
continue;
|
|
@@ -29846,7 +30148,7 @@ async function awsJson(args) {
|
|
|
29846
30148
|
try {
|
|
29847
30149
|
return await run();
|
|
29848
30150
|
} catch {
|
|
29849
|
-
await new Promise((
|
|
30151
|
+
await new Promise((resolve6) => setTimeout(resolve6, AWS_RETRY_DELAY_MS));
|
|
29850
30152
|
return run();
|
|
29851
30153
|
}
|
|
29852
30154
|
}
|
|
@@ -29947,9 +30249,9 @@ function registerSchedulesCommands(program3) {
|
|
|
29947
30249
|
await failGraceful("org schedules --doc: refusing to regenerate the doc from an incomplete read (see warnings above).");
|
|
29948
30250
|
return;
|
|
29949
30251
|
}
|
|
29950
|
-
const docText = await (0,
|
|
30252
|
+
const docText = await (0, import_promises6.readFile)(o.doc, "utf8");
|
|
29951
30253
|
const spliced = spliceDoc(docText, renderDocSection(entries, now.toISOString()));
|
|
29952
|
-
await (0,
|
|
30254
|
+
await (0, import_promises6.writeFile)(o.doc, spliced, "utf8");
|
|
29953
30255
|
reportParked(parkedLines);
|
|
29954
30256
|
reportDrift(drift);
|
|
29955
30257
|
console.log(`org schedules: wrote ${entries.length} entries into ${o.doc}`);
|
|
@@ -30035,8 +30337,8 @@ function registerSchedulesCommands(program3) {
|
|
|
30035
30337
|
}
|
|
30036
30338
|
|
|
30037
30339
|
// src/schedules-lift-command.ts
|
|
30038
|
-
var
|
|
30039
|
-
var
|
|
30340
|
+
var import_promises7 = require("node:fs/promises");
|
|
30341
|
+
var import_node_path34 = require("node:path");
|
|
30040
30342
|
var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
|
|
30041
30343
|
var SCHEDULE_REPO_RE = /^[A-Za-z0-9_.-]+$/;
|
|
30042
30344
|
var SchedulesLiftUsageError = class extends Error {
|
|
@@ -30056,14 +30358,14 @@ var RegistryUnreachableError = class extends Error {
|
|
|
30056
30358
|
async function readWorkflowFiles(dir) {
|
|
30057
30359
|
let names;
|
|
30058
30360
|
try {
|
|
30059
|
-
names = await (0,
|
|
30361
|
+
names = await (0, import_promises7.readdir)(dir);
|
|
30060
30362
|
} catch {
|
|
30061
30363
|
return [];
|
|
30062
30364
|
}
|
|
30063
30365
|
const files = [];
|
|
30064
30366
|
for (const name of names.sort()) {
|
|
30065
30367
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
30066
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0,
|
|
30368
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises7.readFile)((0, import_node_path34.join)(dir, name), "utf8") });
|
|
30067
30369
|
}
|
|
30068
30370
|
return files;
|
|
30069
30371
|
}
|
|
@@ -30211,12 +30513,12 @@ function registerEdgeCommands(program3) {
|
|
|
30211
30513
|
}
|
|
30212
30514
|
|
|
30213
30515
|
// src/bootstrap-commands.ts
|
|
30214
|
-
var
|
|
30516
|
+
var import_node_fs37 = require("node:fs");
|
|
30215
30517
|
var import_node_os14 = require("node:os");
|
|
30216
|
-
var
|
|
30518
|
+
var import_node_path35 = require("node:path");
|
|
30217
30519
|
|
|
30218
30520
|
// src/bootstrap-drift.ts
|
|
30219
|
-
var
|
|
30521
|
+
var import_node_crypto7 = require("node:crypto");
|
|
30220
30522
|
function byteComparableSeeds(manifest, cls) {
|
|
30221
30523
|
return manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self" && s.classes.includes(cls));
|
|
30222
30524
|
}
|
|
@@ -30236,7 +30538,7 @@ function compareSeedBytes(hubContent, repoContent) {
|
|
|
30236
30538
|
return normalize(hubContent) === normalize(repoContent) ? "match" : "drift";
|
|
30237
30539
|
}
|
|
30238
30540
|
function seedContentHash(content) {
|
|
30239
|
-
return (0,
|
|
30541
|
+
return (0, import_node_crypto7.createHash)("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
|
|
30240
30542
|
}
|
|
30241
30543
|
function auditRepoSeedDrift(repo, seeds, hubContents, repoReads) {
|
|
30242
30544
|
const byTarget = new Map(repoReads.map((r) => [r.target, r.content]));
|
|
@@ -31093,13 +31395,13 @@ function registerBootstrapCommands(program3) {
|
|
|
31093
31395
|
client: defaultGitHubClient(),
|
|
31094
31396
|
projectMeta: meta,
|
|
31095
31397
|
deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
|
|
31096
|
-
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0,
|
|
31398
|
+
readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs37.existsSync)(path2) ? (0, import_node_fs37.readFileSync)(path2, "utf8") : null,
|
|
31097
31399
|
// requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
|
|
31098
31400
|
// comma-string — accept either so the seeded value verifies regardless of how it was written.
|
|
31099
31401
|
// #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
|
|
31100
31402
|
// permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
|
|
31101
31403
|
// sanction, which is the pre-#3664 behaviour.
|
|
31102
|
-
sanctionedAdmins: (0,
|
|
31404
|
+
sanctionedAdmins: (0, import_node_fs37.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs37.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
|
|
31103
31405
|
requiredGcpApis: (() => {
|
|
31104
31406
|
const v = meta?.requiredGcpApis;
|
|
31105
31407
|
if (Array.isArray(v)) return v;
|
|
@@ -31152,14 +31454,14 @@ function registerBootstrapCommands(program3) {
|
|
|
31152
31454
|
bootstrap.command("drift").description("#3818: compare every org-owned whole-file seed against MMI-Hub's copy across the registry roster; read-only").option("--repo <owner/repo>", "audit one repo instead of the roster (never a fleet verdict)").option("--json", "machine-readable output").action(async () => {
|
|
31153
31455
|
const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
|
|
31154
31456
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
31155
|
-
if (!(0,
|
|
31457
|
+
if (!(0, import_node_fs37.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
|
|
31156
31458
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
31157
31459
|
if (!seedSource.ok) return fail(`bootstrap drift: ${seedSource.reason}`);
|
|
31158
|
-
const manifest = loadBootstrapSeeds((0,
|
|
31460
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs37.readFileSync)(manifestPath, "utf8"));
|
|
31159
31461
|
const hubContents = /* @__PURE__ */ new Map();
|
|
31160
31462
|
for (const s of manifest.seeds) {
|
|
31161
31463
|
if (s.ownership !== "org" || s.source !== "self") continue;
|
|
31162
|
-
hubContents.set(s.target, (0,
|
|
31464
|
+
hubContents.set(s.target, (0, import_node_fs37.existsSync)(s.target) ? (0, import_node_fs37.readFileSync)(s.target, "utf8") : null);
|
|
31163
31465
|
}
|
|
31164
31466
|
let targets;
|
|
31165
31467
|
let classOf = (_repo) => "deployable";
|
|
@@ -31238,10 +31540,10 @@ function registerBootstrapCommands(program3) {
|
|
|
31238
31540
|
return fail(`bootstrap apply: ${e.message}`);
|
|
31239
31541
|
}
|
|
31240
31542
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
31241
|
-
if (!(0,
|
|
31543
|
+
if (!(0, import_node_fs37.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
|
|
31242
31544
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
31243
31545
|
if (!seedSource.ok) return fail(`bootstrap apply: ${seedSource.reason}`);
|
|
31244
|
-
const manifest = loadBootstrapSeeds((0,
|
|
31546
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs37.readFileSync)(manifestPath, "utf8"));
|
|
31245
31547
|
const baseBranch = o.class === "content" ? "main" : "development";
|
|
31246
31548
|
const slug = parsedRepo.slug;
|
|
31247
31549
|
const onlyTarget = o.only.trim();
|
|
@@ -31252,16 +31554,16 @@ function registerBootstrapCommands(program3) {
|
|
|
31252
31554
|
${known}`);
|
|
31253
31555
|
}
|
|
31254
31556
|
const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
|
|
31255
|
-
const readFile9 = (p) => (0,
|
|
31557
|
+
const readFile9 = (p) => (0, import_node_fs37.existsSync)(p) ? (0, import_node_fs37.readFileSync)(p, "utf8") : null;
|
|
31256
31558
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
31257
31559
|
const putSeed = async (target, content, ref, sha) => {
|
|
31258
|
-
const tmp = (0,
|
|
31259
|
-
(0,
|
|
31560
|
+
const tmp = (0, import_node_path35.join)((0, import_node_os14.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
31561
|
+
(0, import_node_fs37.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
|
|
31260
31562
|
try {
|
|
31261
31563
|
await gh(contentPutInputArgs(repo, target, tmp));
|
|
31262
31564
|
} finally {
|
|
31263
31565
|
try {
|
|
31264
|
-
(0,
|
|
31566
|
+
(0, import_node_fs37.unlinkSync)(tmp);
|
|
31265
31567
|
} catch {
|
|
31266
31568
|
}
|
|
31267
31569
|
}
|
|
@@ -31526,10 +31828,10 @@ LIVE apply to ${repo}:
|
|
|
31526
31828
|
bootstrap.command("propagate").description("#4238: re-entrant canary\u2192wave tick \u2014 plan (or, with --execute, open) per-repo PRs fanning an org-owned seed out to the fleet").option("--target <path>", "the manifest target to propagate (an ownership:org + source:self seed, e.g. .github/workflows/agent-pr.yml)").option("--execute", "LIVE tick via gh (master-gated) \u2014 opens/reuses per-repo seed-propagate PRs; dry-run prints the plan only").option("--json", "machine-readable output").action(async () => {
|
|
31527
31829
|
const o = { target: rawValue("--target", ""), execute: rawFlag("--execute"), json: rawFlag("--json") };
|
|
31528
31830
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
31529
|
-
if (!(0,
|
|
31831
|
+
if (!(0, import_node_fs37.existsSync)(manifestPath)) return fail(`bootstrap propagate: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the desired state this tick propagates`);
|
|
31530
31832
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
31531
31833
|
if (!seedSource.ok) return fail(`bootstrap propagate: ${seedSource.reason}`);
|
|
31532
|
-
const manifest = loadBootstrapSeeds((0,
|
|
31834
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs37.readFileSync)(manifestPath, "utf8"));
|
|
31533
31835
|
const propagatable = manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self");
|
|
31534
31836
|
if (!o.target) {
|
|
31535
31837
|
return fail(`bootstrap propagate: --target <path> is required \u2014 one of:
|
|
@@ -31538,8 +31840,8 @@ LIVE apply to ${repo}:
|
|
|
31538
31840
|
const seed = propagatable.find((s) => s.target === o.target);
|
|
31539
31841
|
if (!seed) return fail(`bootstrap propagate: --target '${o.target}' names no ownership:org + source:self seed in ${manifestPath}. Propagatable targets:
|
|
31540
31842
|
${propagatable.map((s) => s.target).join("\n ")}`);
|
|
31541
|
-
if (!(0,
|
|
31542
|
-
const hubContent = (0,
|
|
31843
|
+
if (!(0, import_node_fs37.existsSync)(seed.target)) return fail(`bootstrap propagate: the Hub's own copy of '${seed.target}' is missing \u2014 nothing to propagate`);
|
|
31844
|
+
const hubContent = (0, import_node_fs37.readFileSync)(seed.target, "utf8");
|
|
31543
31845
|
const isWorkflowSeed = seed.target.startsWith(".github/workflows/");
|
|
31544
31846
|
const cfg = await loadConfig();
|
|
31545
31847
|
const projects = await fetchProjectsList(registryClientDeps(cfg));
|
|
@@ -31548,9 +31850,9 @@ LIVE apply to ${repo}:
|
|
|
31548
31850
|
}
|
|
31549
31851
|
const rosterRepos2 = collectRegistryRepos(projects).filter((r) => r.toLowerCase() !== "mutmutco/mmi-hub");
|
|
31550
31852
|
let independentCount = rosterRepos2.length;
|
|
31551
|
-
if ((0,
|
|
31853
|
+
if ((0, import_node_fs37.existsSync)("projects.json")) {
|
|
31552
31854
|
try {
|
|
31553
|
-
const local = JSON.parse((0,
|
|
31855
|
+
const local = JSON.parse((0, import_node_fs37.readFileSync)("projects.json", "utf8"));
|
|
31554
31856
|
const localRepos = /* @__PURE__ */ new Set();
|
|
31555
31857
|
for (const p of local.projects ?? []) for (const r of p.repos ?? []) {
|
|
31556
31858
|
const full = (r.includes("/") ? r : `mutmutco/${r}`).toLowerCase();
|
|
@@ -31666,13 +31968,13 @@ LIVE apply to ${repo}:
|
|
|
31666
31968
|
} catch {
|
|
31667
31969
|
existingSha = void 0;
|
|
31668
31970
|
}
|
|
31669
|
-
const tmp = (0,
|
|
31670
|
-
(0,
|
|
31971
|
+
const tmp = (0, import_node_path35.join)((0, import_node_os14.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
31972
|
+
(0, import_node_fs37.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, hubContent, branch, existingSha)), "utf8");
|
|
31671
31973
|
try {
|
|
31672
31974
|
await gh(contentPutInputArgs(rec.repo, seed.target, tmp));
|
|
31673
31975
|
} finally {
|
|
31674
31976
|
try {
|
|
31675
|
-
(0,
|
|
31977
|
+
(0, import_node_fs37.unlinkSync)(tmp);
|
|
31676
31978
|
} catch {
|
|
31677
31979
|
}
|
|
31678
31980
|
}
|
|
@@ -31730,10 +32032,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
31730
32032
|
return fail(`bootstrap rollback: ${e.message}`);
|
|
31731
32033
|
}
|
|
31732
32034
|
const manifestPath = "skills/bootstrap/seeds/manifest.json";
|
|
31733
|
-
if (!(0,
|
|
32035
|
+
if (!(0, import_node_fs37.existsSync)(manifestPath)) return fail(`bootstrap rollback: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the manifest names which targets are org-owned and therefore propagated (and rollback-able)`);
|
|
31734
32036
|
const seedSource = await resolveHubSeedSource(execGitForSeedSource);
|
|
31735
32037
|
if (!seedSource.ok) return fail(`bootstrap rollback: ${seedSource.reason}`);
|
|
31736
|
-
const manifest = loadBootstrapSeeds((0,
|
|
32038
|
+
const manifest = loadBootstrapSeeds((0, import_node_fs37.readFileSync)(manifestPath, "utf8"));
|
|
31737
32039
|
const propagatable = manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self");
|
|
31738
32040
|
if (!o.target) {
|
|
31739
32041
|
return fail(`bootstrap rollback: --target <path> is required \u2014 one of:
|
|
@@ -31750,10 +32052,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
31750
32052
|
const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
|
|
31751
32053
|
let candidates;
|
|
31752
32054
|
if (o.record) {
|
|
31753
|
-
if (!(0,
|
|
32055
|
+
if (!(0, import_node_fs37.existsSync)(o.record)) return fail(`bootstrap rollback: --record '${o.record}' not found`);
|
|
31754
32056
|
let parsed;
|
|
31755
32057
|
try {
|
|
31756
|
-
parsed = JSON.parse((0,
|
|
32058
|
+
parsed = JSON.parse((0, import_node_fs37.readFileSync)(o.record, "utf8"));
|
|
31757
32059
|
} catch (e) {
|
|
31758
32060
|
return fail(`bootstrap rollback: --record '${o.record}' is not valid JSON: ${e.message}`);
|
|
31759
32061
|
}
|
|
@@ -31822,13 +32124,13 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
31822
32124
|
} catch {
|
|
31823
32125
|
existingSha = void 0;
|
|
31824
32126
|
}
|
|
31825
|
-
const tmp = (0,
|
|
31826
|
-
(0,
|
|
32127
|
+
const tmp = (0, import_node_path35.join)((0, import_node_os14.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
32128
|
+
(0, import_node_fs37.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
|
|
31827
32129
|
try {
|
|
31828
32130
|
await gh(contentPutInputArgs(repo, seed.target, tmp));
|
|
31829
32131
|
} finally {
|
|
31830
32132
|
try {
|
|
31831
|
-
(0,
|
|
32133
|
+
(0, import_node_fs37.unlinkSync)(tmp);
|
|
31832
32134
|
} catch {
|
|
31833
32135
|
}
|
|
31834
32136
|
}
|
|
@@ -31850,12 +32152,12 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
|
|
|
31850
32152
|
}
|
|
31851
32153
|
|
|
31852
32154
|
// src/stage-commands.ts
|
|
31853
|
-
var
|
|
31854
|
-
var
|
|
32155
|
+
var import_node_fs39 = require("node:fs");
|
|
32156
|
+
var import_node_path37 = require("node:path");
|
|
31855
32157
|
|
|
31856
32158
|
// src/port-registry.ts
|
|
31857
|
-
var
|
|
31858
|
-
var
|
|
32159
|
+
var import_node_fs38 = require("node:fs");
|
|
32160
|
+
var import_node_path36 = require("node:path");
|
|
31859
32161
|
|
|
31860
32162
|
// ../infra/port-geometry.mjs
|
|
31861
32163
|
var PORT_BLOCK = 100;
|
|
@@ -31869,8 +32171,8 @@ function nextPortBlock(registry2) {
|
|
|
31869
32171
|
return [base, base + PORT_SPAN];
|
|
31870
32172
|
}
|
|
31871
32173
|
function loadPortRegistry(path2) {
|
|
31872
|
-
if (!(0,
|
|
31873
|
-
const raw = JSON.parse((0,
|
|
32174
|
+
if (!(0, import_node_fs38.existsSync)(path2)) return {};
|
|
32175
|
+
const raw = JSON.parse((0, import_node_fs38.readFileSync)(path2, "utf8"));
|
|
31874
32176
|
const out = {};
|
|
31875
32177
|
for (const [key, value] of Object.entries(raw)) {
|
|
31876
32178
|
if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
|
|
@@ -31884,9 +32186,9 @@ function ensurePortRange(repo, path2) {
|
|
|
31884
32186
|
const existing = registry2[repo];
|
|
31885
32187
|
if (existing) return existing;
|
|
31886
32188
|
const range = nextPortBlock(registry2);
|
|
31887
|
-
const raw = (0,
|
|
32189
|
+
const raw = (0, import_node_fs38.existsSync)(path2) ? JSON.parse((0, import_node_fs38.readFileSync)(path2, "utf8")) : {};
|
|
31888
32190
|
raw[repo] = range;
|
|
31889
|
-
(0,
|
|
32191
|
+
(0, import_node_fs38.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
|
|
31890
32192
|
return range;
|
|
31891
32193
|
}
|
|
31892
32194
|
function portCursorSeed(registry2) {
|
|
@@ -31908,22 +32210,22 @@ function existingPortRange(repo, registry2) {
|
|
|
31908
32210
|
return registry2[repo] ?? null;
|
|
31909
32211
|
}
|
|
31910
32212
|
function portRangeInfraAt(root, source) {
|
|
31911
|
-
const registryPath = (0,
|
|
31912
|
-
const ddbScriptPath = (0,
|
|
31913
|
-
if (!(0,
|
|
32213
|
+
const registryPath = (0, import_node_path36.join)(root, "infra", "port-ranges.json");
|
|
32214
|
+
const ddbScriptPath = (0, import_node_path36.join)(root, "infra", "port-ddb.mjs");
|
|
32215
|
+
if (!(0, import_node_fs38.existsSync)(registryPath) || !(0, import_node_fs38.existsSync)(ddbScriptPath)) return null;
|
|
31914
32216
|
return { root, source, registryPath, ddbScriptPath };
|
|
31915
32217
|
}
|
|
31916
32218
|
function resolvePortRangeInfra(cwd, packageDir) {
|
|
31917
32219
|
const direct = portRangeInfraAt(cwd, "cwd");
|
|
31918
32220
|
if (direct) return direct;
|
|
31919
|
-
for (let dir = cwd; ; dir = (0,
|
|
31920
|
-
const sibling = portRangeInfraAt((0,
|
|
32221
|
+
for (let dir = cwd; ; dir = (0, import_node_path36.dirname)(dir)) {
|
|
32222
|
+
const sibling = portRangeInfraAt((0, import_node_path36.join)(dir, "MMI-Hub"), "sibling-hub");
|
|
31921
32223
|
if (sibling) return sibling;
|
|
31922
|
-
const parent = (0,
|
|
32224
|
+
const parent = (0, import_node_path36.dirname)(dir);
|
|
31923
32225
|
if (parent === dir) break;
|
|
31924
32226
|
}
|
|
31925
32227
|
if (packageDir) {
|
|
31926
|
-
const pkgRoot = (0,
|
|
32228
|
+
const pkgRoot = (0, import_node_path36.join)(packageDir, "..", "..");
|
|
31927
32229
|
const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
|
|
31928
32230
|
if (pkgFrom) return pkgFrom;
|
|
31929
32231
|
}
|
|
@@ -32117,8 +32419,8 @@ function registerStageCommands(program3) {
|
|
|
32117
32419
|
const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
|
|
32118
32420
|
return decideStage({
|
|
32119
32421
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
32120
|
-
hasCompose: (0,
|
|
32121
|
-
hasEnvExample: (0,
|
|
32422
|
+
hasCompose: (0, import_node_fs39.existsSync)((0, import_node_path37.join)(process.cwd(), "docker-compose.yml")),
|
|
32423
|
+
hasEnvExample: (0, import_node_fs39.existsSync)((0, import_node_path37.join)(process.cwd(), ".env.example"))
|
|
32122
32424
|
});
|
|
32123
32425
|
}
|
|
32124
32426
|
async function fetchStageVaultEnvMerge() {
|
|
@@ -32610,9 +32912,9 @@ function registerBoardCommands(program3) {
|
|
|
32610
32912
|
}
|
|
32611
32913
|
|
|
32612
32914
|
// src/merge-cleanup.ts
|
|
32613
|
-
var
|
|
32614
|
-
var
|
|
32615
|
-
var
|
|
32915
|
+
var import_node_fs40 = require("node:fs");
|
|
32916
|
+
var import_promises9 = require("node:fs/promises");
|
|
32917
|
+
var import_node_path39 = require("node:path");
|
|
32616
32918
|
var import_node_os15 = require("node:os");
|
|
32617
32919
|
var import_node_child_process18 = require("node:child_process");
|
|
32618
32920
|
|
|
@@ -32699,23 +33001,23 @@ function boardAdvanceFailureMessage(result) {
|
|
|
32699
33001
|
}
|
|
32700
33002
|
|
|
32701
33003
|
// src/deferred-registry-store.ts
|
|
32702
|
-
var
|
|
32703
|
-
var
|
|
32704
|
-
var sleep2 = (ms) => new Promise((
|
|
33004
|
+
var import_promises8 = require("node:fs/promises");
|
|
33005
|
+
var import_node_path38 = require("node:path");
|
|
33006
|
+
var sleep2 = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
32705
33007
|
async function atomicWrite(target, contents) {
|
|
32706
33008
|
const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
32707
|
-
await (0,
|
|
33009
|
+
await (0, import_promises8.writeFile)(tmp, contents, "utf8");
|
|
32708
33010
|
try {
|
|
32709
33011
|
await renameWithRetry(tmp, target);
|
|
32710
33012
|
} catch (e) {
|
|
32711
|
-
await (0,
|
|
33013
|
+
await (0, import_promises8.unlink)(tmp).catch(() => void 0);
|
|
32712
33014
|
throw e;
|
|
32713
33015
|
}
|
|
32714
33016
|
}
|
|
32715
33017
|
async function renameWithRetry(from, to, attempts = 5, backoffMs = 20) {
|
|
32716
33018
|
for (let i = 0; ; i++) {
|
|
32717
33019
|
try {
|
|
32718
|
-
await (0,
|
|
33020
|
+
await (0, import_promises8.rename)(from, to);
|
|
32719
33021
|
return;
|
|
32720
33022
|
} catch (e) {
|
|
32721
33023
|
const code = e.code;
|
|
@@ -32727,7 +33029,7 @@ async function renameWithRetry(from, to, attempts = 5, backoffMs = 20) {
|
|
|
32727
33029
|
async function readStrict(registryPath) {
|
|
32728
33030
|
let text;
|
|
32729
33031
|
try {
|
|
32730
|
-
text = await (0,
|
|
33032
|
+
text = await (0, import_promises8.readFile)(registryPath, "utf8");
|
|
32731
33033
|
} catch (e) {
|
|
32732
33034
|
if (e.code === "ENOENT") return [];
|
|
32733
33035
|
throw e;
|
|
@@ -32744,19 +33046,19 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
|
|
|
32744
33046
|
// Lenient read for the sweep's initial fetch: any error (missing/corrupt) yields an empty queue.
|
|
32745
33047
|
read: async () => {
|
|
32746
33048
|
try {
|
|
32747
|
-
return parseDeferredWorktreesFile(await (0,
|
|
33049
|
+
return parseDeferredWorktreesFile(await (0, import_promises8.readFile)(registryPath, "utf8"));
|
|
32748
33050
|
} catch {
|
|
32749
33051
|
return [];
|
|
32750
33052
|
}
|
|
32751
33053
|
},
|
|
32752
33054
|
// Standalone atomic write — THROWS on failure (no best-effort swallow, #2846).
|
|
32753
33055
|
write: async (entries) => {
|
|
32754
|
-
await (0,
|
|
33056
|
+
await (0, import_promises8.mkdir)((0, import_node_path38.dirname)(registryPath), { recursive: true });
|
|
32755
33057
|
await atomicWrite(registryPath, serializeDeferredWorktrees(entries));
|
|
32756
33058
|
},
|
|
32757
33059
|
// Serialized read-modify-write under the repo-wide lock (#2846).
|
|
32758
33060
|
update: async (mutate) => {
|
|
32759
|
-
await (0,
|
|
33061
|
+
await (0, import_promises8.mkdir)((0, import_node_path38.dirname)(registryPath), { recursive: true });
|
|
32760
33062
|
const deadline = Date.now() + opts.maxWaitMs;
|
|
32761
33063
|
for (; ; ) {
|
|
32762
33064
|
const guard = await acquireLock(lockPath, opts, deadline);
|
|
@@ -32920,6 +33222,23 @@ ${err.stderr ?? ""}`;
|
|
|
32920
33222
|
return { step, status: `failed: ${msg}` };
|
|
32921
33223
|
}
|
|
32922
33224
|
}
|
|
33225
|
+
async function bestEffortCloseMissingWorktreeLeases(leaseDir = (0, import_node_path39.join)((0, import_node_os15.homedir)(), ".jerv", "leases"), exists = import_node_fs40.existsSync) {
|
|
33226
|
+
let names = [];
|
|
33227
|
+
try {
|
|
33228
|
+
names = (0, import_node_fs40.readdirSync)(leaseDir);
|
|
33229
|
+
} catch {
|
|
33230
|
+
return;
|
|
33231
|
+
}
|
|
33232
|
+
for (const name of names) {
|
|
33233
|
+
if (!name.endsWith(".json")) continue;
|
|
33234
|
+
try {
|
|
33235
|
+
const rec = JSON.parse((0, import_node_fs40.readFileSync)((0, import_node_path39.join)(leaseDir, name), "utf8"));
|
|
33236
|
+
if (rec.kind !== "worktree" || rec.state === "closed" || typeof rec.ref !== "string" || !rec.ref.trim()) continue;
|
|
33237
|
+
if (!exists(rec.ref)) await bestEffortLeaseClose(rec.ref);
|
|
33238
|
+
} catch {
|
|
33239
|
+
}
|
|
33240
|
+
}
|
|
33241
|
+
}
|
|
32923
33242
|
async function applyGcPlan(plan, remote, opts = {}) {
|
|
32924
33243
|
const result = { removedBranches: [], removedRemoteBranches: [], removedTrackingRefs: [], removedWorktreeDirs: [], refused: [], failed: [], pruned: false };
|
|
32925
33244
|
const beforeWorktrees = parseWorktreePorcelain(
|
|
@@ -32927,7 +33246,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
32927
33246
|
);
|
|
32928
33247
|
const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
32929
33248
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
32930
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
33249
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path39.dirname)((0, import_node_path39.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
32931
33250
|
const gcActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
|
|
32932
33251
|
const owners = readWorktreeOwners(primaryRepoRoot);
|
|
32933
33252
|
const removalNow = Date.now();
|
|
@@ -32936,7 +33255,9 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
32936
33255
|
const activeWorkspaceDeferred = [];
|
|
32937
33256
|
const refusesRemoval = (path2, branch) => {
|
|
32938
33257
|
if (!path2) return false;
|
|
32939
|
-
const activeGuard = decideActiveWorkspaceGuard(path2, activeWorkspaceRoot
|
|
33258
|
+
const activeGuard = decideActiveWorkspaceGuard(path2, activeWorkspaceRoot, process.platform, {
|
|
33259
|
+
cursorAgentHost: isCursorAgentHost()
|
|
33260
|
+
});
|
|
32940
33261
|
if (activeGuard.action === "refuse") {
|
|
32941
33262
|
result.refused.push(activeGuard.message);
|
|
32942
33263
|
const owner2 = findWorktreeOwner(owners, path2);
|
|
@@ -32984,7 +33305,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
32984
33305
|
const cleanup = await cleanupPrMergeLocalBranch(branch.branch, {
|
|
32985
33306
|
beforeWorktrees,
|
|
32986
33307
|
startingPath: branch.worktreePath,
|
|
32987
|
-
pathExists: (p) => (0,
|
|
33308
|
+
pathExists: (p) => (0, import_node_fs40.existsSync)(p),
|
|
32988
33309
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
32989
33310
|
teardownWorktreeStage,
|
|
32990
33311
|
deferredStore,
|
|
@@ -33015,14 +33336,15 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
33015
33336
|
const removeDeps = worktreeRemoveDeps(async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
33016
33337
|
const cleanupRoots = [
|
|
33017
33338
|
opts.root ? resolveExplicitScanRoot(opts.root, primaryRepoRoot) : siblingMmiWorktreesRoot(primaryRepoRoot),
|
|
33018
|
-
agentWorktreesRoot(primaryRepoRoot)
|
|
33339
|
+
agentWorktreesRoot(primaryRepoRoot),
|
|
33340
|
+
...helperWorktreeRoots(primaryRepoRoot)
|
|
33019
33341
|
];
|
|
33020
33342
|
for (const wt of worktreeDirsToRemove) {
|
|
33021
33343
|
const owner = findWorktreeOwner(owners, wt.path);
|
|
33022
33344
|
let removalAttempted = false;
|
|
33023
33345
|
try {
|
|
33024
33346
|
const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, cleanupRoots, {
|
|
33025
|
-
realpath: (path2) => (0,
|
|
33347
|
+
realpath: (path2) => (0, import_node_fs40.realpathSync)(path2)
|
|
33026
33348
|
});
|
|
33027
33349
|
if (!cleanupTarget.ok) {
|
|
33028
33350
|
result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
|
|
@@ -33067,12 +33389,27 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
33067
33389
|
}
|
|
33068
33390
|
}
|
|
33069
33391
|
}
|
|
33392
|
+
for (const head of plan.reapOriginHeads ?? []) {
|
|
33393
|
+
try {
|
|
33394
|
+
await execFileP2("git", ["push", remote, "--delete", head.branch], { timeout: GIT_TIMEOUT_MS });
|
|
33395
|
+
result.removedRemoteBranches.push(head.branch);
|
|
33396
|
+
} catch (e) {
|
|
33397
|
+
const detail = `${e.message}
|
|
33398
|
+
${e.stderr ?? ""}`;
|
|
33399
|
+
if (/not found|does not exist|unable to delete|remote ref does not exist/i.test(detail)) {
|
|
33400
|
+
result.removedRemoteBranches.push(head.branch);
|
|
33401
|
+
} else {
|
|
33402
|
+
result.failed.push(`${head.branch}: origin delete failed (${e.message.split("\n")[0]})`);
|
|
33403
|
+
}
|
|
33404
|
+
}
|
|
33405
|
+
}
|
|
33070
33406
|
try {
|
|
33071
33407
|
await execFileP2("git", ["worktree", "prune"], { timeout: GIT_TIMEOUT_MS });
|
|
33072
33408
|
result.pruned = true;
|
|
33073
33409
|
} catch (e) {
|
|
33074
33410
|
result.failed.push(`worktree prune: ${e.message.split("\n")[0]}`);
|
|
33075
33411
|
}
|
|
33412
|
+
await bestEffortCloseMissingWorktreeLeases();
|
|
33076
33413
|
return result;
|
|
33077
33414
|
}
|
|
33078
33415
|
async function pollGhPrChecks(prNumber, repoArgs) {
|
|
@@ -33109,13 +33446,13 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
|
|
|
33109
33446
|
const commits = JSON.parse(raw).commits ?? [];
|
|
33110
33447
|
const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
|
|
33111
33448
|
if (!body) return void 0;
|
|
33112
|
-
const dir = (0,
|
|
33113
|
-
const path2 = (0,
|
|
33114
|
-
(0,
|
|
33449
|
+
const dir = (0, import_node_fs40.mkdtempSync)((0, import_node_path39.join)((0, import_node_os15.tmpdir)(), "mmi-squash-body-"));
|
|
33450
|
+
const path2 = (0, import_node_path39.join)(dir, "body.txt");
|
|
33451
|
+
(0, import_node_fs40.writeFileSync)(path2, `${body}
|
|
33115
33452
|
`, "utf8");
|
|
33116
33453
|
return { path: path2, cleanup: () => {
|
|
33117
33454
|
try {
|
|
33118
|
-
(0,
|
|
33455
|
+
(0, import_node_fs40.rmSync)(dir, { recursive: true, force: true });
|
|
33119
33456
|
} catch {
|
|
33120
33457
|
}
|
|
33121
33458
|
} };
|
|
@@ -33235,15 +33572,16 @@ async function createDeferredWorktreeStore() {
|
|
|
33235
33572
|
}
|
|
33236
33573
|
var realWorktreeDirRemover = {
|
|
33237
33574
|
probe: (p) => {
|
|
33575
|
+
const target = win32LongPath(p);
|
|
33238
33576
|
let st;
|
|
33239
33577
|
try {
|
|
33240
|
-
st = (0,
|
|
33578
|
+
st = (0, import_node_fs40.lstatSync)(target);
|
|
33241
33579
|
} catch {
|
|
33242
33580
|
return null;
|
|
33243
33581
|
}
|
|
33244
33582
|
if (st.isSymbolicLink()) return "link";
|
|
33245
33583
|
try {
|
|
33246
|
-
(0,
|
|
33584
|
+
(0, import_node_fs40.readlinkSync)(target);
|
|
33247
33585
|
return "link";
|
|
33248
33586
|
} catch {
|
|
33249
33587
|
}
|
|
@@ -33251,7 +33589,7 @@ var realWorktreeDirRemover = {
|
|
|
33251
33589
|
},
|
|
33252
33590
|
readdir: (p) => {
|
|
33253
33591
|
try {
|
|
33254
|
-
return (0,
|
|
33592
|
+
return (0, import_node_fs40.readdirSync)(win32LongPath(p));
|
|
33255
33593
|
} catch {
|
|
33256
33594
|
return [];
|
|
33257
33595
|
}
|
|
@@ -33259,13 +33597,16 @@ var realWorktreeDirRemover = {
|
|
|
33259
33597
|
// A directory reparse point (junction / dir-symlink) is detached with rmdir (unlinks the mount point,
|
|
33260
33598
|
// leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
|
|
33261
33599
|
detachLink: (p) => {
|
|
33600
|
+
const target = win32LongPath(p);
|
|
33262
33601
|
try {
|
|
33263
|
-
(0,
|
|
33602
|
+
(0, import_node_fs40.rmdirSync)(target);
|
|
33264
33603
|
} catch {
|
|
33265
|
-
(0,
|
|
33604
|
+
(0, import_node_fs40.unlinkSync)(target);
|
|
33266
33605
|
}
|
|
33267
33606
|
},
|
|
33268
|
-
|
|
33607
|
+
// #4904: Windows MAX_PATH aborts git worktree remove; the fallback must use \\?\ so the dir
|
|
33608
|
+
// (and its lease) do not survive as an unregistered leftover.
|
|
33609
|
+
removeTree: (p) => (0, import_promises9.rm)(win32LongPath(p), { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
|
|
33269
33610
|
};
|
|
33270
33611
|
async function resolvePrimaryCheckout(execGit) {
|
|
33271
33612
|
try {
|
|
@@ -33277,13 +33618,13 @@ async function resolvePrimaryCheckout(execGit) {
|
|
|
33277
33618
|
function worktreeRemoveDeps(execGit) {
|
|
33278
33619
|
return {
|
|
33279
33620
|
git: execGit,
|
|
33280
|
-
sleep: (ms) => new Promise((
|
|
33621
|
+
sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms)),
|
|
33281
33622
|
// #3064: unlink any reparse point (esp. a `node_modules` junction to base) before the git-native
|
|
33282
33623
|
// `worktree remove --force`, which would otherwise recurse through it and empty the base checkout.
|
|
33283
33624
|
detachReparsePoints: (worktreePath) => detachReparsePoints(worktreePath, realWorktreeDirRemover),
|
|
33284
33625
|
removeWorktreeDir: async (worktreePath) => removeWorktreeTree(worktreePath, await resolvePrimaryCheckout(execGit), realWorktreeDirRemover),
|
|
33285
33626
|
// #4850: verify the directory is actually gone before any caller reports completion.
|
|
33286
|
-
pathExists: (worktreePath) => (0,
|
|
33627
|
+
pathExists: (worktreePath) => (0, import_node_fs40.existsSync)(worktreePath)
|
|
33287
33628
|
};
|
|
33288
33629
|
}
|
|
33289
33630
|
async function worktreeHasStageState(worktreePath) {
|
|
@@ -33297,9 +33638,9 @@ async function worktreeHasStageState(worktreePath) {
|
|
|
33297
33638
|
}
|
|
33298
33639
|
}
|
|
33299
33640
|
function stageStateFileBelongsToWorktree(statePath, worktreePath) {
|
|
33300
|
-
if (!(0,
|
|
33641
|
+
if (!(0, import_node_fs40.existsSync)(statePath)) return false;
|
|
33301
33642
|
try {
|
|
33302
|
-
const state = JSON.parse((0,
|
|
33643
|
+
const state = JSON.parse((0, import_node_fs40.readFileSync)(statePath, "utf8"));
|
|
33303
33644
|
const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
|
|
33304
33645
|
return Boolean(recordedCwd && isPathUnderDirectory2(recordedCwd, worktreePath));
|
|
33305
33646
|
} catch {
|
|
@@ -33456,7 +33797,7 @@ var PR_SNAPSHOT_READ_DELAY_MS = 2e3;
|
|
|
33456
33797
|
async function readRestPrSnapshotWithRetry(prNumber, repo, gh = defaultGhApi, options) {
|
|
33457
33798
|
const retries = options?.retries ?? PR_SNAPSHOT_READ_RETRIES;
|
|
33458
33799
|
const delayMs = options?.delayMs ?? PR_SNAPSHOT_READ_DELAY_MS;
|
|
33459
|
-
const sleep3 = options?.sleep ?? ((ms) => new Promise((
|
|
33800
|
+
const sleep3 = options?.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
|
|
33460
33801
|
let lastError = "no attempt completed";
|
|
33461
33802
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
33462
33803
|
try {
|
|
@@ -33734,14 +34075,15 @@ async function checkDocsIndexAtHead(opts, deps) {
|
|
|
33734
34075
|
}
|
|
33735
34076
|
|
|
33736
34077
|
// src/worktree-lifecycle-commands.ts
|
|
33737
|
-
var
|
|
33738
|
-
var
|
|
33739
|
-
var
|
|
34078
|
+
var import_node_fs42 = require("node:fs");
|
|
34079
|
+
var import_promises10 = require("node:fs/promises");
|
|
34080
|
+
var import_node_os16 = require("node:os");
|
|
34081
|
+
var import_node_path41 = require("node:path");
|
|
33740
34082
|
|
|
33741
34083
|
// src/worktree-install-cache.ts
|
|
33742
|
-
var
|
|
33743
|
-
var
|
|
33744
|
-
var
|
|
34084
|
+
var import_node_crypto8 = require("node:crypto");
|
|
34085
|
+
var import_node_fs41 = require("node:fs");
|
|
34086
|
+
var import_node_path40 = require("node:path");
|
|
33745
34087
|
var CACHE_DIR = "worktree-install-cache";
|
|
33746
34088
|
var MANIFEST = "manifest.json";
|
|
33747
34089
|
var NODE_MODULES2 = "node_modules";
|
|
@@ -33754,24 +34096,24 @@ var LOCKFILE_NAMES = [
|
|
|
33754
34096
|
"package-lock.json"
|
|
33755
34097
|
];
|
|
33756
34098
|
var realWorktreeInstallCacheFs = {
|
|
33757
|
-
exists:
|
|
33758
|
-
readFile: (path2) => (0,
|
|
33759
|
-
lstat: (path2) => (0,
|
|
33760
|
-
copyDir: (from, to) => (0,
|
|
34099
|
+
exists: import_node_fs41.existsSync,
|
|
34100
|
+
readFile: (path2) => (0, import_node_fs41.readFileSync)(path2, "utf8"),
|
|
34101
|
+
lstat: (path2) => (0, import_node_fs41.lstatSync)(path2),
|
|
34102
|
+
copyDir: (from, to) => (0, import_node_fs41.cpSync)(from, to, { recursive: true, force: true }),
|
|
33761
34103
|
mkdirp: (path2) => {
|
|
33762
|
-
(0,
|
|
34104
|
+
(0, import_node_fs41.mkdirSync)(path2, { recursive: true });
|
|
33763
34105
|
},
|
|
33764
|
-
writeFile: (path2, contents) => (0,
|
|
34106
|
+
writeFile: (path2, contents) => (0, import_node_fs41.writeFileSync)(path2, contents, "utf8"),
|
|
33765
34107
|
rm: (path2) => {
|
|
33766
|
-
(0,
|
|
34108
|
+
(0, import_node_fs41.rmSync)(path2, { recursive: true, force: true });
|
|
33767
34109
|
}
|
|
33768
34110
|
};
|
|
33769
34111
|
function hashLockfileBytes(contents) {
|
|
33770
|
-
return (0,
|
|
34112
|
+
return (0, import_node_crypto8.createHash)("sha256").update(contents).digest("hex");
|
|
33771
34113
|
}
|
|
33772
34114
|
function resolveWorktreeInstallLockfile(packageDir, fs2 = realWorktreeInstallCacheFs) {
|
|
33773
34115
|
for (const name of LOCKFILE_NAMES) {
|
|
33774
|
-
const path2 = (0,
|
|
34116
|
+
const path2 = (0, import_node_path40.join)(packageDir, name);
|
|
33775
34117
|
if (!fs2.exists(path2)) continue;
|
|
33776
34118
|
try {
|
|
33777
34119
|
const hash = hashLockfileBytes(fs2.readFile(path2));
|
|
@@ -33786,8 +34128,8 @@ function worktreeInstallCacheEntry(primaryRoot, lockfileHash) {
|
|
|
33786
34128
|
const root = repoRuntimeStatePath(primaryRoot, CACHE_DIR, lockfileHash);
|
|
33787
34129
|
return {
|
|
33788
34130
|
root,
|
|
33789
|
-
manifestPath: (0,
|
|
33790
|
-
nodeModulesPath: (0,
|
|
34131
|
+
manifestPath: (0, import_node_path40.join)(root, MANIFEST),
|
|
34132
|
+
nodeModulesPath: (0, import_node_path40.join)(root, NODE_MODULES2)
|
|
33791
34133
|
};
|
|
33792
34134
|
}
|
|
33793
34135
|
function readWorktreeInstallCacheManifest(manifestPath, fs2 = realWorktreeInstallCacheFs) {
|
|
@@ -33827,7 +34169,7 @@ function invalidateWorktreeInstallCacheEntry(entry, fs2 = realWorktreeInstallCac
|
|
|
33827
34169
|
}
|
|
33828
34170
|
}
|
|
33829
34171
|
function removeMaterializedTree(packageDir, fs2) {
|
|
33830
|
-
const dest = (0,
|
|
34172
|
+
const dest = (0, import_node_path40.join)(packageDir, NODE_MODULES2);
|
|
33831
34173
|
if (!fs2.exists(dest)) return;
|
|
33832
34174
|
fs2.rm(dest);
|
|
33833
34175
|
if (fs2.exists(dest)) {
|
|
@@ -33835,7 +34177,7 @@ function removeMaterializedTree(packageDir, fs2) {
|
|
|
33835
34177
|
}
|
|
33836
34178
|
}
|
|
33837
34179
|
function materializeCachedNodeModules(entry, destPackageDir, fs2 = realWorktreeInstallCacheFs) {
|
|
33838
|
-
const dest = (0,
|
|
34180
|
+
const dest = (0, import_node_path40.join)(destPackageDir, NODE_MODULES2);
|
|
33839
34181
|
try {
|
|
33840
34182
|
if (fs2.exists(dest)) fs2.rm(dest);
|
|
33841
34183
|
fs2.mkdirp(destPackageDir);
|
|
@@ -33850,7 +34192,7 @@ function materializeCachedNodeModules(entry, destPackageDir, fs2 = realWorktreeI
|
|
|
33850
34192
|
}
|
|
33851
34193
|
}
|
|
33852
34194
|
async function storeWorktreeInstallCacheEntry(primaryRoot, lockfile3, command, sourcePackageDir, fs2 = realWorktreeInstallCacheFs, now = Date.now()) {
|
|
33853
|
-
const source = (0,
|
|
34195
|
+
const source = (0, import_node_path40.join)(sourcePackageDir, NODE_MODULES2);
|
|
33854
34196
|
if (!isMaterializableNodeModulesDir(source, fs2)) return;
|
|
33855
34197
|
const entry = worktreeInstallCacheEntry(primaryRoot, lockfile3.hash);
|
|
33856
34198
|
const manifest = {
|
|
@@ -34105,12 +34447,36 @@ function classifyStaleLeaks(input) {
|
|
|
34105
34447
|
remediation: "mmi-cli worktree gc --apply"
|
|
34106
34448
|
});
|
|
34107
34449
|
}
|
|
34450
|
+
for (const leftover of input.originLeftovers ?? []) {
|
|
34451
|
+
leaks.push({
|
|
34452
|
+
kind: "origin-leftover",
|
|
34453
|
+
ref: leftover.branch,
|
|
34454
|
+
detail: leftover.detail,
|
|
34455
|
+
remediation: leftover.autoReap ? "mmi-cli worktree gc --apply (conservative merged-head reap)" : leftover.kind === "open-pr" ? "leave open \u2014 gc will not delete an open PR head" : "mmi-cli worktree gc --apply --train-only (explicit; will not silently delete every non-train branch)"
|
|
34456
|
+
});
|
|
34457
|
+
}
|
|
34458
|
+
for (const helper of input.helperWorktrees ?? []) {
|
|
34459
|
+
leaks.push({
|
|
34460
|
+
kind: "helper-worktree",
|
|
34461
|
+
ref: helper.path,
|
|
34462
|
+
detail: helper.detail,
|
|
34463
|
+
remediation: helper.reapable ? "mmi-cli worktree gc --apply" : "inspect / git -C <primary> worktree remove --force after the helper is abandoned"
|
|
34464
|
+
});
|
|
34465
|
+
}
|
|
34466
|
+
for (const ref of input.missingLeaseRefs ?? []) {
|
|
34467
|
+
leaks.push({
|
|
34468
|
+
kind: "missing-lease",
|
|
34469
|
+
ref,
|
|
34470
|
+
detail: `jerv worktree lease still active but path is gone`,
|
|
34471
|
+
remediation: "mmi-cli worktree gc --apply (closes the lease; JPT #5548 also reaps on sweep)"
|
|
34472
|
+
});
|
|
34473
|
+
}
|
|
34108
34474
|
return leaks;
|
|
34109
34475
|
}
|
|
34110
34476
|
var defaultOrphanDirScanDeps = {
|
|
34111
34477
|
listDirs: (root) => {
|
|
34112
34478
|
try {
|
|
34113
|
-
return (0,
|
|
34479
|
+
return (0, import_node_fs42.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path41.join)(root, e.name));
|
|
34114
34480
|
} catch {
|
|
34115
34481
|
return [];
|
|
34116
34482
|
}
|
|
@@ -34125,8 +34491,12 @@ function scanOrphanDirs(worktreesRoot, worktreeGitRoot, deps = defaultOrphanDirS
|
|
|
34125
34491
|
}
|
|
34126
34492
|
return candidates;
|
|
34127
34493
|
}
|
|
34128
|
-
function formatStaleLeaks(leaks, prLookupFailures = []) {
|
|
34129
|
-
const
|
|
34494
|
+
function formatStaleLeaks(leaks, prLookupFailures = [], audit) {
|
|
34495
|
+
const blocked = Boolean(audit?.blocksGreen);
|
|
34496
|
+
const lines = leaks.length ? [`worktree list --stale: ${leaks.length} leak(s)`] : blocked ? ["worktree list --stale: incomplete \u2014 refusing a clean report"] : ["worktree list --stale: no leaks found"];
|
|
34497
|
+
if (audit) {
|
|
34498
|
+
for (const line of formatEstateAuditLines(audit)) lines.push(` ${line}`);
|
|
34499
|
+
}
|
|
34130
34500
|
for (const leak of leaks) {
|
|
34131
34501
|
lines.push(` [${leak.kind}] ${leak.ref} \u2014 ${leak.detail}`);
|
|
34132
34502
|
lines.push(` fix: ${leak.remediation}`);
|
|
@@ -34276,13 +34646,13 @@ function registerWorktreeCommands(program3) {
|
|
|
34276
34646
|
const detached = headBorn && !symbolicBranch;
|
|
34277
34647
|
const branch = symbolicBranch || (detached ? "HEAD" : "");
|
|
34278
34648
|
if (!wtPath || !branch) return fail("worktree land: not inside a git worktree");
|
|
34279
|
-
const gitFile = (0,
|
|
34280
|
-
const isLinked = (0,
|
|
34649
|
+
const gitFile = (0, import_node_path41.join)(wtPath, ".git");
|
|
34650
|
+
const isLinked = (0, import_node_fs42.existsSync)(gitFile) && (0, import_node_fs42.statSync)(gitFile).isFile();
|
|
34281
34651
|
if (apply && !isLinked) {
|
|
34282
34652
|
return fail("worktree land: run from inside the linked worktree you want to land (this is the primary checkout)");
|
|
34283
34653
|
}
|
|
34284
34654
|
const commonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
|
|
34285
|
-
const primaryCheckout = commonDir ? (0,
|
|
34655
|
+
const primaryCheckout = commonDir ? (0, import_node_path41.dirname)(commonDir) : wtPath;
|
|
34286
34656
|
const localBranchNames = await execFileP2("git", ["-C", primaryCheckout, "for-each-ref", "--format=%(refname:short)", "refs/heads"], { timeout: GIT_TIMEOUT_MS }).then(({ stdout }) => new Set((stdout || "").split("\n").map((l) => l.trim()).filter(Boolean))).catch(() => void 0);
|
|
34287
34657
|
const orphan = classifyOrphanedWorktree({
|
|
34288
34658
|
branch,
|
|
@@ -34320,13 +34690,13 @@ function registerWorktreeCommands(program3) {
|
|
|
34320
34690
|
}
|
|
34321
34691
|
const landStatus = (await execFileP2("git", ["-C", wtPath, "status", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "unreadable" }))).stdout || "";
|
|
34322
34692
|
const gitInWorktree = async (args) => (await execFileP2("git", ["-C", wtPath, ...args], { timeout: GIT_TIMEOUT_MS })).stdout;
|
|
34323
|
-
const orphanDirt = headBorn ? void 0 : await classifyOrphanWorktreeDirt(landStatus, { git: gitInWorktree, readTextFile: (p) => (0,
|
|
34693
|
+
const orphanDirt = headBorn ? void 0 : await classifyOrphanWorktreeDirt(landStatus, { git: gitInWorktree, readTextFile: (p) => (0, import_promises10.readFile)(p, "utf8") });
|
|
34324
34694
|
const landDirty = headBorn ? landStatus.trim().length > 0 : orphanDirt.dirt !== "clean";
|
|
34325
34695
|
if (landDirty) {
|
|
34326
34696
|
return fail(`worktree land: refusing to land '${branch}' \u2014 uncommitted changes in ${wtPath}; commit, stash, or remove the worktree manually`, { code: ERROR_CODES.ERR_BAD_ENUM });
|
|
34327
34697
|
}
|
|
34328
34698
|
const orphanTip = orphan.orphan ? orphanDirt?.tip ?? await readOrphanWorktreeTip(
|
|
34329
|
-
{ git: gitInWorktree, readTextFile: (p) => (0,
|
|
34699
|
+
{ git: gitInWorktree, readTextFile: (p) => (0, import_promises10.readFile)(p, "utf8") },
|
|
34330
34700
|
orphan.kind
|
|
34331
34701
|
) : void 0;
|
|
34332
34702
|
const lastCommit = orphanTip ? {
|
|
@@ -34346,7 +34716,9 @@ function registerWorktreeCommands(program3) {
|
|
|
34346
34716
|
const reportedMergeState = treeOnly ? "not-checked" : mergeVerdict.state;
|
|
34347
34717
|
const landOwner = lookupWorktreeOwner(primaryCheckout, wtPath);
|
|
34348
34718
|
const activeWorkspaceRoot = resolveActiveWorkspaceRoot();
|
|
34349
|
-
const activeGuard = decideActiveWorkspaceGuard(wtPath, activeWorkspaceRoot
|
|
34719
|
+
const activeGuard = decideActiveWorkspaceGuard(wtPath, activeWorkspaceRoot, process.platform, {
|
|
34720
|
+
cursorAgentHost: isCursorAgentHost()
|
|
34721
|
+
});
|
|
34350
34722
|
if (activeGuard.action === "refuse") {
|
|
34351
34723
|
const deferredStore = await createDeferredWorktreeStore();
|
|
34352
34724
|
if (deferredStore) {
|
|
@@ -34422,6 +34794,9 @@ function registerWorktreeCommands(program3) {
|
|
|
34422
34794
|
step: "remove worktree",
|
|
34423
34795
|
status: removeOutcome.status === "removed" ? removeOutcome.recovery ? `done (${removeOutcome.recovery})` : "done" : removeOutcome.remainsOnDisk ? `failed: ${removeOutcome.error ?? "lock held"} \u2014 the directory "${wtPath}" is still on disk; cd every shell out of it (a cwd inside holds it open on Windows), then delete that directory` : `failed: ${removeOutcome.error ?? "lock held"} \u2014 run: git -C "${primaryCheckout}" worktree remove --force "${wtPath}"`
|
|
34424
34796
|
});
|
|
34797
|
+
if (!removeOutcome.remainsOnDisk) {
|
|
34798
|
+
report.push(await bestEffortLeaseClose(wtPath));
|
|
34799
|
+
}
|
|
34425
34800
|
if (!shouldContinueLandCleanup(removeOutcome.status)) {
|
|
34426
34801
|
const result2 = {
|
|
34427
34802
|
dryRun: false,
|
|
@@ -34506,8 +34881,17 @@ function registerWorktreeCommands(program3) {
|
|
|
34506
34881
|
if (o.stale) {
|
|
34507
34882
|
const leaks = classifyStaleLeaks(ctx);
|
|
34508
34883
|
const failures = ctx.prLookupFailures ?? [];
|
|
34509
|
-
|
|
34510
|
-
|
|
34884
|
+
const complete = failures.length === 0 && !ctx.audit?.blocksGreen;
|
|
34885
|
+
if (o.json) {
|
|
34886
|
+
return console.log(JSON.stringify({
|
|
34887
|
+
stale: leaks,
|
|
34888
|
+
count: leaks.length,
|
|
34889
|
+
prLookupFailures: failures,
|
|
34890
|
+
complete,
|
|
34891
|
+
audit: ctx.audit
|
|
34892
|
+
}, null, 2));
|
|
34893
|
+
}
|
|
34894
|
+
return console.log(formatStaleLeaks(leaks, failures, ctx.audit));
|
|
34511
34895
|
}
|
|
34512
34896
|
if (o.json) return console.log(JSON.stringify({ worktrees: ctx.worktrees }, null, 2));
|
|
34513
34897
|
if (!ctx.worktrees.length) return console.log("worktree list: no worktrees");
|
|
@@ -34531,10 +34915,18 @@ async function gatherWorktreeContext() {
|
|
|
34531
34915
|
const branchOut = (await execFileP2("git", ["branch", "--format=%(refname:short)"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
|
|
34532
34916
|
const localBranches = branchOut.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
34533
34917
|
const currentBranch2 = (await execFileP2("git", ["rev-parse", "--abbrev-ref", "HEAD"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || void 0;
|
|
34534
|
-
const
|
|
34918
|
+
const remoteBranchOut = (await execFileP2("git", ["branch", "-r", "--format=%(refname:short)"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
|
|
34919
|
+
const remoteBranches = remoteBranchOut.split(/\r?\n/).map((l) => l.trim()).filter((b) => b.startsWith("origin/") && b !== "origin/HEAD");
|
|
34920
|
+
const remoteNames = remoteBranches.map((b) => b.replace(/^origin\//, ""));
|
|
34921
|
+
const prTargets = [.../* @__PURE__ */ new Set([
|
|
34922
|
+
...localBranches.filter((b) => !PROTECTED_BRANCHES2.has(b)),
|
|
34923
|
+
...remoteNames.filter((b) => !PROTECTED_BRANCHES2.has(b))
|
|
34924
|
+
])];
|
|
34925
|
+
const { prs, failures: prLookupFailures } = await resolveBranchPrs(prTargets, STALE_PR_LOOKUP_LIMIT);
|
|
34535
34926
|
const openPrBranches = /* @__PURE__ */ new Set();
|
|
34536
34927
|
const closedBranches = /* @__PURE__ */ new Set();
|
|
34537
34928
|
const closedUnmergedBranches = /* @__PURE__ */ new Set();
|
|
34929
|
+
const mergedPrBranches = /* @__PURE__ */ new Set();
|
|
34538
34930
|
const byBranch = /* @__PURE__ */ new Map();
|
|
34539
34931
|
for (const pr2 of prs) {
|
|
34540
34932
|
const arr = byBranch.get(pr2.headRefName) ?? [];
|
|
@@ -34545,6 +34937,7 @@ async function gatherWorktreeContext() {
|
|
|
34545
34937
|
if (states.some((s) => s === "OPEN")) openPrBranches.add(br);
|
|
34546
34938
|
else if (states.some((s) => s === "MERGED" || s === "CLOSED")) {
|
|
34547
34939
|
closedBranches.add(br);
|
|
34940
|
+
if (states.some((s) => s === "MERGED")) mergedPrBranches.add(br);
|
|
34548
34941
|
if (!states.some((s) => s === "MERGED") && states.some((s) => s === "CLOSED")) {
|
|
34549
34942
|
closedUnmergedBranches.add(br);
|
|
34550
34943
|
}
|
|
@@ -34556,15 +34949,53 @@ async function gatherWorktreeContext() {
|
|
|
34556
34949
|
if (s) stages.push({ path: wt.path, port: s.port });
|
|
34557
34950
|
}
|
|
34558
34951
|
const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
|
|
34559
|
-
const primaryRepoRoot = worktreeGitRoot ? (0,
|
|
34952
|
+
const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path41.dirname)((0, import_node_path41.dirname)(worktreeGitRoot)) : repoRoot2;
|
|
34560
34953
|
const wtRoot = siblingMmiWorktreesRoot(primaryRepoRoot);
|
|
34561
34954
|
let orphanDirs = [];
|
|
34562
|
-
if ((0,
|
|
34955
|
+
if ((0, import_node_fs42.existsSync)(wtRoot)) {
|
|
34563
34956
|
orphanDirs = scanOrphanDirs(wtRoot, worktreeGitRoot, {
|
|
34564
34957
|
...defaultOrphanDirScanDeps,
|
|
34565
34958
|
listDirs: (root) => worktreeScanDirs(root, primaryRepoRoot, defaultOrphanDirScanDeps.listDirs, isRepoCheckoutDir)
|
|
34566
34959
|
});
|
|
34567
34960
|
}
|
|
34961
|
+
const repoContainer = worktreesRootOf(primaryRepoRoot);
|
|
34962
|
+
if ((0, import_node_fs42.existsSync)(repoContainer)) {
|
|
34963
|
+
for (const dir of defaultOrphanDirScanDeps.listDirs(repoContainer)) {
|
|
34964
|
+
if (orphanDirs.some((o) => o.path === dir)) continue;
|
|
34965
|
+
const inspected = inspectSiblingWorktreeDir(dir, worktreeGitRoot);
|
|
34966
|
+
const classified = classifySiblingWorktreeDir(inspected);
|
|
34967
|
+
if (classified.cleanup) {
|
|
34968
|
+
orphanDirs.push({ path: dir, reason: classified.cleanup.reason });
|
|
34969
|
+
continue;
|
|
34970
|
+
}
|
|
34971
|
+
if (inspected.gitType === "missing" && pathProvesRepoContainerOwnership(dir, repoContainer)) {
|
|
34972
|
+
orphanDirs.push({ path: dir, reason: "orphaned-folder" });
|
|
34973
|
+
}
|
|
34974
|
+
}
|
|
34975
|
+
}
|
|
34976
|
+
const originLeftovers = classifyOriginLeftovers({
|
|
34977
|
+
remoteBranches,
|
|
34978
|
+
localBranches,
|
|
34979
|
+
protectedBranches: PROTECTED_BRANCHES2,
|
|
34980
|
+
openPrBranches,
|
|
34981
|
+
mergedPrBranches,
|
|
34982
|
+
closedUnmergedBranches
|
|
34983
|
+
});
|
|
34984
|
+
const helperWorktrees = scanHelperWorktrees(primaryRepoRoot, worktreeGitRoot);
|
|
34985
|
+
const leaseRefs = readJervWorktreeLeaseRefs();
|
|
34986
|
+
const missingLeaseRefs = leaseRefs.filter((ref) => !(0, import_node_fs42.existsSync)(ref));
|
|
34987
|
+
const remoteUrls = await gitRemoteUrls();
|
|
34988
|
+
const otherCheckouts = discoverOtherPrimaries(primaryRepoRoot).map((path2) => ({
|
|
34989
|
+
path: path2,
|
|
34990
|
+
remoteUrls: gitConfigRemoteUrls(path2)
|
|
34991
|
+
}));
|
|
34992
|
+
const audit = classifyEstateAudit({
|
|
34993
|
+
auditedClone: primaryRepoRoot,
|
|
34994
|
+
remoteUrls,
|
|
34995
|
+
otherCheckouts,
|
|
34996
|
+
leaseRefs,
|
|
34997
|
+
thisWorktreesRoot: repoContainer
|
|
34998
|
+
});
|
|
34568
34999
|
return {
|
|
34569
35000
|
worktrees,
|
|
34570
35001
|
localBranches,
|
|
@@ -34574,9 +35005,96 @@ async function gatherWorktreeContext() {
|
|
|
34574
35005
|
closedUnmergedBranches,
|
|
34575
35006
|
stages,
|
|
34576
35007
|
orphanDirs,
|
|
34577
|
-
prLookupFailures
|
|
35008
|
+
prLookupFailures,
|
|
35009
|
+
originLeftovers,
|
|
35010
|
+
helperWorktrees,
|
|
35011
|
+
missingLeaseRefs,
|
|
35012
|
+
audit
|
|
34578
35013
|
};
|
|
34579
35014
|
}
|
|
35015
|
+
function gitConfigRemoteUrls(checkout) {
|
|
35016
|
+
const gitPath = (0, import_node_path41.join)(checkout, ".git");
|
|
35017
|
+
let configPath = (0, import_node_path41.join)(checkout, ".git", "config");
|
|
35018
|
+
try {
|
|
35019
|
+
const st = (0, import_node_fs42.statSync)(gitPath);
|
|
35020
|
+
if (st.isFile()) return [];
|
|
35021
|
+
} catch {
|
|
35022
|
+
return [];
|
|
35023
|
+
}
|
|
35024
|
+
try {
|
|
35025
|
+
const text = (0, import_node_fs42.readFileSync)(configPath, "utf8");
|
|
35026
|
+
return [...text.matchAll(/^\s*url\s*=\s*(.+)$/gm)].map((m) => m[1].trim());
|
|
35027
|
+
} catch {
|
|
35028
|
+
return [];
|
|
35029
|
+
}
|
|
35030
|
+
}
|
|
35031
|
+
async function gitRemoteUrls() {
|
|
35032
|
+
const out = (await execFileP2("git", ["remote", "-v"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
|
|
35033
|
+
return [...new Set(
|
|
35034
|
+
out.split(/\r?\n/).map((line) => line.trim().split(/\s+/)[1]).filter((url) => Boolean(url))
|
|
35035
|
+
)];
|
|
35036
|
+
}
|
|
35037
|
+
function discoverOtherPrimaries(primaryRepoRoot) {
|
|
35038
|
+
const found = /* @__PURE__ */ new Set();
|
|
35039
|
+
const parent = (0, import_node_path41.dirname)(primaryRepoRoot);
|
|
35040
|
+
try {
|
|
35041
|
+
for (const name of (0, import_node_fs42.readdirSync)(parent)) {
|
|
35042
|
+
const path2 = (0, import_node_path41.join)(parent, name);
|
|
35043
|
+
if (path2 === primaryRepoRoot) continue;
|
|
35044
|
+
if ((0, import_node_fs42.existsSync)((0, import_node_path41.join)(path2, ".git"))) found.add(path2);
|
|
35045
|
+
}
|
|
35046
|
+
} catch {
|
|
35047
|
+
}
|
|
35048
|
+
const mirror = (0, import_node_path41.join)((0, import_node_os16.homedir)(), "Projects", (0, import_node_path41.basename)(primaryRepoRoot));
|
|
35049
|
+
if (mirror !== primaryRepoRoot && (0, import_node_fs42.existsSync)((0, import_node_path41.join)(mirror, ".git"))) found.add(mirror);
|
|
35050
|
+
return [...found];
|
|
35051
|
+
}
|
|
35052
|
+
function readJervWorktreeLeaseRefs(leaseDir = (0, import_node_path41.join)((0, import_node_os16.homedir)(), ".jerv", "leases")) {
|
|
35053
|
+
try {
|
|
35054
|
+
const refs = [];
|
|
35055
|
+
for (const name of (0, import_node_fs42.readdirSync)(leaseDir)) {
|
|
35056
|
+
if (!name.endsWith(".json")) continue;
|
|
35057
|
+
try {
|
|
35058
|
+
const rec = JSON.parse((0, import_node_fs42.readFileSync)((0, import_node_path41.join)(leaseDir, name), "utf8"));
|
|
35059
|
+
if (rec.kind === "worktree" && rec.state !== "closed" && typeof rec.ref === "string" && rec.ref.trim()) {
|
|
35060
|
+
refs.push(rec.ref);
|
|
35061
|
+
}
|
|
35062
|
+
} catch {
|
|
35063
|
+
}
|
|
35064
|
+
}
|
|
35065
|
+
return refs;
|
|
35066
|
+
} catch {
|
|
35067
|
+
return [];
|
|
35068
|
+
}
|
|
35069
|
+
}
|
|
35070
|
+
function scanHelperWorktrees(thisPrimary, thisWorktreeGitRoot) {
|
|
35071
|
+
const primaries = [thisPrimary, ...discoverOtherPrimaries(thisPrimary)];
|
|
35072
|
+
const out = [];
|
|
35073
|
+
for (const primary of primaries) {
|
|
35074
|
+
const gitRoot = primary === thisPrimary ? thisWorktreeGitRoot : (0, import_node_path41.join)(primary, ".git", "worktrees");
|
|
35075
|
+
for (const root of helperWorktreeRoots(primary)) {
|
|
35076
|
+
if (!(0, import_node_fs42.existsSync)(root)) continue;
|
|
35077
|
+
for (const dir of defaultOrphanDirScanDeps.listDirs(root)) {
|
|
35078
|
+
const inspected = inspectSiblingWorktreeDir(dir, gitRoot);
|
|
35079
|
+
const classified = classifySiblingWorktreeDir(inspected);
|
|
35080
|
+
if (classified.cleanup) {
|
|
35081
|
+
out.push({
|
|
35082
|
+
path: dir,
|
|
35083
|
+
detail: `${classified.cleanup.reason} helper worktree under ${root}`,
|
|
35084
|
+
reapable: true
|
|
35085
|
+
});
|
|
35086
|
+
} else {
|
|
35087
|
+
out.push({
|
|
35088
|
+
path: dir,
|
|
35089
|
+
detail: `helper worktree outside ../mmi-worktrees (${classified.skip?.reason ?? inspected.gitType}) at ${dir}`,
|
|
35090
|
+
reapable: false
|
|
35091
|
+
});
|
|
35092
|
+
}
|
|
35093
|
+
}
|
|
35094
|
+
}
|
|
35095
|
+
}
|
|
35096
|
+
return out;
|
|
35097
|
+
}
|
|
34580
35098
|
async function bestEffortGit(args, cwd, step, timeoutMs = GIT_TIMEOUT_MS) {
|
|
34581
35099
|
try {
|
|
34582
35100
|
const fullArgs = cwd ? ["-C", cwd, ...args] : args;
|
|
@@ -34595,8 +35113,8 @@ ${err.stderr ?? ""}`;
|
|
|
34595
35113
|
}
|
|
34596
35114
|
|
|
34597
35115
|
// src/issue-commands.ts
|
|
34598
|
-
var
|
|
34599
|
-
var
|
|
35116
|
+
var import_node_fs43 = require("node:fs");
|
|
35117
|
+
var import_node_crypto9 = require("node:crypto");
|
|
34600
35118
|
var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
|
|
34601
35119
|
var ReparentConflictError = class extends Error {
|
|
34602
35120
|
constructor(message, payload) {
|
|
@@ -34613,7 +35131,7 @@ async function editIssue(client, options, deps = {}) {
|
|
|
34613
35131
|
const url = `https://github.com/${repo}/issues/${parsed.number}`;
|
|
34614
35132
|
const patch = {};
|
|
34615
35133
|
let bodyChanged = false;
|
|
34616
|
-
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0,
|
|
35134
|
+
const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs43.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
|
|
34617
35135
|
if (options.titleFile !== void 0) {
|
|
34618
35136
|
patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
|
|
34619
35137
|
} else if (options.title !== void 0) {
|
|
@@ -34887,7 +35405,7 @@ function rowIdempotencyKey(batchKey, spec) {
|
|
|
34887
35405
|
const identity = `${spec.type}
|
|
34888
35406
|
${spec.title.trim()}
|
|
34889
35407
|
${spec.body ?? ""}`;
|
|
34890
|
-
const hash = (0,
|
|
35408
|
+
const hash = (0, import_node_crypto9.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
|
|
34891
35409
|
return `${batchKey}:${hash}`;
|
|
34892
35410
|
}
|
|
34893
35411
|
var BATCH_SPEC_KEYS = /* @__PURE__ */ new Set(["type", "title", "body", "priority", "labels", "label", "parent", "repo", "surface"]);
|
|
@@ -35218,7 +35736,7 @@ function extendCreateCommand(issue2, batchAttach) {
|
|
|
35218
35736
|
if (opts.batch) {
|
|
35219
35737
|
let specs;
|
|
35220
35738
|
try {
|
|
35221
|
-
const raw = (0,
|
|
35739
|
+
const raw = (0, import_node_fs43.readFileSync)(opts.batch, "utf8");
|
|
35222
35740
|
specs = JSON.parse(raw);
|
|
35223
35741
|
if (!Array.isArray(specs)) throw new Error("batch file must contain a JSON array");
|
|
35224
35742
|
} catch (e) {
|
|
@@ -35293,8 +35811,8 @@ ${lines}`, {
|
|
|
35293
35811
|
}
|
|
35294
35812
|
|
|
35295
35813
|
// src/train-commands.ts
|
|
35296
|
-
var
|
|
35297
|
-
var
|
|
35814
|
+
var import_node_fs44 = require("node:fs");
|
|
35815
|
+
var import_node_path42 = require("node:path");
|
|
35298
35816
|
var RELEASE_BUMP_INTENTS = ["major", "minor", "patch"];
|
|
35299
35817
|
function resolveReleaseBumpIntent(raw) {
|
|
35300
35818
|
const intent = typeof raw === "string" ? raw.trim() : "";
|
|
@@ -35305,7 +35823,7 @@ function resolveReleaseBumpIntent(raw) {
|
|
|
35305
35823
|
}
|
|
35306
35824
|
function readRepoVersion() {
|
|
35307
35825
|
try {
|
|
35308
|
-
return JSON.parse((0,
|
|
35826
|
+
return JSON.parse((0, import_node_fs44.readFileSync)((0, import_node_path42.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
|
|
35309
35827
|
} catch {
|
|
35310
35828
|
return void 0;
|
|
35311
35829
|
}
|
|
@@ -35462,9 +35980,9 @@ function registerDeployCommands(program3) {
|
|
|
35462
35980
|
}
|
|
35463
35981
|
|
|
35464
35982
|
// src/discovery-commands.ts
|
|
35465
|
-
var
|
|
35466
|
-
var
|
|
35467
|
-
var
|
|
35983
|
+
var import_node_fs45 = require("node:fs");
|
|
35984
|
+
var import_node_os17 = require("node:os");
|
|
35985
|
+
var import_node_path43 = require("node:path");
|
|
35468
35986
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
35469
35987
|
async function collectStatus() {
|
|
35470
35988
|
const repo = await resolveRepo();
|
|
@@ -35652,10 +36170,10 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
35652
36170
|
else if (top) nextCommand = `mmi-cli oracle board claim ${top.number} # ${top.title}`;
|
|
35653
36171
|
else nextCommand = "mmi-cli oracle board read \u2014 no claimable items found";
|
|
35654
36172
|
}
|
|
35655
|
-
const home = (0,
|
|
36173
|
+
const home = (0, import_node_os17.homedir)();
|
|
35656
36174
|
const plugin = onboardPluginGate({
|
|
35657
|
-
readKnown: () => readFileSyncSafe((0,
|
|
35658
|
-
readSettings: () => readFileSyncSafe((0,
|
|
36175
|
+
readKnown: () => readFileSyncSafe((0, import_node_path43.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs45.readFileSync),
|
|
36176
|
+
readSettings: () => readFileSyncSafe((0, import_node_path43.join)(home, ".claude", "settings.json"), import_node_fs45.readFileSync)
|
|
35659
36177
|
});
|
|
35660
36178
|
return { track, board, registry: registry2, secrets, plugin, estateCli, doors: opts.doors ?? [], nextCommand };
|
|
35661
36179
|
}
|
|
@@ -35815,7 +36333,7 @@ function formatExplainCommand(cmd, rootName) {
|
|
|
35815
36333
|
return lines.join("\n").trimEnd();
|
|
35816
36334
|
}
|
|
35817
36335
|
function formatExplainGroup(cmd, rootName) {
|
|
35818
|
-
const canonical = (node) =>
|
|
36336
|
+
const canonical = (node) => node.path;
|
|
35819
36337
|
const lines = [
|
|
35820
36338
|
`${rootName} ${canonical(cmd)}${cmd.description ? ` \u2014 ${cmd.description}` : ""}`,
|
|
35821
36339
|
`Category: ${cmd.category} \xB7 Discovery: ${cmd.discovery}`,
|
|
@@ -35841,7 +36359,7 @@ function formatExplainLoop(playbook) {
|
|
|
35841
36359
|
}
|
|
35842
36360
|
function findCommandInManifest(manifest, commandPath3) {
|
|
35843
36361
|
const visit = (command) => {
|
|
35844
|
-
if (command.path === commandPath3 ||
|
|
36362
|
+
if (command.path === commandPath3 || command.flat_path === commandPath3) return command;
|
|
35845
36363
|
for (const child2 of command.subcommands) {
|
|
35846
36364
|
const found = visit(child2);
|
|
35847
36365
|
if (found) return found;
|
|
@@ -35877,7 +36395,7 @@ function registerExplainCommand(program3) {
|
|
|
35877
36395
|
}
|
|
35878
36396
|
|
|
35879
36397
|
// src/pr-commands.ts
|
|
35880
|
-
var
|
|
36398
|
+
var import_promises11 = require("node:fs/promises");
|
|
35881
36399
|
var GC_GH_TIMEOUT_MS4 = 2e4;
|
|
35882
36400
|
var CHECKS_WATCH_POLL_MS = 15e3;
|
|
35883
36401
|
var CHECKS_WATCH_TIMEOUT_MS = 10 * 6e4;
|
|
@@ -36046,10 +36564,10 @@ function registerPrLifecycleCommands(program3) {
|
|
|
36046
36564
|
let body;
|
|
36047
36565
|
try {
|
|
36048
36566
|
if (o.title || o.titleFile) {
|
|
36049
|
-
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile:
|
|
36567
|
+
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises11.readFile, readStdin });
|
|
36050
36568
|
}
|
|
36051
36569
|
if (o.body || o.bodyFile) {
|
|
36052
|
-
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile:
|
|
36570
|
+
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises11.readFile, readStdin });
|
|
36053
36571
|
}
|
|
36054
36572
|
} catch (e) {
|
|
36055
36573
|
return fail(`pr edit: ${e.message}`);
|
|
@@ -36079,7 +36597,7 @@ function registerPrLifecycleCommands(program3) {
|
|
|
36079
36597
|
}
|
|
36080
36598
|
let body;
|
|
36081
36599
|
try {
|
|
36082
|
-
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile:
|
|
36600
|
+
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises11.readFile, readStdin });
|
|
36083
36601
|
} catch (e) {
|
|
36084
36602
|
return fail(`pr comment: ${e.message}`);
|
|
36085
36603
|
}
|
|
@@ -36790,19 +37308,19 @@ function registerOrgHealthQuery(program3, deps = defaultOrgHealthQueryDeps()) {
|
|
|
36790
37308
|
}
|
|
36791
37309
|
|
|
36792
37310
|
// src/plugin-release-catchup.ts
|
|
36793
|
-
var
|
|
36794
|
-
var
|
|
36795
|
-
var
|
|
37311
|
+
var import_node_fs46 = require("node:fs");
|
|
37312
|
+
var import_node_path44 = require("node:path");
|
|
37313
|
+
var import_node_os18 = require("node:os");
|
|
36796
37314
|
var RELEASE_CATCHUP_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
36797
37315
|
var RELEASE_CATCHUP_DISABLE_ENV = "MMI_NO_RELEASE_CATCHUP";
|
|
36798
37316
|
function releaseCatchupStatePath(env = process.env) {
|
|
36799
37317
|
if (env.MMI_RELEASE_CATCHUP_STATE) return env.MMI_RELEASE_CATCHUP_STATE;
|
|
36800
37318
|
if (process.platform === "win32") {
|
|
36801
|
-
const base2 = env.LOCALAPPDATA || (0,
|
|
36802
|
-
return (0,
|
|
37319
|
+
const base2 = env.LOCALAPPDATA || (0, import_node_path44.join)((0, import_node_os18.homedir)(), "AppData", "Local");
|
|
37320
|
+
return (0, import_node_path44.join)(base2, "MMI Future", "mmi-cli", "release-catchup.json");
|
|
36803
37321
|
}
|
|
36804
|
-
const base = env.XDG_STATE_HOME || (0,
|
|
36805
|
-
return (0,
|
|
37322
|
+
const base = env.XDG_STATE_HOME || (0, import_node_path44.join)((0, import_node_os18.homedir)(), ".local", "state");
|
|
37323
|
+
return (0, import_node_path44.join)(base, "mmi-cli", "release-catchup.json");
|
|
36806
37324
|
}
|
|
36807
37325
|
function releaseCatchupDue(state, now, force = false) {
|
|
36808
37326
|
if (force) return true;
|
|
@@ -36812,7 +37330,7 @@ function releaseCatchupDue(state, now, force = false) {
|
|
|
36812
37330
|
function newestCachedPluginVersion(home) {
|
|
36813
37331
|
let names;
|
|
36814
37332
|
try {
|
|
36815
|
-
names = (0,
|
|
37333
|
+
names = (0, import_node_fs46.readdirSync)(pluginCacheRoot(home));
|
|
36816
37334
|
} catch {
|
|
36817
37335
|
return void 0;
|
|
36818
37336
|
}
|
|
@@ -36820,15 +37338,15 @@ function newestCachedPluginVersion(home) {
|
|
|
36820
37338
|
}
|
|
36821
37339
|
function marketplaceClonePath(home) {
|
|
36822
37340
|
try {
|
|
36823
|
-
const parsed = JSON.parse((0,
|
|
37341
|
+
const parsed = JSON.parse((0, import_node_fs46.readFileSync)((0, import_node_path44.join)(home, ".claude", "plugins", "known_marketplaces.json"), "utf8"));
|
|
36824
37342
|
if (parsed.mutmutco?.installLocation) return parsed.mutmutco.installLocation;
|
|
36825
37343
|
} catch {
|
|
36826
37344
|
}
|
|
36827
|
-
return (0,
|
|
37345
|
+
return (0, import_node_path44.join)(home, ".claude", "plugins", "marketplaces", "mutmutco");
|
|
36828
37346
|
}
|
|
36829
37347
|
function readCatalogVersion(home) {
|
|
36830
37348
|
try {
|
|
36831
|
-
const parsed = JSON.parse((0,
|
|
37349
|
+
const parsed = JSON.parse((0, import_node_fs46.readFileSync)((0, import_node_path44.join)(marketplaceClonePath(home), ".claude-plugin", "marketplace.json"), "utf8"));
|
|
36832
37350
|
return parsed.plugins?.find((p) => p.name === "mmi")?.version;
|
|
36833
37351
|
} catch {
|
|
36834
37352
|
return void 0;
|
|
@@ -36836,7 +37354,7 @@ function readCatalogVersion(home) {
|
|
|
36836
37354
|
}
|
|
36837
37355
|
function readMmiInstallRecord(home) {
|
|
36838
37356
|
try {
|
|
36839
|
-
const parsed = JSON.parse((0,
|
|
37357
|
+
const parsed = JSON.parse((0, import_node_fs46.readFileSync)((0, import_node_path44.join)(home, ".claude", "plugins", "installed_plugins.json"), "utf8"));
|
|
36840
37358
|
const record = parsed.plugins?.["mmi@mutmutco"]?.[0];
|
|
36841
37359
|
return record?.version ? { version: record.version, gitCommitSha: record.gitCommitSha } : void 0;
|
|
36842
37360
|
} catch {
|
|
@@ -36865,7 +37383,7 @@ async function runReleaseCatchup(home, env, deps, opts = {}) {
|
|
|
36865
37383
|
cliUpdated = true;
|
|
36866
37384
|
}
|
|
36867
37385
|
const cliUpdateDetail = cliUpdated ? `CLI ${runningCli} \u2192 ${latest}; the next mmi-cli invocation runs ${latest}` : `CLI ${runningCli} is current against released ${latest}`;
|
|
36868
|
-
if (!(0,
|
|
37386
|
+
if (!(0, import_node_fs46.existsSync)(pluginCacheRoot(home))) {
|
|
36869
37387
|
deps.writeState(statePath, { checkedAt: deps.now(), latest });
|
|
36870
37388
|
return {
|
|
36871
37389
|
ok: true,
|
|
@@ -36898,8 +37416,8 @@ async function runReleaseCatchup(home, env, deps, opts = {}) {
|
|
|
36898
37416
|
return { ok: false, detail: `${cliUpdateDetail}; plugin install record could not be cleared (still ${prior.version})` };
|
|
36899
37417
|
}
|
|
36900
37418
|
const installed = await deps.runClaude(["plugin", "install", "mmi@mutmutco"], "claude plugin install mmi@mutmutco");
|
|
36901
|
-
const payload = (0,
|
|
36902
|
-
if (!installed || !(0,
|
|
37419
|
+
const payload = (0, import_node_path44.join)(pluginCacheRoot(home), latest, ".pi-plugin");
|
|
37420
|
+
if (!installed || !(0, import_node_fs46.existsSync)(payload)) {
|
|
36903
37421
|
const why = installed ? `install of ${latest} did not produce a verifiable .pi-plugin payload` : `install of ${latest} failed`;
|
|
36904
37422
|
if (!prior) return { ok: false, detail: `${cliUpdateDetail}; ${why}; no prior record to restore` };
|
|
36905
37423
|
const rollback = await restorePriorRecord(home, prior, deps);
|
|
@@ -37934,10 +38452,10 @@ function checkRepoWorktrees(probe) {
|
|
|
37934
38452
|
ok: !(probe.isOrgRepo && probe.hasRepoLocalWorktrees),
|
|
37935
38453
|
id: "repo-worktrees",
|
|
37936
38454
|
label: "repo worktrees",
|
|
37937
|
-
fix: "repo-local `.worktrees/`
|
|
38455
|
+
fix: "repo-local `.worktrees/` / `.claude/worktrees` are not the canonical path \u2014 use `mmi-cli worktree create <branch>` (sibling `../mmi-worktrees/<Repo>/`), then `mmi-cli worktree gc --apply` for abandoned helper trees",
|
|
37938
38456
|
verbose: [
|
|
37939
38457
|
`org repo: ${probe.isOrgRepo ? "yes" : "no"}`,
|
|
37940
|
-
`repo-local .worktrees/: ${probe.hasRepoLocalWorktrees ? "present" : "absent"}`
|
|
38458
|
+
`repo-local .worktrees/ or .claude/worktrees/: ${probe.hasRepoLocalWorktrees ? "present" : "absent"}`
|
|
37941
38459
|
]
|
|
37942
38460
|
};
|
|
37943
38461
|
}
|
|
@@ -39124,17 +39642,17 @@ function parseOriginRepo(remoteUrl) {
|
|
|
39124
39642
|
}
|
|
39125
39643
|
function ghHostsConfigPath(env, platform2) {
|
|
39126
39644
|
const sep3 = platform2 === "win32" ? "\\" : "/";
|
|
39127
|
-
const
|
|
39645
|
+
const join41 = (...parts) => parts.join(sep3);
|
|
39128
39646
|
const explicit = env.GH_CONFIG_DIR?.trim();
|
|
39129
|
-
if (explicit) return
|
|
39647
|
+
if (explicit) return join41(explicit, "hosts.yml");
|
|
39130
39648
|
if (platform2 === "win32") {
|
|
39131
39649
|
const appData = (env.AppData ?? env.APPDATA)?.trim();
|
|
39132
|
-
return appData ?
|
|
39650
|
+
return appData ? join41(appData, "GitHub CLI", "hosts.yml") : void 0;
|
|
39133
39651
|
}
|
|
39134
39652
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
39135
|
-
if (xdg) return
|
|
39653
|
+
if (xdg) return join41(xdg, "gh", "hosts.yml");
|
|
39136
39654
|
const home = env.HOME?.trim();
|
|
39137
|
-
return home ?
|
|
39655
|
+
return home ? join41(home, ".config", "gh", "hosts.yml") : void 0;
|
|
39138
39656
|
}
|
|
39139
39657
|
function parseGhHostsAccounts(yaml, host = "github.com") {
|
|
39140
39658
|
let hostIndent = null;
|
|
@@ -39184,9 +39702,9 @@ function ghAccountCaveat(announcedLogin, accounts) {
|
|
|
39184
39702
|
}
|
|
39185
39703
|
|
|
39186
39704
|
// src/doctor-io.ts
|
|
39187
|
-
var
|
|
39188
|
-
var
|
|
39189
|
-
var
|
|
39705
|
+
var import_node_fs47 = require("node:fs");
|
|
39706
|
+
var import_node_os19 = require("node:os");
|
|
39707
|
+
var import_node_path45 = require("node:path");
|
|
39190
39708
|
var import_node_child_process19 = require("node:child_process");
|
|
39191
39709
|
var import_node_util8 = require("node:util");
|
|
39192
39710
|
var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process19.execFile);
|
|
@@ -39194,7 +39712,7 @@ var MMI_PLUGIN_ID2 = "mmi@mutmutco";
|
|
|
39194
39712
|
function installedClaudePluginVersion() {
|
|
39195
39713
|
try {
|
|
39196
39714
|
const file = JSON.parse(
|
|
39197
|
-
(0,
|
|
39715
|
+
(0, import_node_fs47.readFileSync)((0, import_node_path45.join)((0, import_node_os19.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
|
|
39198
39716
|
);
|
|
39199
39717
|
const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
|
|
39200
39718
|
if (versions.length === 0) return void 0;
|
|
@@ -39205,7 +39723,7 @@ function installedClaudePluginVersion() {
|
|
|
39205
39723
|
}
|
|
39206
39724
|
function manifestVersion(path2) {
|
|
39207
39725
|
try {
|
|
39208
|
-
const manifest = JSON.parse((0,
|
|
39726
|
+
const manifest = JSON.parse((0, import_node_fs47.readFileSync)(path2, "utf8"));
|
|
39209
39727
|
return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : void 0;
|
|
39210
39728
|
} catch {
|
|
39211
39729
|
return void 0;
|
|
@@ -39215,22 +39733,22 @@ function installedSurfacePluginVersion(surface) {
|
|
|
39215
39733
|
const token = surfaceToken(surface);
|
|
39216
39734
|
if (token === "kilo") {
|
|
39217
39735
|
try {
|
|
39218
|
-
const stamp = (0,
|
|
39736
|
+
const stamp = (0, import_node_fs47.readFileSync)((0, import_node_path45.join)((0, import_node_os19.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
|
|
39219
39737
|
return stamp || void 0;
|
|
39220
39738
|
} catch {
|
|
39221
39739
|
return void 0;
|
|
39222
39740
|
}
|
|
39223
39741
|
}
|
|
39224
39742
|
if (token === "cursor") {
|
|
39225
|
-
return manifestVersion((0,
|
|
39743
|
+
return manifestVersion((0, import_node_path45.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
|
|
39226
39744
|
}
|
|
39227
39745
|
if (token === "jervcode") {
|
|
39228
39746
|
const entry = mmiPiWrapperEntry();
|
|
39229
39747
|
if (!entry) return void 0;
|
|
39230
|
-
return manifestVersion((0,
|
|
39748
|
+
return manifestVersion((0, import_node_path45.join)(decodeURIComponent(entry.replace(/^file:\/\/\/?/, "")), "package.json"));
|
|
39231
39749
|
}
|
|
39232
39750
|
if (token === "kimi") {
|
|
39233
|
-
return manifestVersion((0,
|
|
39751
|
+
return manifestVersion((0, import_node_path45.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
|
|
39234
39752
|
}
|
|
39235
39753
|
if (token === "claude") return installedClaudePluginVersion();
|
|
39236
39754
|
if (token !== "codex") return void 0;
|
|
@@ -39268,13 +39786,13 @@ function worktreeRootSync() {
|
|
|
39268
39786
|
}
|
|
39269
39787
|
var gitignorePath = () => {
|
|
39270
39788
|
const root = worktreeRootSync();
|
|
39271
|
-
return root === null ? null : (0,
|
|
39789
|
+
return root === null ? null : (0, import_node_path45.join)(root, ".gitignore");
|
|
39272
39790
|
};
|
|
39273
39791
|
function readGitignore() {
|
|
39274
39792
|
const path2 = gitignorePath();
|
|
39275
39793
|
if (path2 === null) return null;
|
|
39276
39794
|
try {
|
|
39277
|
-
return (0,
|
|
39795
|
+
return (0, import_node_fs47.readFileSync)(path2, "utf8");
|
|
39278
39796
|
} catch {
|
|
39279
39797
|
return null;
|
|
39280
39798
|
}
|
|
@@ -39283,7 +39801,7 @@ function writeGitignore(content) {
|
|
|
39283
39801
|
const path2 = gitignorePath();
|
|
39284
39802
|
if (path2 === null) return false;
|
|
39285
39803
|
try {
|
|
39286
|
-
(0,
|
|
39804
|
+
(0, import_node_fs47.writeFileSync)(path2, content, "utf8");
|
|
39287
39805
|
return true;
|
|
39288
39806
|
} catch {
|
|
39289
39807
|
return false;
|
|
@@ -39302,7 +39820,7 @@ async function repoRoot() {
|
|
|
39302
39820
|
}
|
|
39303
39821
|
function hasRepoLocalWorktrees() {
|
|
39304
39822
|
const root = worktreeRootSync();
|
|
39305
|
-
return root !== null && (0,
|
|
39823
|
+
return root !== null && ((0, import_node_fs47.existsSync)((0, import_node_path45.join)(root, ".worktrees")) || (0, import_node_fs47.existsSync)((0, import_node_path45.join)(root, ".claude", "worktrees")));
|
|
39306
39824
|
}
|
|
39307
39825
|
|
|
39308
39826
|
// src/cross-repo-filing-issue.ts
|
|
@@ -39398,7 +39916,7 @@ function binaryOnPath(bin) {
|
|
|
39398
39916
|
for (const dir of pathEnvEntries(process.env.PATH ?? "")) {
|
|
39399
39917
|
for (const ext of exts) {
|
|
39400
39918
|
try {
|
|
39401
|
-
if ((0,
|
|
39919
|
+
if ((0, import_node_fs48.existsSync)((0, import_node_path46.join)(dir, `${bin}${ext}`))) return true;
|
|
39402
39920
|
} catch {
|
|
39403
39921
|
}
|
|
39404
39922
|
}
|
|
@@ -39420,8 +39938,8 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
39420
39938
|
function ghMultiAccountCaveat(announcedLogin) {
|
|
39421
39939
|
try {
|
|
39422
39940
|
const hostsPath = ghHostsConfigPath(process.env, process.platform);
|
|
39423
|
-
if (!hostsPath || !(0,
|
|
39424
|
-
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0,
|
|
39941
|
+
if (!hostsPath || !(0, import_node_fs48.existsSync)(hostsPath)) return void 0;
|
|
39942
|
+
return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs48.readFileSync)(hostsPath, "utf8")));
|
|
39425
39943
|
} catch {
|
|
39426
39944
|
return void 0;
|
|
39427
39945
|
}
|
|
@@ -39429,12 +39947,12 @@ function ghMultiAccountCaveat(announcedLogin) {
|
|
|
39429
39947
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
39430
39948
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
39431
39949
|
function envHealLockPath(home) {
|
|
39432
|
-
return (0,
|
|
39950
|
+
return (0, import_node_path46.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
39433
39951
|
}
|
|
39434
39952
|
async function withEnvHealLock(what, run) {
|
|
39435
39953
|
try {
|
|
39436
39954
|
return await withFileLock(
|
|
39437
|
-
envHealLockPath((0,
|
|
39955
|
+
envHealLockPath((0, import_node_os20.homedir)()),
|
|
39438
39956
|
{ staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
|
|
39439
39957
|
run
|
|
39440
39958
|
);
|
|
@@ -39531,7 +40049,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39531
40049
|
const configRoot = surfaceConfigRoot(surface);
|
|
39532
40050
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
39533
40051
|
const plan = buildPluginCachePlan(
|
|
39534
|
-
(0,
|
|
40052
|
+
(0, import_node_os20.homedir)(),
|
|
39535
40053
|
running,
|
|
39536
40054
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
39537
40055
|
{ configRoot, includeStaging: surface !== "codex" }
|
|
@@ -39555,14 +40073,14 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39555
40073
|
const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
|
|
39556
40074
|
const installed = installedActivePluginVersion(surface);
|
|
39557
40075
|
const plan = buildPluginCachePlan(
|
|
39558
|
-
(0,
|
|
40076
|
+
(0, import_node_os20.homedir)(),
|
|
39559
40077
|
running,
|
|
39560
40078
|
pluginCacheFsDeps(configRoot, () => 0),
|
|
39561
40079
|
{ configRoot, includeStaging: surface !== "codex", installedVersion: installed }
|
|
39562
40080
|
);
|
|
39563
40081
|
const result = applyPluginCachePlan(
|
|
39564
40082
|
plan,
|
|
39565
|
-
(p) => (0,
|
|
40083
|
+
(p) => (0, import_node_fs48.rmSync)(p, { recursive: true }),
|
|
39566
40084
|
stagingApplyFsGuard(configRoot)
|
|
39567
40085
|
);
|
|
39568
40086
|
return {
|
|
@@ -39590,12 +40108,12 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39590
40108
|
piPluginState: () => {
|
|
39591
40109
|
const env = { ...process.env };
|
|
39592
40110
|
delete env.CLAUDE_PLUGIN_ROOT;
|
|
39593
|
-
return readPiPluginState((0,
|
|
40111
|
+
return readPiPluginState((0, import_node_os20.homedir)(), env);
|
|
39594
40112
|
},
|
|
39595
40113
|
healPiPlugin: () => {
|
|
39596
40114
|
const env = { ...process.env };
|
|
39597
40115
|
delete env.CLAUDE_PLUGIN_ROOT;
|
|
39598
|
-
return healPiPluginRegistration((0,
|
|
40116
|
+
return healPiPluginRegistration((0, import_node_os20.homedir)(), env);
|
|
39599
40117
|
},
|
|
39600
40118
|
// #4743: the global `claude` binary left as a ~500-byte placeholder by a self-update that died
|
|
39601
40119
|
// EBUSY mid-install. A local read (package.json + the first bytes of two files) — no npm spawn:
|
|
@@ -39634,7 +40152,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39634
40152
|
disableAutoUpdate: () => {
|
|
39635
40153
|
if (detectSurface(process.env) === "codex") return void 0;
|
|
39636
40154
|
return disableOrgMarketplaceBackgroundUpdates(
|
|
39637
|
-
(0,
|
|
40155
|
+
(0, import_node_path46.join)((0, import_node_os20.homedir)(), ...KNOWN_MARKETPLACES_RELATIVE),
|
|
39638
40156
|
[MMI_MARKETPLACE_NAME]
|
|
39639
40157
|
);
|
|
39640
40158
|
}
|
|
@@ -39648,7 +40166,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39648
40166
|
// adopted the generated routing index (MMG-Unlive: `docs/Archive/**` only, no index, no gate.yml) would
|
|
39649
40167
|
// get a permanent — demanding an artifact it never asked for.
|
|
39650
40168
|
docsIndexState: (root) => {
|
|
39651
|
-
if (!(0,
|
|
40169
|
+
if (!(0, import_node_fs48.existsSync)((0, import_node_path46.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
39652
40170
|
const real = createDocsIndexDeps(root);
|
|
39653
40171
|
let docs2;
|
|
39654
40172
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -39657,7 +40175,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39657
40175
|
},
|
|
39658
40176
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
39659
40177
|
healDocsIndex: (root) => {
|
|
39660
|
-
if (!(0,
|
|
40178
|
+
if (!(0, import_node_fs48.existsSync)((0, import_node_path46.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
39661
40179
|
const real = createDocsIndexDeps(root);
|
|
39662
40180
|
let docs2;
|
|
39663
40181
|
const listDocs = () => docs2 ??= real.listDocs();
|
|
@@ -39695,8 +40213,8 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39695
40213
|
});
|
|
39696
40214
|
const raced = await Promise.race([
|
|
39697
40215
|
work.then((r) => ({ ...r, timedOut: false })),
|
|
39698
|
-
new Promise((
|
|
39699
|
-
ceiling = setTimeout(() =>
|
|
40216
|
+
new Promise((resolve6) => {
|
|
40217
|
+
ceiling = setTimeout(() => resolve6({ timedOut: true, scanned: 0, findings: 0, fixed: 0, failed: 0 }), BOARD_DOCTOR_TIMEOUT_MS);
|
|
39700
40218
|
})
|
|
39701
40219
|
]);
|
|
39702
40220
|
if (raced.timedOut) return { scanned: 0, findings: 0, fixed: 0, failed: 0, timedOut: true };
|
|
@@ -39729,8 +40247,8 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
39729
40247
|
incomplete: nb.incomplete,
|
|
39730
40248
|
timedOut: false
|
|
39731
40249
|
})),
|
|
39732
|
-
new Promise((
|
|
39733
|
-
ceiling = setTimeout(() =>
|
|
40250
|
+
new Promise((resolve6) => {
|
|
40251
|
+
ceiling = setTimeout(() => resolve6({ driftLines: [], incomplete: [], timedOut: true }), SCHEDULES_DRIFT_TIMEOUT_MS);
|
|
39734
40252
|
})
|
|
39735
40253
|
]);
|
|
39736
40254
|
if (!raced.timedOut && raced.incomplete.length === 0) writeSchedulesDriftCache(cachePath, raced.driftLines);
|
|
@@ -40023,19 +40541,19 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
40023
40541
|
});
|
|
40024
40542
|
var rules = program2.command("rules").description("org-managed .gitignore delivery");
|
|
40025
40543
|
rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").option("--json", "machine-readable output").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
|
|
40026
|
-
const path2 = (0,
|
|
40027
|
-
const current = (0,
|
|
40544
|
+
const path2 = (0, import_node_path46.join)(process.cwd(), ".gitignore");
|
|
40545
|
+
const current = (0, import_node_fs48.existsSync)(path2) ? (0, import_node_fs48.readFileSync)(path2, "utf8") : null;
|
|
40028
40546
|
const plan = planManagedGitignore(current);
|
|
40029
40547
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
40030
40548
|
if (opts.json) {
|
|
40031
|
-
if (opts.write && plan.changed) (0,
|
|
40549
|
+
if (opts.write && plan.changed) (0, import_node_fs48.writeFileSync)(path2, plan.content, "utf8");
|
|
40032
40550
|
console.log(JSON.stringify(plan, null, 2));
|
|
40033
40551
|
if (!opts.write && plan.changed) process.exitCode = 1;
|
|
40034
40552
|
return;
|
|
40035
40553
|
}
|
|
40036
40554
|
if (opts.write) {
|
|
40037
40555
|
if (plan.changed) {
|
|
40038
|
-
(0,
|
|
40556
|
+
(0, import_node_fs48.writeFileSync)(path2, plan.content, "utf8");
|
|
40039
40557
|
console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
|
|
40040
40558
|
} else {
|
|
40041
40559
|
console.log("mmi-cli devops org rules gitignore: up to date");
|
|
@@ -40177,7 +40695,7 @@ gcCmd.command("sweep-deferred").description("retry IDE-locked deferred worktree
|
|
|
40177
40695
|
process.exit(process.exitCode ?? 0);
|
|
40178
40696
|
});
|
|
40179
40697
|
});
|
|
40180
|
-
gcCmd.option("--dry-run", "show what would be deleted (default)").option("--apply", "delete only the listed clean merged/closed PR local+remote branches, linked worktrees, and stale tracking refs").option("--json", "machine-readable output").option("--scratch", "prune safe local scratch (#1864) instead of git branches/refs; local plans are surfaced advisory-only, never auto-pruned").option("--remote <name>", "remote name", "origin").option("--limit <n>", "PRs to read PER BRANCH \u2014 an effort bound, not a correctness one: merge state is resolved per branch, so no branch is ever skipped for being old (#4227)", "200").option("--force", "remove worktrees even when the ownership registry shows another session created or worked in them recently (#3580), or when a dead worktree directory still holds leftover files (#4779)").option("--root <path>", "sweep an explicitly named worktrees root instead of the authoritative ../mmi-worktrees (#3471) \u2014 descends into this repo container when present; ownership, dead-dir, and content guards still apply").action(async (o) => {
|
|
40698
|
+
gcCmd.option("--dry-run", "show what would be deleted (default)").option("--apply", "delete only the listed clean merged/closed PR local+remote branches, linked worktrees, and stale tracking refs").option("--json", "machine-readable output").option("--scratch", "prune safe local scratch (#1864) instead of git branches/refs; local plans are surfaced advisory-only, never auto-pruned").option("--remote <name>", "remote name", "origin").option("--limit <n>", "PRs to read PER BRANCH \u2014 an effort bound, not a correctness one: merge state is resolved per branch, so no branch is ever skipped for being old (#4227)", "200").option("--force", "remove worktrees even when the ownership registry shows another session created or worked in them recently (#3580), or when a dead worktree directory still holds leftover files (#4779)").option("--root <path>", "sweep an explicitly named worktrees root instead of the authoritative ../mmi-worktrees (#3471) \u2014 descends into this repo container when present; ownership, dead-dir, and content guards still apply").option("--train-only", "explicit keep-only-train path: also reap closed-not-merged / no-PR origin leftovers; never deletes open PR heads (#4903)").action(async (o) => {
|
|
40181
40699
|
if (o.apply && o.dryRun) return fail("worktree gc: choose either --dry-run or --apply");
|
|
40182
40700
|
if (o.scratch) {
|
|
40183
40701
|
try {
|
|
@@ -40194,18 +40712,19 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
|
|
|
40194
40712
|
if (!Number.isFinite(limit) || limit < 1) return fail("worktree gc: --limit must be a positive integer");
|
|
40195
40713
|
let root;
|
|
40196
40714
|
if (o.root !== void 0) {
|
|
40197
|
-
root = (0,
|
|
40198
|
-
if (!(0,
|
|
40715
|
+
root = (0, import_node_path46.resolve)(o.root);
|
|
40716
|
+
if (!(0, import_node_fs48.existsSync)(root) || !(0, import_node_fs48.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
|
|
40199
40717
|
const gcRepoRoot = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
40200
40718
|
if (isPathUnderDirectory2(gcRepoRoot, root)) {
|
|
40201
40719
|
return fail(`worktree gc: --root ${root} contains this checkout \u2014 name a worktrees root, not the repo or an ancestor of it`);
|
|
40202
40720
|
}
|
|
40203
40721
|
}
|
|
40204
40722
|
try {
|
|
40205
|
-
const plan = await gcPlan(o.remote, limit, { root });
|
|
40723
|
+
const plan = await gcPlan(o.remote, limit, { root, trainOnly: o.trainOnly });
|
|
40724
|
+
const auditedClone = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
|
|
40206
40725
|
if (root && !o.json) console.log(`worktree gc: scanning the explicitly named root ${root} for worktrees proven to belong to this repo
|
|
40207
40726
|
`);
|
|
40208
|
-
if (o.apply && !o.json) console.log(formatGcPlan(plan, true));
|
|
40727
|
+
if (o.apply && !o.json) console.log(formatGcPlan(plan, true, auditedClone));
|
|
40209
40728
|
let applyResult;
|
|
40210
40729
|
if (o.apply) {
|
|
40211
40730
|
const deferredStore = await createDeferredWorktreeStore();
|
|
@@ -40224,7 +40743,7 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
|
|
|
40224
40743
|
if (o.json) {
|
|
40225
40744
|
console.log(JSON.stringify({ dryRun: !o.apply, remote: o.remote, ...root ? { root } : {}, plan, applyResult }, null, 2));
|
|
40226
40745
|
} else if (!o.apply) {
|
|
40227
|
-
console.log(formatGcPlan(plan, false));
|
|
40746
|
+
console.log(formatGcPlan(plan, false, auditedClone));
|
|
40228
40747
|
} else {
|
|
40229
40748
|
if (applyResult) console.log(`
|
|
40230
40749
|
${renderGcApplyResult(applyResult, plan.skipped)}`);
|
|
@@ -40243,7 +40762,7 @@ function runWorktreeInstall(command, cwd, quiet, opts) {
|
|
|
40243
40762
|
quiet ? "ignore" : "inherit",
|
|
40244
40763
|
"pipe"
|
|
40245
40764
|
];
|
|
40246
|
-
return new Promise((
|
|
40765
|
+
return new Promise((resolve6, reject) => {
|
|
40247
40766
|
const child2 = opts?.shell ? (0, import_node_child_process20.spawn)(command, { cwd, stdio, windowsHide: true, shell: true }) : (() => {
|
|
40248
40767
|
const [bin, ...args] = command.split(" ");
|
|
40249
40768
|
const file = isWin2 ? "cmd.exe" : bin;
|
|
@@ -40270,7 +40789,7 @@ function runWorktreeInstall(command, cwd, quiet, opts) {
|
|
|
40270
40789
|
});
|
|
40271
40790
|
child2.on("exit", (code) => {
|
|
40272
40791
|
clearTimeout(timer);
|
|
40273
|
-
if (code === 0) return
|
|
40792
|
+
if (code === 0) return resolve6();
|
|
40274
40793
|
const tail = stderrTail.trim();
|
|
40275
40794
|
reject(new Error(`${command} exited ${code} in ${cwd}${tail ? `
|
|
40276
40795
|
${tail}` : ""}`));
|
|
@@ -40288,11 +40807,12 @@ async function currentWorktreeRemovalContext(command, force) {
|
|
|
40288
40807
|
actor: describeActor({ env: process.env, surface: detectSurface(process.env), cwd }),
|
|
40289
40808
|
command,
|
|
40290
40809
|
...force ? { force: true } : {},
|
|
40291
|
-
...activeWorkspaceRoot ? { activeWorkspaceRoot } : {}
|
|
40810
|
+
...activeWorkspaceRoot ? { activeWorkspaceRoot } : {},
|
|
40811
|
+
cursorAgentHost: isCursorAgentHost()
|
|
40292
40812
|
};
|
|
40293
40813
|
}
|
|
40294
40814
|
async function unprovenWorktreeReason(wtPath, repoRoot2) {
|
|
40295
|
-
if (!(0,
|
|
40815
|
+
if (!(0, import_node_fs48.existsSync)(wtPath)) return `${wtPath} does not exist on disk`;
|
|
40296
40816
|
const porcelain = (await execFileP2("git", ["-C", repoRoot2, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
|
|
40297
40817
|
const registered = parseWorktreePorcelainEntries(porcelain);
|
|
40298
40818
|
if (!registered.length) {
|
|
@@ -40322,26 +40842,26 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
|
|
|
40322
40842
|
function acquireWorktreeSetupLock(worktreeRoot) {
|
|
40323
40843
|
const lockPath = repoRuntimeStatePath(worktreeRoot, "worktree-setup.lock");
|
|
40324
40844
|
const take = () => {
|
|
40325
|
-
const fd = (0,
|
|
40845
|
+
const fd = (0, import_node_fs48.openSync)(lockPath, "wx");
|
|
40326
40846
|
try {
|
|
40327
|
-
(0,
|
|
40847
|
+
(0, import_node_fs48.writeSync)(fd, String(Date.now()));
|
|
40328
40848
|
} finally {
|
|
40329
|
-
(0,
|
|
40849
|
+
(0, import_node_fs48.closeSync)(fd);
|
|
40330
40850
|
}
|
|
40331
40851
|
return () => {
|
|
40332
40852
|
try {
|
|
40333
|
-
(0,
|
|
40853
|
+
(0, import_node_fs48.rmSync)(lockPath, { force: true });
|
|
40334
40854
|
} catch {
|
|
40335
40855
|
}
|
|
40336
40856
|
};
|
|
40337
40857
|
};
|
|
40338
40858
|
try {
|
|
40339
|
-
(0,
|
|
40859
|
+
(0, import_node_fs48.mkdirSync)((0, import_node_path46.dirname)(lockPath), { recursive: true });
|
|
40340
40860
|
return take();
|
|
40341
40861
|
} catch {
|
|
40342
40862
|
try {
|
|
40343
|
-
if (Date.now() - (0,
|
|
40344
|
-
(0,
|
|
40863
|
+
if (Date.now() - (0, import_node_fs48.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
|
|
40864
|
+
(0, import_node_fs48.rmSync)(lockPath, { force: true });
|
|
40345
40865
|
return take();
|
|
40346
40866
|
}
|
|
40347
40867
|
} catch {
|
|
@@ -40457,7 +40977,7 @@ withExamples(mutating(
|
|
|
40457
40977
|
}
|
|
40458
40978
|
if (!resumed) {
|
|
40459
40979
|
step = `git worktree add ${wtPath}`;
|
|
40460
|
-
const wtPathPreExisted = (0,
|
|
40980
|
+
const wtPathPreExisted = (0, import_node_fs48.existsSync)(wtPath);
|
|
40461
40981
|
const partialRemove = worktreeRemoveDeps(async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
|
|
40462
40982
|
await withWorktreeAddLock(repoRoot2, () => addWorktreeRobust(wtPath, branch, base, {
|
|
40463
40983
|
// #4834: `-c core.longpaths=true` rides the add command itself — a Windows worktree path
|
|
@@ -40473,13 +40993,13 @@ withExamples(mutating(
|
|
|
40473
40993
|
},
|
|
40474
40994
|
deleteBranch: (b) => execFileP2("git", ["branch", "-D", b], { timeout: GIT_TIMEOUT_MS }).then(() => void 0),
|
|
40475
40995
|
cleanupPartial: async () => {
|
|
40476
|
-
if (wtPathPreExisted || !(0,
|
|
40996
|
+
if (wtPathPreExisted || !(0, import_node_fs48.existsSync)(wtPath)) return;
|
|
40477
40997
|
partialRemove.detachReparsePoints(wtPath);
|
|
40478
40998
|
await execFileP2("git", ["worktree", "remove", "--force", wtPath], { timeout: GIT_TIMEOUT_MS }).catch(() => partialRemove.removeWorktreeDir(wtPath).then(() => void 0));
|
|
40479
40999
|
await execFileP2("git", ["worktree", "prune"], { timeout: GIT_TIMEOUT_MS }).catch(() => {
|
|
40480
41000
|
});
|
|
40481
41001
|
},
|
|
40482
|
-
sleep: (ms) => new Promise((
|
|
41002
|
+
sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms)),
|
|
40483
41003
|
log: (m) => {
|
|
40484
41004
|
if (!o.json) console.error(` ${m}`);
|
|
40485
41005
|
}
|
|
@@ -40931,9 +41451,7 @@ docs.command("refs").description("deterministic doc reference gate: every backti
|
|
|
40931
41451
|
try {
|
|
40932
41452
|
const root = await repoRoot();
|
|
40933
41453
|
const commandPaths = new Set(
|
|
40934
|
-
buildCommandManifest(program2).index.map(
|
|
40935
|
-
(entry) => entry.house && entry.house !== "core" ? `${entry.house} ${entry.path}` : entry.path
|
|
40936
|
-
)
|
|
41454
|
+
buildCommandManifest(program2).index.map((entry) => entry.path)
|
|
40937
41455
|
);
|
|
40938
41456
|
const result = runDocRefs(root, { commandPaths });
|
|
40939
41457
|
if (o.json) {
|
|
@@ -41016,7 +41534,7 @@ async function reportWrite(label, res) {
|
|
|
41016
41534
|
return failGraceful(`${label}: HTTP ${res.status}${detail ? ` \u2014 ${detail}` : ""}`);
|
|
41017
41535
|
}
|
|
41018
41536
|
var tenant = program2.command("tenant").description("tenant runtime control through Hub authority");
|
|
41019
|
-
tenant.command("control <owner/repo> <stage> <action>").description("bounded tenant control plus value-free vault/broker verification; project-admin own dev/rc, master main").option("--watch", "block on the dispatched run and report its conclusion (status/retire/verify-secrets/verify-broker/logs watch by default)").option("--lines <n>", "logs only: trailing lines of the tenant service to return (1-2000, default 200)").option("--json", "machine-readable output").action(async (repo, stage, action, o) => {
|
|
41537
|
+
tenant.command("control <owner/repo> <stage> <action>").description("bounded tenant control plus value-free vault/broker verification; project-admin own dev/rc, master main").option("--watch", "block on the dispatched run and report its conclusion (status/retire/verify-secrets/verify-broker/logs watch by default)").option("--lines <n>", "logs only: trailing lines of the tenant service to return (1-2000, default 200)").option("--task <name>", "run-task only: registry-declared task name (never a command)").option("--artifact <id>", "run-task only: opaque id returned by tenant artifact put").option("--json", "machine-readable output").action(async (repo, stage, action, o) => {
|
|
41020
41538
|
try {
|
|
41021
41539
|
let lines;
|
|
41022
41540
|
if (o.lines !== void 0) {
|
|
@@ -41024,7 +41542,12 @@ tenant.command("control <owner/repo> <stage> <action>").description("bounded ten
|
|
|
41024
41542
|
lines = Number(o.lines);
|
|
41025
41543
|
if (!Number.isInteger(lines) || lines < 1 || lines > 2e3) return fail("runtime tenant control: --lines must be an integer between 1 and 2000");
|
|
41026
41544
|
}
|
|
41027
|
-
|
|
41545
|
+
if (action === "run-task") {
|
|
41546
|
+
if (!o.task) return fail("runtime tenant control: run-task requires --task <declared-name>");
|
|
41547
|
+
} else if (o.task || o.artifact) {
|
|
41548
|
+
return fail("runtime tenant control: --task/--artifact are valid only for run-task");
|
|
41549
|
+
}
|
|
41550
|
+
const result = await runTenantControl(trainApplyDeps(), { repo, stage, action, watch: o.watch, lines, task: o.task, artifact: o.artifact });
|
|
41028
41551
|
if (!o.json && action === "verify-secrets" && result.secrets) {
|
|
41029
41552
|
const body = { ok: result.conclusion === "success", secrets: result.secrets, ssmStatus: result.conclusion === "success" ? "Success" : "Failed", raw: result.secretsRaw };
|
|
41030
41553
|
const { lines: lines2, failure } = renderVerifySecrets(body);
|
|
@@ -41046,6 +41569,15 @@ tenant.command("control <owner/repo> <stage> <action>").description("bounded ten
|
|
|
41046
41569
|
return failGraceful(`runtime tenant control: ${e.message}`);
|
|
41047
41570
|
}
|
|
41048
41571
|
});
|
|
41572
|
+
var tenantArtifact = tenant.command("artifact").description("private stage-bound inputs for declared tenant tasks");
|
|
41573
|
+
tenantArtifact.command("put <owner/repo> <stage> <path>").description("upload a private file through a short-lived Hub grant; prints only an opaque artifact id").option("--json", "machine-readable output").action(async (repo, stage, path2, o) => {
|
|
41574
|
+
try {
|
|
41575
|
+
const receipt = await putTenantArtifact(repo, stage, path2, registryClientDeps(await loadConfig()));
|
|
41576
|
+
printLine(o.json ? JSON.stringify(receipt, null, 2) : `tenant artifact ${receipt.artifactId} ready for ${repo} ${stage} until ${receipt.expiresAt}`);
|
|
41577
|
+
} catch (e) {
|
|
41578
|
+
return failGraceful(e.message);
|
|
41579
|
+
}
|
|
41580
|
+
});
|
|
41049
41581
|
tenant.command("reconcile <owner/repo> <stage>").description("re-render this tenant stage's generated box assets from registry truth; project-admin own dev/rc, master main; watches by default").option("--watch", "wait for the tenant-reconcile.yml run (default)").option("--no-watch", "return after dispatch; do not redeploy until the reconcile run succeeds").option("--json", "machine-readable output").action(async (repo, stage, o) => {
|
|
41050
41582
|
try {
|
|
41051
41583
|
const result = await runTenantReconcile(trainApplyDeps(), { repo, stage, watch: o.watch });
|
|
@@ -41242,7 +41774,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
|
|
|
41242
41774
|
if (dupe) return fail(`org project set: KEY "${dupe}" was passed to both --var and --set; --set is an alias of --var, so pass each KEY once`);
|
|
41243
41775
|
if (o.secretsFile) {
|
|
41244
41776
|
try {
|
|
41245
|
-
vars.push(`secrets=${(0,
|
|
41777
|
+
vars.push(`secrets=${(0, import_node_fs48.readFileSync)(o.secretsFile, "utf8")}`);
|
|
41246
41778
|
} catch (e) {
|
|
41247
41779
|
return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
|
|
41248
41780
|
}
|
|
@@ -41568,7 +42100,7 @@ withExamples(mutating(
|
|
|
41568
42100
|
try {
|
|
41569
42101
|
title = await resolveIssueTitle(
|
|
41570
42102
|
{ title: opts.title, titleFile: opts.titleFile },
|
|
41571
|
-
{ readFile:
|
|
42103
|
+
{ readFile: import_promises12.readFile, readStdin }
|
|
41572
42104
|
);
|
|
41573
42105
|
} catch (e) {
|
|
41574
42106
|
return fail(
|
|
@@ -41610,8 +42142,8 @@ withExamples(mutating(
|
|
|
41610
42142
|
let surfaceFlagLabel;
|
|
41611
42143
|
try {
|
|
41612
42144
|
issueType = resolveCreateType(o.type, "issue create", o.label);
|
|
41613
|
-
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile:
|
|
41614
|
-
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile:
|
|
42145
|
+
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises12.readFile, readStdin });
|
|
42146
|
+
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises12.readFile, readStdin });
|
|
41615
42147
|
if (o.idempotencyKey) body = appendIdempotencyMarker(body, o.idempotencyKey);
|
|
41616
42148
|
priority = resolveCreatePriority(o.priority, "issue create");
|
|
41617
42149
|
extraLabels = [...o.label ?? []];
|
|
@@ -41792,7 +42324,7 @@ jsonParity(issue.command("comment <ref>").description("post a Markdown comment t
|
|
|
41792
42324
|
}
|
|
41793
42325
|
let body;
|
|
41794
42326
|
try {
|
|
41795
|
-
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile:
|
|
42327
|
+
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises12.readFile, readStdin });
|
|
41796
42328
|
} catch (e) {
|
|
41797
42329
|
return fail(`issue comment: ${e.message}`);
|
|
41798
42330
|
}
|
|
@@ -41852,8 +42384,8 @@ program2.command("report").description("file a friction report on the Hub board
|
|
|
41852
42384
|
let title;
|
|
41853
42385
|
const sourceRepo = o.repo ?? await resolveRepo(void 0);
|
|
41854
42386
|
try {
|
|
41855
|
-
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile:
|
|
41856
|
-
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile:
|
|
42387
|
+
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises12.readFile, readStdin });
|
|
42388
|
+
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises12.readFile, readStdin });
|
|
41857
42389
|
priority = resolveCreatePriority(o.priority, "report");
|
|
41858
42390
|
if (!ISSUE_TYPES.includes(o.type)) {
|
|
41859
42391
|
throw new Error(`unknown issue type "${o.type}" \u2014 expected one of: ${ISSUE_TYPES.join(", ")}`);
|
|
@@ -41907,8 +42439,8 @@ program2.command("skill-lesson").description("file a skill-lesson on the Hub boa
|
|
|
41907
42439
|
let args;
|
|
41908
42440
|
try {
|
|
41909
42441
|
skill = assertSkillName(o.skill);
|
|
41910
|
-
rawBody = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile:
|
|
41911
|
-
const rawTitle = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile:
|
|
42442
|
+
rawBody = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises12.readFile, readStdin });
|
|
42443
|
+
const rawTitle = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises12.readFile, readStdin });
|
|
41912
42444
|
title = buildSkillLessonTitle(skill, rawTitle);
|
|
41913
42445
|
priority = resolveCreatePriority(o.priority, "skill-lesson");
|
|
41914
42446
|
body = buildSkillLessonBody(rawBody, sourceRepo, pluginSha);
|
|
@@ -41967,8 +42499,8 @@ withExamples(pr.command("create").description("create a PR and print {number,url
|
|
|
41967
42499
|
let body;
|
|
41968
42500
|
let title;
|
|
41969
42501
|
try {
|
|
41970
|
-
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile:
|
|
41971
|
-
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile:
|
|
42502
|
+
title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises12.readFile, readStdin });
|
|
42503
|
+
body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises12.readFile, readStdin });
|
|
41972
42504
|
} catch (e) {
|
|
41973
42505
|
return fail(`pr create: ${e.message}`, e instanceof TextArgError ? { code: e.code, offending_flag: e.offendingFlag } : void 0);
|
|
41974
42506
|
}
|
|
@@ -42010,11 +42542,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
|
|
|
42010
42542
|
}
|
|
42011
42543
|
});
|
|
42012
42544
|
async function listCiWorkflowPaths(cwd = process.cwd()) {
|
|
42013
|
-
const wfDir = (0,
|
|
42014
|
-
if (!(0,
|
|
42015
|
-
return (0,
|
|
42545
|
+
const wfDir = (0, import_node_path46.join)(cwd, ".github", "workflows");
|
|
42546
|
+
if (!(0, import_node_fs48.existsSync)(wfDir)) return [];
|
|
42547
|
+
return (0, import_node_fs48.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
|
|
42016
42548
|
try {
|
|
42017
|
-
return workflowReportsPrChecks((0,
|
|
42549
|
+
return workflowReportsPrChecks((0, import_node_fs48.readFileSync)((0, import_node_path46.join)(wfDir, name), "utf8"));
|
|
42018
42550
|
} catch {
|
|
42019
42551
|
return true;
|
|
42020
42552
|
}
|
|
@@ -42066,16 +42598,16 @@ function ciAuditDeps() {
|
|
|
42066
42598
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
42067
42599
|
readSeedFile: (path2) => {
|
|
42068
42600
|
if (!root) return null;
|
|
42069
|
-
const fullPath = (0,
|
|
42070
|
-
return (0,
|
|
42601
|
+
const fullPath = (0, import_node_path46.join)(root, path2);
|
|
42602
|
+
return (0, import_node_fs48.existsSync)(fullPath) ? (0, import_node_fs48.readFileSync)(fullPath, "utf8") : null;
|
|
42071
42603
|
}
|
|
42072
42604
|
};
|
|
42073
42605
|
}
|
|
42074
42606
|
function hubRoot() {
|
|
42075
|
-
const fromPkg = (0,
|
|
42607
|
+
const fromPkg = (0, import_node_path46.join)(__dirname, "..", "..");
|
|
42076
42608
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
42077
|
-
if ((0,
|
|
42078
|
-
if ((0,
|
|
42609
|
+
if ((0, import_node_fs48.existsSync)((0, import_node_path46.join)(fromPkg, marker))) return fromPkg;
|
|
42610
|
+
if ((0, import_node_fs48.existsSync)((0, import_node_path46.join)(process.cwd(), marker))) return process.cwd();
|
|
42079
42611
|
return null;
|
|
42080
42612
|
}
|
|
42081
42613
|
async function waitLoopCorePool(label) {
|
|
@@ -42124,7 +42656,7 @@ pr.command("checks-wait <number>").description(`bounded wait for PR checks; skip
|
|
|
42124
42656
|
// reading as "your tests failed". One call per failing run, only at the verdict.
|
|
42125
42657
|
diagnoseFailure: () => waitLoopDiagnosis("pr checks-wait", number, repo),
|
|
42126
42658
|
baseBranch,
|
|
42127
|
-
sleep: (ms) => new Promise((
|
|
42659
|
+
sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms)),
|
|
42128
42660
|
log: (message) => console.warn(message),
|
|
42129
42661
|
timeoutMs,
|
|
42130
42662
|
// Liveness on stderr, one line per poll. A silent bounded wait is indistinguishable from a hang, and
|
|
@@ -42204,7 +42736,7 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
|
|
|
42204
42736
|
// then read its own wall-clock kill as a broken diff.
|
|
42205
42737
|
diagnoseFailure: () => waitLoopDiagnosis("pr land", prNumber, repo),
|
|
42206
42738
|
baseBranch: "development",
|
|
42207
|
-
sleep: (ms) => new Promise((
|
|
42739
|
+
sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms)),
|
|
42208
42740
|
log: (message) => console.warn(message),
|
|
42209
42741
|
// `pr land` inherits the same (raised) checks budget, so it needs the same liveness — otherwise the
|
|
42210
42742
|
// 30m wait is SILENT and reads exactly like the hang #2940 was filed about, only three times longer.
|
|
@@ -42247,7 +42779,7 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
|
|
|
42247
42779
|
} else {
|
|
42248
42780
|
lastFailure = void 0;
|
|
42249
42781
|
}
|
|
42250
|
-
await new Promise((
|
|
42782
|
+
await new Promise((resolve6) => setTimeout(resolve6, PR_LAND_POLL_MS));
|
|
42251
42783
|
}
|
|
42252
42784
|
if (lastFailure) {
|
|
42253
42785
|
throw new Error(
|
|
@@ -42347,7 +42879,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
42347
42879
|
pollRateLimit: () => waitLoopCorePool("pr merge --wait"),
|
|
42348
42880
|
diagnoseFailure: () => waitLoopDiagnosis("pr merge --wait", number, repo),
|
|
42349
42881
|
baseBranch,
|
|
42350
|
-
sleep: (ms) => new Promise((
|
|
42882
|
+
sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms)),
|
|
42351
42883
|
log: (message) => console.warn(message),
|
|
42352
42884
|
progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr merge: --wait checks \u2014 ${state}, ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
|
|
42353
42885
|
});
|
|
@@ -42383,7 +42915,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
42383
42915
|
}
|
|
42384
42916
|
if (!repoForPostCleanup) throw e;
|
|
42385
42917
|
console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
|
|
42386
|
-
const commitMessage = bodyFile ? (0,
|
|
42918
|
+
const commitMessage = bodyFile ? (0, import_node_fs48.readFileSync)(bodyFile, "utf8") : void 0;
|
|
42387
42919
|
await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
|
|
42388
42920
|
body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
|
|
42389
42921
|
timeoutMs: GH_MUTATION_TIMEOUT_MS
|
|
@@ -42479,7 +43011,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
|
|
|
42479
43011
|
localCleanup = await cleanupPrMergeLocalBranch(headRef, {
|
|
42480
43012
|
beforeWorktrees,
|
|
42481
43013
|
startingPath,
|
|
42482
|
-
pathExists: (p) => (0,
|
|
43014
|
+
pathExists: (p) => (0, import_node_fs48.existsSync)(p),
|
|
42483
43015
|
execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
|
|
42484
43016
|
teardownWorktreeStage,
|
|
42485
43017
|
deferredStore,
|
|
@@ -42580,8 +43112,8 @@ function trainApplyDeps() {
|
|
|
42580
43112
|
// Hub-App-authority dispatch of the central tenant-control.yml (#1717) — the Hub fires the
|
|
42581
43113
|
// workflow_dispatch with its App token. Never throws for an expected rejection: it returns the dispatch
|
|
42582
43114
|
// outcome so runTenantControl can map a 5xx (transport-failed, retryable) vs a 4xx (rejected) vs ok.
|
|
42583
|
-
dispatchTenantControl: async ({ repo, stage, action, lines }) => {
|
|
42584
|
-
const res = await tenantControl({ repo, stage, action, ...lines != null ? { lines } : {} }, registryClientDeps(await loadConfig()));
|
|
43115
|
+
dispatchTenantControl: async ({ repo, stage, action, lines, task, artifact }) => {
|
|
43116
|
+
const res = await tenantControl({ repo, stage, action, ...lines != null ? { lines } : {}, ...task ? { task } : {}, ...artifact ? { artifact } : {} }, registryClientDeps(await loadConfig()));
|
|
42585
43117
|
if (res.ok) return { ok: true };
|
|
42586
43118
|
const body = res.body;
|
|
42587
43119
|
return { ok: false, category: body?.category, error: body?.error ?? res.error };
|
|
@@ -42605,8 +43137,8 @@ function trainApplyDeps() {
|
|
|
42605
43137
|
// Slack release announcement (#883): Hub-only + best-effort inside announceRelease itself.
|
|
42606
43138
|
announce: (args) => announceRelease({
|
|
42607
43139
|
run: async (file, cmdArgs) => (await execFileP2(file, cmdArgs, { timeout: GH_TRAIN_TIMEOUT_MS })).stdout,
|
|
42608
|
-
readFile: (path2) => (0,
|
|
42609
|
-
removeFile: (path2) => (0,
|
|
43140
|
+
readFile: (path2) => (0, import_promises12.readFile)(path2, "utf8"),
|
|
43141
|
+
removeFile: (path2) => (0, import_promises12.unlink)(path2)
|
|
42610
43142
|
}, args),
|
|
42611
43143
|
// #4713 (I/O-boundary census): `null` used to mean BOTH "this project configures no edge domains"
|
|
42612
43144
|
// (a real answer) and "the registry read missed" — so a release verdict printed an environments block
|
|
@@ -42843,7 +43375,7 @@ for (const commandName of ["rcand", "release"]) {
|
|
|
42843
43375
|
}
|
|
42844
43376
|
let summaryLines;
|
|
42845
43377
|
try {
|
|
42846
|
-
summaryLines = summaryFileLines(await (0,
|
|
43378
|
+
summaryLines = summaryFileLines(await (0, import_promises12.readFile)(o.announceSummaryFile, "utf8"));
|
|
42847
43379
|
} catch (e) {
|
|
42848
43380
|
return fail(`release: could not read --announce-summary-file ${o.announceSummaryFile}: ${e.message}`);
|
|
42849
43381
|
}
|
|
@@ -43083,12 +43615,12 @@ access.command("audit").description("audit collaborator roles + train-branch pus
|
|
|
43083
43615
|
targets = resolution.targets;
|
|
43084
43616
|
}
|
|
43085
43617
|
const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
|
|
43086
|
-
const fileMatrix = (0,
|
|
43618
|
+
const fileMatrix = (0, import_node_fs48.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs48.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
43087
43619
|
const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
|
|
43088
43620
|
const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
|
|
43089
|
-
const fileContracts = (0,
|
|
43621
|
+
const fileContracts = (0, import_node_fs48.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs48.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
|
|
43090
43622
|
const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
|
|
43091
|
-
const sanctioned = (0,
|
|
43623
|
+
const sanctioned = (0, import_node_fs48.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs48.readFileSync)("access-matrix.json", "utf8")) : {};
|
|
43092
43624
|
const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
|
|
43093
43625
|
console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
|
|
43094
43626
|
if (!report.ok) process.exitCode = 1;
|
|
@@ -43126,16 +43658,16 @@ function directoryBytes(path2) {
|
|
|
43126
43658
|
let total = 0;
|
|
43127
43659
|
let entries;
|
|
43128
43660
|
try {
|
|
43129
|
-
entries = (0,
|
|
43661
|
+
entries = (0, import_node_fs48.readdirSync)(path2, { withFileTypes: true });
|
|
43130
43662
|
} catch {
|
|
43131
43663
|
return 0;
|
|
43132
43664
|
}
|
|
43133
43665
|
for (const entry of entries) {
|
|
43134
|
-
const child2 = (0,
|
|
43666
|
+
const child2 = (0, import_node_path46.join)(path2, entry.name);
|
|
43135
43667
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
43136
43668
|
else {
|
|
43137
43669
|
try {
|
|
43138
|
-
total += (0,
|
|
43670
|
+
total += (0, import_node_fs48.statSync)(child2).size;
|
|
43139
43671
|
} catch {
|
|
43140
43672
|
}
|
|
43141
43673
|
}
|
|
@@ -43143,25 +43675,25 @@ function directoryBytes(path2) {
|
|
|
43143
43675
|
return total;
|
|
43144
43676
|
}
|
|
43145
43677
|
function listDirEntries(dir) {
|
|
43146
|
-
return (0,
|
|
43678
|
+
return (0, import_node_fs48.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
|
|
43147
43679
|
}
|
|
43148
43680
|
function readInstalledPluginRefs(configRoot) {
|
|
43149
43681
|
const p = installedPluginsPathForConfig(configRoot);
|
|
43150
|
-
if (!(0,
|
|
43682
|
+
if (!(0, import_node_fs48.existsSync)(p)) return [];
|
|
43151
43683
|
try {
|
|
43152
|
-
return installedPluginPaths((0,
|
|
43684
|
+
return installedPluginPaths((0, import_node_fs48.readFileSync)(p, "utf8"));
|
|
43153
43685
|
} catch {
|
|
43154
43686
|
return null;
|
|
43155
43687
|
}
|
|
43156
43688
|
}
|
|
43157
43689
|
function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
43158
43690
|
return {
|
|
43159
|
-
exists: (p) => (0,
|
|
43160
|
-
listVersionDirs: (root) => (0,
|
|
43691
|
+
exists: (p) => (0, import_node_fs48.existsSync)(p),
|
|
43692
|
+
listVersionDirs: (root) => (0, import_node_fs48.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
|
|
43161
43693
|
dirBytes,
|
|
43162
|
-
listStagingDirs: (root) => (0,
|
|
43694
|
+
listStagingDirs: (root) => (0, import_node_fs48.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
43163
43695
|
try {
|
|
43164
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
43696
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path46.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs48.statSync)(p).mtimeMs) };
|
|
43165
43697
|
} catch {
|
|
43166
43698
|
return { name: d.name, mtimeMs: Date.now() };
|
|
43167
43699
|
}
|
|
@@ -43175,10 +43707,10 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
43175
43707
|
return {
|
|
43176
43708
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
43177
43709
|
mtimeMs: (name) => {
|
|
43178
|
-
const p = (0,
|
|
43179
|
-
if (!(0,
|
|
43710
|
+
const p = (0, import_node_path46.join)(stagingRoot, name);
|
|
43711
|
+
if (!(0, import_node_fs48.existsSync)(p)) return null;
|
|
43180
43712
|
try {
|
|
43181
|
-
return newestMtimeMs(p, listDirEntries, (q) => (0,
|
|
43713
|
+
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs48.statSync)(q).mtimeMs);
|
|
43182
43714
|
} catch {
|
|
43183
43715
|
return null;
|
|
43184
43716
|
}
|
|
@@ -43198,13 +43730,13 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
43198
43730
|
return;
|
|
43199
43731
|
}
|
|
43200
43732
|
const plan = buildPluginCachePlan(
|
|
43201
|
-
(0,
|
|
43733
|
+
(0, import_node_os20.homedir)(),
|
|
43202
43734
|
running,
|
|
43203
43735
|
pluginCacheFsDeps(configRoot, directoryBytes),
|
|
43204
43736
|
{ withBytes: true, configRoot, includeStaging: surface !== "codex" }
|
|
43205
43737
|
);
|
|
43206
43738
|
const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
|
|
43207
|
-
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0,
|
|
43739
|
+
const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs48.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
|
|
43208
43740
|
const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
|
|
43209
43741
|
if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
|
|
43210
43742
|
else console.log(renderPluginCachePlan(plan, result));
|
|
@@ -43212,7 +43744,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
|
|
|
43212
43744
|
});
|
|
43213
43745
|
function readReleaseCatchupState(path2) {
|
|
43214
43746
|
try {
|
|
43215
|
-
const parsed = JSON.parse((0,
|
|
43747
|
+
const parsed = JSON.parse((0, import_node_fs48.readFileSync)(path2, "utf8"));
|
|
43216
43748
|
return typeof parsed?.checkedAt === "number" ? parsed : void 0;
|
|
43217
43749
|
} catch {
|
|
43218
43750
|
return void 0;
|
|
@@ -43220,8 +43752,8 @@ function readReleaseCatchupState(path2) {
|
|
|
43220
43752
|
}
|
|
43221
43753
|
function writeReleaseCatchupState(path2, state) {
|
|
43222
43754
|
try {
|
|
43223
|
-
(0,
|
|
43224
|
-
(0,
|
|
43755
|
+
(0, import_node_fs48.mkdirSync)((0, import_node_path46.dirname)(path2), { recursive: true });
|
|
43756
|
+
(0, import_node_fs48.writeFileSync)(path2, `${JSON.stringify(state)}
|
|
43225
43757
|
`);
|
|
43226
43758
|
} catch {
|
|
43227
43759
|
}
|
|
@@ -43229,7 +43761,7 @@ function writeReleaseCatchupState(path2, state) {
|
|
|
43229
43761
|
program2.command("plugin-release-catchup").description("install a newer released MMI plugin clone non-destructively and re-point the Pi registration (#4297); TTL-gated no-op when current").option("--force", "skip the 24h TTL (acceptance proof / manual run)").option("--quiet", "print only failures (the detached session-start lane)").option("--json", "machine-readable output").action(async (o) => {
|
|
43230
43762
|
const outcome = await withEnvHealLock(
|
|
43231
43763
|
"plugin release catch-up",
|
|
43232
|
-
() => runReleaseCatchup((0,
|
|
43764
|
+
() => runReleaseCatchup((0, import_node_os20.homedir)(), process.env, {
|
|
43233
43765
|
fetchReleased: fetchNpmReleasedVersion,
|
|
43234
43766
|
runClaude: (args, step) => runPluginCli("claude", args, (msg) => {
|
|
43235
43767
|
if (!o.quiet && !o.json) console.log(msg);
|
|
@@ -43245,7 +43777,7 @@ program2.command("plugin-release-catchup").description("install a newer released
|
|
|
43245
43777
|
},
|
|
43246
43778
|
readState: readReleaseCatchupState,
|
|
43247
43779
|
writeState: writeReleaseCatchupState,
|
|
43248
|
-
healRegistration: defaultRegistrationHeal((0,
|
|
43780
|
+
healRegistration: defaultRegistrationHeal((0, import_node_os20.homedir)(), process.env),
|
|
43249
43781
|
now: () => Date.now()
|
|
43250
43782
|
}, { force: o.force })
|
|
43251
43783
|
);
|
|
@@ -43313,7 +43845,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
|
|
|
43313
43845
|
spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process20.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
43314
43846
|
bannerIo.log(worktreeBanner);
|
|
43315
43847
|
}
|
|
43316
|
-
if (shouldSpawnReleaseCatchup((0,
|
|
43848
|
+
if (shouldSpawnReleaseCatchup((0, import_node_os20.homedir)(), process.env, readReleaseCatchupState)) {
|
|
43317
43849
|
spawnDetachedSelf(["plugin", "release-catchup", "--quiet"], { spawn: import_node_child_process20.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
|
|
43318
43850
|
}
|
|
43319
43851
|
if (isLinkedWorktree(process.cwd())) {
|