@massu/core 1.16.2 → 1.16.3
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/CHANGELOG.md +68 -0
- package/dist/cli.js +1089 -696
- package/dist/hooks/auto-learning-pipeline.js +401 -55
- package/dist/hooks/classify-failure.js +399 -53
- package/dist/hooks/cost-tracker.js +411 -65
- package/dist/hooks/fix-detector.js +410 -64
- package/dist/hooks/incident-pipeline.js +396 -50
- package/dist/hooks/intent-suggester.js +411 -65
- package/dist/hooks/memory-recall.js +415 -69
- package/dist/hooks/post-edit-context.js +413 -67
- package/dist/hooks/post-tool-use.js +439 -93
- package/dist/hooks/pre-compact.js +411 -65
- package/dist/hooks/pre-delete-check.js +415 -69
- package/dist/hooks/pre-tool-use-gate.js +415 -69
- package/dist/hooks/quality-event.js +411 -65
- package/dist/hooks/rule-enforcement-pipeline.js +397 -51
- package/dist/hooks/security-gate.js +411 -65
- package/dist/hooks/session-end.js +440 -94
- package/dist/hooks/session-start.js +532 -282
- package/dist/hooks/user-prompt.js +444 -98
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -49,16 +49,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
49
49
|
));
|
|
50
50
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
51
51
|
|
|
52
|
-
// src/lib/memory-path.ts
|
|
53
|
-
function encodeMemoryDirName(projectRoot) {
|
|
54
|
-
return projectRoot.replace(/\//g, "-");
|
|
55
|
-
}
|
|
56
|
-
var init_memory_path = __esm({
|
|
57
|
-
"src/lib/memory-path.ts"() {
|
|
58
|
-
"use strict";
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
52
|
// src/credentials.ts
|
|
63
53
|
import { homedir } from "os";
|
|
64
54
|
import { resolve } from "path";
|
|
@@ -168,6 +158,385 @@ var init_credentials = __esm({
|
|
|
168
158
|
}
|
|
169
159
|
});
|
|
170
160
|
|
|
161
|
+
// src/lib/fileLock.ts
|
|
162
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
163
|
+
import { dirname } from "path";
|
|
164
|
+
import * as lockfile from "proper-lockfile";
|
|
165
|
+
function readLockHolderPid(lockPath) {
|
|
166
|
+
try {
|
|
167
|
+
const raw = readFileSync2(`${lockPath}.pid`, "utf-8").trim();
|
|
168
|
+
const pid = Number.parseInt(raw, 10);
|
|
169
|
+
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
170
|
+
return pid;
|
|
171
|
+
} catch {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function busyWaitSync(ms) {
|
|
176
|
+
if (typeof SharedArrayBuffer === "undefined" || typeof Atomics === "undefined") {
|
|
177
|
+
throw new Error(
|
|
178
|
+
`withFileLockSync requires SharedArrayBuffer + Atomics for its retry-loop wait. This Node runtime does not provide them \u2014 refusing to fall back to a CPU spinloop. If you hit this in a sandboxed serverless env, the fix is to perform the lock-protected operation in a host runtime that supports Atomics.`
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
const sab = new SharedArrayBuffer(4);
|
|
182
|
+
const view = new Int32Array(sab);
|
|
183
|
+
Atomics.wait(view, 0, 0, ms);
|
|
184
|
+
}
|
|
185
|
+
function withFileLockSync(lockPath, fn, opts = {}) {
|
|
186
|
+
mkdirSync2(dirname(lockPath), { recursive: true });
|
|
187
|
+
const staleMs = opts.staleMs ?? 3e4;
|
|
188
|
+
const blockMs = opts.retries === 0 ? 0 : opts.blockMs ?? 3e4;
|
|
189
|
+
const pollIntervalMs = opts.pollIntervalMs ?? 100;
|
|
190
|
+
const now = opts.now ?? Date.now;
|
|
191
|
+
const sleep = opts.sleep ?? busyWaitSync;
|
|
192
|
+
const makeBusyError = opts.errorFactory ?? ((path, pid, retrySeconds, code) => new FileLockBusyError(path, pid, retrySeconds, code));
|
|
193
|
+
let release = null;
|
|
194
|
+
const deadline = now() + blockMs;
|
|
195
|
+
for (; ; ) {
|
|
196
|
+
try {
|
|
197
|
+
release = lockfile.lockSync(lockPath, {
|
|
198
|
+
stale: staleMs,
|
|
199
|
+
retries: 0,
|
|
200
|
+
realpath: false
|
|
201
|
+
});
|
|
202
|
+
try {
|
|
203
|
+
writeFileSync2(`${lockPath}.pid`, String(process.pid), "utf-8");
|
|
204
|
+
} catch {
|
|
205
|
+
}
|
|
206
|
+
break;
|
|
207
|
+
} catch (err) {
|
|
208
|
+
const e2 = err;
|
|
209
|
+
const code = e2.code;
|
|
210
|
+
if (code !== "ELOCKED" && code !== "EBUSY") {
|
|
211
|
+
throw err;
|
|
212
|
+
}
|
|
213
|
+
if (now() >= deadline) {
|
|
214
|
+
const holderPid = readLockHolderPid(lockPath);
|
|
215
|
+
const remainingMs = Math.max(0, deadline - now());
|
|
216
|
+
const retryAfterSeconds = blockMs === 0 ? Math.round(staleMs / 1e3) : Math.round(remainingMs / 1e3);
|
|
217
|
+
throw makeBusyError(lockPath, holderPid, retryAfterSeconds, code);
|
|
218
|
+
}
|
|
219
|
+
sleep(pollIntervalMs);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
try {
|
|
223
|
+
return fn();
|
|
224
|
+
} finally {
|
|
225
|
+
try {
|
|
226
|
+
if (release) release();
|
|
227
|
+
} catch {
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
rmSync2(`${lockPath}.pid`, { force: true });
|
|
231
|
+
} catch {
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
var FileLockBusyError;
|
|
236
|
+
var init_fileLock = __esm({
|
|
237
|
+
"src/lib/fileLock.ts"() {
|
|
238
|
+
"use strict";
|
|
239
|
+
FileLockBusyError = class extends Error {
|
|
240
|
+
constructor(lockPath, holderPid, retryAfterSeconds, causeCode) {
|
|
241
|
+
const pidPart = holderPid != null ? `(PID=${holderPid})` : "(PID=unknown)";
|
|
242
|
+
super(`File lock at ${lockPath} held by another process ${pidPart} \u2014 try again in ${retryAfterSeconds}s`);
|
|
243
|
+
this.lockPath = lockPath;
|
|
244
|
+
this.holderPid = holderPid;
|
|
245
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
246
|
+
this.causeCode = causeCode;
|
|
247
|
+
this.name = "FileLockBusyError";
|
|
248
|
+
}
|
|
249
|
+
lockPath;
|
|
250
|
+
holderPid;
|
|
251
|
+
retryAfterSeconds;
|
|
252
|
+
causeCode;
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// src/lib/sqlite-loader.ts
|
|
258
|
+
import { createRequire } from "module";
|
|
259
|
+
import { spawnSync } from "child_process";
|
|
260
|
+
import { accessSync, appendFileSync, chmodSync as chmodSync2, constants as fsConstants, existsSync as existsSync2, mkdirSync as mkdirSync3 } from "fs";
|
|
261
|
+
import { dirname as dirname2, join } from "path";
|
|
262
|
+
function isNativeAbiError(err) {
|
|
263
|
+
const code = err?.code;
|
|
264
|
+
if (code === "ERR_DLOPEN_FAILED") return true;
|
|
265
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
266
|
+
return /NODE_MODULE_VERSION/.test(msg) || /was compiled against a different Node\.js version/.test(msg) || /ERR_DLOPEN_FAILED/.test(msg) || /\.node\b/.test(msg) && /dlopen|invalid ELF|mach-o|image not found|no such file|not a valid Win32/i.test(msg);
|
|
267
|
+
}
|
|
268
|
+
function loadBetterSqlite3(opts = {}) {
|
|
269
|
+
if (_testCtor) return _testCtor;
|
|
270
|
+
if (opts.fresh) bustNativeCache();
|
|
271
|
+
return req("better-sqlite3");
|
|
272
|
+
}
|
|
273
|
+
function bustNativeCache() {
|
|
274
|
+
try {
|
|
275
|
+
const cache = req.cache;
|
|
276
|
+
if (!cache) return;
|
|
277
|
+
for (const key of Object.keys(cache)) {
|
|
278
|
+
if (/better[-_]sqlite3|better_sqlite3\.node|[\\/]bindings[\\/]/.test(key)) {
|
|
279
|
+
delete cache[key];
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
} catch {
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
function openDatabase(dbPath, opts = {}) {
|
|
286
|
+
const { selfHeal: selfHealOpt = true, ...ctorOpts } = opts;
|
|
287
|
+
const selfHeal = selfHealOpt && process.env.MASSU_HOOK_RUNTIME !== "1";
|
|
288
|
+
const Ctor = loadBetterSqlite3();
|
|
289
|
+
try {
|
|
290
|
+
return new Ctor(dbPath, ctorOpts);
|
|
291
|
+
} catch (err) {
|
|
292
|
+
if (!isNativeAbiError(err)) {
|
|
293
|
+
throw err;
|
|
294
|
+
}
|
|
295
|
+
if (!selfHeal) {
|
|
296
|
+
recordHealEvent({ phase: "skipped", reason: "self-heal-disabled", abiTo: process.versions.modules });
|
|
297
|
+
throw new MemoryEngineUnusableError("abi-mismatch", detailOf(err));
|
|
298
|
+
}
|
|
299
|
+
const result = (_testHeal ?? attemptNativeHeal)(err);
|
|
300
|
+
if (!result.healed && !result.contended) {
|
|
301
|
+
throw new MemoryEngineUnusableError(result.reason ?? "heal-failed", result.detail ?? detailOf(err));
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
const FreshCtor = loadBetterSqlite3({ fresh: true });
|
|
305
|
+
return new FreshCtor(dbPath, ctorOpts);
|
|
306
|
+
} catch (retryErr) {
|
|
307
|
+
throw new MemoryEngineUnusableError("heal-failed", detailOf(retryErr));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function probeMemoryDbUsable(opts = {}) {
|
|
312
|
+
const selfHeal = opts.selfHeal ?? false;
|
|
313
|
+
try {
|
|
314
|
+
const db = openDatabase(":memory:", { selfHeal });
|
|
315
|
+
try {
|
|
316
|
+
db.prepare("SELECT 1").get();
|
|
317
|
+
} finally {
|
|
318
|
+
db.close();
|
|
319
|
+
}
|
|
320
|
+
} catch (err) {
|
|
321
|
+
if (err instanceof MemoryEngineUnusableError) {
|
|
322
|
+
return { ok: false, reason: err.reason, remedy: err.remedy, detail: err.detail };
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
ok: false,
|
|
326
|
+
reason: isNativeAbiError(err) ? "abi-mismatch" : "unreadable",
|
|
327
|
+
remedy: NATIVE_DB_REMEDY,
|
|
328
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
const dbPath = opts.dbPath;
|
|
332
|
+
if (dbPath && dbPath !== ":memory:" && existsSync2(dbPath)) {
|
|
333
|
+
try {
|
|
334
|
+
const rdb = openDatabase(dbPath, { readonly: true, selfHeal: false });
|
|
335
|
+
try {
|
|
336
|
+
rdb.prepare("SELECT 1").get();
|
|
337
|
+
} finally {
|
|
338
|
+
rdb.close();
|
|
339
|
+
}
|
|
340
|
+
} catch (err) {
|
|
341
|
+
return {
|
|
342
|
+
ok: false,
|
|
343
|
+
reason: err instanceof MemoryEngineUnusableError ? err.reason : "unreadable",
|
|
344
|
+
remedy: NATIVE_DB_REMEDY,
|
|
345
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return { ok: true };
|
|
350
|
+
}
|
|
351
|
+
function attemptNativeHeal(err) {
|
|
352
|
+
const start = Date.now();
|
|
353
|
+
const abiTo = process.versions.modules;
|
|
354
|
+
const abiFrom = parseAbiFrom(err);
|
|
355
|
+
let pkgDir;
|
|
356
|
+
try {
|
|
357
|
+
pkgDir = dirname2(req.resolve("better-sqlite3/package.json"));
|
|
358
|
+
} catch {
|
|
359
|
+
recordHealEvent({ phase: "skipped", reason: "not-resolvable", abiFrom, abiTo });
|
|
360
|
+
return { healed: false, reason: "missing", abiFrom, abiTo, detail: "better-sqlite3 not resolvable" };
|
|
361
|
+
}
|
|
362
|
+
if (!isWritable(pkgDir)) {
|
|
363
|
+
recordHealEvent({ phase: "skipped", reason: "dir-not-writable", abiFrom, abiTo });
|
|
364
|
+
return { healed: false, reason: "heal-failed", abiFrom, abiTo, detail: `install dir not writable: ${pkgDir}` };
|
|
365
|
+
}
|
|
366
|
+
recordHealEvent({ phase: "attempt", reason: "abi-mismatch", abiFrom, abiTo });
|
|
367
|
+
try {
|
|
368
|
+
return withFileLockSync(
|
|
369
|
+
join(credentialsDir(), "native-heal.lock"),
|
|
370
|
+
() => runRebuild(pkgDir, abiFrom, abiTo, start),
|
|
371
|
+
{ blockMs: 6e4, staleMs: 3e5 }
|
|
372
|
+
);
|
|
373
|
+
} catch (lockErr) {
|
|
374
|
+
const detail = lockErr instanceof Error ? lockErr.message : String(lockErr);
|
|
375
|
+
recordHealEvent({ phase: "failed", reason: "heal-failed", abiFrom, abiTo, detail: `lock: ${detail}` });
|
|
376
|
+
return { healed: false, contended: true, reason: "heal-failed", abiFrom, abiTo, detail: `heal lock contended: ${detail}` };
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function runRebuild(pkgDir, abiFrom, abiTo, start) {
|
|
380
|
+
try {
|
|
381
|
+
const FreshCtor = loadBetterSqlite3({ fresh: true });
|
|
382
|
+
new FreshCtor(":memory:").close();
|
|
383
|
+
const res2 = {
|
|
384
|
+
healed: true,
|
|
385
|
+
abiFrom,
|
|
386
|
+
abiTo,
|
|
387
|
+
durationMs: Date.now() - start,
|
|
388
|
+
detail: "already healed by a concurrent process"
|
|
389
|
+
};
|
|
390
|
+
recordHealEvent({ phase: "success", reason: "already-healed", abiFrom, abiTo, durationMs: res2.durationMs });
|
|
391
|
+
return res2;
|
|
392
|
+
} catch (probeErr) {
|
|
393
|
+
if (!isNativeAbiError(probeErr)) {
|
|
394
|
+
return { healed: false, reason: "heal-failed", abiFrom, abiTo, durationMs: Date.now() - start, detail: detailOf(probeErr) };
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
const prebuildBin = resolveLocalBin(pkgDir, "prebuild-install");
|
|
398
|
+
if (prebuildBin) {
|
|
399
|
+
const r2 = spawnSync(process.execPath, [prebuildBin], {
|
|
400
|
+
cwd: pkgDir,
|
|
401
|
+
encoding: "utf-8",
|
|
402
|
+
timeout: 12e4
|
|
403
|
+
});
|
|
404
|
+
if (r2.status === 0) {
|
|
405
|
+
const res2 = { healed: true, method: "prebuild-install", abiFrom, abiTo, durationMs: Date.now() - start };
|
|
406
|
+
recordHealEvent({ phase: "success", reason: "abi-mismatch", ...res2 });
|
|
407
|
+
return res2;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
const gypBin = resolveNodeGypBin();
|
|
411
|
+
if (gypBin && hasCompiler()) {
|
|
412
|
+
const r2 = spawnSync(process.execPath, [gypBin, "rebuild", "--release"], {
|
|
413
|
+
cwd: pkgDir,
|
|
414
|
+
encoding: "utf-8",
|
|
415
|
+
timeout: 3e5
|
|
416
|
+
});
|
|
417
|
+
if (r2.status === 0) {
|
|
418
|
+
const res2 = { healed: true, method: "node-gyp", abiFrom, abiTo, durationMs: Date.now() - start };
|
|
419
|
+
recordHealEvent({ phase: "success", reason: "abi-mismatch", ...res2 });
|
|
420
|
+
return res2;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const res = {
|
|
424
|
+
healed: false,
|
|
425
|
+
reason: "heal-failed",
|
|
426
|
+
abiFrom,
|
|
427
|
+
abiTo,
|
|
428
|
+
durationMs: Date.now() - start,
|
|
429
|
+
detail: prebuildBin ? "prebuild-install failed; node-gyp fallback unavailable or failed" : "no prebuild-install bin resolvable"
|
|
430
|
+
};
|
|
431
|
+
recordHealEvent({ phase: "failed", reason: "heal-failed", abiFrom, abiTo, durationMs: res.durationMs });
|
|
432
|
+
return res;
|
|
433
|
+
}
|
|
434
|
+
function recordHealEvent(event) {
|
|
435
|
+
try {
|
|
436
|
+
const rec = {
|
|
437
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
438
|
+
node: process.versions.node,
|
|
439
|
+
platform: process.platform,
|
|
440
|
+
arch: process.arch,
|
|
441
|
+
...event
|
|
442
|
+
};
|
|
443
|
+
const dir = credentialsDir();
|
|
444
|
+
mkdirSync3(dir, { recursive: true });
|
|
445
|
+
const file = join(dir, "native-heal-events.jsonl");
|
|
446
|
+
appendFileSync(file, JSON.stringify(rec) + "\n", "utf-8");
|
|
447
|
+
try {
|
|
448
|
+
chmodSync2(file, 384);
|
|
449
|
+
} catch {
|
|
450
|
+
}
|
|
451
|
+
} catch {
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
function parseAbiFrom(err) {
|
|
455
|
+
const msg = err instanceof Error ? err.message : String(err ?? "");
|
|
456
|
+
const m3 = /NODE_MODULE_VERSION (\d+)/.exec(msg);
|
|
457
|
+
if (m3 && /^\d+$/.test(m3[1])) return m3[1];
|
|
458
|
+
return void 0;
|
|
459
|
+
}
|
|
460
|
+
function detailOf(err) {
|
|
461
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
462
|
+
return msg.slice(0, 300);
|
|
463
|
+
}
|
|
464
|
+
function isWritable(dir) {
|
|
465
|
+
try {
|
|
466
|
+
accessSync(dir, fsConstants.W_OK);
|
|
467
|
+
return true;
|
|
468
|
+
} catch {
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
function resolveLocalBin(pkgDir, name) {
|
|
473
|
+
const candidates = [
|
|
474
|
+
join(pkgDir, "node_modules", ".bin", name),
|
|
475
|
+
// nested install
|
|
476
|
+
join(pkgDir, "..", ".bin", name),
|
|
477
|
+
// hoisted (node_modules/.bin)
|
|
478
|
+
join(pkgDir, "..", "..", "node_modules", ".bin", name)
|
|
479
|
+
// workspace hoist
|
|
480
|
+
];
|
|
481
|
+
for (const c2 of candidates) {
|
|
482
|
+
if (existsSync2(c2)) return c2;
|
|
483
|
+
}
|
|
484
|
+
return void 0;
|
|
485
|
+
}
|
|
486
|
+
function resolveNodeGypBin() {
|
|
487
|
+
try {
|
|
488
|
+
return req.resolve("node-gyp/bin/node-gyp.js");
|
|
489
|
+
} catch {
|
|
490
|
+
return void 0;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
function hasCompiler() {
|
|
494
|
+
for (const cc of ["cc", "clang", "gcc"]) {
|
|
495
|
+
try {
|
|
496
|
+
const r2 = spawnSync(cc, ["--version"], { encoding: "utf-8", timeout: 5e3 });
|
|
497
|
+
if (r2.status === 0) return true;
|
|
498
|
+
} catch {
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
var req, NATIVE_DB_REMEDY, MemoryEngineUnusableError, _testCtor, _testHeal;
|
|
504
|
+
var init_sqlite_loader = __esm({
|
|
505
|
+
"src/lib/sqlite-loader.ts"() {
|
|
506
|
+
"use strict";
|
|
507
|
+
init_credentials();
|
|
508
|
+
init_fileLock();
|
|
509
|
+
req = createRequire(import.meta.url);
|
|
510
|
+
NATIVE_DB_REMEDY = "Run 'massu heal' to rebuild the database engine for your Node version, then restart your MCP client / Claude Code.";
|
|
511
|
+
MemoryEngineUnusableError = class extends Error {
|
|
512
|
+
reason;
|
|
513
|
+
remedy;
|
|
514
|
+
detail;
|
|
515
|
+
constructor(reason, detail) {
|
|
516
|
+
super(
|
|
517
|
+
`Massu memory engine is unusable (${reason}). ${NATIVE_DB_REMEDY}` + (detail ? ` [${detail}]` : "")
|
|
518
|
+
);
|
|
519
|
+
this.name = "MemoryEngineUnusableError";
|
|
520
|
+
this.reason = reason;
|
|
521
|
+
this.remedy = NATIVE_DB_REMEDY;
|
|
522
|
+
this.detail = detail;
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
_testCtor = null;
|
|
526
|
+
_testHeal = null;
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
// src/lib/memory-path.ts
|
|
531
|
+
function encodeMemoryDirName(projectRoot) {
|
|
532
|
+
return projectRoot.replace(/\//g, "-");
|
|
533
|
+
}
|
|
534
|
+
var init_memory_path = __esm({
|
|
535
|
+
"src/lib/memory-path.ts"() {
|
|
536
|
+
"use strict";
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
|
|
171
540
|
// src/config-memory-schema.ts
|
|
172
541
|
import { z } from "zod";
|
|
173
542
|
var MemoryConfigSchema;
|
|
@@ -317,8 +686,8 @@ __export(config_exports, {
|
|
|
317
686
|
getResolvedPaths: () => getResolvedPaths,
|
|
318
687
|
resetConfig: () => resetConfig
|
|
319
688
|
});
|
|
320
|
-
import { resolve as resolve2, dirname } from "path";
|
|
321
|
-
import { existsSync as
|
|
689
|
+
import { resolve as resolve2, dirname as dirname3 } from "path";
|
|
690
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
322
691
|
import { homedir as homedir2 } from "os";
|
|
323
692
|
import { parse as parseYaml } from "yaml";
|
|
324
693
|
import { z as z2 } from "zod";
|
|
@@ -326,22 +695,22 @@ function findProjectRoot() {
|
|
|
326
695
|
const cwd = process.cwd();
|
|
327
696
|
let dir = cwd;
|
|
328
697
|
while (true) {
|
|
329
|
-
if (
|
|
698
|
+
if (existsSync3(resolve2(dir, "massu.config.yaml"))) {
|
|
330
699
|
return dir;
|
|
331
700
|
}
|
|
332
|
-
const parent =
|
|
701
|
+
const parent = dirname3(dir);
|
|
333
702
|
if (parent === dir) break;
|
|
334
703
|
dir = parent;
|
|
335
704
|
}
|
|
336
705
|
dir = cwd;
|
|
337
706
|
while (true) {
|
|
338
|
-
if (
|
|
707
|
+
if (existsSync3(resolve2(dir, "package.json"))) {
|
|
339
708
|
return dir;
|
|
340
709
|
}
|
|
341
|
-
if (
|
|
710
|
+
if (existsSync3(resolve2(dir, ".git"))) {
|
|
342
711
|
return dir;
|
|
343
712
|
}
|
|
344
|
-
const parent =
|
|
713
|
+
const parent = dirname3(dir);
|
|
345
714
|
if (parent === dir) break;
|
|
346
715
|
dir = parent;
|
|
347
716
|
}
|
|
@@ -358,8 +727,8 @@ function getConfig() {
|
|
|
358
727
|
const root = getProjectRoot();
|
|
359
728
|
const configPath = resolve2(root, "massu.config.yaml");
|
|
360
729
|
let rawYaml = {};
|
|
361
|
-
if (
|
|
362
|
-
const content =
|
|
730
|
+
if (existsSync3(configPath)) {
|
|
731
|
+
const content = readFileSync3(configPath, "utf-8");
|
|
363
732
|
rawYaml = parseYaml(content) ?? {};
|
|
364
733
|
}
|
|
365
734
|
const result = RawConfigSchema.safeParse(rawYaml);
|
|
@@ -440,9 +809,13 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
|
|
|
440
809
|
const resolvedEndpoint = resolveEndpoint({ configEndpoint: _config.cloud?.endpoint });
|
|
441
810
|
if (resolvedKey.apiKey) {
|
|
442
811
|
_config.cloud = {
|
|
443
|
-
enabled: true,
|
|
444
812
|
sync: { memory: true, analytics: true, audit: true },
|
|
445
813
|
..._config.cloud,
|
|
814
|
+
// Spread FIRST, then decide `enabled`, so a workspace that declares a `cloud:`
|
|
815
|
+
// block only to tune `requestTimeoutMs` does not disable its own sync. An
|
|
816
|
+
// EXPLICIT `enabled: false` is still honoured; `undefined` (not stated) means
|
|
817
|
+
// "a key resolved, so turn it on". (plan-2026-07-20-cloud-sync-timeout)
|
|
818
|
+
enabled: _config.cloud?.enabled ?? true,
|
|
446
819
|
apiKey: resolvedKey.apiKey,
|
|
447
820
|
endpoint: resolvedEndpoint
|
|
448
821
|
};
|
|
@@ -671,9 +1044,22 @@ var init_config = __esm({
|
|
|
671
1044
|
})).default([])
|
|
672
1045
|
}).optional();
|
|
673
1046
|
CloudConfigSchema = z2.object({
|
|
674
|
-
|
|
1047
|
+
// OPTIONAL, not `.default(false)`. A zod default is indistinguishable from an
|
|
1048
|
+
// explicit value once parsed, so `.default(false)` meant that merely declaring a
|
|
1049
|
+
// `cloud:` block (to set any OTHER key) silently produced `enabled: false`, which
|
|
1050
|
+
// then overrode the `enabled: true` auto-enable below via object spread — turning
|
|
1051
|
+
// cloud sync OFF as a side effect of tuning it. `undefined` now means "not stated",
|
|
1052
|
+
// and the auto-enable preserves it. (plan-2026-07-20-cloud-sync-timeout)
|
|
1053
|
+
enabled: z2.boolean().optional(),
|
|
675
1054
|
apiKey: z2.string().optional(),
|
|
676
1055
|
endpoint: z2.string().optional(),
|
|
1056
|
+
// Per-request POST budget for the `/sync` ingest. cloud-sync.ts reads this
|
|
1057
|
+
// (`(cloud as { requestTimeoutMs?: number }).requestTimeoutMs`) but it was ABSENT
|
|
1058
|
+
// from this schema, and zod strips unknown keys — so the knob was unreachable from
|
|
1059
|
+
// massu.config.yaml and the default could never be tuned. Measured 2026-07-20: a
|
|
1060
|
+
// 423-observation payload takes ~9.2s against the live ingest. Capped at
|
|
1061
|
+
// SYNC_DEADLINE_MS (20s) since the overall deadline clamps each attempt anyway.
|
|
1062
|
+
requestTimeoutMs: z2.number().int().positive().max(2e4).optional(),
|
|
677
1063
|
sync: z2.object({
|
|
678
1064
|
memory: z2.boolean().default(true),
|
|
679
1065
|
analytics: z2.boolean().default(true),
|
|
@@ -931,27 +1317,26 @@ var init_memory_vector = __esm({
|
|
|
931
1317
|
});
|
|
932
1318
|
|
|
933
1319
|
// src/db-backup.ts
|
|
934
|
-
import
|
|
935
|
-
import {
|
|
936
|
-
import { resolve as resolve3, join, basename, dirname as dirname2 } from "path";
|
|
1320
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readdirSync, statSync, unlinkSync, copyFileSync, rmSync as rmSync3 } from "fs";
|
|
1321
|
+
import { resolve as resolve3, join as join2, basename, dirname as dirname4 } from "path";
|
|
937
1322
|
import { homedir as homedir3 } from "os";
|
|
938
1323
|
function dbBackupsRoot(home = homedir3()) {
|
|
939
1324
|
return resolve3(home, ".massu", "db-backups");
|
|
940
1325
|
}
|
|
941
1326
|
function projectBackupDir(projectRoot, home = homedir3()) {
|
|
942
1327
|
const slug = basename(projectRoot).replace(/[^A-Za-z0-9._-]/g, "_") || "project";
|
|
943
|
-
return
|
|
1328
|
+
return join2(dbBackupsRoot(home), slug);
|
|
944
1329
|
}
|
|
945
1330
|
function backupStamp(nowMs) {
|
|
946
1331
|
return new Date(nowMs).toISOString().replace(/[:.]/g, "-");
|
|
947
1332
|
}
|
|
948
1333
|
function listDbBackups(projectRoot, home = homedir3()) {
|
|
949
1334
|
const dir = projectBackupDir(projectRoot, home);
|
|
950
|
-
if (!
|
|
1335
|
+
if (!existsSync4(dir)) return [];
|
|
951
1336
|
const out = [];
|
|
952
1337
|
for (const f2 of readdirSync(dir)) {
|
|
953
1338
|
if (!f2.endsWith(".db")) continue;
|
|
954
|
-
const p19 =
|
|
1339
|
+
const p19 = join2(dir, f2);
|
|
955
1340
|
const st = statSync(p19);
|
|
956
1341
|
const db = f2.replace(/-\d{4}-\d{2}-\d{2}T.*$/, "");
|
|
957
1342
|
out.push({ db, path: p19, bytes: st.size, mtimeMs: st.mtimeMs });
|
|
@@ -964,13 +1349,13 @@ function hasFreshDbBackup(projectRoot, dbPath, nowMs = Date.now(), home = homedi
|
|
|
964
1349
|
if (backups.length === 0) return false;
|
|
965
1350
|
const newest = backups[0];
|
|
966
1351
|
if (nowMs - newest.mtimeMs > FRESH_WINDOW_MS) return false;
|
|
967
|
-
if (
|
|
1352
|
+
if (existsSync4(dbPath) && statSync(dbPath).mtimeMs > newest.mtimeMs) return false;
|
|
968
1353
|
return true;
|
|
969
1354
|
}
|
|
970
1355
|
function integrityOk(dbPath) {
|
|
971
1356
|
let db = null;
|
|
972
1357
|
try {
|
|
973
|
-
db =
|
|
1358
|
+
db = openDatabase(dbPath, { readonly: true });
|
|
974
1359
|
const rows = db.pragma("integrity_check");
|
|
975
1360
|
return rows.length === 1 && rows[0].integrity_check === "ok";
|
|
976
1361
|
} catch {
|
|
@@ -983,16 +1368,16 @@ function integrityOk(dbPath) {
|
|
|
983
1368
|
}
|
|
984
1369
|
}
|
|
985
1370
|
function backupDb(projectRoot, dbPath, nowMs = Date.now(), home = homedir3()) {
|
|
986
|
-
if (!
|
|
1371
|
+
if (!existsSync4(dbPath)) {
|
|
987
1372
|
throw new DbBackupError(`cannot back up a database that does not exist: ${dbPath}`);
|
|
988
1373
|
}
|
|
989
1374
|
const dir = projectBackupDir(projectRoot, home);
|
|
990
|
-
|
|
1375
|
+
mkdirSync4(dir, { recursive: true });
|
|
991
1376
|
const name = basename(dbPath, ".db");
|
|
992
|
-
const dest =
|
|
1377
|
+
const dest = join2(dir, `${name}-${backupStamp(nowMs)}.db`);
|
|
993
1378
|
let src = null;
|
|
994
1379
|
try {
|
|
995
|
-
src =
|
|
1380
|
+
src = openDatabase(dbPath, { readonly: true });
|
|
996
1381
|
src.exec(`VACUUM INTO '${dest.replace(/'/g, "''")}'`);
|
|
997
1382
|
} catch (err) {
|
|
998
1383
|
throw new DbBackupError(
|
|
@@ -1030,7 +1415,7 @@ function pruneDbBackups(projectRoot, dbName, keep = DEFAULT_RETENTION, home = ho
|
|
|
1030
1415
|
return removed;
|
|
1031
1416
|
}
|
|
1032
1417
|
function restoreDb(backupPath, dbPath, nowMs = Date.now(), home = homedir3()) {
|
|
1033
|
-
if (!
|
|
1418
|
+
if (!existsSync4(backupPath)) {
|
|
1034
1419
|
throw new DbBackupError(`backup not found: ${backupPath}`);
|
|
1035
1420
|
}
|
|
1036
1421
|
if (!integrityOk(backupPath)) {
|
|
@@ -1039,15 +1424,15 @@ function restoreDb(backupPath, dbPath, nowMs = Date.now(), home = homedir3()) {
|
|
|
1039
1424
|
);
|
|
1040
1425
|
}
|
|
1041
1426
|
let preserved = null;
|
|
1042
|
-
if (
|
|
1043
|
-
const dir =
|
|
1044
|
-
|
|
1045
|
-
preserved =
|
|
1427
|
+
if (existsSync4(dbPath)) {
|
|
1428
|
+
const dir = dirname4(backupPath) || projectBackupDir(process.cwd(), home);
|
|
1429
|
+
mkdirSync4(dir, { recursive: true });
|
|
1430
|
+
preserved = join2(dir, `${basename(dbPath, ".db")}.corrupt-${backupStamp(nowMs)}.db`);
|
|
1046
1431
|
copyFileSync(dbPath, preserved);
|
|
1047
1432
|
}
|
|
1048
1433
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
1049
1434
|
const p19 = `${dbPath}${suffix}`;
|
|
1050
|
-
if (
|
|
1435
|
+
if (existsSync4(p19)) rmSync3(p19, { force: true });
|
|
1051
1436
|
}
|
|
1052
1437
|
copyFileSync(backupPath, dbPath);
|
|
1053
1438
|
if (!integrityOk(dbPath)) {
|
|
@@ -1059,7 +1444,7 @@ function restoreDb(backupPath, dbPath, nowMs = Date.now(), home = homedir3()) {
|
|
|
1059
1444
|
}
|
|
1060
1445
|
function backupBeforeSchemaChange(projectRoot, dbPath, onError, nowMs = Date.now(), home = homedir3()) {
|
|
1061
1446
|
try {
|
|
1062
|
-
if (!
|
|
1447
|
+
if (!existsSync4(dbPath)) return null;
|
|
1063
1448
|
if (hasFreshDbBackup(projectRoot, dbPath, nowMs, home)) return null;
|
|
1064
1449
|
return backupDb(projectRoot, dbPath, nowMs, home);
|
|
1065
1450
|
} catch (err) {
|
|
@@ -1071,6 +1456,7 @@ var DEFAULT_RETENTION, FRESH_WINDOW_MS, DbBackupError;
|
|
|
1071
1456
|
var init_db_backup = __esm({
|
|
1072
1457
|
"src/db-backup.ts"() {
|
|
1073
1458
|
"use strict";
|
|
1459
|
+
init_sqlite_loader();
|
|
1074
1460
|
DEFAULT_RETENTION = 5;
|
|
1075
1461
|
FRESH_WINDOW_MS = 24 * 60 * 60 * 1e3;
|
|
1076
1462
|
DbBackupError = class extends Error {
|
|
@@ -1233,21 +1619,21 @@ var init_rule_candidate_store = __esm({
|
|
|
1233
1619
|
});
|
|
1234
1620
|
|
|
1235
1621
|
// src/hooks/lib/hook-failure-signal.ts
|
|
1236
|
-
import { appendFileSync, mkdirSync as
|
|
1237
|
-
import { join as
|
|
1622
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync5, existsSync as existsSync5 } from "fs";
|
|
1623
|
+
import { join as join3, dirname as dirname5 } from "path";
|
|
1238
1624
|
function resolveFailureLogPath() {
|
|
1239
1625
|
const explicit = process.env.MASSU_HOOK_FAILURE_LOG;
|
|
1240
1626
|
if (explicit) return explicit;
|
|
1241
1627
|
let dir = process.cwd();
|
|
1242
1628
|
for (let i = 0; i < 12; i++) {
|
|
1243
|
-
if (
|
|
1244
|
-
return
|
|
1629
|
+
if (existsSync5(join3(dir, ".massu")) || existsSync5(join3(dir, "massu.config.yaml"))) {
|
|
1630
|
+
return join3(dir, ".massu", "hook-failures.jsonl");
|
|
1245
1631
|
}
|
|
1246
|
-
const parent =
|
|
1632
|
+
const parent = dirname5(dir);
|
|
1247
1633
|
if (parent === dir) break;
|
|
1248
1634
|
dir = parent;
|
|
1249
1635
|
}
|
|
1250
|
-
return
|
|
1636
|
+
return join3(process.cwd(), ".massu", "hook-failures.jsonl");
|
|
1251
1637
|
}
|
|
1252
1638
|
function recordHookFailure(hook, error, context) {
|
|
1253
1639
|
const record = {
|
|
@@ -1260,8 +1646,8 @@ function recordHookFailure(hook, error, context) {
|
|
|
1260
1646
|
let wroteSomething = false;
|
|
1261
1647
|
try {
|
|
1262
1648
|
const path = resolveFailureLogPath();
|
|
1263
|
-
|
|
1264
|
-
|
|
1649
|
+
mkdirSync5(dirname5(path), { recursive: true });
|
|
1650
|
+
appendFileSync2(path, JSON.stringify(record) + "\n", "utf-8");
|
|
1265
1651
|
wroteSomething = true;
|
|
1266
1652
|
} catch {
|
|
1267
1653
|
}
|
|
@@ -1304,9 +1690,9 @@ var init_hook_failure_signal = __esm({
|
|
|
1304
1690
|
});
|
|
1305
1691
|
|
|
1306
1692
|
// src/memory-embedder-tokenizer.ts
|
|
1307
|
-
import { readFileSync as
|
|
1693
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
1308
1694
|
function loadVocab(vocabPath) {
|
|
1309
|
-
const lines =
|
|
1695
|
+
const lines = readFileSync4(vocabPath, "utf-8").split("\n");
|
|
1310
1696
|
const vocab = /* @__PURE__ */ new Map();
|
|
1311
1697
|
for (let i = 0; i < lines.length; i++) {
|
|
1312
1698
|
const tok = lines[i].replace(/\r$/, "");
|
|
@@ -1426,9 +1812,9 @@ var init_memory_embedder_tokenizer = __esm({
|
|
|
1426
1812
|
|
|
1427
1813
|
// src/memory-embedder.ts
|
|
1428
1814
|
import { fileURLToPath } from "url";
|
|
1429
|
-
import { dirname as
|
|
1430
|
-
import { existsSync as
|
|
1431
|
-
import { createRequire } from "module";
|
|
1815
|
+
import { dirname as dirname6, join as join4 } from "path";
|
|
1816
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1817
|
+
import { createRequire as createRequire2 } from "module";
|
|
1432
1818
|
function getActiveEmbedModel() {
|
|
1433
1819
|
return _activeModel;
|
|
1434
1820
|
}
|
|
@@ -1449,21 +1835,21 @@ function embeddingsDisabled() {
|
|
|
1449
1835
|
}
|
|
1450
1836
|
function resolveModelDir() {
|
|
1451
1837
|
try {
|
|
1452
|
-
let dir =
|
|
1838
|
+
let dir = dirname6(fileURLToPath(import.meta.url));
|
|
1453
1839
|
let root = null;
|
|
1454
1840
|
for (let i = 0; i < 8; i++) {
|
|
1455
|
-
if (
|
|
1841
|
+
if (existsSync6(join4(dir, "package.json"))) {
|
|
1456
1842
|
root = dir;
|
|
1457
1843
|
break;
|
|
1458
1844
|
}
|
|
1459
|
-
const parent =
|
|
1845
|
+
const parent = dirname6(dir);
|
|
1460
1846
|
if (parent === dir) break;
|
|
1461
1847
|
dir = parent;
|
|
1462
1848
|
}
|
|
1463
1849
|
if (!root) return null;
|
|
1464
1850
|
for (const rel of ["dist/embedder", "assets/embedder"]) {
|
|
1465
|
-
const candidate =
|
|
1466
|
-
if (
|
|
1851
|
+
const candidate = join4(root, rel);
|
|
1852
|
+
if (existsSync6(join4(candidate, "model_quantized.onnx"))) return candidate;
|
|
1467
1853
|
}
|
|
1468
1854
|
return null;
|
|
1469
1855
|
} catch {
|
|
@@ -1472,9 +1858,9 @@ function resolveModelDir() {
|
|
|
1472
1858
|
}
|
|
1473
1859
|
function resolveWasmDir() {
|
|
1474
1860
|
try {
|
|
1475
|
-
const
|
|
1476
|
-
const main2 =
|
|
1477
|
-
return
|
|
1861
|
+
const req2 = createRequire2(import.meta.url);
|
|
1862
|
+
const main2 = req2.resolve(ORT_PKG);
|
|
1863
|
+
return dirname6(main2);
|
|
1478
1864
|
} catch {
|
|
1479
1865
|
return null;
|
|
1480
1866
|
}
|
|
@@ -1494,8 +1880,8 @@ async function getSession() {
|
|
|
1494
1880
|
ort.env.wasm.proxy = false;
|
|
1495
1881
|
ort.env.wasm.wasmPaths = wasmDir.endsWith("/") ? wasmDir : wasmDir + "/";
|
|
1496
1882
|
_ortTensor = ort.Tensor;
|
|
1497
|
-
const modelPath =
|
|
1498
|
-
const vocabPath =
|
|
1883
|
+
const modelPath = join4(modelDir, "model_quantized.onnx");
|
|
1884
|
+
const vocabPath = join4(modelDir, "vocab.txt");
|
|
1499
1885
|
_vocab = loadVocab(vocabPath);
|
|
1500
1886
|
const session = await ort.InferenceSession.create(modelPath, {
|
|
1501
1887
|
executionProviders: ["wasm"],
|
|
@@ -1847,9 +2233,8 @@ __export(memory_db_exports, {
|
|
|
1847
2233
|
upsertObservationEmbedding: () => upsertObservationEmbedding,
|
|
1848
2234
|
usageWarmupElapsed: () => usageWarmupElapsed
|
|
1849
2235
|
});
|
|
1850
|
-
import
|
|
1851
|
-
import {
|
|
1852
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
2236
|
+
import { dirname as dirname7, basename as basename2 } from "path";
|
|
2237
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync6 } from "fs";
|
|
1853
2238
|
function sanitizeFts5Query(raw) {
|
|
1854
2239
|
const trimmed = raw.trim();
|
|
1855
2240
|
if (!trimmed) return '""';
|
|
@@ -1865,12 +2250,12 @@ function sanitizeFts5QueryOr(raw) {
|
|
|
1865
2250
|
}
|
|
1866
2251
|
function getMemoryDb() {
|
|
1867
2252
|
const dbPath = getResolvedPaths().memoryDbPath;
|
|
1868
|
-
const dir =
|
|
1869
|
-
if (!
|
|
1870
|
-
|
|
2253
|
+
const dir = dirname7(dbPath);
|
|
2254
|
+
if (!existsSync7(dir)) {
|
|
2255
|
+
mkdirSync6(dir, { recursive: true });
|
|
1871
2256
|
}
|
|
1872
|
-
const preExisting =
|
|
1873
|
-
const db =
|
|
2257
|
+
const preExisting = existsSync7(dbPath);
|
|
2258
|
+
const db = openDatabase(dbPath);
|
|
1874
2259
|
db.pragma("journal_mode = WAL");
|
|
1875
2260
|
db.pragma("foreign_keys = ON");
|
|
1876
2261
|
const onDisk = db.pragma("user_version", { simple: true });
|
|
@@ -3576,6 +3961,7 @@ var MEMORY_SCHEMA_VERSION, TOOL_COST_EVENTS_RETENTION_DAYS, MAX_DRAFT_TEXT_LEN,
|
|
|
3576
3961
|
var init_memory_db = __esm({
|
|
3577
3962
|
"src/memory-db.ts"() {
|
|
3578
3963
|
"use strict";
|
|
3964
|
+
init_sqlite_loader();
|
|
3579
3965
|
init_config();
|
|
3580
3966
|
init_memory_vector();
|
|
3581
3967
|
init_db_backup();
|
|
@@ -3596,8 +3982,8 @@ var init_memory_db = __esm({
|
|
|
3596
3982
|
});
|
|
3597
3983
|
|
|
3598
3984
|
// src/memory-file-ingest.ts
|
|
3599
|
-
import { readFileSync as
|
|
3600
|
-
import { join as
|
|
3985
|
+
import { readFileSync as readFileSync5, existsSync as existsSync8, readdirSync as readdirSync2 } from "fs";
|
|
3986
|
+
import { join as join5 } from "path";
|
|
3601
3987
|
import { parse as parseYaml2 } from "yaml";
|
|
3602
3988
|
import { basename as pathBasename } from "path";
|
|
3603
3989
|
import { createHash } from "crypto";
|
|
@@ -3703,8 +4089,8 @@ function upsertMemoryFileMirror(db, f2) {
|
|
|
3703
4089
|
return "inserted";
|
|
3704
4090
|
}
|
|
3705
4091
|
function ingestMemoryFile(db, sessionId, filePath) {
|
|
3706
|
-
if (!
|
|
3707
|
-
const content =
|
|
4092
|
+
if (!existsSync8(filePath)) return "skipped";
|
|
4093
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
3708
4094
|
if (Buffer.byteLength(content, "utf-8") > MAX_MEMORY_FILE_BYTES) {
|
|
3709
4095
|
process.stderr.write(
|
|
3710
4096
|
`[massu] memory file exceeds ${MAX_MEMORY_FILE_BYTES} bytes, not ingested: ${filePath}
|
|
@@ -3774,14 +4160,14 @@ ${body}` : body;
|
|
|
3774
4160
|
}
|
|
3775
4161
|
function backfillMemoryFiles(db, memoryDir, sessionId) {
|
|
3776
4162
|
const stats = { inserted: 0, updated: 0, skipped: 0, total: 0 };
|
|
3777
|
-
if (!
|
|
4163
|
+
if (!existsSync8(memoryDir)) return stats;
|
|
3778
4164
|
const files = readdirSync2(memoryDir).filter(
|
|
3779
4165
|
(f2) => f2.endsWith(".md") && f2 !== "MEMORY.md"
|
|
3780
4166
|
);
|
|
3781
4167
|
stats.total = files.length;
|
|
3782
4168
|
const sid = sessionId ?? `backfill-${Date.now()}`;
|
|
3783
4169
|
for (const file of files) {
|
|
3784
|
-
const result = ingestMemoryFile(db, sid,
|
|
4170
|
+
const result = ingestMemoryFile(db, sid, join5(memoryDir, file));
|
|
3785
4171
|
stats[result]++;
|
|
3786
4172
|
}
|
|
3787
4173
|
return stats;
|
|
@@ -3990,16 +4376,16 @@ var init_template_engine = __esm({
|
|
|
3990
4376
|
// src/lib/settings-local.ts
|
|
3991
4377
|
import {
|
|
3992
4378
|
closeSync,
|
|
3993
|
-
existsSync as
|
|
4379
|
+
existsSync as existsSync9,
|
|
3994
4380
|
fsyncSync,
|
|
3995
|
-
mkdirSync as
|
|
4381
|
+
mkdirSync as mkdirSync7,
|
|
3996
4382
|
openSync,
|
|
3997
|
-
readFileSync as
|
|
4383
|
+
readFileSync as readFileSync6,
|
|
3998
4384
|
renameSync,
|
|
3999
|
-
rmSync as
|
|
4385
|
+
rmSync as rmSync4,
|
|
4000
4386
|
writeSync
|
|
4001
4387
|
} from "fs";
|
|
4002
|
-
import { dirname as
|
|
4388
|
+
import { dirname as dirname8, resolve as resolve5 } from "path";
|
|
4003
4389
|
function atomicWriteFile(targetPath, content, mode = 420) {
|
|
4004
4390
|
const tmpPath = `${targetPath}.${process.pid}.tmp`;
|
|
4005
4391
|
try {
|
|
@@ -4013,9 +4399,9 @@ function atomicWriteFile(targetPath, content, mode = 420) {
|
|
|
4013
4399
|
}
|
|
4014
4400
|
renameSync(tmpPath, targetPath);
|
|
4015
4401
|
} catch (err) {
|
|
4016
|
-
if (
|
|
4402
|
+
if (existsSync9(tmpPath)) {
|
|
4017
4403
|
try {
|
|
4018
|
-
|
|
4404
|
+
rmSync4(tmpPath, { force: true });
|
|
4019
4405
|
} catch {
|
|
4020
4406
|
}
|
|
4021
4407
|
}
|
|
@@ -4023,11 +4409,11 @@ function atomicWriteFile(targetPath, content, mode = 420) {
|
|
|
4023
4409
|
}
|
|
4024
4410
|
}
|
|
4025
4411
|
function readSettingsAtPath(absolutePath) {
|
|
4026
|
-
if (!
|
|
4412
|
+
if (!existsSync9(absolutePath)) {
|
|
4027
4413
|
return {};
|
|
4028
4414
|
}
|
|
4029
4415
|
try {
|
|
4030
|
-
const raw =
|
|
4416
|
+
const raw = readFileSync6(absolutePath, "utf-8");
|
|
4031
4417
|
const parsed = JSON.parse(raw);
|
|
4032
4418
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
4033
4419
|
return {};
|
|
@@ -4042,9 +4428,9 @@ function readSettingsLocal(claudeDir) {
|
|
|
4042
4428
|
}
|
|
4043
4429
|
function writeSettingsLocalAtomic(claudeDir, settings) {
|
|
4044
4430
|
const targetPath = resolve5(claudeDir, "settings.local.json");
|
|
4045
|
-
const dir =
|
|
4046
|
-
if (!
|
|
4047
|
-
|
|
4431
|
+
const dir = dirname8(targetPath);
|
|
4432
|
+
if (!existsSync9(dir)) {
|
|
4433
|
+
mkdirSync7(dir, { recursive: true });
|
|
4048
4434
|
}
|
|
4049
4435
|
const content = JSON.stringify(settings, null, 2) + "\n";
|
|
4050
4436
|
atomicWriteFile(targetPath, content);
|
|
@@ -4058,7 +4444,7 @@ var init_settings_local = __esm({
|
|
|
4058
4444
|
// src/permissions.ts
|
|
4059
4445
|
import { createHash as createHash2 } from "crypto";
|
|
4060
4446
|
import { homedir as homedir4 } from "os";
|
|
4061
|
-
import { join as
|
|
4447
|
+
import { join as join6 } from "path";
|
|
4062
4448
|
function canonicalJson(value) {
|
|
4063
4449
|
if (value === null || typeof value !== "object") {
|
|
4064
4450
|
return JSON.stringify(value);
|
|
@@ -4097,7 +4483,7 @@ function detectInvalidDefaultMode(settings) {
|
|
|
4097
4483
|
return { invalid: false };
|
|
4098
4484
|
}
|
|
4099
4485
|
function readGlobalSettings() {
|
|
4100
|
-
return readSettingsAtPath(
|
|
4486
|
+
return readSettingsAtPath(join6(homedir4(), ".claude", "settings.json"));
|
|
4101
4487
|
}
|
|
4102
4488
|
function mergedPermissionState(global2, local, canonical) {
|
|
4103
4489
|
const globalPerm = global2.permissions ?? {};
|
|
@@ -4285,17 +4671,17 @@ __export(install_commands_exports, {
|
|
|
4285
4671
|
syncDirectory: () => syncDirectory
|
|
4286
4672
|
});
|
|
4287
4673
|
import {
|
|
4288
|
-
existsSync as
|
|
4289
|
-
readFileSync as
|
|
4290
|
-
mkdirSync as
|
|
4674
|
+
existsSync as existsSync10,
|
|
4675
|
+
readFileSync as readFileSync7,
|
|
4676
|
+
mkdirSync as mkdirSync8,
|
|
4291
4677
|
readdirSync as readdirSync3,
|
|
4292
4678
|
statSync as statSync2
|
|
4293
4679
|
} from "fs";
|
|
4294
|
-
import { resolve as resolve6, dirname as
|
|
4680
|
+
import { resolve as resolve6, dirname as dirname9, join as join7 } from "path";
|
|
4295
4681
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4296
4682
|
import { createHash as createHash3 } from "crypto";
|
|
4297
4683
|
function dirHoldsAssets(dir) {
|
|
4298
|
-
if (!
|
|
4684
|
+
if (!existsSync10(dir)) return false;
|
|
4299
4685
|
try {
|
|
4300
4686
|
return readdirSync3(dir).some(
|
|
4301
4687
|
(e2) => e2.endsWith(".md") || statSync2(resolve6(dir, e2)).isDirectory()
|
|
@@ -4330,10 +4716,10 @@ function canonicalizeOwned(content) {
|
|
|
4330
4716
|
}
|
|
4331
4717
|
function massuOwnedSourceIsValid(resolvedSourcePath, content) {
|
|
4332
4718
|
const sidecar = `${resolvedSourcePath}.sha256`;
|
|
4333
|
-
if (!
|
|
4719
|
+
if (!existsSync10(sidecar)) return false;
|
|
4334
4720
|
let expected;
|
|
4335
4721
|
try {
|
|
4336
|
-
expected =
|
|
4722
|
+
expected = readFileSync7(sidecar, "utf-8").trim();
|
|
4337
4723
|
} catch {
|
|
4338
4724
|
return false;
|
|
4339
4725
|
}
|
|
@@ -4344,11 +4730,11 @@ function hashContent(content) {
|
|
|
4344
4730
|
}
|
|
4345
4731
|
function loadManifest(claudeDir) {
|
|
4346
4732
|
const path = resolve6(claudeDir, MANIFEST_RELPATH);
|
|
4347
|
-
if (!
|
|
4733
|
+
if (!existsSync10(path)) {
|
|
4348
4734
|
return emptyManifest();
|
|
4349
4735
|
}
|
|
4350
4736
|
try {
|
|
4351
|
-
const raw =
|
|
4737
|
+
const raw = readFileSync7(path, "utf-8");
|
|
4352
4738
|
const parsed = JSON.parse(raw);
|
|
4353
4739
|
if (!parsed || typeof parsed !== "object" || !parsed.entries) {
|
|
4354
4740
|
return emptyManifest();
|
|
@@ -4360,8 +4746,8 @@ function loadManifest(claudeDir) {
|
|
|
4360
4746
|
}
|
|
4361
4747
|
function saveManifest(claudeDir, manifest) {
|
|
4362
4748
|
const dir = resolve6(claudeDir, ".massu");
|
|
4363
|
-
if (!
|
|
4364
|
-
|
|
4749
|
+
if (!existsSync10(dir)) {
|
|
4750
|
+
mkdirSync8(dir, { recursive: true });
|
|
4365
4751
|
}
|
|
4366
4752
|
const finalPath = resolve6(dir, "install-manifest.json");
|
|
4367
4753
|
manifest.generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4426,17 +4812,17 @@ function pickVariant(baseName, sourceDir, framework) {
|
|
|
4426
4812
|
for (const cand of candidates) {
|
|
4427
4813
|
if (cand.subFramework) {
|
|
4428
4814
|
const subPath = resolve6(sourceDir, `${baseName}.${cand.lang}-${cand.subFramework}.md`);
|
|
4429
|
-
if (
|
|
4815
|
+
if (existsSync10(subPath)) {
|
|
4430
4816
|
return { kind: "hit", suffix: `.${cand.lang}-${cand.subFramework}` };
|
|
4431
4817
|
}
|
|
4432
4818
|
}
|
|
4433
4819
|
const langPath = resolve6(sourceDir, `${baseName}.${cand.lang}.md`);
|
|
4434
|
-
if (
|
|
4820
|
+
if (existsSync10(langPath)) {
|
|
4435
4821
|
return { kind: "hit", suffix: `.${cand.lang}` };
|
|
4436
4822
|
}
|
|
4437
4823
|
}
|
|
4438
4824
|
const defaultPath = resolve6(sourceDir, `${baseName}.md`);
|
|
4439
|
-
if (
|
|
4825
|
+
if (existsSync10(defaultPath)) {
|
|
4440
4826
|
return { kind: "hit", suffix: "" };
|
|
4441
4827
|
}
|
|
4442
4828
|
if (framework.type === "multi" && !framework.primary) {
|
|
@@ -4452,8 +4838,8 @@ function isVariantFilename(entry) {
|
|
|
4452
4838
|
}
|
|
4453
4839
|
function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPrefix, topLevel = true, templateVars = {}) {
|
|
4454
4840
|
const stats = { installed: 0, updated: 0, skipped: 0, kept: 0 };
|
|
4455
|
-
if (!
|
|
4456
|
-
|
|
4841
|
+
if (!existsSync10(targetDir)) {
|
|
4842
|
+
mkdirSync8(targetDir, { recursive: true });
|
|
4457
4843
|
}
|
|
4458
4844
|
const entries = readdirSync3(sourceDir);
|
|
4459
4845
|
for (const entry of entries) {
|
|
@@ -4490,12 +4876,12 @@ function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPre
|
|
|
4490
4876
|
sourceFilename = suffix === "" ? `${baseName}.md` : `${baseName}${suffix}.md`;
|
|
4491
4877
|
}
|
|
4492
4878
|
const resolvedSourcePath = resolve6(sourceDir, sourceFilename);
|
|
4493
|
-
if (!
|
|
4879
|
+
if (!existsSync10(resolvedSourcePath)) {
|
|
4494
4880
|
continue;
|
|
4495
4881
|
}
|
|
4496
4882
|
const targetFilename = topLevel ? `${baseName}.md` : entry;
|
|
4497
4883
|
const targetPath = resolve6(targetDir, targetFilename);
|
|
4498
|
-
const rawContent =
|
|
4884
|
+
const rawContent = readFileSync7(resolvedSourcePath, "utf-8");
|
|
4499
4885
|
let sourceContent;
|
|
4500
4886
|
try {
|
|
4501
4887
|
sourceContent = renderTemplate(rawContent, templateVars);
|
|
@@ -4513,11 +4899,11 @@ function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPre
|
|
|
4513
4899
|
const sourceHash = hashContent(sourceContent);
|
|
4514
4900
|
const manifestKey = manifestKeyPrefix === "" ? targetFilename : `${manifestKeyPrefix}/${targetFilename}`;
|
|
4515
4901
|
const lastInstalledHash = manifest.entries[manifestKey];
|
|
4516
|
-
if (
|
|
4902
|
+
if (existsSync10(targetPath)) {
|
|
4517
4903
|
let existingContent;
|
|
4518
4904
|
try {
|
|
4519
4905
|
if (!statSync2(targetPath).isFile()) throw new Error("not a regular file");
|
|
4520
|
-
existingContent =
|
|
4906
|
+
existingContent = readFileSync7(targetPath, "utf-8");
|
|
4521
4907
|
} catch (err) {
|
|
4522
4908
|
process.stderr.write(
|
|
4523
4909
|
`massu: skipping ${manifestKey}: target is not a readable file (${err instanceof Error ? err.message : String(err)}). Remove it and re-install.
|
|
@@ -4600,8 +4986,8 @@ function installCommands(projectRoot, opts = {}) {
|
|
|
4600
4986
|
const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
|
|
4601
4987
|
const claudeDir = resolve6(projectRoot, claudeDirName);
|
|
4602
4988
|
const targetDir = resolve6(claudeDir, "commands");
|
|
4603
|
-
if (!
|
|
4604
|
-
|
|
4989
|
+
if (!existsSync10(targetDir)) {
|
|
4990
|
+
mkdirSync8(targetDir, { recursive: true });
|
|
4605
4991
|
}
|
|
4606
4992
|
const sourceDir = resolveAssetDir("commands");
|
|
4607
4993
|
if (!sourceDir) {
|
|
@@ -4730,7 +5116,7 @@ var init_install_commands = __esm({
|
|
|
4730
5116
|
init_settings_local();
|
|
4731
5117
|
init_permissions();
|
|
4732
5118
|
__filename = fileURLToPath2(import.meta.url);
|
|
4733
|
-
__dirname =
|
|
5119
|
+
__dirname = dirname9(__filename);
|
|
4734
5120
|
ASSET_TYPES = [
|
|
4735
5121
|
{ name: "commands", targetSubdir: "commands", description: "slash commands" },
|
|
4736
5122
|
{ name: "agents", targetSubdir: "agents", description: "agent definitions" },
|
|
@@ -4739,7 +5125,7 @@ var init_install_commands = __esm({
|
|
|
4739
5125
|
{ name: "reference", targetSubdir: "reference", description: "reference files" }
|
|
4740
5126
|
];
|
|
4741
5127
|
MANIFEST_VERSION = 1;
|
|
4742
|
-
MANIFEST_RELPATH =
|
|
5128
|
+
MANIFEST_RELPATH = join7(".massu", "install-manifest.json");
|
|
4743
5129
|
MASSU_OWNED_PATHS = ["commands/_verification-laws.md"];
|
|
4744
5130
|
PASSTHROUGH_LANG_KEYS = [
|
|
4745
5131
|
"typescript",
|
|
@@ -4952,17 +5338,17 @@ var init_manifest_registry = __esm({
|
|
|
4952
5338
|
});
|
|
4953
5339
|
|
|
4954
5340
|
// src/detect/package-detector.ts
|
|
4955
|
-
import { readFileSync as
|
|
4956
|
-
import { join as
|
|
5341
|
+
import { readFileSync as readFileSync8, existsSync as existsSync11, statSync as statSync3, lstatSync, readdirSync as readdirSync4 } from "fs";
|
|
5342
|
+
import { join as join8, relative as relative2 } from "path";
|
|
4957
5343
|
import { parse as parseToml } from "smol-toml";
|
|
4958
5344
|
function safeRead(path) {
|
|
4959
5345
|
try {
|
|
4960
|
-
if (!
|
|
5346
|
+
if (!existsSync11(path)) return null;
|
|
4961
5347
|
const ls = lstatSync(path);
|
|
4962
5348
|
if (ls.isSymbolicLink()) return null;
|
|
4963
5349
|
const st = statSync3(path);
|
|
4964
5350
|
if (!st.isFile()) return null;
|
|
4965
|
-
return
|
|
5351
|
+
return readFileSync8(path, "utf-8");
|
|
4966
5352
|
} catch {
|
|
4967
5353
|
return null;
|
|
4968
5354
|
}
|
|
@@ -4993,7 +5379,7 @@ function parsePackageJson(path, directory, root, warnings) {
|
|
|
4993
5379
|
const peer = Object.keys(
|
|
4994
5380
|
pkg.peerDependencies ?? {}
|
|
4995
5381
|
);
|
|
4996
|
-
const hasTs = deps.includes("typescript") || devDeps.includes("typescript") ||
|
|
5382
|
+
const hasTs = deps.includes("typescript") || devDeps.includes("typescript") || existsSync11(join8(directory, "tsconfig.json"));
|
|
4997
5383
|
const language = hasTs ? "typescript" : "javascript";
|
|
4998
5384
|
const scripts = Object.keys(
|
|
4999
5385
|
pkg.scripts ?? {}
|
|
@@ -5419,8 +5805,8 @@ function detectManifestsInDir(dir, root, warnings) {
|
|
|
5419
5805
|
let dirEntries = null;
|
|
5420
5806
|
for (const entry of getManifestRegistry2()) {
|
|
5421
5807
|
if (!entry.pattern.startsWith("*")) {
|
|
5422
|
-
const path =
|
|
5423
|
-
if (!
|
|
5808
|
+
const path = join8(dir, entry.pattern);
|
|
5809
|
+
if (!existsSync11(path)) continue;
|
|
5424
5810
|
const m3 = entry.parse(path, dir, root, warnings);
|
|
5425
5811
|
if (m3 !== null) out.push(m3);
|
|
5426
5812
|
} else {
|
|
@@ -5433,8 +5819,8 @@ function detectManifestsInDir(dir, root, warnings) {
|
|
|
5433
5819
|
}
|
|
5434
5820
|
for (const fname of dirEntries) {
|
|
5435
5821
|
if (!matchManifestPattern2(fname, entry.pattern)) continue;
|
|
5436
|
-
const path =
|
|
5437
|
-
if (!
|
|
5822
|
+
const path = join8(dir, fname);
|
|
5823
|
+
if (!existsSync11(path)) continue;
|
|
5438
5824
|
const m3 = entry.parse(path, dir, root, warnings);
|
|
5439
5825
|
if (m3 !== null) out.push(m3);
|
|
5440
5826
|
}
|
|
@@ -5444,7 +5830,7 @@ function detectManifestsInDir(dir, root, warnings) {
|
|
|
5444
5830
|
}
|
|
5445
5831
|
function listSubdirs(dir) {
|
|
5446
5832
|
try {
|
|
5447
|
-
return readdirSync4(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && !IGNORED_DIRS.has(e2.name)).map((e2) =>
|
|
5833
|
+
return readdirSync4(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && !IGNORED_DIRS.has(e2.name)).map((e2) => join8(dir, e2.name));
|
|
5448
5834
|
} catch {
|
|
5449
5835
|
return [];
|
|
5450
5836
|
}
|
|
@@ -5454,8 +5840,8 @@ function detectPackageManifests(projectRoot) {
|
|
|
5454
5840
|
const manifests = [];
|
|
5455
5841
|
manifests.push(...detectManifestsInDir(projectRoot, projectRoot, warnings));
|
|
5456
5842
|
for (const ws of WORKSPACE_DIRS) {
|
|
5457
|
-
const wsRoot =
|
|
5458
|
-
if (!
|
|
5843
|
+
const wsRoot = join8(projectRoot, ws);
|
|
5844
|
+
if (!existsSync11(wsRoot)) continue;
|
|
5459
5845
|
for (const sub of listSubdirs(wsRoot)) {
|
|
5460
5846
|
manifests.push(...detectManifestsInDir(sub, projectRoot, warnings));
|
|
5461
5847
|
for (const sub2 of listSubdirs(sub)) {
|
|
@@ -5702,20 +6088,20 @@ var init_framework_detector = __esm({
|
|
|
5702
6088
|
|
|
5703
6089
|
// src/lib/safe-write.ts
|
|
5704
6090
|
import {
|
|
5705
|
-
writeFileSync as
|
|
6091
|
+
writeFileSync as writeFileSync3,
|
|
5706
6092
|
renameSync as renameSync2,
|
|
5707
6093
|
openSync as openSync2,
|
|
5708
6094
|
fsyncSync as fsyncSync2,
|
|
5709
6095
|
closeSync as closeSync2,
|
|
5710
|
-
existsSync as
|
|
6096
|
+
existsSync as existsSync12,
|
|
5711
6097
|
realpathSync,
|
|
5712
6098
|
unlinkSync as unlinkSync2,
|
|
5713
|
-
accessSync,
|
|
6099
|
+
accessSync as accessSync2,
|
|
5714
6100
|
statSync as statSync4,
|
|
5715
|
-
chmodSync as
|
|
6101
|
+
chmodSync as chmodSync3,
|
|
5716
6102
|
constants
|
|
5717
6103
|
} from "fs";
|
|
5718
|
-
import { dirname as
|
|
6104
|
+
import { dirname as dirname10, resolve as resolve7, relative as relative3, isAbsolute, basename as basename3, sep } from "path";
|
|
5719
6105
|
import { createHash as createHash4 } from "crypto";
|
|
5720
6106
|
function assertContainedIn(rootDir, candidate, opts = {}) {
|
|
5721
6107
|
if (candidate.includes("\0")) {
|
|
@@ -5747,8 +6133,8 @@ function isContainedIn(rootDir, candidate, opts = {}) {
|
|
|
5747
6133
|
if (rel === "" && !opts.allowRoot) return false;
|
|
5748
6134
|
if (rel.startsWith("..") || isAbsolute(rel)) return false;
|
|
5749
6135
|
let probe = abs;
|
|
5750
|
-
while (!
|
|
5751
|
-
const parent =
|
|
6136
|
+
while (!existsSync12(probe) && probe.length > 1) {
|
|
6137
|
+
const parent = dirname10(probe);
|
|
5752
6138
|
if (parent === probe) break;
|
|
5753
6139
|
probe = parent;
|
|
5754
6140
|
}
|
|
@@ -5765,17 +6151,17 @@ function isContainedIn(rootDir, candidate, opts = {}) {
|
|
|
5765
6151
|
return !realRel.startsWith("..") && !isAbsolute(realRel);
|
|
5766
6152
|
}
|
|
5767
6153
|
function atomicWriteFileSync(destPath, contents) {
|
|
5768
|
-
const dir =
|
|
6154
|
+
const dir = dirname10(destPath);
|
|
5769
6155
|
const tmp = resolve7(dir, `.${basename3(destPath)}.massu-tmp-${process.pid}`);
|
|
5770
6156
|
let destMode;
|
|
5771
|
-
if (
|
|
5772
|
-
|
|
6157
|
+
if (existsSync12(destPath)) {
|
|
6158
|
+
accessSync2(destPath, constants.W_OK);
|
|
5773
6159
|
destMode = statSync4(destPath).mode & 511;
|
|
5774
6160
|
}
|
|
5775
6161
|
let fd;
|
|
5776
6162
|
try {
|
|
5777
|
-
|
|
5778
|
-
if (destMode !== void 0)
|
|
6163
|
+
writeFileSync3(tmp, contents, "utf-8");
|
|
6164
|
+
if (destMode !== void 0) chmodSync3(tmp, destMode);
|
|
5779
6165
|
fd = openSync2(tmp, "r+");
|
|
5780
6166
|
fsyncSync2(fd);
|
|
5781
6167
|
closeSync2(fd);
|
|
@@ -5789,7 +6175,7 @@ function atomicWriteFileSync(destPath, contents) {
|
|
|
5789
6175
|
}
|
|
5790
6176
|
}
|
|
5791
6177
|
try {
|
|
5792
|
-
if (
|
|
6178
|
+
if (existsSync12(tmp)) unlinkSync2(tmp);
|
|
5793
6179
|
} catch {
|
|
5794
6180
|
}
|
|
5795
6181
|
throw err;
|
|
@@ -11800,38 +12186,38 @@ var init_source_dir_detector = __esm({
|
|
|
11800
12186
|
});
|
|
11801
12187
|
|
|
11802
12188
|
// src/detect/monorepo-detector.ts
|
|
11803
|
-
import { readFileSync as
|
|
11804
|
-
import { join as
|
|
12189
|
+
import { readFileSync as readFileSync9, existsSync as existsSync13, statSync as statSync5, lstatSync as lstatSync2, readdirSync as readdirSync5 } from "fs";
|
|
12190
|
+
import { join as join9, relative as relative4 } from "path";
|
|
11805
12191
|
import { parse as parseYaml3 } from "yaml";
|
|
11806
12192
|
import { parse as parseToml2 } from "smol-toml";
|
|
11807
12193
|
function safeReadText(path) {
|
|
11808
12194
|
try {
|
|
11809
|
-
if (!
|
|
12195
|
+
if (!existsSync13(path)) return null;
|
|
11810
12196
|
const ls = lstatSync2(path);
|
|
11811
12197
|
if (ls.isSymbolicLink()) return null;
|
|
11812
12198
|
const st = statSync5(path);
|
|
11813
12199
|
if (!st.isFile()) return null;
|
|
11814
|
-
return
|
|
12200
|
+
return readFileSync9(path, "utf-8");
|
|
11815
12201
|
} catch {
|
|
11816
12202
|
return null;
|
|
11817
12203
|
}
|
|
11818
12204
|
}
|
|
11819
12205
|
function firstManifestIn(dir) {
|
|
11820
12206
|
for (const m3 of MANIFEST_PRIORITY) {
|
|
11821
|
-
if (
|
|
12207
|
+
if (existsSync13(join9(dir, m3))) return m3;
|
|
11822
12208
|
}
|
|
11823
12209
|
return null;
|
|
11824
12210
|
}
|
|
11825
12211
|
function manifestName(dir, manifest) {
|
|
11826
12212
|
try {
|
|
11827
12213
|
if (manifest === "package.json") {
|
|
11828
|
-
const raw = safeReadText(
|
|
12214
|
+
const raw = safeReadText(join9(dir, "package.json"));
|
|
11829
12215
|
if (!raw) return null;
|
|
11830
12216
|
const pkg = JSON.parse(raw);
|
|
11831
12217
|
return typeof pkg.name === "string" ? pkg.name : null;
|
|
11832
12218
|
}
|
|
11833
12219
|
if (manifest === "pyproject.toml") {
|
|
11834
|
-
const raw = safeReadText(
|
|
12220
|
+
const raw = safeReadText(join9(dir, "pyproject.toml"));
|
|
11835
12221
|
if (!raw) return null;
|
|
11836
12222
|
const toml = parseToml2(raw);
|
|
11837
12223
|
const project = toml.project;
|
|
@@ -11842,7 +12228,7 @@ function manifestName(dir, manifest) {
|
|
|
11842
12228
|
return null;
|
|
11843
12229
|
}
|
|
11844
12230
|
if (manifest === "Cargo.toml") {
|
|
11845
|
-
const raw = safeReadText(
|
|
12231
|
+
const raw = safeReadText(join9(dir, "Cargo.toml"));
|
|
11846
12232
|
if (!raw) return null;
|
|
11847
12233
|
const toml = parseToml2(raw);
|
|
11848
12234
|
const pkg = toml.package;
|
|
@@ -11850,7 +12236,7 @@ function manifestName(dir, manifest) {
|
|
|
11850
12236
|
return null;
|
|
11851
12237
|
}
|
|
11852
12238
|
if (manifest === "go.mod") {
|
|
11853
|
-
const raw = safeReadText(
|
|
12239
|
+
const raw = safeReadText(join9(dir, "go.mod"));
|
|
11854
12240
|
if (!raw) return null;
|
|
11855
12241
|
for (const line of raw.split(/\r?\n/)) {
|
|
11856
12242
|
const trimmed = line.trim();
|
|
@@ -11874,7 +12260,7 @@ function pkgFromDir(root, dir) {
|
|
|
11874
12260
|
}
|
|
11875
12261
|
function listSubdirs2(dir) {
|
|
11876
12262
|
try {
|
|
11877
|
-
return readdirSync5(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && !IGNORED_DIRS2.has(e2.name)).map((e2) =>
|
|
12263
|
+
return readdirSync5(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && !IGNORED_DIRS2.has(e2.name)).map((e2) => join9(dir, e2.name));
|
|
11878
12264
|
} catch {
|
|
11879
12265
|
return [];
|
|
11880
12266
|
}
|
|
@@ -11882,8 +12268,8 @@ function listSubdirs2(dir) {
|
|
|
11882
12268
|
function genericWorkspaces(root) {
|
|
11883
12269
|
const out = [];
|
|
11884
12270
|
for (const parent of CONVENTIONAL_WORKSPACE_PARENTS) {
|
|
11885
|
-
const p19 =
|
|
11886
|
-
if (!
|
|
12271
|
+
const p19 = join9(root, parent);
|
|
12272
|
+
if (!existsSync13(p19)) continue;
|
|
11887
12273
|
for (const sub of listSubdirs2(p19)) {
|
|
11888
12274
|
const pkg = pkgFromDir(root, sub);
|
|
11889
12275
|
if (pkg) out.push(pkg);
|
|
@@ -11892,7 +12278,7 @@ function genericWorkspaces(root) {
|
|
|
11892
12278
|
return out;
|
|
11893
12279
|
}
|
|
11894
12280
|
function detectYarnWorkspaces(root) {
|
|
11895
|
-
const raw = safeReadText(
|
|
12281
|
+
const raw = safeReadText(join9(root, "package.json"));
|
|
11896
12282
|
if (!raw) return null;
|
|
11897
12283
|
let pkg;
|
|
11898
12284
|
try {
|
|
@@ -11907,7 +12293,7 @@ function detectYarnWorkspaces(root) {
|
|
|
11907
12293
|
return expandWorkspaceGlobs(root, globs);
|
|
11908
12294
|
}
|
|
11909
12295
|
function detectPnpmWorkspaces(root) {
|
|
11910
|
-
const raw = safeReadText(
|
|
12296
|
+
const raw = safeReadText(join9(root, "pnpm-workspace.yaml"));
|
|
11911
12297
|
if (!raw) return null;
|
|
11912
12298
|
try {
|
|
11913
12299
|
const parsed = parseYaml3(raw);
|
|
@@ -11923,8 +12309,8 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
11923
12309
|
for (const pattern of globs) {
|
|
11924
12310
|
const parts = pattern.split("/");
|
|
11925
12311
|
if (parts.length === 2 && (parts[1] === "*" || parts[1] === "**")) {
|
|
11926
|
-
const parent =
|
|
11927
|
-
if (!
|
|
12312
|
+
const parent = join9(root, parts[0]);
|
|
12313
|
+
if (!existsSync13(parent)) continue;
|
|
11928
12314
|
for (const sub of listSubdirs2(parent)) {
|
|
11929
12315
|
const pkg = pkgFromDir(root, sub);
|
|
11930
12316
|
if (pkg && !seen.has(pkg.path)) {
|
|
@@ -11934,8 +12320,8 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
11934
12320
|
}
|
|
11935
12321
|
continue;
|
|
11936
12322
|
}
|
|
11937
|
-
const direct =
|
|
11938
|
-
if (
|
|
12323
|
+
const direct = join9(root, pattern);
|
|
12324
|
+
if (existsSync13(direct)) {
|
|
11939
12325
|
const pkg = pkgFromDir(root, direct);
|
|
11940
12326
|
if (pkg && !seen.has(pkg.path)) {
|
|
11941
12327
|
seen.add(pkg.path);
|
|
@@ -11946,16 +12332,16 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
11946
12332
|
return out;
|
|
11947
12333
|
}
|
|
11948
12334
|
function hasTurbo(root) {
|
|
11949
|
-
return
|
|
12335
|
+
return existsSync13(join9(root, "turbo.json"));
|
|
11950
12336
|
}
|
|
11951
12337
|
function hasNx(root) {
|
|
11952
|
-
return
|
|
12338
|
+
return existsSync13(join9(root, "nx.json"));
|
|
11953
12339
|
}
|
|
11954
12340
|
function hasLerna(root) {
|
|
11955
|
-
return
|
|
12341
|
+
return existsSync13(join9(root, "lerna.json"));
|
|
11956
12342
|
}
|
|
11957
12343
|
function hasBazel(root) {
|
|
11958
|
-
return
|
|
12344
|
+
return existsSync13(join9(root, "WORKSPACE")) || existsSync13(join9(root, "WORKSPACE.bazel")) || existsSync13(join9(root, "MODULE.bazel"));
|
|
11959
12345
|
}
|
|
11960
12346
|
function detectMonorepo(projectRoot) {
|
|
11961
12347
|
const nested = [];
|
|
@@ -12146,8 +12532,8 @@ var init_vr_command_map = __esm({
|
|
|
12146
12532
|
});
|
|
12147
12533
|
|
|
12148
12534
|
// src/detect/domain-inferrer.ts
|
|
12149
|
-
import { existsSync as
|
|
12150
|
-
import { join as
|
|
12535
|
+
import { existsSync as existsSync14, readdirSync as readdirSync6 } from "fs";
|
|
12536
|
+
import { join as join10 } from "path";
|
|
12151
12537
|
function titleCase(s) {
|
|
12152
12538
|
if (!s) return s;
|
|
12153
12539
|
return s.split(/[-_\s]+/).filter(Boolean).map((p19) => p19.charAt(0).toUpperCase() + p19.slice(1)).join(" ");
|
|
@@ -12167,8 +12553,8 @@ function topLevelSrcSubdirs(root, sourceDirs) {
|
|
|
12167
12553
|
const effective = sourceDirs.length > 0 ? sourceDirs : ["src"];
|
|
12168
12554
|
const seen = /* @__PURE__ */ new Set();
|
|
12169
12555
|
for (const rel of effective) {
|
|
12170
|
-
const abs =
|
|
12171
|
-
if (!
|
|
12556
|
+
const abs = join10(root, rel);
|
|
12557
|
+
if (!existsSync14(abs)) continue;
|
|
12172
12558
|
try {
|
|
12173
12559
|
for (const e2 of readdirSync6(abs, { withFileTypes: true })) {
|
|
12174
12560
|
if (!e2.isDirectory()) continue;
|
|
@@ -12255,8 +12641,8 @@ var init_domain_inferrer = __esm({
|
|
|
12255
12641
|
});
|
|
12256
12642
|
|
|
12257
12643
|
// src/detect/regex-fallback.ts
|
|
12258
|
-
import { existsSync as
|
|
12259
|
-
import { resolve as resolve8, join as
|
|
12644
|
+
import { existsSync as existsSync15, readdirSync as readdirSync7, readFileSync as readFileSync10, statSync as statSync6 } from "fs";
|
|
12645
|
+
import { resolve as resolve8, join as join11, basename as basename4 } from "path";
|
|
12260
12646
|
function introspectPython(detection, projectRoot) {
|
|
12261
12647
|
const sourceDir = resolveSourceDir(detection, "python", projectRoot);
|
|
12262
12648
|
if (!sourceDir) return null;
|
|
@@ -12423,9 +12809,9 @@ function resolveSourceDir(detection, lang, projectRoot) {
|
|
|
12423
12809
|
if (list.length > 0) {
|
|
12424
12810
|
const first = list[0];
|
|
12425
12811
|
const abs = resolve8(projectRoot, first);
|
|
12426
|
-
return
|
|
12812
|
+
return existsSync15(abs) ? abs : null;
|
|
12427
12813
|
}
|
|
12428
|
-
return
|
|
12814
|
+
return existsSync15(projectRoot) ? projectRoot : null;
|
|
12429
12815
|
}
|
|
12430
12816
|
function sampleFiles(dir, nameRegex, pathFilter) {
|
|
12431
12817
|
const out = [];
|
|
@@ -12445,7 +12831,7 @@ function sampleFiles(dir, nameRegex, pathFilter) {
|
|
|
12445
12831
|
if (entry === "__pycache__") continue;
|
|
12446
12832
|
if (entry === "venv" || entry === ".venv") continue;
|
|
12447
12833
|
if (entry === "dist" || entry === "build") continue;
|
|
12448
|
-
const child =
|
|
12834
|
+
const child = join11(path, entry);
|
|
12449
12835
|
let st;
|
|
12450
12836
|
try {
|
|
12451
12837
|
st = statSync6(child);
|
|
@@ -12470,7 +12856,7 @@ function readSafe(path) {
|
|
|
12470
12856
|
try {
|
|
12471
12857
|
const st = statSync6(path);
|
|
12472
12858
|
if (st.size > MAX_FILE_BYTES) return null;
|
|
12473
|
-
return
|
|
12859
|
+
return readFileSync10(path, "utf-8");
|
|
12474
12860
|
} catch {
|
|
12475
12861
|
return null;
|
|
12476
12862
|
}
|
|
@@ -12551,8 +12937,8 @@ var init_parse_guard = __esm({
|
|
|
12551
12937
|
|
|
12552
12938
|
// src/detect/adapters/runner.ts
|
|
12553
12939
|
import { basename as basename5, relative as relative5 } from "path";
|
|
12554
|
-
import { existsSync as
|
|
12555
|
-
import { join as
|
|
12940
|
+
import { existsSync as existsSync16, readdirSync as readdirSync8, readFileSync as readFileSync11, statSync as statSync7 } from "fs";
|
|
12941
|
+
import { join as join12 } from "path";
|
|
12556
12942
|
async function runAdapters(adapters, rootDir, signals, options = {}) {
|
|
12557
12943
|
const out = {
|
|
12558
12944
|
byAdapter: {},
|
|
@@ -12649,7 +13035,7 @@ function buildDetectionSignals(rootDir) {
|
|
|
12649
13035
|
for (const entry of readdirSync8(rootDir)) {
|
|
12650
13036
|
if (entry.startsWith(".")) continue;
|
|
12651
13037
|
try {
|
|
12652
|
-
const st = statSync7(
|
|
13038
|
+
const st = statSync7(join12(rootDir, entry));
|
|
12653
13039
|
if (st.isDirectory()) presentDirs.add(entry);
|
|
12654
13040
|
else if (st.isFile()) presentFiles.add(entry);
|
|
12655
13041
|
} catch {
|
|
@@ -12658,14 +13044,14 @@ function buildDetectionSignals(rootDir) {
|
|
|
12658
13044
|
} catch {
|
|
12659
13045
|
}
|
|
12660
13046
|
return {
|
|
12661
|
-
packageJson: tryReadJson(
|
|
12662
|
-
pyprojectToml: tryReadToml(
|
|
12663
|
-
gemfile: tryReadString(
|
|
12664
|
-
cargoToml: tryReadToml(
|
|
12665
|
-
goMod: tryReadString(
|
|
12666
|
-
mixExs: tryReadString(
|
|
13047
|
+
packageJson: tryReadJson(join12(rootDir, "package.json")),
|
|
13048
|
+
pyprojectToml: tryReadToml(join12(rootDir, "pyproject.toml")),
|
|
13049
|
+
gemfile: tryReadString(join12(rootDir, "Gemfile")),
|
|
13050
|
+
cargoToml: tryReadToml(join12(rootDir, "Cargo.toml")),
|
|
13051
|
+
goMod: tryReadString(join12(rootDir, "go.mod")),
|
|
13052
|
+
mixExs: tryReadString(join12(rootDir, "mix.exs")),
|
|
12667
13053
|
csproj: tryReadFirstCsproj(rootDir, presentFiles),
|
|
12668
|
-
pomXml: tryReadString(
|
|
13054
|
+
pomXml: tryReadString(join12(rootDir, "pom.xml")),
|
|
12669
13055
|
gradleBuild: tryReadGradleBuild(rootDir, presentFiles),
|
|
12670
13056
|
presentDirs,
|
|
12671
13057
|
presentFiles
|
|
@@ -12674,21 +13060,21 @@ function buildDetectionSignals(rootDir) {
|
|
|
12674
13060
|
function tryReadFirstCsproj(rootDir, presentFiles) {
|
|
12675
13061
|
const csprojNames = [...presentFiles].filter((f2) => f2.endsWith(".csproj")).sort();
|
|
12676
13062
|
if (csprojNames.length === 0) return void 0;
|
|
12677
|
-
return tryReadString(
|
|
13063
|
+
return tryReadString(join12(rootDir, csprojNames[0]));
|
|
12678
13064
|
}
|
|
12679
13065
|
function tryReadGradleBuild(rootDir, presentFiles) {
|
|
12680
13066
|
if (presentFiles.has("build.gradle.kts")) {
|
|
12681
|
-
return tryReadString(
|
|
13067
|
+
return tryReadString(join12(rootDir, "build.gradle.kts"));
|
|
12682
13068
|
}
|
|
12683
13069
|
if (presentFiles.has("build.gradle")) {
|
|
12684
|
-
return tryReadString(
|
|
13070
|
+
return tryReadString(join12(rootDir, "build.gradle"));
|
|
12685
13071
|
}
|
|
12686
13072
|
return void 0;
|
|
12687
13073
|
}
|
|
12688
13074
|
function tryReadString(path) {
|
|
12689
|
-
if (!
|
|
13075
|
+
if (!existsSync16(path)) return void 0;
|
|
12690
13076
|
try {
|
|
12691
|
-
return
|
|
13077
|
+
return readFileSync11(path, "utf-8");
|
|
12692
13078
|
} catch {
|
|
12693
13079
|
return void 0;
|
|
12694
13080
|
}
|
|
@@ -12805,24 +13191,24 @@ ${querySource}`
|
|
|
12805
13191
|
// src/detect/adapters/tree-sitter-loader.ts
|
|
12806
13192
|
import { createHash as createHash5 } from "crypto";
|
|
12807
13193
|
import {
|
|
12808
|
-
mkdirSync as
|
|
13194
|
+
mkdirSync as mkdirSync9,
|
|
12809
13195
|
readdirSync as readdirSync9,
|
|
12810
|
-
readFileSync as
|
|
12811
|
-
writeFileSync as
|
|
13196
|
+
readFileSync as readFileSync12,
|
|
13197
|
+
writeFileSync as writeFileSync4,
|
|
12812
13198
|
renameSync as renameSync3,
|
|
12813
13199
|
unlinkSync as unlinkSync3,
|
|
12814
13200
|
lstatSync as lstatSync3,
|
|
12815
|
-
chmodSync as
|
|
13201
|
+
chmodSync as chmodSync4,
|
|
12816
13202
|
utimesSync
|
|
12817
13203
|
} from "fs";
|
|
12818
13204
|
import { homedir as homedir5 } from "os";
|
|
12819
|
-
import { dirname as
|
|
13205
|
+
import { dirname as dirname11, join as join13 } from "path";
|
|
12820
13206
|
import { Language, Parser } from "web-tree-sitter";
|
|
12821
13207
|
function getCacheDir() {
|
|
12822
|
-
return process.env.MASSU_WASM_CACHE_DIR ??
|
|
13208
|
+
return process.env.MASSU_WASM_CACHE_DIR ?? join13(homedir5(), ".massu", "wasm-cache");
|
|
12823
13209
|
}
|
|
12824
13210
|
function getCachedPath(language, sha) {
|
|
12825
|
-
return
|
|
13211
|
+
return join13(getCacheDir(), `${language}-${sha}.wasm`);
|
|
12826
13212
|
}
|
|
12827
13213
|
function getCacheRetainCount() {
|
|
12828
13214
|
const env = process.env.MASSU_WASM_CACHE_RETAIN;
|
|
@@ -12850,7 +13236,7 @@ function evictBeyondRetainCount(retain = getCacheRetainCount()) {
|
|
|
12850
13236
|
const candidates = [];
|
|
12851
13237
|
for (const name of entries) {
|
|
12852
13238
|
if (!name.endsWith(".wasm")) continue;
|
|
12853
|
-
const path =
|
|
13239
|
+
const path = join13(dir, name);
|
|
12854
13240
|
let stat;
|
|
12855
13241
|
try {
|
|
12856
13242
|
stat = lstatSync3(path);
|
|
@@ -12907,7 +13293,7 @@ async function loadGrammar(language, options = {}) {
|
|
|
12907
13293
|
}
|
|
12908
13294
|
let bytes;
|
|
12909
13295
|
try {
|
|
12910
|
-
bytes =
|
|
13296
|
+
bytes = readFileSync12(cachePath);
|
|
12911
13297
|
} catch (e2) {
|
|
12912
13298
|
bytes = new Uint8Array(0);
|
|
12913
13299
|
}
|
|
@@ -12947,21 +13333,21 @@ async function loadGrammar(language, options = {}) {
|
|
|
12947
13333
|
throw new GrammarSHAMismatchError(language, manifest.sha256, downloadedSha);
|
|
12948
13334
|
}
|
|
12949
13335
|
try {
|
|
12950
|
-
|
|
13336
|
+
mkdirSync9(dirname11(cachePath), { recursive: true, mode: 448 });
|
|
12951
13337
|
try {
|
|
12952
|
-
|
|
13338
|
+
chmodSync4(dirname11(cachePath), 448);
|
|
12953
13339
|
} catch {
|
|
12954
13340
|
}
|
|
12955
13341
|
const tmpPath = `${cachePath}.tmp.${process.pid}`;
|
|
12956
|
-
|
|
13342
|
+
writeFileSync4(tmpPath, body, { mode: 384 });
|
|
12957
13343
|
try {
|
|
12958
|
-
|
|
13344
|
+
chmodSync4(tmpPath, 384);
|
|
12959
13345
|
} catch {
|
|
12960
13346
|
}
|
|
12961
13347
|
try {
|
|
12962
13348
|
renameSync3(tmpPath, cachePath);
|
|
12963
13349
|
try {
|
|
12964
|
-
|
|
13350
|
+
chmodSync4(cachePath, 384);
|
|
12965
13351
|
} catch {
|
|
12966
13352
|
}
|
|
12967
13353
|
} catch (e2) {
|
|
@@ -13825,8 +14211,8 @@ __export(file_sampler_exports, {
|
|
|
13825
14211
|
SAMPLE_TEST_FILE_PATTERNS: () => SAMPLE_TEST_FILE_PATTERNS,
|
|
13826
14212
|
sampleFilesForAdapter: () => sampleFilesForAdapter
|
|
13827
14213
|
});
|
|
13828
|
-
import { readdirSync as readdirSync10, readFileSync as
|
|
13829
|
-
import { join as
|
|
14214
|
+
import { readdirSync as readdirSync10, readFileSync as readFileSync13, lstatSync as lstatSync4 } from "node:fs";
|
|
14215
|
+
import { join as join14, extname } from "node:path";
|
|
13830
14216
|
function sampleFilesForAdapter(adapter, projectRoot, detection, options = {}) {
|
|
13831
14217
|
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
13832
14218
|
const maxFiles = options.maxFilesPerAdapter ?? DEFAULT_MAX_FILES;
|
|
@@ -13841,7 +14227,7 @@ function sampleFilesForAdapter(adapter, projectRoot, detection, options = {}) {
|
|
|
13841
14227
|
const langDetection = detection.sourceDirs[langKey];
|
|
13842
14228
|
const candidateDirs = [];
|
|
13843
14229
|
if (langDetection?.source_dirs && langDetection.source_dirs.length > 0) {
|
|
13844
|
-
candidateDirs.push(...langDetection.source_dirs.map((d2) =>
|
|
14230
|
+
candidateDirs.push(...langDetection.source_dirs.map((d2) => join14(projectRoot, d2)));
|
|
13845
14231
|
} else {
|
|
13846
14232
|
candidateDirs.push(projectRoot);
|
|
13847
14233
|
}
|
|
@@ -13865,7 +14251,7 @@ function walkDir(dir, exts, testPatterns, lang, maxDepth, curDepth, out, seen, m
|
|
|
13865
14251
|
if (out.length >= maxFiles) return;
|
|
13866
14252
|
if (entry.startsWith(".")) continue;
|
|
13867
14253
|
if (IGNORED_DIRS3.has(entry)) continue;
|
|
13868
|
-
const fullPath =
|
|
14254
|
+
const fullPath = join14(dir, entry);
|
|
13869
14255
|
let st;
|
|
13870
14256
|
try {
|
|
13871
14257
|
st = lstatSync4(fullPath);
|
|
@@ -13886,7 +14272,7 @@ function walkDir(dir, exts, testPatterns, lang, maxDepth, curDepth, out, seen, m
|
|
|
13886
14272
|
seen.add(fullPath);
|
|
13887
14273
|
let content;
|
|
13888
14274
|
try {
|
|
13889
|
-
content =
|
|
14275
|
+
content = readFileSync13(fullPath, "utf-8");
|
|
13890
14276
|
} catch {
|
|
13891
14277
|
continue;
|
|
13892
14278
|
}
|
|
@@ -15310,8 +15696,8 @@ __export(init_exports, {
|
|
|
15310
15696
|
validateWrittenConfig: () => validateWrittenConfig,
|
|
15311
15697
|
writeConfigAtomic: () => writeConfigAtomic
|
|
15312
15698
|
});
|
|
15313
|
-
import { closeSync as closeSync3, existsSync as
|
|
15314
|
-
import { resolve as resolve9, basename as basename6, dirname as
|
|
15699
|
+
import { closeSync as closeSync3, existsSync as existsSync17, fsyncSync as fsyncSync3, openSync as openSync3, readFileSync as readFileSync14, writeFileSync as writeFileSync5, writeSync as writeSync2, mkdirSync as mkdirSync10, readdirSync as readdirSync11, renameSync as renameSync4, rmSync as rmSync5, statSync as statSync9, chmodSync as chmodSync5 } from "fs";
|
|
15700
|
+
import { resolve as resolve9, basename as basename6, dirname as dirname12 } from "path";
|
|
15315
15701
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
15316
15702
|
import { homedir as homedir6 } from "os";
|
|
15317
15703
|
import { stringify as yamlStringify, parse as yamlParse } from "yaml";
|
|
@@ -15323,9 +15709,9 @@ function detectFramework(projectRoot) {
|
|
|
15323
15709
|
ui: "none"
|
|
15324
15710
|
};
|
|
15325
15711
|
const pkgPath = resolve9(projectRoot, "package.json");
|
|
15326
|
-
if (!
|
|
15712
|
+
if (!existsSync17(pkgPath)) return result;
|
|
15327
15713
|
try {
|
|
15328
|
-
const pkg = JSON.parse(
|
|
15714
|
+
const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
15329
15715
|
const allDeps = {
|
|
15330
15716
|
...pkg.dependencies,
|
|
15331
15717
|
...pkg.devDependencies
|
|
@@ -15359,7 +15745,7 @@ function detectPython(projectRoot) {
|
|
|
15359
15745
|
alembicDir: null
|
|
15360
15746
|
};
|
|
15361
15747
|
const markers = ["pyproject.toml", "setup.py", "requirements.txt", "Pipfile"];
|
|
15362
|
-
const hasMarker = markers.some((m3) =>
|
|
15748
|
+
const hasMarker = markers.some((m3) => existsSync17(resolve9(projectRoot, m3)));
|
|
15363
15749
|
if (!hasMarker) return result;
|
|
15364
15750
|
result.detected = true;
|
|
15365
15751
|
const depFiles = [
|
|
@@ -15370,32 +15756,32 @@ function detectPython(projectRoot) {
|
|
|
15370
15756
|
];
|
|
15371
15757
|
for (const { file } of depFiles) {
|
|
15372
15758
|
const filePath = resolve9(projectRoot, file);
|
|
15373
|
-
if (
|
|
15759
|
+
if (existsSync17(filePath)) {
|
|
15374
15760
|
try {
|
|
15375
|
-
const content =
|
|
15761
|
+
const content = readFileSync14(filePath, "utf-8").toLowerCase();
|
|
15376
15762
|
if (content.includes("fastapi")) result.hasFastapi = true;
|
|
15377
15763
|
if (content.includes("sqlalchemy")) result.hasSqlalchemy = true;
|
|
15378
15764
|
} catch {
|
|
15379
15765
|
}
|
|
15380
15766
|
}
|
|
15381
15767
|
}
|
|
15382
|
-
if (
|
|
15768
|
+
if (existsSync17(resolve9(projectRoot, "alembic.ini"))) {
|
|
15383
15769
|
result.hasAlembic = true;
|
|
15384
|
-
if (
|
|
15770
|
+
if (existsSync17(resolve9(projectRoot, "alembic"))) {
|
|
15385
15771
|
result.alembicDir = "alembic";
|
|
15386
15772
|
}
|
|
15387
|
-
} else if (
|
|
15773
|
+
} else if (existsSync17(resolve9(projectRoot, "alembic"))) {
|
|
15388
15774
|
result.hasAlembic = true;
|
|
15389
15775
|
result.alembicDir = "alembic";
|
|
15390
15776
|
}
|
|
15391
15777
|
const candidateRoots = ["app", "src", "backend", "api"];
|
|
15392
15778
|
for (const candidate of candidateRoots) {
|
|
15393
15779
|
const candidatePath = resolve9(projectRoot, candidate);
|
|
15394
|
-
if (
|
|
15780
|
+
if (existsSync17(candidatePath) && existsSync17(resolve9(candidatePath, "__init__.py"))) {
|
|
15395
15781
|
result.root = candidate;
|
|
15396
15782
|
break;
|
|
15397
15783
|
}
|
|
15398
|
-
if (
|
|
15784
|
+
if (existsSync17(candidatePath)) {
|
|
15399
15785
|
try {
|
|
15400
15786
|
const files = readdirSync11(candidatePath);
|
|
15401
15787
|
if (files.some((f2) => f2.endsWith(".py"))) {
|
|
@@ -15416,7 +15802,7 @@ function generateConfig(projectRoot, framework) {
|
|
|
15416
15802
|
"[@massu/core] generateConfig() is deprecated since 1.2.1 \u2014 use buildConfigFromDetection instead. It cannot produce valid configs for monorepos."
|
|
15417
15803
|
);
|
|
15418
15804
|
const configPath = resolve9(projectRoot, "massu.config.yaml");
|
|
15419
|
-
if (
|
|
15805
|
+
if (existsSync17(configPath)) {
|
|
15420
15806
|
return false;
|
|
15421
15807
|
}
|
|
15422
15808
|
const projectName = basename6(projectRoot);
|
|
@@ -15462,7 +15848,7 @@ function generateConfig(projectRoot, framework) {
|
|
|
15462
15848
|
# Documentation: https://massu.ai/docs/getting-started/configuration
|
|
15463
15849
|
|
|
15464
15850
|
${yamlStringify(config)}`;
|
|
15465
|
-
|
|
15851
|
+
writeFileSync5(configPath, yamlContent, "utf-8");
|
|
15466
15852
|
return true;
|
|
15467
15853
|
}
|
|
15468
15854
|
function monorepoCommonRoot(packages) {
|
|
@@ -15527,10 +15913,10 @@ function buildConfigFromDetection(opts) {
|
|
|
15527
15913
|
pathsSource = monorepoCommonRoot(detection.monorepo.packages);
|
|
15528
15914
|
}
|
|
15529
15915
|
}
|
|
15530
|
-
if (pathsSource === "src" && !
|
|
15916
|
+
if (pathsSource === "src" && !existsSync17(resolve9(projectRoot, "src"))) {
|
|
15531
15917
|
const fallbacks = ["app", "pages", "."];
|
|
15532
15918
|
for (const fallback of fallbacks) {
|
|
15533
|
-
if (fallback === "." ||
|
|
15919
|
+
if (fallback === "." || existsSync17(resolve9(projectRoot, fallback))) {
|
|
15534
15920
|
pathsSource = fallback;
|
|
15535
15921
|
break;
|
|
15536
15922
|
}
|
|
@@ -15614,7 +16000,7 @@ function buildConfigFromDetection(opts) {
|
|
|
15614
16000
|
};
|
|
15615
16001
|
if (pyFw?.framework) pythonBlock.framework = pyFw.framework;
|
|
15616
16002
|
if (pyFw?.orm) pythonBlock.orm = pyFw.orm;
|
|
15617
|
-
if (
|
|
16003
|
+
if (existsSync17(resolve9(projectRoot, "alembic.ini")) || existsSync17(resolve9(projectRoot, "alembic"))) {
|
|
15618
16004
|
pythonBlock.alembic_dir = "alembic";
|
|
15619
16005
|
}
|
|
15620
16006
|
config.python = pythonBlock;
|
|
@@ -15639,10 +16025,10 @@ function applyVariantTemplate(config, templatesDir) {
|
|
|
15639
16025
|
}
|
|
15640
16026
|
if (templateId === null) return config;
|
|
15641
16027
|
const templatePath = resolve9(templatesDir, templateId, "massu.config.yaml");
|
|
15642
|
-
if (!
|
|
16028
|
+
if (!existsSync17(templatePath)) return config;
|
|
15643
16029
|
let template;
|
|
15644
16030
|
try {
|
|
15645
|
-
template = yamlParse(
|
|
16031
|
+
template = yamlParse(readFileSync14(templatePath, "utf-8"));
|
|
15646
16032
|
} catch {
|
|
15647
16033
|
return config;
|
|
15648
16034
|
}
|
|
@@ -15701,7 +16087,7 @@ ${yamlStringify(config)}`;
|
|
|
15701
16087
|
function writeConfigAtomic(configPath, content) {
|
|
15702
16088
|
const tmpPath = `${configPath}.tmp`;
|
|
15703
16089
|
let existingMode;
|
|
15704
|
-
if (
|
|
16090
|
+
if (existsSync17(configPath)) {
|
|
15705
16091
|
try {
|
|
15706
16092
|
existingMode = statSync9(configPath).mode;
|
|
15707
16093
|
} catch {
|
|
@@ -15709,7 +16095,7 @@ function writeConfigAtomic(configPath, content) {
|
|
|
15709
16095
|
}
|
|
15710
16096
|
}
|
|
15711
16097
|
try {
|
|
15712
|
-
|
|
16098
|
+
mkdirSync10(dirname12(configPath), { recursive: true });
|
|
15713
16099
|
const fd = openSync3(tmpPath, "w", 420);
|
|
15714
16100
|
try {
|
|
15715
16101
|
const buf = Buffer.from(content, "utf-8");
|
|
@@ -15725,15 +16111,15 @@ function writeConfigAtomic(configPath, content) {
|
|
|
15725
16111
|
renameSync4(tmpPath, configPath);
|
|
15726
16112
|
if (existingMode !== void 0) {
|
|
15727
16113
|
try {
|
|
15728
|
-
|
|
16114
|
+
chmodSync5(configPath, existingMode);
|
|
15729
16115
|
} catch {
|
|
15730
16116
|
}
|
|
15731
16117
|
}
|
|
15732
16118
|
return { validated: true };
|
|
15733
16119
|
} catch (err) {
|
|
15734
|
-
if (
|
|
16120
|
+
if (existsSync17(tmpPath)) {
|
|
15735
16121
|
try {
|
|
15736
|
-
|
|
16122
|
+
rmSync5(tmpPath, { force: true });
|
|
15737
16123
|
} catch {
|
|
15738
16124
|
}
|
|
15739
16125
|
}
|
|
@@ -15742,8 +16128,8 @@ function writeConfigAtomic(configPath, content) {
|
|
|
15742
16128
|
}
|
|
15743
16129
|
function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
15744
16130
|
try {
|
|
15745
|
-
if (!
|
|
15746
|
-
const content =
|
|
16131
|
+
if (!existsSync17(configPath)) return "Config file does not exist after write";
|
|
16132
|
+
const content = readFileSync14(configPath, "utf-8");
|
|
15747
16133
|
const parsed = yamlParse(content);
|
|
15748
16134
|
if (parsed === null || typeof parsed !== "object") {
|
|
15749
16135
|
return "Config is not a valid YAML object";
|
|
@@ -15764,7 +16150,7 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
|
15764
16150
|
const src = cfg.paths.source;
|
|
15765
16151
|
if (src && src !== ".") {
|
|
15766
16152
|
const srcAbs = resolve9(projectRoot, src);
|
|
15767
|
-
if (!
|
|
16153
|
+
if (!existsSync17(srcAbs)) {
|
|
15768
16154
|
return `paths.source '${src}' does not exist on disk`;
|
|
15769
16155
|
}
|
|
15770
16156
|
}
|
|
@@ -15775,7 +16161,7 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
|
15775
16161
|
for (const d2 of rawDirs) {
|
|
15776
16162
|
if (typeof d2 !== "string" || d2 === ".") continue;
|
|
15777
16163
|
const abs = resolve9(projectRoot, d2);
|
|
15778
|
-
if (!
|
|
16164
|
+
if (!existsSync17(abs)) {
|
|
15779
16165
|
return `framework.languages.${lang}.source_dirs '${d2}' does not exist on disk`;
|
|
15780
16166
|
}
|
|
15781
16167
|
}
|
|
@@ -15784,7 +16170,7 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
|
15784
16170
|
if (Array.isArray(mRoots)) {
|
|
15785
16171
|
for (const r2 of mRoots) {
|
|
15786
16172
|
if (typeof r2 !== "string" || r2 === ".") continue;
|
|
15787
|
-
if (!
|
|
16173
|
+
if (!existsSync17(resolve9(projectRoot, r2))) {
|
|
15788
16174
|
return `paths.monorepo_roots '${r2}' does not exist on disk`;
|
|
15789
16175
|
}
|
|
15790
16176
|
}
|
|
@@ -15830,7 +16216,7 @@ function resolveTemplatesDir() {
|
|
|
15830
16216
|
resolve9(__dirname2, "../../../templates")
|
|
15831
16217
|
];
|
|
15832
16218
|
for (const c2 of candidates) {
|
|
15833
|
-
if (
|
|
16219
|
+
if (existsSync17(c2)) return c2;
|
|
15834
16220
|
}
|
|
15835
16221
|
return null;
|
|
15836
16222
|
}
|
|
@@ -15840,13 +16226,13 @@ function copyTemplateConfig(templateName, targetPath, projectName) {
|
|
|
15840
16226
|
return { success: false, error: `Templates directory not found (looked in node_modules and dist/src)` };
|
|
15841
16227
|
}
|
|
15842
16228
|
const srcPath = resolve9(templatesDir, templateName, "massu.config.yaml");
|
|
15843
|
-
if (!
|
|
16229
|
+
if (!existsSync17(srcPath)) {
|
|
15844
16230
|
return { success: false, error: `Template '${templateName}' not found at ${srcPath}` };
|
|
15845
16231
|
}
|
|
15846
16232
|
try {
|
|
15847
|
-
let content =
|
|
16233
|
+
let content = readFileSync14(srcPath, "utf-8");
|
|
15848
16234
|
content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
|
|
15849
|
-
|
|
16235
|
+
writeFileSync5(targetPath, content, "utf-8");
|
|
15850
16236
|
return { success: true };
|
|
15851
16237
|
} catch (err) {
|
|
15852
16238
|
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -15858,9 +16244,9 @@ function getInstallerVersion() {
|
|
|
15858
16244
|
resolve9(__dirname2, "../../package.json")
|
|
15859
16245
|
];
|
|
15860
16246
|
for (const candidate of candidates) {
|
|
15861
|
-
if (
|
|
16247
|
+
if (existsSync17(candidate)) {
|
|
15862
16248
|
try {
|
|
15863
|
-
const pkg = JSON.parse(
|
|
16249
|
+
const pkg = JSON.parse(readFileSync14(candidate, "utf-8"));
|
|
15864
16250
|
if (typeof pkg.version === "string" && pkg.version.length > 0 && pkg.name === "@massu/core") {
|
|
15865
16251
|
return pkg.version;
|
|
15866
16252
|
}
|
|
@@ -15875,9 +16261,9 @@ function getInstallerVersion() {
|
|
|
15875
16261
|
function registerMcpServer(projectRoot) {
|
|
15876
16262
|
const mcpPath = resolve9(projectRoot, ".mcp.json");
|
|
15877
16263
|
let existing = {};
|
|
15878
|
-
if (
|
|
16264
|
+
if (existsSync17(mcpPath)) {
|
|
15879
16265
|
try {
|
|
15880
|
-
existing = JSON.parse(
|
|
16266
|
+
existing = JSON.parse(readFileSync14(mcpPath, "utf-8"));
|
|
15881
16267
|
} catch {
|
|
15882
16268
|
existing = {};
|
|
15883
16269
|
}
|
|
@@ -15893,17 +16279,17 @@ function registerMcpServer(projectRoot) {
|
|
|
15893
16279
|
args: ["-y", `@massu/core@${version}`]
|
|
15894
16280
|
};
|
|
15895
16281
|
existing.mcpServers = servers;
|
|
15896
|
-
|
|
16282
|
+
writeFileSync5(mcpPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
15897
16283
|
return true;
|
|
15898
16284
|
}
|
|
15899
16285
|
function resolveHooksDir() {
|
|
15900
16286
|
const cwd = process.cwd();
|
|
15901
16287
|
const nodeModulesPath = resolve9(cwd, "node_modules/@massu/core/dist/hooks");
|
|
15902
|
-
if (
|
|
16288
|
+
if (existsSync17(nodeModulesPath)) {
|
|
15903
16289
|
return "node_modules/@massu/core/dist/hooks";
|
|
15904
16290
|
}
|
|
15905
16291
|
const localPath = resolve9(__dirname2, "../dist/hooks");
|
|
15906
|
-
if (
|
|
16292
|
+
if (existsSync17(localPath)) {
|
|
15907
16293
|
return localPath;
|
|
15908
16294
|
}
|
|
15909
16295
|
return "node_modules/@massu/core/dist/hooks";
|
|
@@ -16053,8 +16439,8 @@ function installHooks(projectRoot) {
|
|
|
16053
16439
|
claudeDirName = ".claude";
|
|
16054
16440
|
}
|
|
16055
16441
|
const claudeDir = resolve9(projectRoot, claudeDirName);
|
|
16056
|
-
if (!
|
|
16057
|
-
|
|
16442
|
+
if (!existsSync17(claudeDir)) {
|
|
16443
|
+
mkdirSync10(claudeDir, { recursive: true });
|
|
16058
16444
|
}
|
|
16059
16445
|
const settings = readSettingsLocal(claudeDir);
|
|
16060
16446
|
const hooksConfig = buildHooksConfig(resolveHooksDir());
|
|
@@ -16075,14 +16461,14 @@ function initMemoryDir(projectRoot) {
|
|
|
16075
16461
|
const memoryDir = resolve9(homedir6(), `.claude/projects/${encodedRoot}/memory`);
|
|
16076
16462
|
let migratedFromLegacy = false;
|
|
16077
16463
|
const legacyDir = resolve9(homedir6(), `.claude/projects/-${encodedRoot}/memory`);
|
|
16078
|
-
if (
|
|
16464
|
+
if (existsSync17(legacyDir) && !existsSync17(memoryDir)) {
|
|
16079
16465
|
try {
|
|
16080
|
-
|
|
16466
|
+
mkdirSync10(resolve9(memoryDir, ".."), { recursive: true });
|
|
16081
16467
|
renameSync4(legacyDir, memoryDir);
|
|
16082
16468
|
try {
|
|
16083
16469
|
const legacyParent = resolve9(legacyDir, "..");
|
|
16084
|
-
if (
|
|
16085
|
-
|
|
16470
|
+
if (existsSync17(legacyParent) && readdirSync11(legacyParent).length === 0) {
|
|
16471
|
+
rmSync5(legacyParent, { recursive: false });
|
|
16086
16472
|
}
|
|
16087
16473
|
} catch {
|
|
16088
16474
|
}
|
|
@@ -16091,13 +16477,13 @@ function initMemoryDir(projectRoot) {
|
|
|
16091
16477
|
}
|
|
16092
16478
|
}
|
|
16093
16479
|
let created = false;
|
|
16094
|
-
if (!
|
|
16095
|
-
|
|
16480
|
+
if (!existsSync17(memoryDir)) {
|
|
16481
|
+
mkdirSync10(memoryDir, { recursive: true });
|
|
16096
16482
|
created = true;
|
|
16097
16483
|
}
|
|
16098
16484
|
const memoryMdPath = resolve9(memoryDir, "MEMORY.md");
|
|
16099
16485
|
let memoryMdCreated = false;
|
|
16100
|
-
if (!
|
|
16486
|
+
if (!existsSync17(memoryMdPath)) {
|
|
16101
16487
|
const projectName = basename6(projectRoot);
|
|
16102
16488
|
const memoryContent = `# ${projectName} - Massu Memory
|
|
16103
16489
|
|
|
@@ -16113,7 +16499,7 @@ function initMemoryDir(projectRoot) {
|
|
|
16113
16499
|
## File Index
|
|
16114
16500
|
<!-- Significant files and directories -->
|
|
16115
16501
|
`;
|
|
16116
|
-
|
|
16502
|
+
writeFileSync5(memoryMdPath, memoryContent, "utf-8");
|
|
16117
16503
|
memoryMdCreated = true;
|
|
16118
16504
|
}
|
|
16119
16505
|
return { created, memoryMdCreated, migratedFromLegacy };
|
|
@@ -16212,7 +16598,7 @@ async function runInit(argv, overrides) {
|
|
|
16212
16598
|
log("========================");
|
|
16213
16599
|
log("");
|
|
16214
16600
|
const configPath = resolve9(projectRoot, "massu.config.yaml");
|
|
16215
|
-
if (
|
|
16601
|
+
if (existsSync17(configPath)) {
|
|
16216
16602
|
if (opts.ci && !opts.force) {
|
|
16217
16603
|
errLog(`error: massu.config.yaml already exists at ${configPath}`);
|
|
16218
16604
|
errLog(" rerun with --force to overwrite, or remove the file first");
|
|
@@ -16240,7 +16626,7 @@ async function runInit(argv, overrides) {
|
|
|
16240
16626
|
const validation2 = validateWrittenConfig(configPath, projectRoot, false);
|
|
16241
16627
|
if (validation2 !== null) {
|
|
16242
16628
|
try {
|
|
16243
|
-
|
|
16629
|
+
rmSync5(configPath, { force: true });
|
|
16244
16630
|
} catch {
|
|
16245
16631
|
}
|
|
16246
16632
|
errLog(`error: template config failed validation: ${validation2}`);
|
|
@@ -16318,7 +16704,7 @@ async function runInit(argv, overrides) {
|
|
|
16318
16704
|
const validation = validateWrittenConfig(configPath, projectRoot, !emptyStack);
|
|
16319
16705
|
if (validation !== null) {
|
|
16320
16706
|
try {
|
|
16321
|
-
|
|
16707
|
+
rmSync5(configPath, { force: true });
|
|
16322
16708
|
} catch {
|
|
16323
16709
|
}
|
|
16324
16710
|
errLog(`error: generated config failed validation: ${validation}`);
|
|
@@ -16352,7 +16738,7 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
|
|
|
16352
16738
|
const stackResolved = !emptyStack && commandStats && (commandStats.installed > 0 || commandStats.updated > 0 || commandStats.kept > 0);
|
|
16353
16739
|
if (!stackResolved) {
|
|
16354
16740
|
const placeholderPath = resolve9(cmdResult.claudeDir, "commands", "_massu-needs-stack.md");
|
|
16355
|
-
if (!
|
|
16741
|
+
if (!existsSync17(placeholderPath)) {
|
|
16356
16742
|
const placeholderBody = [
|
|
16357
16743
|
"# Massu \u2014 stack not yet detected",
|
|
16358
16744
|
"",
|
|
@@ -16373,8 +16759,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
|
|
|
16373
16759
|
"\u2014 Massu"
|
|
16374
16760
|
].join("\n");
|
|
16375
16761
|
try {
|
|
16376
|
-
|
|
16377
|
-
|
|
16762
|
+
mkdirSync10(resolve9(cmdResult.claudeDir, "commands"), { recursive: true });
|
|
16763
|
+
writeFileSync5(placeholderPath, placeholderBody, "utf-8");
|
|
16378
16764
|
log(" Wrote _massu-needs-stack.md placeholder (no stack detected yet)");
|
|
16379
16765
|
} catch {
|
|
16380
16766
|
}
|
|
@@ -16397,7 +16783,7 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
|
|
|
16397
16783
|
try {
|
|
16398
16784
|
const encodedRoot = encodeMemoryDirName(projectRoot);
|
|
16399
16785
|
const memoryDir = resolve9(homedir6(), ".claude", "projects", encodedRoot, "memory");
|
|
16400
|
-
const memFiles =
|
|
16786
|
+
const memFiles = existsSync17(memoryDir) ? readdirSync11(memoryDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md") : [];
|
|
16401
16787
|
if (memFiles.length > 0) {
|
|
16402
16788
|
const { getMemoryDb: getMemoryDb2 } = await Promise.resolve().then(() => (init_memory_db(), memory_db_exports));
|
|
16403
16789
|
const db = getMemoryDb2();
|
|
@@ -16460,7 +16846,7 @@ var init_init = __esm({
|
|
|
16460
16846
|
init_detect();
|
|
16461
16847
|
init_drift();
|
|
16462
16848
|
__filename2 = fileURLToPath3(import.meta.url);
|
|
16463
|
-
__dirname2 =
|
|
16849
|
+
__dirname2 = dirname12(__filename2);
|
|
16464
16850
|
FRAMEWORK_TO_TEMPLATE_ID = {
|
|
16465
16851
|
rails: "rails",
|
|
16466
16852
|
phoenix: "phoenix",
|
|
@@ -17081,21 +17467,22 @@ var init_hook_registry = __esm({
|
|
|
17081
17467
|
// src/commands/doctor.ts
|
|
17082
17468
|
var doctor_exports = {};
|
|
17083
17469
|
__export(doctor_exports, {
|
|
17470
|
+
checkNativeModules: () => checkNativeModules,
|
|
17084
17471
|
formatLicenseCheck: () => formatLicenseCheck,
|
|
17085
17472
|
runDoctor: () => runDoctor,
|
|
17086
17473
|
runValidateConfig: () => runValidateConfig
|
|
17087
17474
|
});
|
|
17088
|
-
import { existsSync as
|
|
17089
|
-
import { resolve as resolve10, dirname as
|
|
17475
|
+
import { existsSync as existsSync18, readFileSync as readFileSync15, readdirSync as readdirSync12 } from "fs";
|
|
17476
|
+
import { resolve as resolve10, dirname as dirname13 } from "path";
|
|
17090
17477
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
17091
17478
|
import { parse as parseYaml4 } from "yaml";
|
|
17092
17479
|
function checkConfig(projectRoot) {
|
|
17093
17480
|
const configPath = resolve10(projectRoot, "massu.config.yaml");
|
|
17094
|
-
if (!
|
|
17481
|
+
if (!existsSync18(configPath)) {
|
|
17095
17482
|
return { name: "Configuration", status: "fail", detail: "massu.config.yaml not found. Run: npx massu init" };
|
|
17096
17483
|
}
|
|
17097
17484
|
try {
|
|
17098
|
-
const content =
|
|
17485
|
+
const content = readFileSync15(configPath, "utf-8");
|
|
17099
17486
|
const parsed = parseYaml4(content);
|
|
17100
17487
|
if (!parsed || typeof parsed !== "object") {
|
|
17101
17488
|
return { name: "Configuration", status: "fail", detail: "massu.config.yaml is empty or invalid YAML" };
|
|
@@ -17107,11 +17494,11 @@ function checkConfig(projectRoot) {
|
|
|
17107
17494
|
}
|
|
17108
17495
|
function checkMcpServer(projectRoot) {
|
|
17109
17496
|
const mcpPath = getResolvedPaths().mcpJsonPath;
|
|
17110
|
-
if (!
|
|
17497
|
+
if (!existsSync18(mcpPath)) {
|
|
17111
17498
|
return { name: "MCP Server", status: "fail", detail: ".mcp.json not found. Run: npx massu init" };
|
|
17112
17499
|
}
|
|
17113
17500
|
try {
|
|
17114
|
-
const content = JSON.parse(
|
|
17501
|
+
const content = JSON.parse(readFileSync15(mcpPath, "utf-8"));
|
|
17115
17502
|
const servers = content.mcpServers ?? {};
|
|
17116
17503
|
if (!servers.massu) {
|
|
17117
17504
|
return { name: "MCP Server", status: "fail", detail: "massu not registered in .mcp.json. Run: npx massu init" };
|
|
@@ -17123,7 +17510,7 @@ function checkMcpServer(projectRoot) {
|
|
|
17123
17510
|
}
|
|
17124
17511
|
function checkHooksConfig(projectRoot) {
|
|
17125
17512
|
const settingsPath = getResolvedPaths().settingsLocalPath;
|
|
17126
|
-
if (!
|
|
17513
|
+
if (!existsSync18(settingsPath)) {
|
|
17127
17514
|
return { name: "Hooks Config", status: "fail", detail: ".claude/settings.local.json not found. Run: npx massu init" };
|
|
17128
17515
|
}
|
|
17129
17516
|
try {
|
|
@@ -17153,9 +17540,9 @@ function checkHooksConfig(projectRoot) {
|
|
|
17153
17540
|
function checkHookFiles(projectRoot) {
|
|
17154
17541
|
const nodeModulesHooksDir = resolve10(projectRoot, "node_modules/@massu/core/dist/hooks");
|
|
17155
17542
|
let hooksDir = nodeModulesHooksDir;
|
|
17156
|
-
if (!
|
|
17543
|
+
if (!existsSync18(nodeModulesHooksDir)) {
|
|
17157
17544
|
const devHooksDir = resolve10(__dirname3, "../../dist/hooks");
|
|
17158
|
-
if (
|
|
17545
|
+
if (existsSync18(devHooksDir)) {
|
|
17159
17546
|
hooksDir = devHooksDir;
|
|
17160
17547
|
} else {
|
|
17161
17548
|
return { name: "Hook Files", status: "fail", detail: "Compiled hooks not found. Run: npm install @massu/core" };
|
|
@@ -17163,7 +17550,7 @@ function checkHookFiles(projectRoot) {
|
|
|
17163
17550
|
}
|
|
17164
17551
|
const missing = [];
|
|
17165
17552
|
for (const hookFile of EXPECTED_HOOKS) {
|
|
17166
|
-
if (!
|
|
17553
|
+
if (!existsSync18(resolve10(hooksDir, hookFile))) {
|
|
17167
17554
|
missing.push(hookFile);
|
|
17168
17555
|
}
|
|
17169
17556
|
}
|
|
@@ -17173,12 +17560,17 @@ function checkHookFiles(projectRoot) {
|
|
|
17173
17560
|
return { name: "Hook Files", status: "pass", detail: `${EXPECTED_HOOKS.length}/${EXPECTED_HOOKS.length} compiled hooks present` };
|
|
17174
17561
|
}
|
|
17175
17562
|
async function checkNativeModules() {
|
|
17176
|
-
|
|
17177
|
-
|
|
17178
|
-
|
|
17179
|
-
|
|
17180
|
-
return { name: "Native Modules", status: "fail", detail: `better-sqlite3 failed: ${err instanceof Error ? err.message : String(err)}. Try: npm rebuild better-sqlite3` };
|
|
17563
|
+
const memoryDbPath = getResolvedPaths().memoryDbPath;
|
|
17564
|
+
const verdict = probeMemoryDbUsable({ dbPath: memoryDbPath, selfHeal: false });
|
|
17565
|
+
if (verdict.ok) {
|
|
17566
|
+
return { name: "Native Modules", status: "pass", detail: "better-sqlite3 constructs a DB and SELECT 1 succeeds" };
|
|
17181
17567
|
}
|
|
17568
|
+
const because = verdict.detail ? ` (${verdict.detail})` : "";
|
|
17569
|
+
return {
|
|
17570
|
+
name: "Native Modules",
|
|
17571
|
+
status: "fail",
|
|
17572
|
+
detail: `better-sqlite3 unusable \u2014 ${verdict.reason}${because}. ${verdict.remedy ?? NATIVE_DB_REMEDY}`
|
|
17573
|
+
};
|
|
17182
17574
|
}
|
|
17183
17575
|
function checkNodeVersion() {
|
|
17184
17576
|
const version = process.versions.node;
|
|
@@ -17190,12 +17582,12 @@ function checkNodeVersion() {
|
|
|
17190
17582
|
}
|
|
17191
17583
|
async function checkGitRepo(projectRoot) {
|
|
17192
17584
|
const gitDir = resolve10(projectRoot, ".git");
|
|
17193
|
-
if (!
|
|
17585
|
+
if (!existsSync18(gitDir)) {
|
|
17194
17586
|
return { name: "Git Repository", status: "warn", detail: "Not a git repository (optional but recommended)" };
|
|
17195
17587
|
}
|
|
17196
17588
|
try {
|
|
17197
|
-
const { spawnSync:
|
|
17198
|
-
const result =
|
|
17589
|
+
const { spawnSync: spawnSync5 } = await import("child_process");
|
|
17590
|
+
const result = spawnSync5("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
17199
17591
|
encoding: "utf-8",
|
|
17200
17592
|
timeout: 5e3,
|
|
17201
17593
|
cwd: projectRoot
|
|
@@ -17208,7 +17600,7 @@ async function checkGitRepo(projectRoot) {
|
|
|
17208
17600
|
}
|
|
17209
17601
|
function checkKnowledgeDb(projectRoot) {
|
|
17210
17602
|
const knowledgeDbPath = getResolvedPaths().memoryDbPath;
|
|
17211
|
-
if (!
|
|
17603
|
+
if (!existsSync18(knowledgeDbPath)) {
|
|
17212
17604
|
return {
|
|
17213
17605
|
name: "Knowledge DB",
|
|
17214
17606
|
status: "warn",
|
|
@@ -17219,7 +17611,7 @@ function checkKnowledgeDb(projectRoot) {
|
|
|
17219
17611
|
}
|
|
17220
17612
|
function checkMemoryDir(_projectRoot2) {
|
|
17221
17613
|
const memoryDir = getResolvedPaths().memoryDir;
|
|
17222
|
-
if (!
|
|
17614
|
+
if (!existsSync18(memoryDir)) {
|
|
17223
17615
|
return {
|
|
17224
17616
|
name: "Memory Directory",
|
|
17225
17617
|
status: "warn",
|
|
@@ -17230,7 +17622,7 @@ function checkMemoryDir(_projectRoot2) {
|
|
|
17230
17622
|
}
|
|
17231
17623
|
function checkShellHooksWired(_projectRoot2) {
|
|
17232
17624
|
const settingsPath = getResolvedPaths().settingsLocalPath;
|
|
17233
|
-
if (!
|
|
17625
|
+
if (!existsSync18(settingsPath)) {
|
|
17234
17626
|
return {
|
|
17235
17627
|
name: "Shell Hooks",
|
|
17236
17628
|
status: "fail",
|
|
@@ -17306,8 +17698,8 @@ async function checkLicenseStatus() {
|
|
|
17306
17698
|
async function checkApiKeyHygiene(projectRoot) {
|
|
17307
17699
|
if (getResolvedApiKeySource() !== "config") return null;
|
|
17308
17700
|
try {
|
|
17309
|
-
const { spawnSync:
|
|
17310
|
-
const res =
|
|
17701
|
+
const { spawnSync: spawnSync5 } = await import("child_process");
|
|
17702
|
+
const res = spawnSync5("git", ["ls-files", "--error-unmatch", "massu.config.yaml"], {
|
|
17311
17703
|
cwd: projectRoot,
|
|
17312
17704
|
timeout: 5e3,
|
|
17313
17705
|
encoding: "utf-8"
|
|
@@ -17327,7 +17719,7 @@ function checkPythonHealth(projectRoot) {
|
|
|
17327
17719
|
const config = getConfig();
|
|
17328
17720
|
if (!config.python?.root) return null;
|
|
17329
17721
|
const pythonRoot = resolve10(projectRoot, config.python.root);
|
|
17330
|
-
if (!
|
|
17722
|
+
if (!existsSync18(pythonRoot)) {
|
|
17331
17723
|
return {
|
|
17332
17724
|
name: "Python",
|
|
17333
17725
|
status: "fail",
|
|
@@ -17347,7 +17739,7 @@ function checkPythonHealth(projectRoot) {
|
|
|
17347
17739
|
const excludeDirs = config.python?.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
|
|
17348
17740
|
if (!excludeDirs.includes(entry.name)) {
|
|
17349
17741
|
const subdir = resolve10(dir, entry.name);
|
|
17350
|
-
if (depth <= 2 && !
|
|
17742
|
+
if (depth <= 2 && !existsSync18(resolve10(subdir, "__init__.py"))) {
|
|
17351
17743
|
try {
|
|
17352
17744
|
const subEntries = readdirSync12(subdir);
|
|
17353
17745
|
if (subEntries.some((f2) => f2.endsWith(".py") && f2 !== "__init__.py")) {
|
|
@@ -17445,14 +17837,14 @@ async function runDoctor() {
|
|
|
17445
17837
|
async function runValidateConfig() {
|
|
17446
17838
|
const projectRoot = process.cwd();
|
|
17447
17839
|
const configPath = resolve10(projectRoot, "massu.config.yaml");
|
|
17448
|
-
if (!
|
|
17840
|
+
if (!existsSync18(configPath)) {
|
|
17449
17841
|
console.error("Error: massu.config.yaml not found in current directory");
|
|
17450
17842
|
console.error("Run: npx massu init");
|
|
17451
17843
|
process.exit(1);
|
|
17452
17844
|
return;
|
|
17453
17845
|
}
|
|
17454
17846
|
try {
|
|
17455
|
-
const content =
|
|
17847
|
+
const content = readFileSync15(configPath, "utf-8");
|
|
17456
17848
|
const parsed = parseYaml4(content);
|
|
17457
17849
|
if (!parsed || typeof parsed !== "object") {
|
|
17458
17850
|
console.error("Error: massu.config.yaml is empty or not a valid YAML object");
|
|
@@ -17493,8 +17885,9 @@ var init_doctor = __esm({
|
|
|
17493
17885
|
init_credentials();
|
|
17494
17886
|
init_settings_local();
|
|
17495
17887
|
init_hook_registry();
|
|
17888
|
+
init_sqlite_loader();
|
|
17496
17889
|
__filename3 = fileURLToPath4(import.meta.url);
|
|
17497
|
-
__dirname3 =
|
|
17890
|
+
__dirname3 = dirname13(__filename3);
|
|
17498
17891
|
EXPECTED_HOOKS = getExpectedHookFiles();
|
|
17499
17892
|
}
|
|
17500
17893
|
});
|
|
@@ -17682,6 +18075,67 @@ var init_login = __esm({
|
|
|
17682
18075
|
}
|
|
17683
18076
|
});
|
|
17684
18077
|
|
|
18078
|
+
// src/commands/heal.ts
|
|
18079
|
+
var heal_exports = {};
|
|
18080
|
+
__export(heal_exports, {
|
|
18081
|
+
runHeal: () => runHeal
|
|
18082
|
+
});
|
|
18083
|
+
async function runHeal(argv = []) {
|
|
18084
|
+
const check = argv.includes("--check");
|
|
18085
|
+
const runningAbi = process.versions.modules;
|
|
18086
|
+
const verdict = probeMemoryDbUsable({ selfHeal: false });
|
|
18087
|
+
const abiFrom = (() => {
|
|
18088
|
+
const m3 = /NODE_MODULE_VERSION (\d+)/.exec(verdict.detail ?? "");
|
|
18089
|
+
return m3 ? m3[1] : void 0;
|
|
18090
|
+
})();
|
|
18091
|
+
const probe = { ok: verdict.ok, abiFrom, detail: verdict.detail };
|
|
18092
|
+
if (check) {
|
|
18093
|
+
if (probe.ok) {
|
|
18094
|
+
process.stdout.write(
|
|
18095
|
+
`massu heal --check: native engine OK \u2014 better-sqlite3 matches this Node (NODE_MODULE_VERSION ${runningAbi}, ${process.version}).
|
|
18096
|
+
`
|
|
18097
|
+
);
|
|
18098
|
+
return { exitCode: 0 };
|
|
18099
|
+
}
|
|
18100
|
+
process.stdout.write(
|
|
18101
|
+
`massu heal --check: ABI MISMATCH \u2014 this Node needs NODE_MODULE_VERSION ${runningAbi} (${process.version})` + (probe.abiFrom ? `, the installed binary is ${probe.abiFrom}` : "") + `. Run 'massu heal' to rebuild it.
|
|
18102
|
+
`
|
|
18103
|
+
);
|
|
18104
|
+
return { exitCode: 1 };
|
|
18105
|
+
}
|
|
18106
|
+
if (probe.ok) {
|
|
18107
|
+
process.stdout.write(
|
|
18108
|
+
`massu heal: native engine already healthy for ${process.version} (NODE_MODULE_VERSION ${runningAbi}) \u2014 nothing to do.
|
|
18109
|
+
`
|
|
18110
|
+
);
|
|
18111
|
+
return { exitCode: 0 };
|
|
18112
|
+
}
|
|
18113
|
+
process.stdout.write(
|
|
18114
|
+
`massu heal: rebuilding better-sqlite3 for ${process.version} (NODE_MODULE_VERSION ${runningAbi})\u2026
|
|
18115
|
+
`
|
|
18116
|
+
);
|
|
18117
|
+
const result = attemptNativeHeal(probe.detail ? new Error(probe.detail) : void 0);
|
|
18118
|
+
if (result.healed) {
|
|
18119
|
+
process.stdout.write(
|
|
18120
|
+
`massu heal: rebuilt via ${result.method} (ABI ${result.abiFrom ?? "?"} \u2192 ${result.abiTo}, ${result.durationMs ?? "?"}ms). Restart your MCP client / Claude Code to pick up the healed engine.
|
|
18121
|
+
`
|
|
18122
|
+
);
|
|
18123
|
+
return { exitCode: 0 };
|
|
18124
|
+
}
|
|
18125
|
+
process.stderr.write(
|
|
18126
|
+
`massu heal: could not rebuild the native engine (${result.reason}${result.detail ? `: ${result.detail}` : ""}).
|
|
18127
|
+
Ensure the install directory is writable and either network access (for a compiler-free prebuilt) or a C/C++ toolchain (for a source build) is available, then retry.
|
|
18128
|
+
`
|
|
18129
|
+
);
|
|
18130
|
+
return { exitCode: 1 };
|
|
18131
|
+
}
|
|
18132
|
+
var init_heal = __esm({
|
|
18133
|
+
"src/commands/heal.ts"() {
|
|
18134
|
+
"use strict";
|
|
18135
|
+
init_sqlite_loader();
|
|
18136
|
+
}
|
|
18137
|
+
});
|
|
18138
|
+
|
|
17685
18139
|
// src/commands/install-hooks.ts
|
|
17686
18140
|
var install_hooks_exports = {};
|
|
17687
18141
|
__export(install_hooks_exports, {
|
|
@@ -17713,8 +18167,8 @@ __export(hook_runner_exports, {
|
|
|
17713
18167
|
resolveHookFile: () => resolveHookFile,
|
|
17714
18168
|
runHookRunner: () => runHookRunner
|
|
17715
18169
|
});
|
|
17716
|
-
import { existsSync as
|
|
17717
|
-
import { resolve as resolve11, dirname as
|
|
18170
|
+
import { existsSync as existsSync19 } from "fs";
|
|
18171
|
+
import { resolve as resolve11, dirname as dirname14 } from "path";
|
|
17718
18172
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
17719
18173
|
import { spawn } from "child_process";
|
|
17720
18174
|
function resolveHookFile(hookName) {
|
|
@@ -17733,7 +18187,7 @@ function resolveHookFile(hookName) {
|
|
|
17733
18187
|
resolve11(__dirname4, "../../dist/hooks", file)
|
|
17734
18188
|
];
|
|
17735
18189
|
for (const candidate of candidates) {
|
|
17736
|
-
if (
|
|
18190
|
+
if (existsSync19(candidate)) {
|
|
17737
18191
|
return candidate;
|
|
17738
18192
|
}
|
|
17739
18193
|
}
|
|
@@ -17763,7 +18217,10 @@ Recognized: ${Object.keys(HOOK_NAME_TO_FILE).join(", ")}
|
|
|
17763
18217
|
return new Promise((resolvePromise) => {
|
|
17764
18218
|
const child = spawn(process.execPath, [hookFile], {
|
|
17765
18219
|
stdio: ["inherit", "inherit", "inherit"],
|
|
17766
|
-
|
|
18220
|
+
// MASSU_HOOK_RUNTIME marks the hook process so the SSOT SQLite loader NEVER
|
|
18221
|
+
// self-heals here (5s budget / CR-12, CR-65 P0-003) — even for indirect opens
|
|
18222
|
+
// via getMemoryDb(). The next server/CLI touch performs the rebuild.
|
|
18223
|
+
env: { ...process.env, MASSU_HOOK_RUNTIME: "1" }
|
|
17767
18224
|
});
|
|
17768
18225
|
child.on("exit", (code, signal) => {
|
|
17769
18226
|
if (signal) {
|
|
@@ -17785,7 +18242,7 @@ var init_hook_runner = __esm({
|
|
|
17785
18242
|
"use strict";
|
|
17786
18243
|
init_hook_registry();
|
|
17787
18244
|
__filename4 = fileURLToPath5(import.meta.url);
|
|
17788
|
-
__dirname4 =
|
|
18245
|
+
__dirname4 = dirname14(__filename4);
|
|
17789
18246
|
HOOK_NAME_TO_FILE = Object.freeze(
|
|
17790
18247
|
Object.fromEntries(REGISTERED_HOOKS.map((name) => [name, `${name}.js`]))
|
|
17791
18248
|
);
|
|
@@ -17924,7 +18381,7 @@ var db_exports = {};
|
|
|
17924
18381
|
__export(db_exports, {
|
|
17925
18382
|
handleDbSubcommand: () => handleDbSubcommand
|
|
17926
18383
|
});
|
|
17927
|
-
import { existsSync as
|
|
18384
|
+
import { existsSync as existsSync20, statSync as statSync10 } from "fs";
|
|
17928
18385
|
import { basename as basename7 } from "path";
|
|
17929
18386
|
function human(bytes) {
|
|
17930
18387
|
if (bytes < 1024) return `${bytes} B`;
|
|
@@ -17969,7 +18426,7 @@ async function handleDbSubcommand(args2) {
|
|
|
17969
18426
|
|
|
17970
18427
|
`);
|
|
17971
18428
|
for (const store of allStores()) {
|
|
17972
|
-
if (!
|
|
18429
|
+
if (!existsSync20(store.path)) {
|
|
17973
18430
|
process.stdout.write(` ${"\u2014"} ${store.name.padEnd(10)} not present, skipped
|
|
17974
18431
|
`);
|
|
17975
18432
|
continue;
|
|
@@ -18067,7 +18524,7 @@ Restore with: massu db restore --latest memory
|
|
|
18067
18524
|
target = found.path;
|
|
18068
18525
|
}
|
|
18069
18526
|
try {
|
|
18070
|
-
const before =
|
|
18527
|
+
const before = existsSync20(target) ? statSync10(target).size : 0;
|
|
18071
18528
|
const res = restoreDb(backupPath, target);
|
|
18072
18529
|
process.stdout.write(
|
|
18073
18530
|
`Restored ${basename7(target)}
|
|
@@ -18233,7 +18690,7 @@ var init_license2 = __esm({
|
|
|
18233
18690
|
});
|
|
18234
18691
|
|
|
18235
18692
|
// src/changelog-generator.ts
|
|
18236
|
-
import { existsSync as
|
|
18693
|
+
import { existsSync as existsSync21, readFileSync as readFileSync16, readdirSync as readdirSync13 } from "fs";
|
|
18237
18694
|
import { resolve as resolve13 } from "path";
|
|
18238
18695
|
function parseCommitsForPlanTokens(subjects) {
|
|
18239
18696
|
const tokens = /* @__PURE__ */ new Set();
|
|
@@ -18251,7 +18708,7 @@ function parseCommitsForPlanTokens(subjects) {
|
|
|
18251
18708
|
function loadPlanSummaries(tokens, planDir) {
|
|
18252
18709
|
const result = /* @__PURE__ */ new Map();
|
|
18253
18710
|
if (tokens.size === 0) return result;
|
|
18254
|
-
if (!
|
|
18711
|
+
if (!existsSync21(planDir)) {
|
|
18255
18712
|
throw new Error(`Plan directory does not exist: ${planDir}`);
|
|
18256
18713
|
}
|
|
18257
18714
|
const files = readdirSync13(planDir).filter((f2) => f2.endsWith(".md"));
|
|
@@ -18260,7 +18717,7 @@ function loadPlanSummaries(tokens, planDir) {
|
|
|
18260
18717
|
let content = "";
|
|
18261
18718
|
for (const file of files) {
|
|
18262
18719
|
const path = resolve13(planDir, file);
|
|
18263
|
-
const text18 =
|
|
18720
|
+
const text18 = readFileSync16(path, "utf-8");
|
|
18264
18721
|
const tokenRe = new RegExp(
|
|
18265
18722
|
`^\\*\\*Plan Token\\*\\*:\\s*\`?${token.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}\`?(\\s|$)`,
|
|
18266
18723
|
"m"
|
|
@@ -18341,7 +18798,7 @@ __export(changelog_exports, {
|
|
|
18341
18798
|
printChangelogHelp: () => printChangelogHelp
|
|
18342
18799
|
});
|
|
18343
18800
|
import { execSync } from "child_process";
|
|
18344
|
-
import { existsSync as
|
|
18801
|
+
import { existsSync as existsSync22, readFileSync as readFileSync17 } from "fs";
|
|
18345
18802
|
import { resolve as resolve14 } from "path";
|
|
18346
18803
|
function resolveRepoRoot() {
|
|
18347
18804
|
try {
|
|
@@ -18367,8 +18824,8 @@ function getCommitSubjects(range) {
|
|
|
18367
18824
|
}
|
|
18368
18825
|
function getCurrentVersion(repoRoot) {
|
|
18369
18826
|
const pkgPath = resolve14(repoRoot, "packages/core/package.json");
|
|
18370
|
-
if (!
|
|
18371
|
-
const pkg = JSON.parse(
|
|
18827
|
+
if (!existsSync22(pkgPath)) return "0.0.0";
|
|
18828
|
+
const pkg = JSON.parse(readFileSync17(pkgPath, "utf-8"));
|
|
18372
18829
|
return pkg.version || "0.0.0";
|
|
18373
18830
|
}
|
|
18374
18831
|
function todayDate() {
|
|
@@ -18376,8 +18833,8 @@ function todayDate() {
|
|
|
18376
18833
|
}
|
|
18377
18834
|
function getLatestChangelogEntryBody(repoRoot) {
|
|
18378
18835
|
const path = resolve14(repoRoot, "CHANGELOG.md");
|
|
18379
|
-
if (!
|
|
18380
|
-
const content =
|
|
18836
|
+
if (!existsSync22(path)) return "";
|
|
18837
|
+
const content = readFileSync17(path, "utf-8");
|
|
18381
18838
|
const m3 = content.match(/^## \[[\d.]+\][^\n]*\n([\s\S]*?)(?=\n## \[|$)/m);
|
|
18382
18839
|
return m3 ? m3[1] : "";
|
|
18383
18840
|
}
|
|
@@ -18472,16 +18929,15 @@ var init_changelog = __esm({
|
|
|
18472
18929
|
});
|
|
18473
18930
|
|
|
18474
18931
|
// src/knowledge-db.ts
|
|
18475
|
-
import
|
|
18476
|
-
import {
|
|
18477
|
-
import { existsSync as existsSync22, mkdirSync as mkdirSync9 } from "fs";
|
|
18932
|
+
import { dirname as dirname15 } from "path";
|
|
18933
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync11 } from "fs";
|
|
18478
18934
|
function getKnowledgeDb() {
|
|
18479
18935
|
const dbPath = getResolvedPaths().knowledgeDbPath;
|
|
18480
|
-
const dir =
|
|
18481
|
-
if (!
|
|
18482
|
-
|
|
18936
|
+
const dir = dirname15(dbPath);
|
|
18937
|
+
if (!existsSync23(dir)) {
|
|
18938
|
+
mkdirSync11(dir, { recursive: true });
|
|
18483
18939
|
}
|
|
18484
|
-
const db =
|
|
18940
|
+
const db = openDatabase(dbPath);
|
|
18485
18941
|
db.pragma("journal_mode = WAL");
|
|
18486
18942
|
db.pragma("foreign_keys = ON");
|
|
18487
18943
|
initKnowledgeSchema(db);
|
|
@@ -18693,6 +19149,7 @@ async function embedMissingChunks(db, opts = {}) {
|
|
|
18693
19149
|
var init_knowledge_db = __esm({
|
|
18694
19150
|
"src/knowledge-db.ts"() {
|
|
18695
19151
|
"use strict";
|
|
19152
|
+
init_sqlite_loader();
|
|
18696
19153
|
init_config();
|
|
18697
19154
|
init_memory_vector();
|
|
18698
19155
|
init_memory_embed_sweep();
|
|
@@ -18704,7 +19161,7 @@ var memory_embed_backfill_exports = {};
|
|
|
18704
19161
|
__export(memory_embed_backfill_exports, {
|
|
18705
19162
|
runMemoryEmbedBackfill: () => runMemoryEmbedBackfill
|
|
18706
19163
|
});
|
|
18707
|
-
import { existsSync as
|
|
19164
|
+
import { existsSync as existsSync24 } from "fs";
|
|
18708
19165
|
async function runMemoryEmbedBackfill(_args = []) {
|
|
18709
19166
|
await embed("massu memory embed backfill probe");
|
|
18710
19167
|
const active = getActiveEmbedModel();
|
|
@@ -18731,7 +19188,7 @@ async function runMemoryEmbedBackfill(_args = []) {
|
|
|
18731
19188
|
);
|
|
18732
19189
|
let chunks = { embedded: 0, scanned: 0 };
|
|
18733
19190
|
const knowledgeDbPath = getResolvedPaths().knowledgeDbPath;
|
|
18734
|
-
if (
|
|
19191
|
+
if (existsSync24(knowledgeDbPath)) {
|
|
18735
19192
|
const knowledgeDb = getKnowledgeDb();
|
|
18736
19193
|
try {
|
|
18737
19194
|
chunks = await embedMissingChunks(knowledgeDb);
|
|
@@ -18764,7 +19221,7 @@ var init_memory_embed_backfill = __esm({
|
|
|
18764
19221
|
|
|
18765
19222
|
// src/memory-authorship.ts
|
|
18766
19223
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
18767
|
-
import { readFileSync as
|
|
19224
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync6, existsSync as existsSync25, mkdirSync as mkdirSync12, chmodSync as chmodSync6, statSync as statSync11 } from "fs";
|
|
18768
19225
|
import { homedir as homedir7 } from "os";
|
|
18769
19226
|
import { resolve as resolve15 } from "path";
|
|
18770
19227
|
function renderKeyPath(home = homedir7()) {
|
|
@@ -18773,8 +19230,8 @@ function renderKeyPath(home = homedir7()) {
|
|
|
18773
19230
|
function readRenderKey(home = homedir7()) {
|
|
18774
19231
|
const p19 = renderKeyPath(home);
|
|
18775
19232
|
try {
|
|
18776
|
-
if (!
|
|
18777
|
-
const raw =
|
|
19233
|
+
if (!existsSync25(p19)) return void 0;
|
|
19234
|
+
const raw = readFileSync18(p19);
|
|
18778
19235
|
if (raw.length !== 32) return void 0;
|
|
18779
19236
|
return raw;
|
|
18780
19237
|
} catch {
|
|
@@ -18787,15 +19244,15 @@ function ensureRenderKey(home = homedir7()) {
|
|
|
18787
19244
|
const p19 = renderKeyPath(home);
|
|
18788
19245
|
try {
|
|
18789
19246
|
const dir = resolve15(home, ".massu");
|
|
18790
|
-
|
|
19247
|
+
mkdirSync12(dir, { recursive: true, mode: 448 });
|
|
18791
19248
|
try {
|
|
18792
|
-
|
|
19249
|
+
chmodSync6(dir, 448);
|
|
18793
19250
|
} catch {
|
|
18794
19251
|
}
|
|
18795
19252
|
const key = randomBytes(32);
|
|
18796
|
-
|
|
19253
|
+
writeFileSync6(p19, key, { mode: 384 });
|
|
18797
19254
|
try {
|
|
18798
|
-
|
|
19255
|
+
chmodSync6(p19, 384);
|
|
18799
19256
|
} catch {
|
|
18800
19257
|
}
|
|
18801
19258
|
return key;
|
|
@@ -18844,11 +19301,11 @@ var init_memory_authorship = __esm({
|
|
|
18844
19301
|
});
|
|
18845
19302
|
|
|
18846
19303
|
// src/rule-candidate-snapshot.ts
|
|
18847
|
-
import { existsSync as
|
|
19304
|
+
import { existsSync as existsSync26, readFileSync as readFileSync19, writeFileSync as writeFileSync7, unlinkSync as unlinkSync4 } from "fs";
|
|
18848
19305
|
function takeSnapshots(paths) {
|
|
18849
19306
|
const out = /* @__PURE__ */ new Map();
|
|
18850
19307
|
for (const p19 of paths) {
|
|
18851
|
-
if (
|
|
19308
|
+
if (existsSync26(p19)) out.set(p19, readFileSync19(p19, "utf-8"));
|
|
18852
19309
|
else out.set(p19, null);
|
|
18853
19310
|
}
|
|
18854
19311
|
return out;
|
|
@@ -18858,9 +19315,9 @@ function restoreSnapshots(snapshot) {
|
|
|
18858
19315
|
for (const [path, content] of snapshot) {
|
|
18859
19316
|
try {
|
|
18860
19317
|
if (content === null) {
|
|
18861
|
-
if (
|
|
19318
|
+
if (existsSync26(path)) unlinkSync4(path);
|
|
18862
19319
|
} else {
|
|
18863
|
-
|
|
19320
|
+
writeFileSync7(path, content, "utf-8");
|
|
18864
19321
|
}
|
|
18865
19322
|
} catch (err) {
|
|
18866
19323
|
errors.push(`${path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -18938,18 +19395,18 @@ var init_memory_render_path = __esm({
|
|
|
18938
19395
|
});
|
|
18939
19396
|
|
|
18940
19397
|
// src/memory-tombstones.ts
|
|
18941
|
-
import { readFileSync as
|
|
18942
|
-
import { join as
|
|
19398
|
+
import { readFileSync as readFileSync20, appendFileSync as appendFileSync3, existsSync as existsSync27 } from "fs";
|
|
19399
|
+
import { join as join15 } from "path";
|
|
18943
19400
|
function tombstoneLedgerPath(memoryDir) {
|
|
18944
|
-
return
|
|
19401
|
+
return join15(memoryDir, TOMBSTONE_LEDGER);
|
|
18945
19402
|
}
|
|
18946
19403
|
function readTombstones(memoryDir) {
|
|
18947
19404
|
const out = /* @__PURE__ */ new Map();
|
|
18948
19405
|
const p19 = tombstoneLedgerPath(memoryDir);
|
|
18949
|
-
if (!
|
|
19406
|
+
if (!existsSync27(p19)) return out;
|
|
18950
19407
|
let raw;
|
|
18951
19408
|
try {
|
|
18952
|
-
raw =
|
|
19409
|
+
raw = readFileSync20(p19, "utf8");
|
|
18953
19410
|
} catch {
|
|
18954
19411
|
return out;
|
|
18955
19412
|
}
|
|
@@ -18977,7 +19434,7 @@ function tombstone(memoryDir, relPath, reason, nowEpoch) {
|
|
|
18977
19434
|
reason
|
|
18978
19435
|
};
|
|
18979
19436
|
try {
|
|
18980
|
-
|
|
19437
|
+
appendFileSync3(tombstoneLedgerPath(memoryDir), `${JSON.stringify(entry)}
|
|
18981
19438
|
`, "utf8");
|
|
18982
19439
|
} catch (err) {
|
|
18983
19440
|
throw new TombstoneWriteError(
|
|
@@ -19173,15 +19630,15 @@ var init_memory_llm = __esm({
|
|
|
19173
19630
|
// src/memory-backup.ts
|
|
19174
19631
|
import {
|
|
19175
19632
|
readdirSync as readdirSync14,
|
|
19176
|
-
mkdirSync as
|
|
19633
|
+
mkdirSync as mkdirSync13,
|
|
19177
19634
|
copyFileSync as copyFileSync2,
|
|
19178
19635
|
statSync as statSync12,
|
|
19179
|
-
existsSync as
|
|
19180
|
-
rmSync as
|
|
19181
|
-
readFileSync as
|
|
19636
|
+
existsSync as existsSync28,
|
|
19637
|
+
rmSync as rmSync6,
|
|
19638
|
+
readFileSync as readFileSync21
|
|
19182
19639
|
} from "fs";
|
|
19183
19640
|
import { homedir as homedir8 } from "os";
|
|
19184
|
-
import { join as
|
|
19641
|
+
import { join as join16, resolve as resolve16 } from "path";
|
|
19185
19642
|
function backupsRoot(home = homedir8()) {
|
|
19186
19643
|
return resolve16(home, ".massu", "memory-backups");
|
|
19187
19644
|
}
|
|
@@ -19189,10 +19646,10 @@ function backupStamp2(nowMs) {
|
|
|
19189
19646
|
return new Date(nowMs).toISOString().replace(/:/g, "-");
|
|
19190
19647
|
}
|
|
19191
19648
|
function newestMtimeMs(dir) {
|
|
19192
|
-
if (!
|
|
19649
|
+
if (!existsSync28(dir)) return 0;
|
|
19193
19650
|
let newest = 0;
|
|
19194
19651
|
for (const entry of readdirSync14(dir)) {
|
|
19195
|
-
const p19 =
|
|
19652
|
+
const p19 = join16(dir, entry);
|
|
19196
19653
|
try {
|
|
19197
19654
|
const st = statSync12(p19);
|
|
19198
19655
|
if (st.isFile() && st.mtimeMs > newest) newest = st.mtimeMs;
|
|
@@ -19204,10 +19661,10 @@ function newestMtimeMs(dir) {
|
|
|
19204
19661
|
}
|
|
19205
19662
|
function listBackups(home = homedir8()) {
|
|
19206
19663
|
const root = backupsRoot(home);
|
|
19207
|
-
if (!
|
|
19664
|
+
if (!existsSync28(root)) return [];
|
|
19208
19665
|
const out = [];
|
|
19209
19666
|
for (const stamp of readdirSync14(root)) {
|
|
19210
|
-
const dir =
|
|
19667
|
+
const dir = join16(root, stamp);
|
|
19211
19668
|
try {
|
|
19212
19669
|
const st = statSync12(dir);
|
|
19213
19670
|
if (!st.isDirectory()) continue;
|
|
@@ -19225,21 +19682,21 @@ function hasFreshBackup(memoryDir, home = homedir8()) {
|
|
|
19225
19682
|
return backups[0].createdMs >= newestChange;
|
|
19226
19683
|
}
|
|
19227
19684
|
function takeBackup(memoryDir, nowMs, home = homedir8(), retention = DEFAULT_RETENTION2) {
|
|
19228
|
-
if (!
|
|
19685
|
+
if (!existsSync28(memoryDir)) {
|
|
19229
19686
|
throw new BackupError(`memory directory does not exist: ${memoryDir}`);
|
|
19230
19687
|
}
|
|
19231
19688
|
const stamp = backupStamp2(nowMs);
|
|
19232
|
-
const dir =
|
|
19689
|
+
const dir = join16(backupsRoot(home), stamp);
|
|
19233
19690
|
try {
|
|
19234
|
-
|
|
19691
|
+
mkdirSync13(dir, { recursive: true, mode: 448 });
|
|
19235
19692
|
for (const entry of readdirSync14(memoryDir)) {
|
|
19236
|
-
const srcPath =
|
|
19693
|
+
const srcPath = join16(memoryDir, entry);
|
|
19237
19694
|
try {
|
|
19238
19695
|
if (!statSync12(srcPath).isFile()) continue;
|
|
19239
19696
|
} catch {
|
|
19240
19697
|
continue;
|
|
19241
19698
|
}
|
|
19242
|
-
copyFileSync2(srcPath,
|
|
19699
|
+
copyFileSync2(srcPath, join16(dir, entry));
|
|
19243
19700
|
}
|
|
19244
19701
|
} catch (err) {
|
|
19245
19702
|
throw new BackupError(`backup failed: ${err.message}`);
|
|
@@ -19251,7 +19708,7 @@ function pruneBackups(home = homedir8(), retention = DEFAULT_RETENTION2) {
|
|
|
19251
19708
|
const backups = listBackups(home);
|
|
19252
19709
|
for (const stale of backups.slice(Math.max(retention, 1))) {
|
|
19253
19710
|
try {
|
|
19254
|
-
|
|
19711
|
+
rmSync6(stale.dir, { recursive: true, force: true });
|
|
19255
19712
|
} catch {
|
|
19256
19713
|
}
|
|
19257
19714
|
}
|
|
@@ -19265,14 +19722,14 @@ function restoreBackup(memoryDir, opts = {}) {
|
|
|
19265
19722
|
const wouldRestore = [];
|
|
19266
19723
|
for (const entry of readdirSync14(chosen.dir)) {
|
|
19267
19724
|
try {
|
|
19268
|
-
if (statSync12(
|
|
19725
|
+
if (statSync12(join16(chosen.dir, entry)).isFile()) wouldRestore.push(entry);
|
|
19269
19726
|
} catch {
|
|
19270
19727
|
continue;
|
|
19271
19728
|
}
|
|
19272
19729
|
}
|
|
19273
|
-
const currentFiles =
|
|
19730
|
+
const currentFiles = existsSync28(memoryDir) ? readdirSync14(memoryDir).filter((e2) => {
|
|
19274
19731
|
try {
|
|
19275
|
-
return statSync12(
|
|
19732
|
+
return statSync12(join16(memoryDir, e2)).isFile();
|
|
19276
19733
|
} catch {
|
|
19277
19734
|
return false;
|
|
19278
19735
|
}
|
|
@@ -19287,10 +19744,10 @@ function restoreBackup(memoryDir, opts = {}) {
|
|
|
19287
19744
|
presentButNotInBackup
|
|
19288
19745
|
};
|
|
19289
19746
|
}
|
|
19290
|
-
|
|
19747
|
+
mkdirSync13(memoryDir, { recursive: true });
|
|
19291
19748
|
let n = 0;
|
|
19292
19749
|
for (const entry of wouldRestore) {
|
|
19293
|
-
copyFileSync2(
|
|
19750
|
+
copyFileSync2(join16(chosen.dir, entry), join16(memoryDir, entry));
|
|
19294
19751
|
n++;
|
|
19295
19752
|
}
|
|
19296
19753
|
return {
|
|
@@ -19315,105 +19772,9 @@ var init_memory_backup = __esm({
|
|
|
19315
19772
|
}
|
|
19316
19773
|
});
|
|
19317
19774
|
|
|
19318
|
-
// src/lib/fileLock.ts
|
|
19319
|
-
import { mkdirSync as mkdirSync12, readFileSync as readFileSync21, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
19320
|
-
import { dirname as dirname14 } from "path";
|
|
19321
|
-
import * as lockfile from "proper-lockfile";
|
|
19322
|
-
function readLockHolderPid(lockPath) {
|
|
19323
|
-
try {
|
|
19324
|
-
const raw = readFileSync21(`${lockPath}.pid`, "utf-8").trim();
|
|
19325
|
-
const pid = Number.parseInt(raw, 10);
|
|
19326
|
-
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
19327
|
-
return pid;
|
|
19328
|
-
} catch {
|
|
19329
|
-
return null;
|
|
19330
|
-
}
|
|
19331
|
-
}
|
|
19332
|
-
function busyWaitSync(ms) {
|
|
19333
|
-
if (typeof SharedArrayBuffer === "undefined" || typeof Atomics === "undefined") {
|
|
19334
|
-
throw new Error(
|
|
19335
|
-
`withFileLockSync requires SharedArrayBuffer + Atomics for its retry-loop wait. This Node runtime does not provide them \u2014 refusing to fall back to a CPU spinloop. If you hit this in a sandboxed serverless env, the fix is to perform the lock-protected operation in a host runtime that supports Atomics.`
|
|
19336
|
-
);
|
|
19337
|
-
}
|
|
19338
|
-
const sab = new SharedArrayBuffer(4);
|
|
19339
|
-
const view = new Int32Array(sab);
|
|
19340
|
-
Atomics.wait(view, 0, 0, ms);
|
|
19341
|
-
}
|
|
19342
|
-
function withFileLockSync(lockPath, fn, opts = {}) {
|
|
19343
|
-
mkdirSync12(dirname14(lockPath), { recursive: true });
|
|
19344
|
-
const staleMs = opts.staleMs ?? 3e4;
|
|
19345
|
-
const blockMs = opts.retries === 0 ? 0 : opts.blockMs ?? 3e4;
|
|
19346
|
-
const pollIntervalMs = opts.pollIntervalMs ?? 100;
|
|
19347
|
-
const now = opts.now ?? Date.now;
|
|
19348
|
-
const sleep = opts.sleep ?? busyWaitSync;
|
|
19349
|
-
const makeBusyError = opts.errorFactory ?? ((path, pid, retrySeconds, code) => new FileLockBusyError(path, pid, retrySeconds, code));
|
|
19350
|
-
let release = null;
|
|
19351
|
-
const deadline = now() + blockMs;
|
|
19352
|
-
for (; ; ) {
|
|
19353
|
-
try {
|
|
19354
|
-
release = lockfile.lockSync(lockPath, {
|
|
19355
|
-
stale: staleMs,
|
|
19356
|
-
retries: 0,
|
|
19357
|
-
realpath: false
|
|
19358
|
-
});
|
|
19359
|
-
try {
|
|
19360
|
-
writeFileSync7(`${lockPath}.pid`, String(process.pid), "utf-8");
|
|
19361
|
-
} catch {
|
|
19362
|
-
}
|
|
19363
|
-
break;
|
|
19364
|
-
} catch (err) {
|
|
19365
|
-
const e2 = err;
|
|
19366
|
-
const code = e2.code;
|
|
19367
|
-
if (code !== "ELOCKED" && code !== "EBUSY") {
|
|
19368
|
-
throw err;
|
|
19369
|
-
}
|
|
19370
|
-
if (now() >= deadline) {
|
|
19371
|
-
const holderPid = readLockHolderPid(lockPath);
|
|
19372
|
-
const remainingMs = Math.max(0, deadline - now());
|
|
19373
|
-
const retryAfterSeconds = blockMs === 0 ? Math.round(staleMs / 1e3) : Math.round(remainingMs / 1e3);
|
|
19374
|
-
throw makeBusyError(lockPath, holderPid, retryAfterSeconds, code);
|
|
19375
|
-
}
|
|
19376
|
-
sleep(pollIntervalMs);
|
|
19377
|
-
}
|
|
19378
|
-
}
|
|
19379
|
-
try {
|
|
19380
|
-
return fn();
|
|
19381
|
-
} finally {
|
|
19382
|
-
try {
|
|
19383
|
-
if (release) release();
|
|
19384
|
-
} catch {
|
|
19385
|
-
}
|
|
19386
|
-
try {
|
|
19387
|
-
rmSync6(`${lockPath}.pid`, { force: true });
|
|
19388
|
-
} catch {
|
|
19389
|
-
}
|
|
19390
|
-
}
|
|
19391
|
-
}
|
|
19392
|
-
var FileLockBusyError;
|
|
19393
|
-
var init_fileLock = __esm({
|
|
19394
|
-
"src/lib/fileLock.ts"() {
|
|
19395
|
-
"use strict";
|
|
19396
|
-
FileLockBusyError = class extends Error {
|
|
19397
|
-
constructor(lockPath, holderPid, retryAfterSeconds, causeCode) {
|
|
19398
|
-
const pidPart = holderPid != null ? `(PID=${holderPid})` : "(PID=unknown)";
|
|
19399
|
-
super(`File lock at ${lockPath} held by another process ${pidPart} \u2014 try again in ${retryAfterSeconds}s`);
|
|
19400
|
-
this.lockPath = lockPath;
|
|
19401
|
-
this.holderPid = holderPid;
|
|
19402
|
-
this.retryAfterSeconds = retryAfterSeconds;
|
|
19403
|
-
this.causeCode = causeCode;
|
|
19404
|
-
this.name = "FileLockBusyError";
|
|
19405
|
-
}
|
|
19406
|
-
lockPath;
|
|
19407
|
-
holderPid;
|
|
19408
|
-
retryAfterSeconds;
|
|
19409
|
-
causeCode;
|
|
19410
|
-
};
|
|
19411
|
-
}
|
|
19412
|
-
});
|
|
19413
|
-
|
|
19414
19775
|
// src/memory-index-region.ts
|
|
19415
|
-
import { readFileSync as readFileSync22, existsSync as
|
|
19416
|
-
import { dirname as
|
|
19776
|
+
import { readFileSync as readFileSync22, existsSync as existsSync29 } from "fs";
|
|
19777
|
+
import { dirname as dirname16, join as join17 } from "path";
|
|
19417
19778
|
function parseRegion(content) {
|
|
19418
19779
|
const begins = countOccurrences(content, BEGIN_SENTINEL);
|
|
19419
19780
|
const ends = countOccurrences(content, END_SENTINEL);
|
|
@@ -19505,7 +19866,7 @@ function readRegionLines(content) {
|
|
|
19505
19866
|
return inner.split("\n").filter((l2) => l2.trim().length > 0);
|
|
19506
19867
|
}
|
|
19507
19868
|
function memoryIndexLockPath(memoryDir) {
|
|
19508
|
-
return
|
|
19869
|
+
return join17(dirname16(memoryDir), ".massu-memory-index.lock");
|
|
19509
19870
|
}
|
|
19510
19871
|
function withMemoryIndexLock(memoryDir, fn) {
|
|
19511
19872
|
return withFileLockSync(memoryIndexLockPath(memoryDir), fn, {
|
|
@@ -19517,7 +19878,7 @@ function withMemoryIndexLock(memoryDir, fn) {
|
|
|
19517
19878
|
});
|
|
19518
19879
|
}
|
|
19519
19880
|
function readMemoryIndex(indexPath) {
|
|
19520
|
-
if (!
|
|
19881
|
+
if (!existsSync29(indexPath)) return void 0;
|
|
19521
19882
|
try {
|
|
19522
19883
|
return readFileSync22(indexPath, "utf8");
|
|
19523
19884
|
} catch {
|
|
@@ -19590,8 +19951,8 @@ var init_memory_files_config = __esm({
|
|
|
19590
19951
|
});
|
|
19591
19952
|
|
|
19592
19953
|
// src/memory-renderer.ts
|
|
19593
|
-
import { existsSync as
|
|
19594
|
-
import { join as
|
|
19954
|
+
import { existsSync as existsSync30, readFileSync as readFileSync23 } from "fs";
|
|
19955
|
+
import { join as join18 } from "path";
|
|
19595
19956
|
import { homedir as homedir9 } from "os";
|
|
19596
19957
|
import { createHash as createHash8 } from "crypto";
|
|
19597
19958
|
function renderMemoryFiles(db, candidates, opts) {
|
|
@@ -19605,7 +19966,7 @@ function renderMemoryFiles(db, candidates, opts) {
|
|
|
19605
19966
|
const audit = opts.audit ?? (() => {
|
|
19606
19967
|
});
|
|
19607
19968
|
const { memoryDir } = opts;
|
|
19608
|
-
if (!
|
|
19969
|
+
if (!existsSync30(memoryDir)) {
|
|
19609
19970
|
return EMPTY("no_memory_dir");
|
|
19610
19971
|
}
|
|
19611
19972
|
try {
|
|
@@ -19673,7 +20034,7 @@ function renderLocked(db, candidates, o) {
|
|
|
19673
20034
|
refusals.push({ name: c2.name, reason: "tombstoned" });
|
|
19674
20035
|
continue;
|
|
19675
20036
|
}
|
|
19676
|
-
if (
|
|
20037
|
+
if (existsSync30(absPath)) {
|
|
19677
20038
|
const existing = readFileSync23(absPath, "utf8");
|
|
19678
20039
|
const fm = readMemoryFileFrontmatter(existing);
|
|
19679
20040
|
const storeRow = db.prepare(
|
|
@@ -19723,7 +20084,7 @@ function renderLocked(db, candidates, o) {
|
|
|
19723
20084
|
return EMPTY("backup_failed");
|
|
19724
20085
|
}
|
|
19725
20086
|
}
|
|
19726
|
-
const indexPath = o.indexPath ??
|
|
20087
|
+
const indexPath = o.indexPath ?? join18(memoryDir, "MEMORY.md");
|
|
19727
20088
|
const targets = [...planned.map((p19) => p19.absPath), indexPath];
|
|
19728
20089
|
const snapshot = takeSnapshots(targets);
|
|
19729
20090
|
let bytesWritten = 0;
|
|
@@ -19895,16 +20256,16 @@ var init_memory_renderer = __esm({
|
|
|
19895
20256
|
});
|
|
19896
20257
|
|
|
19897
20258
|
// src/commands/memory-render-cli.ts
|
|
19898
|
-
import { readdirSync as readdirSync15, readFileSync as readFileSync24, existsSync as
|
|
19899
|
-
import { join as
|
|
20259
|
+
import { readdirSync as readdirSync15, readFileSync as readFileSync24, existsSync as existsSync31, rmSync as rmSync7, statSync as statSync13 } from "fs";
|
|
20260
|
+
import { join as join19 } from "path";
|
|
19900
20261
|
import { homedir as homedir10 } from "os";
|
|
19901
20262
|
function memoryFiles(memoryDir) {
|
|
19902
|
-
if (!
|
|
20263
|
+
if (!existsSync31(memoryDir)) return [];
|
|
19903
20264
|
return readdirSync15(memoryDir).filter((f2) => {
|
|
19904
20265
|
if (!f2.endsWith(".md")) return false;
|
|
19905
20266
|
if (f2 === "MEMORY.md") return false;
|
|
19906
20267
|
try {
|
|
19907
|
-
return statSync13(
|
|
20268
|
+
return statSync13(join19(memoryDir, f2)).isFile();
|
|
19908
20269
|
} catch {
|
|
19909
20270
|
return false;
|
|
19910
20271
|
}
|
|
@@ -19915,7 +20276,7 @@ function massuAuthoredFiles(db, memoryDir, home = homedir10()) {
|
|
|
19915
20276
|
for (const f2 of memoryFiles(memoryDir)) {
|
|
19916
20277
|
let content;
|
|
19917
20278
|
try {
|
|
19918
|
-
content = readFileSync24(
|
|
20279
|
+
content = readFileSync24(join19(memoryDir, f2), "utf8");
|
|
19919
20280
|
} catch {
|
|
19920
20281
|
continue;
|
|
19921
20282
|
}
|
|
@@ -19943,7 +20304,7 @@ function memoryAdopt(db, memoryDir, opts = {}) {
|
|
|
19943
20304
|
for (const f2 of memoryFiles(memoryDir)) {
|
|
19944
20305
|
const row = db.prepare(`SELECT id FROM memory_files WHERE rel_path = ? COLLATE NOCASE`).get(f2);
|
|
19945
20306
|
if (row) continue;
|
|
19946
|
-
const content = readFileSync24(
|
|
20307
|
+
const content = readFileSync24(join19(memoryDir, f2), "utf8");
|
|
19947
20308
|
if (verifyAuthorship(stripFrontmatter(content), readMemoryFileFrontmatter(content), null, home)) {
|
|
19948
20309
|
candidates.push(f2);
|
|
19949
20310
|
}
|
|
@@ -19973,7 +20334,7 @@ function memoryAdopt(db, memoryDir, opts = {}) {
|
|
|
19973
20334
|
db.exec("BEGIN");
|
|
19974
20335
|
try {
|
|
19975
20336
|
for (const f2 of candidates) {
|
|
19976
|
-
const content = readFileSync24(
|
|
20337
|
+
const content = readFileSync24(join19(memoryDir, f2), "utf8");
|
|
19977
20338
|
const fm = readMemoryFileFrontmatter(content);
|
|
19978
20339
|
insert.run(
|
|
19979
20340
|
f2,
|
|
@@ -20021,7 +20382,7 @@ function memoryUnrender(db, memoryDir, opts = {}) {
|
|
|
20021
20382
|
for (const f2 of targets) {
|
|
20022
20383
|
tombstone(memoryDir, f2, "unrendered", now);
|
|
20023
20384
|
try {
|
|
20024
|
-
rmSync7(
|
|
20385
|
+
rmSync7(join19(memoryDir, f2));
|
|
20025
20386
|
} catch {
|
|
20026
20387
|
}
|
|
20027
20388
|
db.prepare(`UPDATE memory_files SET tombstoned_at_epoch = ? WHERE rel_path = ? COLLATE NOCASE`).run(
|
|
@@ -20029,7 +20390,7 @@ function memoryUnrender(db, memoryDir, opts = {}) {
|
|
|
20029
20390
|
f2
|
|
20030
20391
|
);
|
|
20031
20392
|
}
|
|
20032
|
-
const indexPath =
|
|
20393
|
+
const indexPath = join19(memoryDir, "MEMORY.md");
|
|
20033
20394
|
const pre = readMemoryIndex(indexPath);
|
|
20034
20395
|
if (pre !== void 0) {
|
|
20035
20396
|
try {
|
|
@@ -20104,16 +20465,16 @@ var init_memory_render_candidates = __esm({
|
|
|
20104
20465
|
});
|
|
20105
20466
|
|
|
20106
20467
|
// src/capability-advisor.ts
|
|
20107
|
-
import { existsSync as
|
|
20468
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync14, readFileSync as readFileSync25, writeFileSync as writeFileSync8 } from "fs";
|
|
20108
20469
|
import { homedir as homedir11 } from "os";
|
|
20109
|
-
import { dirname as
|
|
20470
|
+
import { dirname as dirname17, join as join20 } from "path";
|
|
20110
20471
|
function advisorStatePath(home = homedir11()) {
|
|
20111
|
-
return
|
|
20472
|
+
return join20(home, ".massu", "advisor-state.json");
|
|
20112
20473
|
}
|
|
20113
20474
|
function readAdvisorState(home) {
|
|
20114
20475
|
try {
|
|
20115
20476
|
const p19 = advisorStatePath(home);
|
|
20116
|
-
if (!
|
|
20477
|
+
if (!existsSync32(p19)) return {};
|
|
20117
20478
|
return JSON.parse(readFileSync25(p19, "utf-8"));
|
|
20118
20479
|
} catch {
|
|
20119
20480
|
return {};
|
|
@@ -20122,7 +20483,7 @@ function readAdvisorState(home) {
|
|
|
20122
20483
|
function writeAdvisorState(state, home) {
|
|
20123
20484
|
try {
|
|
20124
20485
|
const p19 = advisorStatePath(home);
|
|
20125
|
-
|
|
20486
|
+
mkdirSync14(dirname17(p19), { recursive: true });
|
|
20126
20487
|
writeFileSync8(p19, JSON.stringify(state, null, 2), { mode: 384 });
|
|
20127
20488
|
} catch {
|
|
20128
20489
|
}
|
|
@@ -20714,8 +21075,8 @@ var init_rule_candidate_hardened = __esm({
|
|
|
20714
21075
|
});
|
|
20715
21076
|
|
|
20716
21077
|
// src/rule-candidate-applier.ts
|
|
20717
|
-
import { readFileSync as readFileSync26, writeFileSync as writeFileSync9, existsSync as
|
|
20718
|
-
import { join as
|
|
21078
|
+
import { readFileSync as readFileSync26, writeFileSync as writeFileSync9, existsSync as existsSync33, unlinkSync as unlinkSync5, mkdirSync as mkdirSync15 } from "fs";
|
|
21079
|
+
import { join as join21, dirname as dirname18, isAbsolute as isAbsolute2 } from "path";
|
|
20719
21080
|
import { homedir as homedir12 } from "os";
|
|
20720
21081
|
import { createHash as createHash9 } from "crypto";
|
|
20721
21082
|
function isRuleDestination(value) {
|
|
@@ -20725,10 +21086,10 @@ function isTeamShareableDestination(destination) {
|
|
|
20725
21086
|
return TEAM_SHAREABLE_DESTINATIONS.includes(destination);
|
|
20726
21087
|
}
|
|
20727
21088
|
function resolveMemoryDir(projectRoot = getProjectRoot(), home = homedir12()) {
|
|
20728
|
-
return
|
|
21089
|
+
return join21(home, ".claude", "projects", encodeMemoryDirName(projectRoot), "memory");
|
|
20729
21090
|
}
|
|
20730
21091
|
function resolveMemoryIndexPath(projectRoot = getProjectRoot(), home = homedir12()) {
|
|
20731
|
-
return
|
|
21092
|
+
return join21(resolveMemoryDir(projectRoot, home), "MEMORY.md");
|
|
20732
21093
|
}
|
|
20733
21094
|
function deriveSlug2(prompt) {
|
|
20734
21095
|
return deriveSlug(prompt);
|
|
@@ -20747,17 +21108,17 @@ function writeIndexLineInRegion(indexPath, line) {
|
|
|
20747
21108
|
function correctionsMdTargets(slug, projectRoot, home) {
|
|
20748
21109
|
const dir = resolveMemoryDir(projectRoot, home);
|
|
20749
21110
|
return {
|
|
20750
|
-
correctionsMd:
|
|
20751
|
-
feedbackMd:
|
|
20752
|
-
memoryIndex:
|
|
21111
|
+
correctionsMd: join21(dir, "corrections.md"),
|
|
21112
|
+
feedbackMd: join21(dir, `feedback_${slug}.md`),
|
|
21113
|
+
memoryIndex: join21(dir, "MEMORY.md")
|
|
20753
21114
|
};
|
|
20754
21115
|
}
|
|
20755
21116
|
function readCandidate(candidateId, projectRoot = getProjectRoot()) {
|
|
20756
21117
|
if (!CANDIDATE_ID_PATTERN.test(candidateId)) {
|
|
20757
21118
|
throw new InvalidCandidateIdError(candidateId);
|
|
20758
21119
|
}
|
|
20759
|
-
const path =
|
|
20760
|
-
if (!
|
|
21120
|
+
const path = join21(projectRoot, ".massu", "rule-candidates", `${candidateId}.json`);
|
|
21121
|
+
if (!existsSync33(path)) throw new CandidateNotFoundError(path);
|
|
20761
21122
|
const raw = readFileSync26(path, "utf-8");
|
|
20762
21123
|
let parsed;
|
|
20763
21124
|
try {
|
|
@@ -20869,7 +21230,7 @@ async function applyRuleCandidate(db, opts) {
|
|
|
20869
21230
|
throw new InvalidSlugError(slug);
|
|
20870
21231
|
}
|
|
20871
21232
|
const memoryIndex = resolveMemoryIndexPath(projectRoot, home);
|
|
20872
|
-
if (!
|
|
21233
|
+
if (!existsSync33(memoryIndex)) {
|
|
20873
21234
|
throw new MemoryIndexMissingError(memoryIndex);
|
|
20874
21235
|
}
|
|
20875
21236
|
let destTargets;
|
|
@@ -20878,7 +21239,7 @@ async function applyRuleCandidate(db, opts) {
|
|
|
20878
21239
|
} catch (err) {
|
|
20879
21240
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
20880
21241
|
}
|
|
20881
|
-
const candidatePath =
|
|
21242
|
+
const candidatePath = join21(projectRoot, ".massu", "rule-candidates", `${opts.candidateId}.json`);
|
|
20882
21243
|
const snapshot = takeSnapshots([
|
|
20883
21244
|
...destTargets.paths,
|
|
20884
21245
|
memoryIndex
|
|
@@ -20925,7 +21286,7 @@ async function applyRuleCandidate(db, opts) {
|
|
|
20925
21286
|
throw err;
|
|
20926
21287
|
}
|
|
20927
21288
|
}
|
|
20928
|
-
if (
|
|
21289
|
+
if (existsSync33(candidatePath)) unlinkSync5(candidatePath);
|
|
20929
21290
|
let teamShared = false;
|
|
20930
21291
|
const localOrigin = candidate.provenance?.origin !== "team" && candidate.provenance?.origin !== "pack";
|
|
20931
21292
|
const entitledTeam = entitledForTeamSharedPromotion(ent.currentTier);
|
|
@@ -20970,15 +21331,15 @@ function computeDestinationTargets(opts, candidate, slug, projectRoot, home) {
|
|
|
20970
21331
|
if (opts.patternScannerCheckNumber == null) {
|
|
20971
21332
|
throw new DestinationConfigError("pattern-scanner destination requires patternScannerCheckNumber");
|
|
20972
21333
|
}
|
|
20973
|
-
const scannerPath =
|
|
20974
|
-
const driftGuardPath =
|
|
21334
|
+
const scannerPath = join21(projectRoot, "scripts", "massu-pattern-scanner.sh");
|
|
21335
|
+
const driftGuardPath = join21(projectRoot, "packages", "core", "src", "__tests__", `${slug}-drift-guard.test.ts`);
|
|
20975
21336
|
return { primary: scannerPath, paths: [scannerPath, driftGuardPath] };
|
|
20976
21337
|
}
|
|
20977
21338
|
if (opts.destination === "claude-md-cr") {
|
|
20978
21339
|
if (opts.claudeMdCrNumber == null) {
|
|
20979
21340
|
throw new DestinationConfigError("claude-md-cr destination requires claudeMdCrNumber");
|
|
20980
21341
|
}
|
|
20981
|
-
const claudePath =
|
|
21342
|
+
const claudePath = join21(projectRoot, ".claude", "CLAUDE.md");
|
|
20982
21343
|
return { primary: claudePath, paths: [claudePath] };
|
|
20983
21344
|
}
|
|
20984
21345
|
if (opts.destination === "custom-destination") {
|
|
@@ -20986,7 +21347,7 @@ function computeDestinationTargets(opts, candidate, slug, projectRoot, home) {
|
|
|
20986
21347
|
throw new DestinationConfigError("custom-destination requires customDestination config entry");
|
|
20987
21348
|
}
|
|
20988
21349
|
const raw = opts.customDestination.path;
|
|
20989
|
-
const absolute = isAbsolute2(raw) ? raw :
|
|
21350
|
+
const absolute = isAbsolute2(raw) ? raw : join21(projectRoot, raw);
|
|
20990
21351
|
try {
|
|
20991
21352
|
assertContainedIn(projectRoot, absolute, { allowNested: true });
|
|
20992
21353
|
} catch (err) {
|
|
@@ -21026,8 +21387,8 @@ function writeCorrectionsMd(opts, candidate, slug, targets, auditRowId) {
|
|
|
21026
21387
|
audit_log_id: auditRowId
|
|
21027
21388
|
}
|
|
21028
21389
|
});
|
|
21029
|
-
if (!
|
|
21030
|
-
const correctionsPre =
|
|
21390
|
+
if (!existsSync33(dirname18(correctionsMd))) mkdirSync15(dirname18(correctionsMd), { recursive: true });
|
|
21391
|
+
const correctionsPre = existsSync33(correctionsMd) ? readFileSync26(correctionsMd, "utf-8") : "# Corrections\n";
|
|
21031
21392
|
writeFileSync9(correctionsMd, correctionsPre + correctionsEntry, "utf-8");
|
|
21032
21393
|
const body = `${opts.draftText}
|
|
21033
21394
|
`;
|
|
@@ -21053,7 +21414,7 @@ function writeCorrectionsMd(opts, candidate, slug, targets, auditRowId) {
|
|
|
21053
21414
|
function writePatternScanner(opts, candidate, slug, targets) {
|
|
21054
21415
|
const [scannerPath, driftGuardPath] = targets.paths;
|
|
21055
21416
|
const checkNumber = opts.patternScannerCheckNumber;
|
|
21056
|
-
const scannerPre =
|
|
21417
|
+
const scannerPre = existsSync33(scannerPath) ? readFileSync26(scannerPath, "utf-8") : "";
|
|
21057
21418
|
if (scannerPre.includes(`${SCANNER_AUTOGEN_MARKER_PREFIX} ${candidate.prompt_hash}`)) {
|
|
21058
21419
|
throw new DestinationConfigError(
|
|
21059
21420
|
`scanner already contains a check auto-generated for prompt_hash ${candidate.prompt_hash}`
|
|
@@ -21089,13 +21450,13 @@ function writePatternScanner(opts, candidate, slug, targets) {
|
|
|
21089
21450
|
`});`,
|
|
21090
21451
|
""
|
|
21091
21452
|
].join("\n");
|
|
21092
|
-
if (!
|
|
21453
|
+
if (!existsSync33(dirname18(driftGuardPath))) mkdirSync15(dirname18(driftGuardPath), { recursive: true });
|
|
21093
21454
|
writeFileSync9(driftGuardPath, driftGuard, "utf-8");
|
|
21094
21455
|
}
|
|
21095
21456
|
function writeClaudeMdCr(opts, candidate, slug, targets) {
|
|
21096
21457
|
const [claudePath] = targets.paths;
|
|
21097
21458
|
const crNumber = opts.claudeMdCrNumber;
|
|
21098
|
-
const pre =
|
|
21459
|
+
const pre = existsSync33(claudePath) ? readFileSync26(claudePath, "utf-8") : "";
|
|
21099
21460
|
if (new RegExp(`\\bCR-${crNumber}\\b`).test(pre)) {
|
|
21100
21461
|
throw new DestinationConfigError(`CLAUDE.md already contains a CR-${crNumber} entry`);
|
|
21101
21462
|
}
|
|
@@ -21137,8 +21498,8 @@ function writeCustomDestination(opts, candidate, slug, targets) {
|
|
|
21137
21498
|
destination_name: entry.name
|
|
21138
21499
|
};
|
|
21139
21500
|
const rendered = renderTemplate2(entry.template, vars);
|
|
21140
|
-
if (!
|
|
21141
|
-
const pre =
|
|
21501
|
+
if (!existsSync33(dirname18(destPath))) mkdirSync15(dirname18(destPath), { recursive: true });
|
|
21502
|
+
const pre = existsSync33(destPath) ? readFileSync26(destPath, "utf-8") : "";
|
|
21142
21503
|
const sep3 = pre && !pre.endsWith("\n") ? "\n" : "";
|
|
21143
21504
|
writeFileSync9(destPath, pre + sep3 + rendered + (rendered.endsWith("\n") ? "" : "\n"), "utf-8");
|
|
21144
21505
|
}
|
|
@@ -21148,7 +21509,7 @@ function dismissRuleCandidate(db, opts) {
|
|
|
21148
21509
|
}
|
|
21149
21510
|
const projectRoot = opts.projectRoot ?? getProjectRoot();
|
|
21150
21511
|
const candidate = readCandidate(opts.candidateId, projectRoot);
|
|
21151
|
-
const candidatePath =
|
|
21512
|
+
const candidatePath = join21(projectRoot, ".massu", "rule-candidates", `${opts.candidateId}.json`);
|
|
21152
21513
|
db.exec("BEGIN");
|
|
21153
21514
|
let auditRowId;
|
|
21154
21515
|
let blacklistUpdates = 0;
|
|
@@ -21190,9 +21551,9 @@ function dismissRuleCandidate(db, opts) {
|
|
|
21190
21551
|
};
|
|
21191
21552
|
}
|
|
21192
21553
|
try {
|
|
21193
|
-
const dismissedLog =
|
|
21194
|
-
if (!
|
|
21195
|
-
const pre =
|
|
21554
|
+
const dismissedLog = join21(projectRoot, ".massu", "rule-candidates", ".dismissed.jsonl");
|
|
21555
|
+
if (!existsSync33(dirname18(dismissedLog))) mkdirSync15(dirname18(dismissedLog), { recursive: true });
|
|
21556
|
+
const pre = existsSync33(dismissedLog) ? readFileSync26(dismissedLog, "utf-8") : "";
|
|
21196
21557
|
const sep3 = pre && !pre.endsWith("\n") ? "\n" : "";
|
|
21197
21558
|
writeFileSync9(dismissedLog, pre + sep3 + JSON.stringify({
|
|
21198
21559
|
id: opts.candidateId,
|
|
@@ -21203,7 +21564,7 @@ function dismissRuleCandidate(db, opts) {
|
|
|
21203
21564
|
audit_log_id: auditRowId,
|
|
21204
21565
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
21205
21566
|
}) + "\n", "utf-8");
|
|
21206
|
-
if (
|
|
21567
|
+
if (existsSync33(candidatePath)) unlinkSync5(candidatePath);
|
|
21207
21568
|
} catch (err) {
|
|
21208
21569
|
appendFailureLog(projectRoot, {
|
|
21209
21570
|
candidate_id: opts.candidateId,
|
|
@@ -21221,10 +21582,10 @@ function dismissRuleCandidate(db, opts) {
|
|
|
21221
21582
|
}
|
|
21222
21583
|
function appendFailureLog(projectRoot, entry) {
|
|
21223
21584
|
try {
|
|
21224
|
-
const dir =
|
|
21225
|
-
if (!
|
|
21226
|
-
const logPath =
|
|
21227
|
-
const pre =
|
|
21585
|
+
const dir = join21(projectRoot, ".massu", "rule-candidates");
|
|
21586
|
+
if (!existsSync33(dir)) mkdirSync15(dir, { recursive: true });
|
|
21587
|
+
const logPath = join21(dir, ".failures.jsonl");
|
|
21588
|
+
const pre = existsSync33(logPath) ? readFileSync26(logPath, "utf-8") : "";
|
|
21228
21589
|
const sep3 = pre && !pre.endsWith("\n") ? "\n" : "";
|
|
21229
21590
|
writeFileSync9(logPath, pre + sep3 + JSON.stringify(entry) + "\n", "utf-8");
|
|
21230
21591
|
} catch {
|
|
@@ -21283,7 +21644,7 @@ var init_rule_candidate_applier = __esm({
|
|
|
21283
21644
|
];
|
|
21284
21645
|
MemoryIndexMissingError = class extends Error {
|
|
21285
21646
|
constructor(path) {
|
|
21286
|
-
super(`MEMORY.md missing at ${path} \u2014 v0.2 requires the canonical memory index. Create it via \`mkdir -p ${
|
|
21647
|
+
super(`MEMORY.md missing at ${path} \u2014 v0.2 requires the canonical memory index. Create it via \`mkdir -p ${dirname18(path)} && echo '# Memory Index' > ${path}\` or run \`/massu-bearings\``);
|
|
21287
21648
|
this.path = path;
|
|
21288
21649
|
this.name = "MemoryIndexMissingError";
|
|
21289
21650
|
}
|
|
@@ -21307,7 +21668,7 @@ var init_rule_candidate_applier = __esm({
|
|
|
21307
21668
|
});
|
|
21308
21669
|
|
|
21309
21670
|
// src/rule-candidate-preview.ts
|
|
21310
|
-
import { readFileSync as readFileSync27, writeFileSync as writeFileSync10, existsSync as
|
|
21671
|
+
import { readFileSync as readFileSync27, writeFileSync as writeFileSync10, existsSync as existsSync34 } from "node:fs";
|
|
21311
21672
|
function renderHardenedPreview(destination, body) {
|
|
21312
21673
|
const rendered = String(body ?? "");
|
|
21313
21674
|
const riskFindings = RISK_PATTERNS.filter((p19) => p19.re.test(rendered)).map((p19) => p19.label);
|
|
@@ -21335,7 +21696,7 @@ function validateReviewAttestation(att) {
|
|
|
21335
21696
|
};
|
|
21336
21697
|
}
|
|
21337
21698
|
function recordHardenedReviewAttestation(sidecarPath2, attestation) {
|
|
21338
|
-
if (!
|
|
21699
|
+
if (!existsSync34(sidecarPath2)) {
|
|
21339
21700
|
throw new Error(`candidate sidecar not found: ${sidecarPath2}`);
|
|
21340
21701
|
}
|
|
21341
21702
|
const validated = validateReviewAttestation(attestation);
|
|
@@ -21447,7 +21808,7 @@ var init_rule_candidate_renderer = __esm({
|
|
|
21447
21808
|
});
|
|
21448
21809
|
|
|
21449
21810
|
// src/rule-promotion-effectiveness.ts
|
|
21450
|
-
import { readFileSync as readFileSync28, existsSync as
|
|
21811
|
+
import { readFileSync as readFileSync28, existsSync as existsSync35 } from "fs";
|
|
21451
21812
|
function evaluateCr53Effectiveness(inputs) {
|
|
21452
21813
|
const { db, failureLogPath, knownLimitations } = inputs;
|
|
21453
21814
|
const allowlist = new Set(knownLimitations.map((k3) => k3.promptHash));
|
|
@@ -21478,7 +21839,7 @@ function evaluateCr53Effectiveness(inputs) {
|
|
|
21478
21839
|
});
|
|
21479
21840
|
}
|
|
21480
21841
|
const failureLogViolations = [];
|
|
21481
|
-
if (
|
|
21842
|
+
if (existsSync35(failureLogPath)) {
|
|
21482
21843
|
try {
|
|
21483
21844
|
const raw = readFileSync28(failureLogPath, "utf-8");
|
|
21484
21845
|
const cutoff = Date.now() - SEVEN_DAYS_MS;
|
|
@@ -21657,8 +22018,8 @@ var init_rule_pack_schema = __esm({
|
|
|
21657
22018
|
});
|
|
21658
22019
|
|
|
21659
22020
|
// src/rule-pack-sync.ts
|
|
21660
|
-
import { existsSync as
|
|
21661
|
-
import { join as
|
|
22021
|
+
import { existsSync as existsSync36, writeFileSync as writeFileSync11, mkdirSync as mkdirSync16 } from "fs";
|
|
22022
|
+
import { join as join22, dirname as dirname19 } from "path";
|
|
21662
22023
|
async function pullInstalledPackRules(db, opts = {}) {
|
|
21663
22024
|
const config = getConfig();
|
|
21664
22025
|
const cloud = config.cloud;
|
|
@@ -21721,7 +22082,7 @@ async function pullInstalledPackRules(db, opts = {}) {
|
|
|
21721
22082
|
result.pulled += 1;
|
|
21722
22083
|
const promptHash = packRulePromptHash(pack.slug, rule);
|
|
21723
22084
|
const candidatePath = sidecarPath(projectRoot, promptHash);
|
|
21724
|
-
if (
|
|
22085
|
+
if (existsSync36(candidatePath) || alreadyApplied(db, promptHash)) {
|
|
21725
22086
|
result.skipped += 1;
|
|
21726
22087
|
continue;
|
|
21727
22088
|
}
|
|
@@ -21759,7 +22120,7 @@ function packRulePromptHash(slug, rule) {
|
|
|
21759
22120
|
return createHash15("sha256").update(body, "utf-8").digest("hex").slice(0, 16);
|
|
21760
22121
|
}
|
|
21761
22122
|
function sidecarPath(projectRoot, promptHash) {
|
|
21762
|
-
return
|
|
22123
|
+
return join22(projectRoot, ".massu", "rule-candidates", `${promptHash}.json`);
|
|
21763
22124
|
}
|
|
21764
22125
|
function alreadyApplied(db, promptHash) {
|
|
21765
22126
|
try {
|
|
@@ -21808,8 +22169,8 @@ function materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack
|
|
|
21808
22169
|
destination: rule.destination,
|
|
21809
22170
|
draft_text: draftText
|
|
21810
22171
|
};
|
|
21811
|
-
const dir =
|
|
21812
|
-
if (!
|
|
22172
|
+
const dir = dirname19(candidatePath);
|
|
22173
|
+
if (!existsSync36(dir)) mkdirSync16(dir, { recursive: true });
|
|
21813
22174
|
writeFileSync11(candidatePath, JSON.stringify(sidecar, null, 2), "utf-8");
|
|
21814
22175
|
}
|
|
21815
22176
|
function emitDropTelemetry(db, eventType, data) {
|
|
@@ -21852,7 +22213,7 @@ var rule_exports = {};
|
|
|
21852
22213
|
__export(rule_exports, {
|
|
21853
22214
|
handleRuleSubcommand: () => handleRuleSubcommand
|
|
21854
22215
|
});
|
|
21855
|
-
import { join as
|
|
22216
|
+
import { join as join23 } from "node:path";
|
|
21856
22217
|
function flagValue2(args2, flag) {
|
|
21857
22218
|
const i = args2.indexOf(flag);
|
|
21858
22219
|
if (i === -1 || i === args2.length - 1) return void 0;
|
|
@@ -21909,7 +22270,7 @@ async function handleRuleSubcommand(args2) {
|
|
|
21909
22270
|
case "effectiveness": {
|
|
21910
22271
|
const db = getMemoryDb();
|
|
21911
22272
|
try {
|
|
21912
|
-
const failureLogPath =
|
|
22273
|
+
const failureLogPath = join23(
|
|
21913
22274
|
getProjectRoot(),
|
|
21914
22275
|
".massu",
|
|
21915
22276
|
"rule-candidates",
|
|
@@ -22032,7 +22393,7 @@ ${"-".repeat(60)}
|
|
|
22032
22393
|
reviewed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22033
22394
|
note: flagValue2(args2, "--attest-note") ?? ""
|
|
22034
22395
|
});
|
|
22035
|
-
const sidecar =
|
|
22396
|
+
const sidecar = join23(getProjectRoot(), ".massu", "rule-candidates", `${candidateId}.json`);
|
|
22036
22397
|
recordHardenedReviewAttestation(sidecar, attestation);
|
|
22037
22398
|
process.stdout.write(`Attestation recorded by '${operator}'. This candidate may now be approved.
|
|
22038
22399
|
`);
|
|
@@ -22680,8 +23041,8 @@ var init_memory_supersede = __esm({
|
|
|
22680
23041
|
|
|
22681
23042
|
// src/memory-consolidate.ts
|
|
22682
23043
|
import { createHash as createHash10 } from "crypto";
|
|
22683
|
-
import { existsSync as
|
|
22684
|
-
import { join as
|
|
23044
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync17, writeFileSync as writeFileSync12 } from "fs";
|
|
23045
|
+
import { join as join24 } from "path";
|
|
22685
23046
|
function emptyResult() {
|
|
22686
23047
|
return {
|
|
22687
23048
|
deduped: 0,
|
|
@@ -22843,7 +23204,7 @@ ${r2.detail ?? ""}`.trim());
|
|
|
22843
23204
|
}
|
|
22844
23205
|
clusters.push(cluster);
|
|
22845
23206
|
}
|
|
22846
|
-
const candidateDir =
|
|
23207
|
+
const candidateDir = join24(projectRoot, ".massu", "rule-candidates");
|
|
22847
23208
|
let promoted = 0;
|
|
22848
23209
|
for (const cluster of clusters) {
|
|
22849
23210
|
const occurrences = cluster.reduce((n, c2) => n + (c2.recurrence_count || 1), 0);
|
|
@@ -22856,13 +23217,13 @@ ${r2.detail ?? ""}`.trim());
|
|
|
22856
23217
|
${representative.detail}` : ""}`;
|
|
22857
23218
|
const clusterKey = cluster.map((c2) => c2.title.toLowerCase().replace(/\s+/g, " ").trim()).sort().join("|");
|
|
22858
23219
|
const promptHash = createHash10("sha256").update(clusterKey).digest("hex").slice(0, 16);
|
|
22859
|
-
const candidatePath =
|
|
22860
|
-
if (
|
|
23220
|
+
const candidatePath = join24(candidateDir, `${promptHash}.json`);
|
|
23221
|
+
if (existsSync37(candidatePath)) continue;
|
|
22861
23222
|
if (dryRun) {
|
|
22862
23223
|
promoted++;
|
|
22863
23224
|
continue;
|
|
22864
23225
|
}
|
|
22865
|
-
|
|
23226
|
+
mkdirSync17(candidateDir, { recursive: true });
|
|
22866
23227
|
writeFileSync12(
|
|
22867
23228
|
candidatePath,
|
|
22868
23229
|
JSON.stringify(
|
|
@@ -23049,7 +23410,23 @@ async function runConsolidateCommand(args2 = []) {
|
|
|
23049
23410
|
return { exitCode: 1 };
|
|
23050
23411
|
}
|
|
23051
23412
|
const cfg = resolveConsolidationConfig();
|
|
23052
|
-
|
|
23413
|
+
let db;
|
|
23414
|
+
try {
|
|
23415
|
+
db = getMemoryDb();
|
|
23416
|
+
} catch (err) {
|
|
23417
|
+
if (err instanceof MemoryEngineUnusableError) {
|
|
23418
|
+
if (json) {
|
|
23419
|
+
process.stdout.write(
|
|
23420
|
+
JSON.stringify({ error: "memory-engine-unusable", reason: err.reason, remedy: err.remedy }) + "\n"
|
|
23421
|
+
);
|
|
23422
|
+
} else {
|
|
23423
|
+
process.stderr.write(`massu consolidate: the memory engine is unavailable \u2014 ${err.remedy}
|
|
23424
|
+
`);
|
|
23425
|
+
}
|
|
23426
|
+
return { exitCode: 1 };
|
|
23427
|
+
}
|
|
23428
|
+
throw err;
|
|
23429
|
+
}
|
|
23053
23430
|
try {
|
|
23054
23431
|
const result = await runConsolidation(db, {
|
|
23055
23432
|
config: cfg,
|
|
@@ -23125,6 +23502,7 @@ var init_consolidate = __esm({
|
|
|
23125
23502
|
init_memory_db();
|
|
23126
23503
|
init_memory_consolidate();
|
|
23127
23504
|
init_consolidation_config();
|
|
23505
|
+
init_sqlite_loader();
|
|
23128
23506
|
}
|
|
23129
23507
|
});
|
|
23130
23508
|
|
|
@@ -23133,7 +23511,7 @@ var show_template_exports = {};
|
|
|
23133
23511
|
__export(show_template_exports, {
|
|
23134
23512
|
runShowTemplate: () => runShowTemplate
|
|
23135
23513
|
});
|
|
23136
|
-
import { existsSync as
|
|
23514
|
+
import { existsSync as existsSync38, readFileSync as readFileSync29 } from "fs";
|
|
23137
23515
|
import { resolve as resolve17 } from "path";
|
|
23138
23516
|
function normalizeBaseName(input) {
|
|
23139
23517
|
return input.endsWith(".md") ? input.slice(0, -".md".length) : input;
|
|
@@ -23163,7 +23541,7 @@ async function runShowTemplate(args2) {
|
|
|
23163
23541
|
}
|
|
23164
23542
|
const suffix = choice.kind === "hit" ? choice.suffix : "";
|
|
23165
23543
|
const file = suffix === "" ? resolve17(sourceDir, `${baseName}.md`) : resolve17(sourceDir, `${baseName}${suffix}.md`);
|
|
23166
|
-
if (!
|
|
23544
|
+
if (!existsSync38(file)) {
|
|
23167
23545
|
process.stderr.write(`massu: resolved template "${file}" no longer exists
|
|
23168
23546
|
`);
|
|
23169
23547
|
process.exit(1);
|
|
@@ -23264,7 +23642,7 @@ __export(config_refresh_exports, {
|
|
|
23264
23642
|
mergeRefresh: () => mergeRefresh,
|
|
23265
23643
|
runConfigRefresh: () => runConfigRefresh
|
|
23266
23644
|
});
|
|
23267
|
-
import { existsSync as
|
|
23645
|
+
import { existsSync as existsSync39, readFileSync as readFileSync30, rmSync as rmSync8 } from "fs";
|
|
23268
23646
|
import { resolve as resolve19 } from "path";
|
|
23269
23647
|
import { parse as parseYaml5 } from "yaml";
|
|
23270
23648
|
function flatten(obj, prefix3 = "") {
|
|
@@ -23396,7 +23774,7 @@ async function runConfigRefresh(opts = {}) {
|
|
|
23396
23774
|
const configPath = resolve19(cwd, "massu.config.yaml");
|
|
23397
23775
|
const log = opts.silent ? () => {
|
|
23398
23776
|
} : (s) => process.stdout.write(s);
|
|
23399
|
-
if (!
|
|
23777
|
+
if (!existsSync39(configPath)) {
|
|
23400
23778
|
const message = "massu.config.yaml not found. Run: npx massu init";
|
|
23401
23779
|
if (!opts.silent) process.stderr.write(message + "\n");
|
|
23402
23780
|
return { exitCode: 1, applied: false, dryRun: !!opts.dryRun, diff: [], message };
|
|
@@ -23477,7 +23855,7 @@ async function runConfigRefresh(opts = {}) {
|
|
|
23477
23855
|
const stackResolved = installResult.totalInstalled > 0 || installResult.totalUpdated > 0;
|
|
23478
23856
|
if (stackResolved) {
|
|
23479
23857
|
const placeholderPath = resolve19(installResult.claudeDir, "commands", "_massu-needs-stack.md");
|
|
23480
|
-
if (
|
|
23858
|
+
if (existsSync39(placeholderPath)) {
|
|
23481
23859
|
try {
|
|
23482
23860
|
rmSync8(placeholderPath, { force: true });
|
|
23483
23861
|
log("Removed _massu-needs-stack.md (stack now declared).\n");
|
|
@@ -23527,7 +23905,7 @@ var init_config_refresh = __esm({
|
|
|
23527
23905
|
});
|
|
23528
23906
|
|
|
23529
23907
|
// src/lib/pidLiveness.ts
|
|
23530
|
-
import { spawnSync } from "child_process";
|
|
23908
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
23531
23909
|
function isPidAlive(pid, opts = {}) {
|
|
23532
23910
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
23533
23911
|
const platform = opts.platformOverride ?? process.platform;
|
|
@@ -23552,7 +23930,7 @@ function checkPosix(pid, killOverride) {
|
|
|
23552
23930
|
}
|
|
23553
23931
|
function checkWindows(pid) {
|
|
23554
23932
|
try {
|
|
23555
|
-
const res =
|
|
23933
|
+
const res = spawnSync2("tasklist", ["/FI", `PID eq ${pid}`, "/NH"], {
|
|
23556
23934
|
encoding: "utf-8",
|
|
23557
23935
|
windowsHide: true
|
|
23558
23936
|
});
|
|
@@ -23570,9 +23948,9 @@ var init_pidLiveness = __esm({
|
|
|
23570
23948
|
});
|
|
23571
23949
|
|
|
23572
23950
|
// src/lib/gitToplevel.ts
|
|
23573
|
-
import { spawnSync as
|
|
23951
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
23574
23952
|
function gitToplevel(cwd) {
|
|
23575
|
-
const res =
|
|
23953
|
+
const res = spawnSync3("git", ["rev-parse", "--show-toplevel"], {
|
|
23576
23954
|
cwd,
|
|
23577
23955
|
encoding: "utf-8"
|
|
23578
23956
|
});
|
|
@@ -23586,12 +23964,12 @@ var init_gitToplevel = __esm({
|
|
|
23586
23964
|
});
|
|
23587
23965
|
|
|
23588
23966
|
// src/watch/lockfile-detector.ts
|
|
23589
|
-
import { existsSync as
|
|
23967
|
+
import { existsSync as existsSync40, statSync as statSync14 } from "fs";
|
|
23590
23968
|
import { resolve as resolve20 } from "path";
|
|
23591
23969
|
function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WINDOW_MS) {
|
|
23592
23970
|
for (const lf of KNOWN_LOCKFILES) {
|
|
23593
23971
|
const p19 = resolve20(projectRoot, lf);
|
|
23594
|
-
if (!
|
|
23972
|
+
if (!existsSync40(p19)) continue;
|
|
23595
23973
|
try {
|
|
23596
23974
|
const stat = statSync14(p19);
|
|
23597
23975
|
const delta = now - stat.mtimeMs;
|
|
@@ -23604,7 +23982,7 @@ function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WIN
|
|
|
23604
23982
|
function gitMidOperation(projectRoot) {
|
|
23605
23983
|
const sentinels = ["MERGE_HEAD", "REBASE_HEAD", "CHERRY_PICK_HEAD", "rebase-apply", "rebase-merge"];
|
|
23606
23984
|
for (const s of sentinels) {
|
|
23607
|
-
if (
|
|
23985
|
+
if (existsSync40(resolve20(projectRoot, ".git", s))) return true;
|
|
23608
23986
|
}
|
|
23609
23987
|
return false;
|
|
23610
23988
|
}
|
|
@@ -23803,8 +24181,8 @@ var init_paths = __esm({
|
|
|
23803
24181
|
});
|
|
23804
24182
|
|
|
23805
24183
|
// src/watch/state.ts
|
|
23806
|
-
import { closeSync as closeSync4, existsSync as
|
|
23807
|
-
import { dirname as
|
|
24184
|
+
import { closeSync as closeSync4, existsSync as existsSync41, fsyncSync as fsyncSync4, mkdirSync as mkdirSync18, openSync as openSync4, readFileSync as readFileSync31, renameSync as renameSync5, rmSync as rmSync9, writeFileSync as writeFileSync13, writeSync as writeSync3 } from "fs";
|
|
24185
|
+
import { dirname as dirname20, resolve as resolve21 } from "path";
|
|
23808
24186
|
function watchStatePath(projectRoot) {
|
|
23809
24187
|
return resolve21(projectRoot, ".massu", "watch-state.json");
|
|
23810
24188
|
}
|
|
@@ -23813,7 +24191,7 @@ function backupStatePath(projectRoot) {
|
|
|
23813
24191
|
}
|
|
23814
24192
|
function readState(projectRoot) {
|
|
23815
24193
|
const path = watchStatePath(projectRoot);
|
|
23816
|
-
if (!
|
|
24194
|
+
if (!existsSync41(path)) return { ...DEFAULT_STATE };
|
|
23817
24195
|
const content = readFileSync31(path, "utf-8");
|
|
23818
24196
|
let raw;
|
|
23819
24197
|
try {
|
|
@@ -23856,14 +24234,14 @@ function readState(projectRoot) {
|
|
|
23856
24234
|
}
|
|
23857
24235
|
function archiveCorrupt(projectRoot, content) {
|
|
23858
24236
|
const bak = backupStatePath(projectRoot);
|
|
23859
|
-
|
|
24237
|
+
mkdirSync18(dirname20(bak), { recursive: true });
|
|
23860
24238
|
writeFileSync13(bak, content, "utf-8");
|
|
23861
24239
|
}
|
|
23862
24240
|
function writeStateAtomic(projectRoot, state) {
|
|
23863
24241
|
const path = watchStatePath(projectRoot);
|
|
23864
24242
|
writeStateAtomicCounter = writeStateAtomicCounter + 1 >>> 0;
|
|
23865
24243
|
const tmp = `${path}.${process.pid}.${writeStateAtomicCounter}.tmp`;
|
|
23866
|
-
|
|
24244
|
+
mkdirSync18(dirname20(path), { recursive: true });
|
|
23867
24245
|
let renamed = false;
|
|
23868
24246
|
try {
|
|
23869
24247
|
const fd = openSync4(tmp, "w");
|
|
@@ -23877,7 +24255,7 @@ function writeStateAtomic(projectRoot, state) {
|
|
|
23877
24255
|
renameSync5(tmp, path);
|
|
23878
24256
|
renamed = true;
|
|
23879
24257
|
} finally {
|
|
23880
|
-
if (!renamed &&
|
|
24258
|
+
if (!renamed && existsSync41(tmp)) {
|
|
23881
24259
|
try {
|
|
23882
24260
|
rmSync9(tmp, { force: true });
|
|
23883
24261
|
} catch {
|
|
@@ -24158,9 +24536,9 @@ __export(watch_exports, {
|
|
|
24158
24536
|
runOnQuiescent: () => runOnQuiescent,
|
|
24159
24537
|
runWatch: () => runWatch
|
|
24160
24538
|
});
|
|
24161
|
-
import { spawnSync as
|
|
24162
|
-
import { basename as basename8, dirname as
|
|
24163
|
-
import { appendFileSync as
|
|
24539
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
24540
|
+
import { basename as basename8, dirname as dirname21, resolve as resolve22 } from "path";
|
|
24541
|
+
import { appendFileSync as appendFileSync4, existsSync as existsSync42, mkdirSync as mkdirSync19, readFileSync as readFileSync32 } from "fs";
|
|
24164
24542
|
function parseFlags(args2) {
|
|
24165
24543
|
const out = {
|
|
24166
24544
|
foreground: false,
|
|
@@ -24186,8 +24564,8 @@ function parseFlags(args2) {
|
|
|
24186
24564
|
function findClaudeBg() {
|
|
24187
24565
|
const home = process.env.HOME ?? "";
|
|
24188
24566
|
const fixed = home ? resolve22(home, ".claude", "bin", "claude-bg") : null;
|
|
24189
|
-
if (fixed &&
|
|
24190
|
-
const which =
|
|
24567
|
+
if (fixed && existsSync42(fixed)) return fixed;
|
|
24568
|
+
const which = spawnSync4("which", ["claude-bg"], { encoding: "utf-8" });
|
|
24191
24569
|
if (which.status === 0 && which.stdout) {
|
|
24192
24570
|
const p19 = which.stdout.trim();
|
|
24193
24571
|
if (p19) return p19;
|
|
@@ -24229,7 +24607,7 @@ async function runWatch(args2) {
|
|
|
24229
24607
|
return { exitCode: 1, message: msg };
|
|
24230
24608
|
}
|
|
24231
24609
|
const name = watchName(root);
|
|
24232
|
-
const res =
|
|
24610
|
+
const res = spawnSync4(claudeBg, [
|
|
24233
24611
|
"start",
|
|
24234
24612
|
"--name",
|
|
24235
24613
|
name,
|
|
@@ -24247,7 +24625,7 @@ async function runWatch(args2) {
|
|
|
24247
24625
|
}
|
|
24248
24626
|
function runStatus(root) {
|
|
24249
24627
|
const path = watchStatePath(root);
|
|
24250
|
-
if (!
|
|
24628
|
+
if (!existsSync42(path)) {
|
|
24251
24629
|
process.stdout.write("massu watch: not running (no state file)\n");
|
|
24252
24630
|
return { exitCode: 0 };
|
|
24253
24631
|
}
|
|
@@ -24285,7 +24663,7 @@ function runStop(root) {
|
|
|
24285
24663
|
process.stderr.write("massu watch --stop: claude-bg not found; cannot kill registered daemon\n");
|
|
24286
24664
|
return { exitCode: 1 };
|
|
24287
24665
|
}
|
|
24288
|
-
const res =
|
|
24666
|
+
const res = spawnSync4(claudeBg, ["kill", watchName(root)], { stdio: "inherit" });
|
|
24289
24667
|
return { exitCode: (res.status ?? 1) === 0 ? 0 : 1 };
|
|
24290
24668
|
}
|
|
24291
24669
|
async function runApplyNow(root) {
|
|
@@ -24405,14 +24783,14 @@ function refreshLogPath(projectRoot) {
|
|
|
24405
24783
|
function appendRefreshLog(projectRoot, event) {
|
|
24406
24784
|
const path = refreshLogPath(projectRoot);
|
|
24407
24785
|
try {
|
|
24408
|
-
|
|
24409
|
-
|
|
24786
|
+
mkdirSync19(dirname21(path), { recursive: true });
|
|
24787
|
+
appendFileSync4(path, JSON.stringify(event) + "\n", "utf-8");
|
|
24410
24788
|
} catch {
|
|
24411
24789
|
}
|
|
24412
24790
|
}
|
|
24413
24791
|
function readRefreshLog(projectRoot, limit = 10, opts = {}) {
|
|
24414
24792
|
const path = refreshLogPath(projectRoot);
|
|
24415
|
-
if (!
|
|
24793
|
+
if (!existsSync42(path)) return [];
|
|
24416
24794
|
const warn = opts.warn ?? ((s) => {
|
|
24417
24795
|
process.stderr.write(s);
|
|
24418
24796
|
});
|
|
@@ -24487,28 +24865,28 @@ var init_refresh_log = __esm({
|
|
|
24487
24865
|
|
|
24488
24866
|
// src/security/atomic-write.ts
|
|
24489
24867
|
import {
|
|
24490
|
-
chmodSync as
|
|
24868
|
+
chmodSync as chmodSync7,
|
|
24491
24869
|
closeSync as closeSync5,
|
|
24492
|
-
existsSync as
|
|
24870
|
+
existsSync as existsSync43,
|
|
24493
24871
|
fsyncSync as fsyncSync5,
|
|
24494
|
-
mkdirSync as
|
|
24872
|
+
mkdirSync as mkdirSync20,
|
|
24495
24873
|
openSync as openSync5,
|
|
24496
24874
|
renameSync as renameSync6,
|
|
24497
24875
|
rmSync as rmSync10,
|
|
24498
24876
|
statSync as statSync15,
|
|
24499
24877
|
writeSync as writeSync4
|
|
24500
24878
|
} from "node:fs";
|
|
24501
|
-
import { dirname as
|
|
24879
|
+
import { dirname as dirname22 } from "node:path";
|
|
24502
24880
|
function atomicWrite(path, content, opts = {}) {
|
|
24503
24881
|
const tmpPath = `${path}.tmp`;
|
|
24504
|
-
const parentDir =
|
|
24882
|
+
const parentDir = dirname22(path);
|
|
24505
24883
|
try {
|
|
24506
|
-
if (!
|
|
24884
|
+
if (!existsSync43(parentDir)) {
|
|
24507
24885
|
const mkdirOpts = { recursive: true };
|
|
24508
24886
|
if (opts.ensureParentDirMode !== void 0) {
|
|
24509
24887
|
mkdirOpts.mode = opts.ensureParentDirMode;
|
|
24510
24888
|
}
|
|
24511
|
-
|
|
24889
|
+
mkdirSync20(parentDir, mkdirOpts);
|
|
24512
24890
|
}
|
|
24513
24891
|
const buf = typeof content === "string" ? Buffer.from(content, "utf-8") : content;
|
|
24514
24892
|
const openMode = opts.mode ?? 420;
|
|
@@ -24520,12 +24898,12 @@ function atomicWrite(path, content, opts = {}) {
|
|
|
24520
24898
|
closeSync5(fd);
|
|
24521
24899
|
}
|
|
24522
24900
|
if (opts.mode !== void 0) {
|
|
24523
|
-
|
|
24901
|
+
chmodSync7(tmpPath, opts.mode);
|
|
24524
24902
|
}
|
|
24525
24903
|
renameSync6(tmpPath, path);
|
|
24526
24904
|
return { written: true };
|
|
24527
24905
|
} catch (err) {
|
|
24528
|
-
if (
|
|
24906
|
+
if (existsSync43(tmpPath)) {
|
|
24529
24907
|
try {
|
|
24530
24908
|
rmSync10(tmpPath, { force: true });
|
|
24531
24909
|
} catch {
|
|
@@ -24874,7 +25252,7 @@ var init_fetcher = __esm({
|
|
|
24874
25252
|
});
|
|
24875
25253
|
|
|
24876
25254
|
// src/security/manifest-cache.ts
|
|
24877
|
-
import { existsSync as
|
|
25255
|
+
import { existsSync as existsSync44, readFileSync as readFileSync33, statSync as statSync16 } from "node:fs";
|
|
24878
25256
|
import { homedir as homedir13 } from "node:os";
|
|
24879
25257
|
import { resolve as resolve23 } from "node:path";
|
|
24880
25258
|
import { z as z5 } from "zod";
|
|
@@ -24886,7 +25264,7 @@ function defaultCachePaths() {
|
|
|
24886
25264
|
};
|
|
24887
25265
|
}
|
|
24888
25266
|
function loadCachedManifest(paths = defaultCachePaths()) {
|
|
24889
|
-
if (!
|
|
25267
|
+
if (!existsSync44(paths.cachePath)) {
|
|
24890
25268
|
return { kind: "absent" };
|
|
24891
25269
|
}
|
|
24892
25270
|
let raw;
|
|
@@ -25080,7 +25458,7 @@ var init_adapter_origin = __esm({
|
|
|
25080
25458
|
});
|
|
25081
25459
|
|
|
25082
25460
|
// src/security/local-fingerprint.ts
|
|
25083
|
-
import { existsSync as
|
|
25461
|
+
import { existsSync as existsSync45, readFileSync as readFileSync34, lstatSync as lstatSync5 } from "node:fs";
|
|
25084
25462
|
import { homedir as homedir14 } from "node:os";
|
|
25085
25463
|
import { resolve as resolve24, isAbsolute as isAbsolute3 } from "node:path";
|
|
25086
25464
|
import { createHash as createHash12 } from "node:crypto";
|
|
@@ -25109,7 +25487,7 @@ function computeLocalFingerprint(localPaths, projectRoot) {
|
|
|
25109
25487
|
return createHash12("sha256").update(canonical).digest("hex");
|
|
25110
25488
|
}
|
|
25111
25489
|
function readFingerprintSentinel(path = FINGERPRINT_PATH) {
|
|
25112
|
-
if (!
|
|
25490
|
+
if (!existsSync45(path)) return null;
|
|
25113
25491
|
let raw;
|
|
25114
25492
|
try {
|
|
25115
25493
|
raw = JSON.parse(readFileSync34(path, "utf-8"));
|
|
@@ -25176,15 +25554,15 @@ var init_local_fingerprint = __esm({
|
|
|
25176
25554
|
});
|
|
25177
25555
|
|
|
25178
25556
|
// src/security/install-tracking.ts
|
|
25179
|
-
import { readFileSync as readFileSync35, readdirSync as readdirSync16, lstatSync as lstatSync6, existsSync as
|
|
25180
|
-
import { join as
|
|
25557
|
+
import { readFileSync as readFileSync35, readdirSync as readdirSync16, lstatSync as lstatSync6, existsSync as existsSync46 } from "node:fs";
|
|
25558
|
+
import { join as join25, relative as relative7, sep as sep2 } from "node:path";
|
|
25181
25559
|
import { homedir as homedir15 } from "node:os";
|
|
25182
25560
|
import { resolve as resolve25 } from "node:path";
|
|
25183
25561
|
import { createHash as createHash13 } from "node:crypto";
|
|
25184
25562
|
import { z as z7 } from "zod";
|
|
25185
25563
|
function containsHiddenDirs(packageDir) {
|
|
25186
25564
|
for (const hidden of EXCLUDED_DIR_NAMES) {
|
|
25187
|
-
if (
|
|
25565
|
+
if (existsSync46(`${packageDir}/${hidden}`)) {
|
|
25188
25566
|
return hidden;
|
|
25189
25567
|
}
|
|
25190
25568
|
}
|
|
@@ -25201,7 +25579,7 @@ function sha256OfDir(dir, opts = {}) {
|
|
|
25201
25579
|
return;
|
|
25202
25580
|
}
|
|
25203
25581
|
for (const entry of entries.sort()) {
|
|
25204
|
-
const absPath =
|
|
25582
|
+
const absPath = join25(currentDir, entry);
|
|
25205
25583
|
let lst;
|
|
25206
25584
|
try {
|
|
25207
25585
|
lst = lstatSync6(absPath);
|
|
@@ -25239,7 +25617,7 @@ function sha256OfDir(dir, opts = {}) {
|
|
|
25239
25617
|
return top.digest("hex");
|
|
25240
25618
|
}
|
|
25241
25619
|
function readInstalledManifest(path = INSTALLED_MANIFEST_PATH) {
|
|
25242
|
-
if (!
|
|
25620
|
+
if (!existsSync46(path)) return {};
|
|
25243
25621
|
let raw;
|
|
25244
25622
|
try {
|
|
25245
25623
|
raw = JSON.parse(readFileSync35(path, "utf-8"));
|
|
@@ -25329,12 +25707,12 @@ var init_install_tracking = __esm({
|
|
|
25329
25707
|
});
|
|
25330
25708
|
|
|
25331
25709
|
// src/detect/adapters/discover.ts
|
|
25332
|
-
import { existsSync as
|
|
25710
|
+
import { existsSync as existsSync47, readdirSync as readdirSync17, readFileSync as readFileSync36, lstatSync as lstatSync7 } from "node:fs";
|
|
25333
25711
|
import { resolve as resolve26, isAbsolute as isAbsolute4 } from "node:path";
|
|
25334
25712
|
import { z as z8 } from "zod";
|
|
25335
25713
|
function walkNodeModules(projectRoot, warnings) {
|
|
25336
25714
|
const nodeModulesDir = resolve26(projectRoot, "node_modules");
|
|
25337
|
-
if (!
|
|
25715
|
+
if (!existsSync47(nodeModulesDir)) {
|
|
25338
25716
|
return [];
|
|
25339
25717
|
}
|
|
25340
25718
|
const candidates = [];
|
|
@@ -25389,7 +25767,7 @@ function walkNodeModules(projectRoot, warnings) {
|
|
|
25389
25767
|
}
|
|
25390
25768
|
function tryReadAdapterPackage(packageDir, warnings) {
|
|
25391
25769
|
const pkgJsonPath = resolve26(packageDir, "package.json");
|
|
25392
|
-
if (!
|
|
25770
|
+
if (!existsSync47(pkgJsonPath)) return null;
|
|
25393
25771
|
let raw;
|
|
25394
25772
|
try {
|
|
25395
25773
|
raw = JSON.parse(readFileSync36(pkgJsonPath, "utf-8"));
|
|
@@ -25527,7 +25905,7 @@ function discoverAdapters(opts) {
|
|
|
25527
25905
|
for (const localPath of opts.configLocalPaths) {
|
|
25528
25906
|
if (seenIds.has(localPath)) continue;
|
|
25529
25907
|
const absPath = isAbsolute4(localPath) ? localPath : resolve26(opts.projectRoot, localPath);
|
|
25530
|
-
if (!
|
|
25908
|
+
if (!existsSync47(absPath)) {
|
|
25531
25909
|
warnings.push(
|
|
25532
25910
|
`local adapter file not found: ${localPath} (resolved to ${absPath}). Remove via: massu adapters remove-local ${localPath}`
|
|
25533
25911
|
);
|
|
@@ -25609,7 +25987,7 @@ __export(adapters_exports, {
|
|
|
25609
25987
|
runAdaptersResyncLocalFingerprint: () => runAdaptersResyncLocalFingerprint,
|
|
25610
25988
|
runAdaptersSearch: () => runAdaptersSearch
|
|
25611
25989
|
});
|
|
25612
|
-
import { existsSync as
|
|
25990
|
+
import { existsSync as existsSync48, readFileSync as readFileSync37 } from "node:fs";
|
|
25613
25991
|
import { resolve as resolve27 } from "node:path";
|
|
25614
25992
|
import { parseDocument } from "yaml";
|
|
25615
25993
|
async function handleAdaptersSubcommand(args2) {
|
|
@@ -25878,7 +26256,7 @@ function mutateLocalArray(mutator, command) {
|
|
|
25878
26256
|
return { exitCode: 2 };
|
|
25879
26257
|
}
|
|
25880
26258
|
const yamlPath = resolve27(projectRoot, "massu.config.yaml");
|
|
25881
|
-
if (!
|
|
26259
|
+
if (!existsSync48(yamlPath)) {
|
|
25882
26260
|
process.stderr.write(
|
|
25883
26261
|
`${command}: massu.config.yaml not found at ${yamlPath}. Run \`massu init\` first.
|
|
25884
26262
|
`
|
|
@@ -25978,7 +26356,7 @@ async function runAdaptersInstall(args2) {
|
|
|
25978
26356
|
return { exitCode: 1 };
|
|
25979
26357
|
}
|
|
25980
26358
|
const packageDir = resolve27(projectRoot, "node_modules", ...packageName.split("/"));
|
|
25981
|
-
if (!
|
|
26359
|
+
if (!existsSync48(packageDir)) {
|
|
25982
26360
|
process.stderr.write(
|
|
25983
26361
|
`install: ${packageName} is not installed in node_modules. Run \`npm install ${packageName}\` first.
|
|
25984
26362
|
`
|
|
@@ -25986,7 +26364,7 @@ async function runAdaptersInstall(args2) {
|
|
|
25986
26364
|
return { exitCode: 1 };
|
|
25987
26365
|
}
|
|
25988
26366
|
const pkgJsonPath = resolve27(packageDir, "package.json");
|
|
25989
|
-
if (!
|
|
26367
|
+
if (!existsSync48(pkgJsonPath)) {
|
|
25990
26368
|
process.stderr.write(`install: ${packageName} has no package.json at ${pkgJsonPath}
|
|
25991
26369
|
`);
|
|
25992
26370
|
return { exitCode: 1 };
|
|
@@ -26113,7 +26491,7 @@ async function runAdaptersResign(_args) {
|
|
|
26113
26491
|
continue;
|
|
26114
26492
|
}
|
|
26115
26493
|
const packageDir = resolve27(projectRoot, "node_modules", ...name.split("/"));
|
|
26116
|
-
if (!
|
|
26494
|
+
if (!existsSync48(packageDir)) {
|
|
26117
26495
|
removed++;
|
|
26118
26496
|
warnings.push(`${name}@${entry.version}: not present in node_modules \u2014 REMOVED from sidecar`);
|
|
26119
26497
|
removeInstalledManifestEntry(name);
|
|
@@ -26199,16 +26577,15 @@ var init_adapters2 = __esm({
|
|
|
26199
26577
|
});
|
|
26200
26578
|
|
|
26201
26579
|
// src/preflight.ts
|
|
26202
|
-
import
|
|
26203
|
-
import { existsSync as existsSync48 } from "fs";
|
|
26580
|
+
import { existsSync as existsSync49 } from "fs";
|
|
26204
26581
|
function assertCodegraphUsable(dbPath) {
|
|
26205
26582
|
const path = dbPath ?? getResolvedPaths().codegraphDbPath;
|
|
26206
|
-
if (!
|
|
26583
|
+
if (!existsSync49(path)) {
|
|
26207
26584
|
throw new CodegraphDbUnusableError(path, "missing");
|
|
26208
26585
|
}
|
|
26209
26586
|
let db = null;
|
|
26210
26587
|
try {
|
|
26211
|
-
db =
|
|
26588
|
+
db = openDatabase(path, { readonly: true });
|
|
26212
26589
|
const row = db.prepare("SELECT COUNT(*) AS c FROM files").get();
|
|
26213
26590
|
const files = row?.c ?? 0;
|
|
26214
26591
|
if (files === 0) {
|
|
@@ -26232,6 +26609,7 @@ var CodegraphDbNotInitializedError, CodegraphDbUnusableError;
|
|
|
26232
26609
|
var init_preflight = __esm({
|
|
26233
26610
|
"src/preflight.ts"() {
|
|
26234
26611
|
"use strict";
|
|
26612
|
+
init_sqlite_loader();
|
|
26235
26613
|
init_config();
|
|
26236
26614
|
CodegraphDbNotInitializedError = class extends Error {
|
|
26237
26615
|
dbPath;
|
|
@@ -26291,23 +26669,22 @@ var init_sql_table_names = __esm({
|
|
|
26291
26669
|
});
|
|
26292
26670
|
|
|
26293
26671
|
// src/db.ts
|
|
26294
|
-
import
|
|
26295
|
-
import {
|
|
26296
|
-
import { existsSync as existsSync49, mkdirSync as mkdirSync20, readdirSync as readdirSync18, statSync as statSync17 } from "fs";
|
|
26672
|
+
import { dirname as dirname23, join as join26 } from "path";
|
|
26673
|
+
import { existsSync as existsSync50, mkdirSync as mkdirSync21, readdirSync as readdirSync18, statSync as statSync17 } from "fs";
|
|
26297
26674
|
function getCodeGraphDb() {
|
|
26298
26675
|
const dbPath = getResolvedPaths().codegraphDbPath;
|
|
26299
26676
|
assertCodegraphUsable(dbPath);
|
|
26300
|
-
const db =
|
|
26677
|
+
const db = openDatabase(dbPath, { readonly: true });
|
|
26301
26678
|
db.pragma("journal_mode = WAL");
|
|
26302
26679
|
return db;
|
|
26303
26680
|
}
|
|
26304
26681
|
function getDataDb() {
|
|
26305
26682
|
const dbPath = getResolvedPaths().dataDbPath;
|
|
26306
|
-
const dir =
|
|
26307
|
-
if (!
|
|
26308
|
-
|
|
26683
|
+
const dir = dirname23(dbPath);
|
|
26684
|
+
if (!existsSync50(dir)) {
|
|
26685
|
+
mkdirSync21(dir, { recursive: true });
|
|
26309
26686
|
}
|
|
26310
|
-
const db =
|
|
26687
|
+
const db = openDatabase(dbPath);
|
|
26311
26688
|
db.pragma("journal_mode = WAL");
|
|
26312
26689
|
db.pragma("foreign_keys = ON");
|
|
26313
26690
|
initDataSchema(db);
|
|
@@ -26577,7 +26954,7 @@ function isPythonDataStale(dataDb, pythonRoot) {
|
|
|
26577
26954
|
try {
|
|
26578
26955
|
const entries = readdirSync18(dir, { withFileTypes: true });
|
|
26579
26956
|
for (const entry of entries) {
|
|
26580
|
-
const fullPath =
|
|
26957
|
+
const fullPath = join26(dir, entry.name);
|
|
26581
26958
|
if (entry.isDirectory()) {
|
|
26582
26959
|
if (["__pycache__", ".venv", "venv", "node_modules", ".mypy_cache", ".pytest_cache"].includes(entry.name)) continue;
|
|
26583
26960
|
if (checkDir(fullPath)) return true;
|
|
@@ -26597,6 +26974,7 @@ function updatePythonBuildTimestamp(dataDb) {
|
|
|
26597
26974
|
var init_db2 = __esm({
|
|
26598
26975
|
"src/db.ts"() {
|
|
26599
26976
|
"use strict";
|
|
26977
|
+
init_sqlite_loader();
|
|
26600
26978
|
init_preflight();
|
|
26601
26979
|
init_config();
|
|
26602
26980
|
init_sql_table_names();
|
|
@@ -26681,8 +27059,8 @@ var init_rules = __esm({
|
|
|
26681
27059
|
});
|
|
26682
27060
|
|
|
26683
27061
|
// src/import-resolver.ts
|
|
26684
|
-
import { readFileSync as readFileSync38, existsSync as
|
|
26685
|
-
import { resolve as resolve29, dirname as
|
|
27062
|
+
import { readFileSync as readFileSync38, existsSync as existsSync51, statSync as statSync18 } from "fs";
|
|
27063
|
+
import { resolve as resolve29, dirname as dirname24, join as join27 } from "path";
|
|
26686
27064
|
function parseImports(source) {
|
|
26687
27065
|
const imports = [];
|
|
26688
27066
|
const lines = source.split("\n");
|
|
@@ -26740,21 +27118,21 @@ function resolveImportPath(specifier, fromFile) {
|
|
|
26740
27118
|
const paths = getResolvedPaths();
|
|
26741
27119
|
basePath = resolve29(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
|
|
26742
27120
|
} else {
|
|
26743
|
-
basePath = resolve29(
|
|
27121
|
+
basePath = resolve29(dirname24(fromFile), specifier);
|
|
26744
27122
|
}
|
|
26745
|
-
if (
|
|
27123
|
+
if (existsSync51(basePath) && !isDirectory(basePath)) {
|
|
26746
27124
|
return toRelative(basePath);
|
|
26747
27125
|
}
|
|
26748
27126
|
const resolvedPaths = getResolvedPaths();
|
|
26749
27127
|
for (const ext of resolvedPaths.extensions) {
|
|
26750
27128
|
const withExt = basePath + ext;
|
|
26751
|
-
if (
|
|
27129
|
+
if (existsSync51(withExt)) {
|
|
26752
27130
|
return toRelative(withExt);
|
|
26753
27131
|
}
|
|
26754
27132
|
}
|
|
26755
27133
|
for (const indexFile of resolvedPaths.indexFiles) {
|
|
26756
|
-
const indexPath =
|
|
26757
|
-
if (
|
|
27134
|
+
const indexPath = join27(basePath, indexFile);
|
|
27135
|
+
if (existsSync51(indexPath)) {
|
|
26758
27136
|
return toRelative(indexPath);
|
|
26759
27137
|
}
|
|
26760
27138
|
}
|
|
@@ -26791,7 +27169,7 @@ function buildImportIndex(dataDb, codegraphDb) {
|
|
|
26791
27169
|
let batch = [];
|
|
26792
27170
|
for (const file of files) {
|
|
26793
27171
|
const absPath = ensureWithinRoot(resolve29(projectRoot, file.path), projectRoot);
|
|
26794
|
-
if (!
|
|
27172
|
+
if (!existsSync51(absPath)) continue;
|
|
26795
27173
|
let source;
|
|
26796
27174
|
try {
|
|
26797
27175
|
source = readFileSync38(absPath, "utf-8");
|
|
@@ -26831,12 +27209,12 @@ var init_import_resolver = __esm({
|
|
|
26831
27209
|
});
|
|
26832
27210
|
|
|
26833
27211
|
// src/trpc-index.ts
|
|
26834
|
-
import { readFileSync as readFileSync39, existsSync as
|
|
26835
|
-
import { resolve as resolve30, join as
|
|
27212
|
+
import { readFileSync as readFileSync39, existsSync as existsSync52, readdirSync as readdirSync19 } from "fs";
|
|
27213
|
+
import { resolve as resolve30, join as join28 } from "path";
|
|
26836
27214
|
function parseRootRouter() {
|
|
26837
27215
|
const paths = getResolvedPaths();
|
|
26838
27216
|
const rootPath = paths.rootRouterPath;
|
|
26839
|
-
if (!
|
|
27217
|
+
if (!existsSync52(rootPath)) {
|
|
26840
27218
|
throw new Error(`Root router not found at ${rootPath}`);
|
|
26841
27219
|
}
|
|
26842
27220
|
const source = readFileSync39(rootPath, "utf-8");
|
|
@@ -26851,12 +27229,12 @@ function parseRootRouter() {
|
|
|
26851
27229
|
for (const ext of [".ts", ".tsx", ""]) {
|
|
26852
27230
|
const candidate = fullPath + ext;
|
|
26853
27231
|
const routersRelPath = getConfig().paths.routers ?? "src/server/api/routers";
|
|
26854
|
-
if (
|
|
27232
|
+
if (existsSync52(candidate)) {
|
|
26855
27233
|
filePath = routersRelPath + "/" + filePath + ext;
|
|
26856
27234
|
break;
|
|
26857
27235
|
}
|
|
26858
|
-
const indexCandidate =
|
|
26859
|
-
if (
|
|
27236
|
+
const indexCandidate = join28(fullPath, "index.ts");
|
|
27237
|
+
if (existsSync52(indexCandidate)) {
|
|
26860
27238
|
filePath = routersRelPath + "/" + filePath + "/index.ts";
|
|
26861
27239
|
break;
|
|
26862
27240
|
}
|
|
@@ -26876,7 +27254,7 @@ function parseRootRouter() {
|
|
|
26876
27254
|
}
|
|
26877
27255
|
function extractProcedures(routerFilePath) {
|
|
26878
27256
|
const absPath = resolve30(getProjectRoot(), routerFilePath);
|
|
26879
|
-
if (!
|
|
27257
|
+
if (!existsSync52(absPath)) return [];
|
|
26880
27258
|
const source = readFileSync39(absPath, "utf-8");
|
|
26881
27259
|
const procedures = [];
|
|
26882
27260
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -26906,7 +27284,7 @@ function findUICallSites(routerKey, procedureName) {
|
|
|
26906
27284
|
];
|
|
26907
27285
|
const searchPattern = `api.${routerKey}.${procedureName}`;
|
|
26908
27286
|
for (const dir of searchDirs) {
|
|
26909
|
-
if (!
|
|
27287
|
+
if (!existsSync52(dir)) continue;
|
|
26910
27288
|
searchDirectory(dir, searchPattern, callSites);
|
|
26911
27289
|
}
|
|
26912
27290
|
return callSites;
|
|
@@ -26914,7 +27292,7 @@ function findUICallSites(routerKey, procedureName) {
|
|
|
26914
27292
|
function searchDirectory(dir, pattern, results) {
|
|
26915
27293
|
const entries = readdirSync19(dir, { withFileTypes: true });
|
|
26916
27294
|
for (const entry of entries) {
|
|
26917
|
-
const fullPath =
|
|
27295
|
+
const fullPath = join28(dir, entry.name);
|
|
26918
27296
|
if (entry.isDirectory()) {
|
|
26919
27297
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
26920
27298
|
searchDirectory(fullPath, pattern, results);
|
|
@@ -26984,7 +27362,7 @@ var init_trpc_index = __esm({
|
|
|
26984
27362
|
});
|
|
26985
27363
|
|
|
26986
27364
|
// src/page-deps.ts
|
|
26987
|
-
import { readFileSync as readFileSync40, existsSync as
|
|
27365
|
+
import { readFileSync as readFileSync40, existsSync as existsSync53 } from "fs";
|
|
26988
27366
|
import { resolve as resolve31 } from "path";
|
|
26989
27367
|
function deriveRoute(pageFile) {
|
|
26990
27368
|
let route = pageFile.replace(/^src\/app/, "").replace(/\/page\.tsx?$/, "").replace(/\/page\.jsx?$/, "");
|
|
@@ -27024,7 +27402,7 @@ function findRouterCalls(files) {
|
|
|
27024
27402
|
const projectRoot = getProjectRoot();
|
|
27025
27403
|
for (const file of files) {
|
|
27026
27404
|
const absPath = ensureWithinRoot(resolve31(projectRoot, file), projectRoot);
|
|
27027
|
-
if (!
|
|
27405
|
+
if (!existsSync53(absPath)) continue;
|
|
27028
27406
|
try {
|
|
27029
27407
|
const source = readFileSync40(absPath, "utf-8");
|
|
27030
27408
|
const apiCallRegex = /api\.(\w+)\.\w+/g;
|
|
@@ -27045,7 +27423,7 @@ function findTablesFromRouters(routerNames, dataDb) {
|
|
|
27045
27423
|
).all(routerName);
|
|
27046
27424
|
for (const proc of procs) {
|
|
27047
27425
|
const absPath = ensureWithinRoot(resolve31(getProjectRoot(), proc.router_file), getProjectRoot());
|
|
27048
|
-
if (!
|
|
27426
|
+
if (!existsSync53(absPath)) continue;
|
|
27049
27427
|
try {
|
|
27050
27428
|
const source = readFileSync40(absPath, "utf-8");
|
|
27051
27429
|
const dbPattern = getConfig().dbAccessPattern ?? "ctx.db.{table}";
|
|
@@ -27319,11 +27697,11 @@ var init_domains = __esm({
|
|
|
27319
27697
|
});
|
|
27320
27698
|
|
|
27321
27699
|
// src/schema-mapper.ts
|
|
27322
|
-
import { readFileSync as readFileSync41, existsSync as
|
|
27323
|
-
import { join as
|
|
27700
|
+
import { readFileSync as readFileSync41, existsSync as existsSync54, readdirSync as readdirSync20 } from "fs";
|
|
27701
|
+
import { join as join29 } from "path";
|
|
27324
27702
|
function parsePrismaSchema() {
|
|
27325
27703
|
const schemaPath = getResolvedPaths().prismaSchemaPath;
|
|
27326
|
-
if (!
|
|
27704
|
+
if (!existsSync54(schemaPath)) {
|
|
27327
27705
|
throw new Error(`Prisma schema not found at ${schemaPath}`);
|
|
27328
27706
|
}
|
|
27329
27707
|
const source = readFileSync41(schemaPath, "utf-8");
|
|
@@ -27384,14 +27762,14 @@ function toSnakeCase(str) {
|
|
|
27384
27762
|
function findColumnUsageInRouters(tableName) {
|
|
27385
27763
|
const usage2 = /* @__PURE__ */ new Map();
|
|
27386
27764
|
const routersDir = getResolvedPaths().routersDir;
|
|
27387
|
-
if (!
|
|
27765
|
+
if (!existsSync54(routersDir)) return usage2;
|
|
27388
27766
|
scanDirectory(routersDir, tableName, usage2);
|
|
27389
27767
|
return usage2;
|
|
27390
27768
|
}
|
|
27391
27769
|
function scanDirectory(dir, tableName, usage2) {
|
|
27392
27770
|
const entries = readdirSync20(dir, { withFileTypes: true });
|
|
27393
27771
|
for (const entry of entries) {
|
|
27394
|
-
const fullPath =
|
|
27772
|
+
const fullPath = join29(dir, entry.name);
|
|
27395
27773
|
if (entry.isDirectory()) {
|
|
27396
27774
|
scanDirectory(fullPath, tableName, usage2);
|
|
27397
27775
|
} else if (entry.name.endsWith(".ts")) {
|
|
@@ -27446,10 +27824,10 @@ function detectMismatches(models) {
|
|
|
27446
27824
|
}
|
|
27447
27825
|
function findFilesUsingColumn(dir, column, tableName) {
|
|
27448
27826
|
const result = [];
|
|
27449
|
-
if (!
|
|
27827
|
+
if (!existsSync54(dir)) return result;
|
|
27450
27828
|
const entries = readdirSync20(dir, { withFileTypes: true });
|
|
27451
27829
|
for (const entry of entries) {
|
|
27452
|
-
const fullPath =
|
|
27830
|
+
const fullPath = join29(dir, entry.name);
|
|
27453
27831
|
if (entry.isDirectory()) {
|
|
27454
27832
|
result.push(...findFilesUsingColumn(fullPath, column, tableName));
|
|
27455
27833
|
} else if (entry.name.endsWith(".ts")) {
|
|
@@ -27599,34 +27977,34 @@ var init_import_parser = __esm({
|
|
|
27599
27977
|
});
|
|
27600
27978
|
|
|
27601
27979
|
// src/python/import-resolver.ts
|
|
27602
|
-
import { readFileSync as readFileSync42, existsSync as
|
|
27603
|
-
import { resolve as resolve33, join as
|
|
27980
|
+
import { readFileSync as readFileSync42, existsSync as existsSync55, readdirSync as readdirSync21 } from "fs";
|
|
27981
|
+
import { resolve as resolve33, join as join30, relative as relative8, dirname as dirname25 } from "path";
|
|
27604
27982
|
function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
|
|
27605
27983
|
const projectRoot = getProjectRoot();
|
|
27606
27984
|
if (level > 0) {
|
|
27607
|
-
let baseDir =
|
|
27985
|
+
let baseDir = dirname25(resolve33(projectRoot, fromFile));
|
|
27608
27986
|
for (let i = 1; i < level; i++) {
|
|
27609
|
-
baseDir =
|
|
27987
|
+
baseDir = dirname25(baseDir);
|
|
27610
27988
|
}
|
|
27611
27989
|
const modulePart = module.replace(/^\.+/, "");
|
|
27612
27990
|
if (modulePart) {
|
|
27613
27991
|
const parts2 = modulePart.split(".");
|
|
27614
|
-
return tryResolvePythonPath(
|
|
27992
|
+
return tryResolvePythonPath(join30(baseDir, ...parts2), projectRoot);
|
|
27615
27993
|
}
|
|
27616
27994
|
return tryResolvePythonPath(baseDir, projectRoot);
|
|
27617
27995
|
}
|
|
27618
27996
|
const parts = module.split(".");
|
|
27619
|
-
const candidate =
|
|
27997
|
+
const candidate = join30(resolve33(projectRoot, pythonRoot), ...parts);
|
|
27620
27998
|
return tryResolvePythonPath(candidate, projectRoot);
|
|
27621
27999
|
}
|
|
27622
28000
|
function tryResolvePythonPath(basePath, projectRoot) {
|
|
27623
|
-
if (
|
|
28001
|
+
if (existsSync55(basePath + ".py")) {
|
|
27624
28002
|
return relative8(projectRoot, basePath + ".py");
|
|
27625
28003
|
}
|
|
27626
|
-
if (
|
|
27627
|
-
return relative8(projectRoot,
|
|
28004
|
+
if (existsSync55(join30(basePath, "__init__.py"))) {
|
|
28005
|
+
return relative8(projectRoot, join30(basePath, "__init__.py"));
|
|
27628
28006
|
}
|
|
27629
|
-
if (basePath.endsWith(".py") &&
|
|
28007
|
+
if (basePath.endsWith(".py") && existsSync55(basePath)) {
|
|
27630
28008
|
return relative8(projectRoot, basePath);
|
|
27631
28009
|
}
|
|
27632
28010
|
return null;
|
|
@@ -27638,9 +28016,9 @@ function walkPythonFiles(dir, excludeDirs) {
|
|
|
27638
28016
|
for (const entry of entries) {
|
|
27639
28017
|
if (entry.isDirectory()) {
|
|
27640
28018
|
if (excludeDirs.includes(entry.name)) continue;
|
|
27641
|
-
files.push(...walkPythonFiles(
|
|
28019
|
+
files.push(...walkPythonFiles(join30(dir, entry.name), excludeDirs));
|
|
27642
28020
|
} else if (entry.name.endsWith(".py")) {
|
|
27643
|
-
files.push(
|
|
28021
|
+
files.push(join30(dir, entry.name));
|
|
27644
28022
|
}
|
|
27645
28023
|
}
|
|
27646
28024
|
} catch {
|
|
@@ -27934,7 +28312,7 @@ var init_route_parser = __esm({
|
|
|
27934
28312
|
|
|
27935
28313
|
// src/python/route-indexer.ts
|
|
27936
28314
|
import { readFileSync as readFileSync43, readdirSync as readdirSync22 } from "fs";
|
|
27937
|
-
import { join as
|
|
28315
|
+
import { join as join31, relative as relative9 } from "path";
|
|
27938
28316
|
function walkPyFiles(dir, excludeDirs) {
|
|
27939
28317
|
const files = [];
|
|
27940
28318
|
try {
|
|
@@ -27942,9 +28320,9 @@ function walkPyFiles(dir, excludeDirs) {
|
|
|
27942
28320
|
for (const entry of entries) {
|
|
27943
28321
|
if (entry.isDirectory()) {
|
|
27944
28322
|
if (excludeDirs.includes(entry.name)) continue;
|
|
27945
|
-
files.push(...walkPyFiles(
|
|
28323
|
+
files.push(...walkPyFiles(join31(dir, entry.name), excludeDirs));
|
|
27946
28324
|
} else if (entry.name.endsWith(".py")) {
|
|
27947
|
-
files.push(
|
|
28325
|
+
files.push(join31(dir, entry.name));
|
|
27948
28326
|
}
|
|
27949
28327
|
}
|
|
27950
28328
|
} catch {
|
|
@@ -27953,7 +28331,7 @@ function walkPyFiles(dir, excludeDirs) {
|
|
|
27953
28331
|
}
|
|
27954
28332
|
function buildPythonRouteIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
|
|
27955
28333
|
const projectRoot = getProjectRoot();
|
|
27956
|
-
const absRoot =
|
|
28334
|
+
const absRoot = join31(projectRoot, pythonRoot);
|
|
27957
28335
|
dataDb.exec(`DELETE FROM ${t("py_routes")}`);
|
|
27958
28336
|
dataDb.exec(`DELETE FROM ${t("py_route_callers")}`);
|
|
27959
28337
|
const insertStmt = dataDb.prepare(
|
|
@@ -28179,7 +28557,7 @@ var init_model_parser = __esm({
|
|
|
28179
28557
|
|
|
28180
28558
|
// src/python/model-indexer.ts
|
|
28181
28559
|
import { readFileSync as readFileSync44, readdirSync as readdirSync23 } from "fs";
|
|
28182
|
-
import { join as
|
|
28560
|
+
import { join as join32, relative as relative10 } from "path";
|
|
28183
28561
|
function walkPyFiles2(dir, excludeDirs) {
|
|
28184
28562
|
const files = [];
|
|
28185
28563
|
try {
|
|
@@ -28187,9 +28565,9 @@ function walkPyFiles2(dir, excludeDirs) {
|
|
|
28187
28565
|
for (const entry of entries) {
|
|
28188
28566
|
if (entry.isDirectory()) {
|
|
28189
28567
|
if (excludeDirs.includes(entry.name)) continue;
|
|
28190
|
-
files.push(...walkPyFiles2(
|
|
28568
|
+
files.push(...walkPyFiles2(join32(dir, entry.name), excludeDirs));
|
|
28191
28569
|
} else if (entry.name.endsWith(".py")) {
|
|
28192
|
-
files.push(
|
|
28570
|
+
files.push(join32(dir, entry.name));
|
|
28193
28571
|
}
|
|
28194
28572
|
}
|
|
28195
28573
|
} catch {
|
|
@@ -28198,7 +28576,7 @@ function walkPyFiles2(dir, excludeDirs) {
|
|
|
28198
28576
|
}
|
|
28199
28577
|
function buildPythonModelIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
|
|
28200
28578
|
const projectRoot = getProjectRoot();
|
|
28201
|
-
const absRoot =
|
|
28579
|
+
const absRoot = join32(projectRoot, pythonRoot);
|
|
28202
28580
|
dataDb.exec(`DELETE FROM ${t("py_models")}`);
|
|
28203
28581
|
dataDb.exec(`DELETE FROM ${t("py_fk_edges")}`);
|
|
28204
28582
|
const insertModel = dataDb.prepare(
|
|
@@ -28476,18 +28854,18 @@ var init_migration_parser = __esm({
|
|
|
28476
28854
|
|
|
28477
28855
|
// src/python/migration-indexer.ts
|
|
28478
28856
|
import { readFileSync as readFileSync45, readdirSync as readdirSync24 } from "fs";
|
|
28479
|
-
import { join as
|
|
28857
|
+
import { join as join33, relative as relative11 } from "path";
|
|
28480
28858
|
function buildPythonMigrationIndex(dataDb, alembicDir) {
|
|
28481
28859
|
const projectRoot = getProjectRoot();
|
|
28482
|
-
const absDir =
|
|
28860
|
+
const absDir = join33(projectRoot, alembicDir);
|
|
28483
28861
|
dataDb.exec(`DELETE FROM ${t("py_migrations")}`);
|
|
28484
|
-
const versionsDir =
|
|
28862
|
+
const versionsDir = join33(absDir, "versions");
|
|
28485
28863
|
let files = [];
|
|
28486
28864
|
try {
|
|
28487
|
-
files = readdirSync24(versionsDir).filter((f2) => f2.endsWith(".py")).map((f2) =>
|
|
28865
|
+
files = readdirSync24(versionsDir).filter((f2) => f2.endsWith(".py")).map((f2) => join33(versionsDir, f2));
|
|
28488
28866
|
} catch {
|
|
28489
28867
|
try {
|
|
28490
|
-
files = readdirSync24(absDir).filter((f2) => f2.endsWith(".py") && f2 !== "env.py").map((f2) =>
|
|
28868
|
+
files = readdirSync24(absDir).filter((f2) => f2.endsWith(".py") && f2 !== "env.py").map((f2) => join33(absDir, f2));
|
|
28491
28869
|
} catch {
|
|
28492
28870
|
}
|
|
28493
28871
|
}
|
|
@@ -28537,11 +28915,11 @@ var init_migration_indexer = __esm({
|
|
|
28537
28915
|
|
|
28538
28916
|
// src/python/coupling-detector.ts
|
|
28539
28917
|
import { readFileSync as readFileSync46, readdirSync as readdirSync25 } from "fs";
|
|
28540
|
-
import { join as
|
|
28918
|
+
import { join as join34, relative as relative12 } from "path";
|
|
28541
28919
|
function buildPythonCouplingIndex(dataDb) {
|
|
28542
28920
|
const projectRoot = getProjectRoot();
|
|
28543
28921
|
const config = getConfig();
|
|
28544
|
-
const srcDir =
|
|
28922
|
+
const srcDir = join34(projectRoot, config.paths.source);
|
|
28545
28923
|
const routes = dataDb.prepare(`SELECT id, method, path FROM ${t("py_routes")}`).all();
|
|
28546
28924
|
if (routes.length === 0) return 0;
|
|
28547
28925
|
dataDb.exec(`DELETE FROM ${t("py_route_callers")}`);
|
|
@@ -28605,9 +28983,9 @@ function walkFrontendFiles(dir) {
|
|
|
28605
28983
|
for (const entry of entries) {
|
|
28606
28984
|
if (entry.isDirectory()) {
|
|
28607
28985
|
if (exclude.includes(entry.name)) continue;
|
|
28608
|
-
files.push(...walkFrontendFiles(
|
|
28986
|
+
files.push(...walkFrontendFiles(join34(dir, entry.name)));
|
|
28609
28987
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
28610
|
-
files.push(
|
|
28988
|
+
files.push(join34(dir, entry.name));
|
|
28611
28989
|
}
|
|
28612
28990
|
}
|
|
28613
28991
|
} catch {
|
|
@@ -28979,7 +29357,7 @@ var init_memory_tools = __esm({
|
|
|
28979
29357
|
});
|
|
28980
29358
|
|
|
28981
29359
|
// src/docs-tools.ts
|
|
28982
|
-
import { readFileSync as readFileSync47, existsSync as
|
|
29360
|
+
import { readFileSync as readFileSync47, existsSync as existsSync56 } from "fs";
|
|
28983
29361
|
import { resolve as resolve34, basename as basename9 } from "path";
|
|
28984
29362
|
function p4(baseName) {
|
|
28985
29363
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
@@ -29035,7 +29413,7 @@ function handleDocsToolCall(name, args2) {
|
|
|
29035
29413
|
}
|
|
29036
29414
|
function loadDocsMap() {
|
|
29037
29415
|
const mapPath = getResolvedPaths().docsMapPath;
|
|
29038
|
-
if (!
|
|
29416
|
+
if (!existsSync56(mapPath)) {
|
|
29039
29417
|
throw new Error(`docs-map.json not found at ${mapPath}`);
|
|
29040
29418
|
}
|
|
29041
29419
|
return JSON.parse(readFileSync47(mapPath, "utf-8"));
|
|
@@ -29109,9 +29487,9 @@ function extractFrontmatter(content) {
|
|
|
29109
29487
|
function extractProcedureNames(routerPath) {
|
|
29110
29488
|
const root = getProjectRoot();
|
|
29111
29489
|
const absPath = ensureWithinRoot(resolve34(getResolvedPaths().srcDir, "..", routerPath), root);
|
|
29112
|
-
if (!
|
|
29490
|
+
if (!existsSync56(absPath)) {
|
|
29113
29491
|
const altPath = ensureWithinRoot(resolve34(getResolvedPaths().srcDir, "../server/api/routers", basename9(routerPath)), root);
|
|
29114
|
-
if (!
|
|
29492
|
+
if (!existsSync56(altPath)) return [];
|
|
29115
29493
|
return extractProcedureNamesFromContent(readFileSync47(altPath, "utf-8"));
|
|
29116
29494
|
}
|
|
29117
29495
|
return extractProcedureNamesFromContent(readFileSync47(absPath, "utf-8"));
|
|
@@ -29155,7 +29533,7 @@ function handleDocsAudit(args2) {
|
|
|
29155
29533
|
const mapping = docsMap.mappings.find((m3) => m3.id === mappingId);
|
|
29156
29534
|
if (!mapping) continue;
|
|
29157
29535
|
const helpPagePath = ensureWithinRoot(resolve34(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
|
|
29158
|
-
if (!
|
|
29536
|
+
if (!existsSync56(helpPagePath)) {
|
|
29159
29537
|
results.push({
|
|
29160
29538
|
helpPage: mapping.helpPage,
|
|
29161
29539
|
mappingId,
|
|
@@ -29208,7 +29586,7 @@ function handleDocsAudit(args2) {
|
|
|
29208
29586
|
for (const [guideName, parentId] of Object.entries(docsMap.userGuideInheritance.examples)) {
|
|
29209
29587
|
if (parentId === mappingId) {
|
|
29210
29588
|
const guidePath = ensureWithinRoot(resolve34(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
|
|
29211
|
-
if (
|
|
29589
|
+
if (existsSync56(guidePath)) {
|
|
29212
29590
|
const guideContent = readFileSync47(guidePath, "utf-8");
|
|
29213
29591
|
const guideFrontmatter = extractFrontmatter(guideContent);
|
|
29214
29592
|
if (!guideFrontmatter?.lastVerified || status === "STALE") {
|
|
@@ -29243,7 +29621,7 @@ function handleDocsCoverage(args2) {
|
|
|
29243
29621
|
const mappings = filterDomain ? docsMap.mappings.filter((m3) => m3.id === filterDomain) : docsMap.mappings;
|
|
29244
29622
|
for (const mapping of mappings) {
|
|
29245
29623
|
const helpPagePath = ensureWithinRoot(resolve34(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
|
|
29246
|
-
const exists =
|
|
29624
|
+
const exists = existsSync56(helpPagePath);
|
|
29247
29625
|
let hasContent = false;
|
|
29248
29626
|
let lineCount = 0;
|
|
29249
29627
|
let lastVerified = null;
|
|
@@ -29590,7 +29968,7 @@ var init_observability_tools = __esm({
|
|
|
29590
29968
|
});
|
|
29591
29969
|
|
|
29592
29970
|
// src/sentinel-db.ts
|
|
29593
|
-
import { existsSync as
|
|
29971
|
+
import { existsSync as existsSync57 } from "fs";
|
|
29594
29972
|
import { resolve as resolve35 } from "path";
|
|
29595
29973
|
function parsePortalScope(raw) {
|
|
29596
29974
|
if (!raw) return [];
|
|
@@ -29828,22 +30206,22 @@ function validateFeatures(db, domainFilter) {
|
|
|
29828
30206
|
const missingPages = [];
|
|
29829
30207
|
for (const comp of components) {
|
|
29830
30208
|
const absPath = resolve35(PROJECT_ROOT, comp.component_file);
|
|
29831
|
-
if (!
|
|
30209
|
+
if (!existsSync57(absPath)) {
|
|
29832
30210
|
missingComponents.push(comp.component_file);
|
|
29833
30211
|
}
|
|
29834
30212
|
}
|
|
29835
30213
|
for (const proc of procedures) {
|
|
29836
30214
|
const routerPath = resolve35(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
|
|
29837
|
-
if (!
|
|
30215
|
+
if (!existsSync57(routerPath)) {
|
|
29838
30216
|
missingProcedures.push({ router: proc.router_name, procedure: proc.procedure_name });
|
|
29839
30217
|
}
|
|
29840
30218
|
}
|
|
29841
30219
|
for (const page of pages) {
|
|
29842
30220
|
const routeToPath = page.page_route.replace(/^\/(portal-[^/]+\/)?/, "src/app/").replace(/\/$/, "") + "/page.tsx";
|
|
29843
30221
|
const absPath = resolve35(PROJECT_ROOT, routeToPath);
|
|
29844
|
-
if (page.page_route.startsWith("/") && !
|
|
30222
|
+
if (page.page_route.startsWith("/") && !existsSync57(absPath)) {
|
|
29845
30223
|
const altPath = resolve35(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
|
|
29846
|
-
if (!
|
|
30224
|
+
if (!existsSync57(altPath)) {
|
|
29847
30225
|
missingPages.push(page.page_route);
|
|
29848
30226
|
}
|
|
29849
30227
|
}
|
|
@@ -30369,8 +30747,8 @@ var init_sentinel_tools = __esm({
|
|
|
30369
30747
|
});
|
|
30370
30748
|
|
|
30371
30749
|
// src/sentinel-scanner.ts
|
|
30372
|
-
import { readFileSync as readFileSync48, existsSync as
|
|
30373
|
-
import { resolve as resolve36, join as
|
|
30750
|
+
import { readFileSync as readFileSync48, existsSync as existsSync58, readdirSync as readdirSync26, statSync as statSync19 } from "fs";
|
|
30751
|
+
import { resolve as resolve36, join as join35, basename as basename10, dirname as dirname26, relative as relative13 } from "path";
|
|
30374
30752
|
function inferDomain(filePath) {
|
|
30375
30753
|
const domains = getConfig().domains;
|
|
30376
30754
|
const path = filePath.toLowerCase();
|
|
@@ -30502,7 +30880,7 @@ function scanComponentExports(dataDb) {
|
|
|
30502
30880
|
const componentsBase = config.paths.components ?? config.paths.source + "/components";
|
|
30503
30881
|
const componentDirs = [];
|
|
30504
30882
|
const basePath = resolve36(projectRoot, componentsBase);
|
|
30505
|
-
if (
|
|
30883
|
+
if (existsSync58(basePath)) {
|
|
30506
30884
|
try {
|
|
30507
30885
|
const entries = readdirSync26(basePath, { withFileTypes: true });
|
|
30508
30886
|
for (const entry of entries) {
|
|
@@ -30515,7 +30893,7 @@ function scanComponentExports(dataDb) {
|
|
|
30515
30893
|
}
|
|
30516
30894
|
for (const dir of componentDirs) {
|
|
30517
30895
|
const absDir = resolve36(projectRoot, dir);
|
|
30518
|
-
if (!
|
|
30896
|
+
if (!existsSync58(absDir)) continue;
|
|
30519
30897
|
const files = walkDir2(absDir).filter((f2) => f2.endsWith(".tsx") || f2.endsWith(".ts"));
|
|
30520
30898
|
for (const file of files) {
|
|
30521
30899
|
const relPath = relative13(projectRoot, file);
|
|
@@ -30545,7 +30923,7 @@ function scanComponentExports(dataDb) {
|
|
|
30545
30923
|
if (hasHandlers && exportMatch) {
|
|
30546
30924
|
const componentName = exportMatch[1];
|
|
30547
30925
|
const domain = inferDomain(relPath);
|
|
30548
|
-
const subdomain = basename10(
|
|
30926
|
+
const subdomain = basename10(dirname26(relPath));
|
|
30549
30927
|
const featureKey = `component.${subdomain}.${componentName.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "")}`;
|
|
30550
30928
|
if (!annotations.some((a2) => a2.featureKey === featureKey)) {
|
|
30551
30929
|
features.push({
|
|
@@ -30570,7 +30948,7 @@ function walkDir2(dir) {
|
|
|
30570
30948
|
try {
|
|
30571
30949
|
const entries = readdirSync26(dir);
|
|
30572
30950
|
for (const entry of entries) {
|
|
30573
|
-
const fullPath =
|
|
30951
|
+
const fullPath = join35(dir, entry);
|
|
30574
30952
|
try {
|
|
30575
30953
|
const stat = statSync19(fullPath);
|
|
30576
30954
|
if (stat.isDirectory()) {
|
|
@@ -31280,7 +31658,7 @@ var init_prompt_analyzer = __esm({
|
|
|
31280
31658
|
});
|
|
31281
31659
|
|
|
31282
31660
|
// src/validation-engine.ts
|
|
31283
|
-
import { existsSync as
|
|
31661
|
+
import { existsSync as existsSync59, readFileSync as readFileSync49 } from "fs";
|
|
31284
31662
|
function p10(baseName) {
|
|
31285
31663
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
31286
31664
|
}
|
|
@@ -31311,7 +31689,7 @@ function validateFile(filePath, projectRoot) {
|
|
|
31311
31689
|
});
|
|
31312
31690
|
return checks;
|
|
31313
31691
|
}
|
|
31314
|
-
if (!
|
|
31692
|
+
if (!existsSync59(absPath)) {
|
|
31315
31693
|
checks.push({
|
|
31316
31694
|
name: "file_exists",
|
|
31317
31695
|
severity: "error",
|
|
@@ -31779,7 +32157,7 @@ var init_adr_generator = __esm({
|
|
|
31779
32157
|
});
|
|
31780
32158
|
|
|
31781
32159
|
// src/security-scorer.ts
|
|
31782
|
-
import { existsSync as
|
|
32160
|
+
import { existsSync as existsSync60, readFileSync as readFileSync50 } from "fs";
|
|
31783
32161
|
function p12(baseName) {
|
|
31784
32162
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
31785
32163
|
}
|
|
@@ -31803,7 +32181,7 @@ function scoreFileSecurity(filePath, projectRoot) {
|
|
|
31803
32181
|
}]
|
|
31804
32182
|
};
|
|
31805
32183
|
}
|
|
31806
|
-
if (!
|
|
32184
|
+
if (!existsSync60(absPath)) {
|
|
31807
32185
|
return { riskScore: 0, findings: [] };
|
|
31808
32186
|
}
|
|
31809
32187
|
let source;
|
|
@@ -32098,7 +32476,7 @@ var init_security_scorer = __esm({
|
|
|
32098
32476
|
});
|
|
32099
32477
|
|
|
32100
32478
|
// src/dependency-scorer.ts
|
|
32101
|
-
import { existsSync as
|
|
32479
|
+
import { existsSync as existsSync61, readFileSync as readFileSync51 } from "fs";
|
|
32102
32480
|
import { resolve as resolve37 } from "path";
|
|
32103
32481
|
function p13(baseName) {
|
|
32104
32482
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
@@ -32132,7 +32510,7 @@ function calculateDepRisk(factors) {
|
|
|
32132
32510
|
}
|
|
32133
32511
|
function getInstalledPackages(projectRoot) {
|
|
32134
32512
|
const pkgPath = resolve37(projectRoot, "package.json");
|
|
32135
|
-
if (!
|
|
32513
|
+
if (!existsSync61(pkgPath)) return /* @__PURE__ */ new Map();
|
|
32136
32514
|
try {
|
|
32137
32515
|
const pkg = JSON.parse(readFileSync51(pkgPath, "utf-8"));
|
|
32138
32516
|
const packages = /* @__PURE__ */ new Map();
|
|
@@ -32740,7 +33118,7 @@ var init_regression_detector = __esm({
|
|
|
32740
33118
|
|
|
32741
33119
|
// src/knowledge-indexer.ts
|
|
32742
33120
|
import { createHash as createHash14 } from "crypto";
|
|
32743
|
-
import { readFileSync as readFileSync52, readdirSync as readdirSync27, statSync as statSync20, existsSync as
|
|
33121
|
+
import { readFileSync as readFileSync52, readdirSync as readdirSync27, statSync as statSync20, existsSync as existsSync62 } from "fs";
|
|
32744
33122
|
import { resolve as resolve38, relative as relative14, basename as basename11, extname as extname2 } from "path";
|
|
32745
33123
|
function getKnowledgePaths() {
|
|
32746
33124
|
const resolved = getResolvedPaths();
|
|
@@ -33048,11 +33426,11 @@ function indexAllKnowledge(db) {
|
|
|
33048
33426
|
files.push(...memFiles);
|
|
33049
33427
|
} catch {
|
|
33050
33428
|
}
|
|
33051
|
-
if (
|
|
33429
|
+
if (existsSync62(paths.plansDir)) {
|
|
33052
33430
|
const planFiles = discoverMarkdownFiles(paths.plansDir);
|
|
33053
33431
|
files.push(...planFiles);
|
|
33054
33432
|
}
|
|
33055
|
-
if (
|
|
33433
|
+
if (existsSync62(paths.docsDir)) {
|
|
33056
33434
|
const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
|
|
33057
33435
|
const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
|
|
33058
33436
|
files.push(...docsFiles);
|
|
@@ -33095,7 +33473,7 @@ function indexAllKnowledge(db) {
|
|
|
33095
33473
|
} catch {
|
|
33096
33474
|
}
|
|
33097
33475
|
for (const filePath of files) {
|
|
33098
|
-
if (!
|
|
33476
|
+
if (!existsSync62(filePath)) continue;
|
|
33099
33477
|
const content = readFileSync52(filePath, "utf-8");
|
|
33100
33478
|
const hash = hashContent2(content);
|
|
33101
33479
|
const relPath = filePath.startsWith(paths.claudeDir) ? relative14(paths.claudeDir, filePath) : filePath.startsWith(paths.plansDir) ? "plans/" + relative14(paths.plansDir, filePath) : filePath.startsWith(paths.docsDir) ? "docs/" + relative14(paths.docsDir, filePath) : filePath.startsWith(paths.memoryDir) ? `memory/${relative14(paths.memoryDir, filePath)}` : basename11(filePath);
|
|
@@ -33231,10 +33609,10 @@ function isKnowledgeStale(db) {
|
|
|
33231
33609
|
files.push(...discoverMarkdownFiles(paths.memoryDir));
|
|
33232
33610
|
} catch {
|
|
33233
33611
|
}
|
|
33234
|
-
if (
|
|
33612
|
+
if (existsSync62(paths.plansDir)) {
|
|
33235
33613
|
files.push(...discoverMarkdownFiles(paths.plansDir));
|
|
33236
33614
|
}
|
|
33237
|
-
if (
|
|
33615
|
+
if (existsSync62(paths.docsDir)) {
|
|
33238
33616
|
const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
|
|
33239
33617
|
const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
|
|
33240
33618
|
files.push(...docsFiles);
|
|
@@ -33263,7 +33641,7 @@ var init_knowledge_indexer = __esm({
|
|
|
33263
33641
|
});
|
|
33264
33642
|
|
|
33265
33643
|
// src/knowledge-tools.ts
|
|
33266
|
-
import { readFileSync as readFileSync53, writeFileSync as writeFileSync14, appendFileSync as
|
|
33644
|
+
import { readFileSync as readFileSync53, writeFileSync as writeFileSync14, appendFileSync as appendFileSync5, readdirSync as readdirSync28 } from "fs";
|
|
33267
33645
|
import { resolve as resolve39, basename as basename12 } from "path";
|
|
33268
33646
|
function p16(baseName) {
|
|
33269
33647
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
@@ -34025,7 +34403,7 @@ function handleCorrect(db, args2) {
|
|
|
34025
34403
|
const after = existing.slice(archiveIdx);
|
|
34026
34404
|
writeFileSync14(correctionsPath, before + entry + after);
|
|
34027
34405
|
} else {
|
|
34028
|
-
|
|
34406
|
+
appendFileSync5(correctionsPath, entry);
|
|
34029
34407
|
}
|
|
34030
34408
|
const doc = db.prepare("SELECT id FROM knowledge_documents WHERE file_path LIKE ?").get("%corrections.md");
|
|
34031
34409
|
if (doc) {
|
|
@@ -34972,7 +35350,7 @@ var init_framework_supports = __esm({
|
|
|
34972
35350
|
});
|
|
34973
35351
|
|
|
34974
35352
|
// src/tools.ts
|
|
34975
|
-
import { readFileSync as readFileSync54, existsSync as
|
|
35353
|
+
import { readFileSync as readFileSync54, existsSync as existsSync64 } from "fs";
|
|
34976
35354
|
import { resolve as resolve40, basename as basename13 } from "path";
|
|
34977
35355
|
function prefix2() {
|
|
34978
35356
|
return getConfig().toolPrefix;
|
|
@@ -35392,7 +35770,7 @@ function handleContext(file, dataDb, codegraphDb) {
|
|
|
35392
35770
|
const resolvedPaths = getResolvedPaths();
|
|
35393
35771
|
const root = getProjectRoot();
|
|
35394
35772
|
const absFilePath = ensureWithinRoot(resolve40(resolvedPaths.srcDir, "..", file), root);
|
|
35395
|
-
if (
|
|
35773
|
+
if (existsSync64(absFilePath)) {
|
|
35396
35774
|
const fileContent = readFileSync54(absFilePath, "utf-8").slice(0, 3e3);
|
|
35397
35775
|
const keywords = [];
|
|
35398
35776
|
if (fileContent.includes("ctx.db")) keywords.push("database", "schema");
|
|
@@ -35831,7 +36209,7 @@ function handleSchema(args2) {
|
|
|
35831
36209
|
lines.push("");
|
|
35832
36210
|
const projectRoot = getProjectRoot();
|
|
35833
36211
|
const absPath = ensureWithinRoot(resolve40(projectRoot, file), projectRoot);
|
|
35834
|
-
if (!
|
|
36212
|
+
if (!existsSync64(absPath)) {
|
|
35835
36213
|
return text17(`File not found: ${file}`);
|
|
35836
36214
|
}
|
|
35837
36215
|
const source = readFileSync54(absPath, "utf-8");
|
|
@@ -36267,7 +36645,7 @@ var init_startup_health = __esm({
|
|
|
36267
36645
|
// src/server.ts
|
|
36268
36646
|
var server_exports = {};
|
|
36269
36647
|
import { readFileSync as readFileSync55 } from "fs";
|
|
36270
|
-
import { resolve as resolve41, dirname as
|
|
36648
|
+
import { resolve as resolve41, dirname as dirname27 } from "path";
|
|
36271
36649
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
36272
36650
|
function pruneMemoryOnStartup() {
|
|
36273
36651
|
try {
|
|
@@ -36310,7 +36688,9 @@ var init_server = __esm({
|
|
|
36310
36688
|
init_license();
|
|
36311
36689
|
init_server_dispatch();
|
|
36312
36690
|
init_startup_health();
|
|
36313
|
-
|
|
36691
|
+
init_sqlite_loader();
|
|
36692
|
+
init_config();
|
|
36693
|
+
__dirname5 = dirname27(fileURLToPath6(import.meta.url));
|
|
36314
36694
|
PKG_VERSION = (() => {
|
|
36315
36695
|
try {
|
|
36316
36696
|
const pkg = JSON.parse(readFileSync55(resolve41(__dirname5, "..", "package.json"), "utf-8"));
|
|
@@ -36321,7 +36701,14 @@ var init_server = __esm({
|
|
|
36321
36701
|
})();
|
|
36322
36702
|
dispatcher = createDispatcher({ serverInfoVersion: PKG_VERSION });
|
|
36323
36703
|
try {
|
|
36324
|
-
|
|
36704
|
+
const memoryDbPath = getResolvedPaths().memoryDbPath;
|
|
36705
|
+
const verdict = probeMemoryDbUsable({ dbPath: memoryDbPath, selfHeal: true });
|
|
36706
|
+
if (!verdict.ok) {
|
|
36707
|
+
throw new FatalStartupError(
|
|
36708
|
+
`the memory database engine is unusable (reason: ${verdict.reason}). ${verdict.detail ? verdict.detail.trim() + " " : ""}${NATIVE_DB_REMEDY}`
|
|
36709
|
+
);
|
|
36710
|
+
}
|
|
36711
|
+
assertMemoryEngineHealthy(() => openDatabase(memoryDbPath));
|
|
36325
36712
|
} catch (error) {
|
|
36326
36713
|
if (error instanceof FatalStartupError) {
|
|
36327
36714
|
process.stderr.write(`massu: FATAL \u2014 ${error.message}
|
|
@@ -36609,7 +36996,7 @@ var config_upgrade_exports = {};
|
|
|
36609
36996
|
__export(config_upgrade_exports, {
|
|
36610
36997
|
runConfigUpgrade: () => runConfigUpgrade
|
|
36611
36998
|
});
|
|
36612
|
-
import { existsSync as
|
|
36999
|
+
import { existsSync as existsSync65, readFileSync as readFileSync56, writeFileSync as writeFileSync15, copyFileSync as copyFileSync3, unlinkSync as unlinkSync6 } from "fs";
|
|
36613
37000
|
import { resolve as resolve42 } from "path";
|
|
36614
37001
|
import { parse as parseYaml6 } from "yaml";
|
|
36615
37002
|
async function runConfigUpgrade(opts = {}) {
|
|
@@ -36621,7 +37008,7 @@ async function runConfigUpgrade(opts = {}) {
|
|
|
36621
37008
|
const err = opts.silent ? () => {
|
|
36622
37009
|
} : (s) => process.stderr.write(s);
|
|
36623
37010
|
if (opts.rollback) {
|
|
36624
|
-
if (!
|
|
37011
|
+
if (!existsSync65(bakPath)) {
|
|
36625
37012
|
const message = `No backup found at ${bakPath}`;
|
|
36626
37013
|
err(message + "\n");
|
|
36627
37014
|
return { exitCode: 1, action: "none", message };
|
|
@@ -36637,7 +37024,7 @@ async function runConfigUpgrade(opts = {}) {
|
|
|
36637
37024
|
return { exitCode: 2, action: "none", message };
|
|
36638
37025
|
}
|
|
36639
37026
|
}
|
|
36640
|
-
if (!
|
|
37027
|
+
if (!existsSync65(configPath)) {
|
|
36641
37028
|
const message = "massu.config.yaml not found. Run: npx massu init";
|
|
36642
37029
|
err(message + "\n");
|
|
36643
37030
|
return { exitCode: 1, action: "none", message };
|
|
@@ -36701,7 +37088,7 @@ var config_check_drift_exports = {};
|
|
|
36701
37088
|
__export(config_check_drift_exports, {
|
|
36702
37089
|
runConfigCheckDrift: () => runConfigCheckDrift
|
|
36703
37090
|
});
|
|
36704
|
-
import { existsSync as
|
|
37091
|
+
import { existsSync as existsSync66, readFileSync as readFileSync57 } from "fs";
|
|
36705
37092
|
import { resolve as resolve43 } from "path";
|
|
36706
37093
|
import { parse as parseYaml7 } from "yaml";
|
|
36707
37094
|
function renderChanges(changes) {
|
|
@@ -36715,7 +37102,7 @@ async function runConfigCheckDrift(opts = {}) {
|
|
|
36715
37102
|
} : (s) => process.stdout.write(s);
|
|
36716
37103
|
const err = opts.silent ? () => {
|
|
36717
37104
|
} : (s) => process.stderr.write(s);
|
|
36718
|
-
if (!
|
|
37105
|
+
if (!existsSync66(configPath)) {
|
|
36719
37106
|
const message = "massu.config.yaml not found. Run: npx massu init";
|
|
36720
37107
|
err(message + "\n");
|
|
36721
37108
|
return {
|
|
@@ -36797,10 +37184,10 @@ var init_config_check_drift = __esm({
|
|
|
36797
37184
|
|
|
36798
37185
|
// src/cli.ts
|
|
36799
37186
|
import { readFileSync as readFileSync58 } from "fs";
|
|
36800
|
-
import { resolve as resolve44, dirname as
|
|
37187
|
+
import { resolve as resolve44, dirname as dirname28 } from "path";
|
|
36801
37188
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
36802
37189
|
var __filename5 = fileURLToPath7(import.meta.url);
|
|
36803
|
-
var __dirname6 =
|
|
37190
|
+
var __dirname6 = dirname28(__filename5);
|
|
36804
37191
|
var args = process.argv.slice(2);
|
|
36805
37192
|
var subcommand = args[0];
|
|
36806
37193
|
async function main() {
|
|
@@ -36820,6 +37207,12 @@ async function main() {
|
|
|
36820
37207
|
await runLogin2(args.slice(1));
|
|
36821
37208
|
break;
|
|
36822
37209
|
}
|
|
37210
|
+
case "heal": {
|
|
37211
|
+
const { runHeal: runHeal2 } = await Promise.resolve().then(() => (init_heal(), heal_exports));
|
|
37212
|
+
const result = await runHeal2(args.slice(1));
|
|
37213
|
+
process.exit(result.exitCode);
|
|
37214
|
+
return;
|
|
37215
|
+
}
|
|
36823
37216
|
case "logout": {
|
|
36824
37217
|
const { runLogout: runLogout2 } = await Promise.resolve().then(() => (init_login(), login_exports));
|
|
36825
37218
|
await runLogout2();
|