@massu/core 2.5.0 → 2.5.2
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 +231 -0
- package/commands/massu-bearings.md +1 -1
- package/commands/massu-recap.md +16 -5
- package/dist/cli.js +716 -258
- package/dist/hooks/auto-learning-pipeline.js +297 -56
- package/dist/hooks/classify-failure.js +323 -25
- package/dist/hooks/cost-tracker.js +47 -10
- package/dist/hooks/fix-detector.js +94 -10
- package/dist/hooks/incident-pipeline.js +77 -10
- package/dist/hooks/intent-suggester.js +77 -10
- package/dist/hooks/memory-recall.js +77 -10
- package/dist/hooks/post-edit-context.js +125 -11
- package/dist/hooks/post-tool-use.js +44 -7
- package/dist/hooks/pre-compact.js +43 -7
- package/dist/hooks/pre-delete-check.js +125 -13
- package/dist/hooks/pre-tool-use-gate.js +135 -17
- package/dist/hooks/quality-event.js +47 -10
- package/dist/hooks/rule-enforcement-pipeline.js +77 -10
- package/dist/hooks/security-gate.js +75 -10
- package/dist/hooks/session-end.js +43 -7
- package/dist/hooks/session-start.js +144 -38
- package/dist/hooks/user-prompt.js +43 -7
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1355,6 +1355,13 @@ var init_config = __esm({
|
|
|
1355
1355
|
});
|
|
1356
1356
|
LanguageFrameworkEntrySchema = z2.object({
|
|
1357
1357
|
framework: z2.string().optional(),
|
|
1358
|
+
// `source_dirs` is emitted by `init` (commands/init.ts:417) and existence-checked
|
|
1359
|
+
// by config validation (init.ts:868-876), but until 2026-08-14 it survived only via
|
|
1360
|
+
// `.passthrough()` — declared in the file, untyped in the schema. `lib/source-layout.ts`
|
|
1361
|
+
// derives the index builders' candidate set from it, so it is typed here rather than
|
|
1362
|
+
// read as an unknown: a consumer that has to shape-check its own SoT is one branch away
|
|
1363
|
+
// from silently contributing `undefined` to a path predicate.
|
|
1364
|
+
source_dirs: z2.array(z2.string()).optional(),
|
|
1358
1365
|
test_framework: z2.string().optional(),
|
|
1359
1366
|
test: z2.string().optional(),
|
|
1360
1367
|
runtime: z2.string().optional(),
|
|
@@ -1548,6 +1555,16 @@ var init_memory_vector = __esm({
|
|
|
1548
1555
|
}
|
|
1549
1556
|
});
|
|
1550
1557
|
|
|
1558
|
+
// src/lib/timestamps.ts
|
|
1559
|
+
function nowIso() {
|
|
1560
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1561
|
+
}
|
|
1562
|
+
var init_timestamps = __esm({
|
|
1563
|
+
"src/lib/timestamps.ts"() {
|
|
1564
|
+
"use strict";
|
|
1565
|
+
}
|
|
1566
|
+
});
|
|
1567
|
+
|
|
1551
1568
|
// src/db-backup.ts
|
|
1552
1569
|
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readdirSync as readdirSync2, statSync as statSync2, unlinkSync, copyFileSync, rmSync as rmSync4 } from "fs";
|
|
1553
1570
|
import { resolve as resolve3, join as join3, basename, dirname as dirname4 } from "path";
|
|
@@ -3972,8 +3989,8 @@ function linkSessionToTask(db, sessionId, taskId) {
|
|
|
3972
3989
|
}
|
|
3973
3990
|
function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantResponse, toolCallsJson, toolCallCount, promptTokens, responseTokens) {
|
|
3974
3991
|
const result = db.prepare(`
|
|
3975
|
-
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens)
|
|
3976
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3992
|
+
INSERT INTO conversation_turns (session_id, turn_number, user_prompt, assistant_response, tool_calls_json, tool_call_count, prompt_tokens, response_tokens, created_at)
|
|
3993
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3977
3994
|
`).run(
|
|
3978
3995
|
sessionId,
|
|
3979
3996
|
turnNumber,
|
|
@@ -3982,14 +3999,15 @@ function addConversationTurn(db, sessionId, turnNumber, userPrompt, assistantRes
|
|
|
3982
3999
|
toolCallsJson,
|
|
3983
4000
|
toolCallCount,
|
|
3984
4001
|
promptTokens,
|
|
3985
|
-
responseTokens
|
|
4002
|
+
responseTokens,
|
|
4003
|
+
nowIso()
|
|
3986
4004
|
);
|
|
3987
4005
|
return Number(result.lastInsertRowid);
|
|
3988
4006
|
}
|
|
3989
4007
|
function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, inputSize, outputSize, success, filesInvolved) {
|
|
3990
4008
|
db.prepare(`
|
|
3991
|
-
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved)
|
|
3992
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
4009
|
+
INSERT INTO tool_call_details (session_id, turn_number, tool_name, tool_input_summary, tool_input_size, tool_output_size, tool_success, files_involved, created_at)
|
|
4010
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3993
4011
|
`).run(
|
|
3994
4012
|
sessionId,
|
|
3995
4013
|
turnNumber,
|
|
@@ -3998,7 +4016,8 @@ function addToolCallDetail(db, sessionId, turnNumber, toolName, inputSummary, in
|
|
|
3998
4016
|
inputSize,
|
|
3999
4017
|
outputSize,
|
|
4000
4018
|
success ? 1 : 0,
|
|
4001
|
-
filesInvolved ? JSON.stringify(filesInvolved) : null
|
|
4019
|
+
filesInvolved ? JSON.stringify(filesInvolved) : null,
|
|
4020
|
+
nowIso()
|
|
4002
4021
|
);
|
|
4003
4022
|
}
|
|
4004
4023
|
function getLastProcessedLine(db, sessionId) {
|
|
@@ -4257,6 +4276,7 @@ var init_memory_db = __esm({
|
|
|
4257
4276
|
init_db_driver();
|
|
4258
4277
|
init_config();
|
|
4259
4278
|
init_memory_vector();
|
|
4279
|
+
init_timestamps();
|
|
4260
4280
|
init_db_backup();
|
|
4261
4281
|
init_rule_delivery();
|
|
4262
4282
|
init_rule_candidate_store();
|
|
@@ -9973,21 +9993,21 @@ var require_pattern = __commonJS({
|
|
|
9973
9993
|
exports.removeDuplicateSlashes = removeDuplicateSlashes;
|
|
9974
9994
|
function partitionAbsoluteAndRelative(patterns) {
|
|
9975
9995
|
const absolute = [];
|
|
9976
|
-
const
|
|
9996
|
+
const relative16 = [];
|
|
9977
9997
|
for (const pattern of patterns) {
|
|
9978
|
-
if (
|
|
9998
|
+
if (isAbsolute6(pattern)) {
|
|
9979
9999
|
absolute.push(pattern);
|
|
9980
10000
|
} else {
|
|
9981
|
-
|
|
10001
|
+
relative16.push(pattern);
|
|
9982
10002
|
}
|
|
9983
10003
|
}
|
|
9984
|
-
return [absolute,
|
|
10004
|
+
return [absolute, relative16];
|
|
9985
10005
|
}
|
|
9986
10006
|
exports.partitionAbsoluteAndRelative = partitionAbsoluteAndRelative;
|
|
9987
|
-
function
|
|
10007
|
+
function isAbsolute6(pattern) {
|
|
9988
10008
|
return path.isAbsolute(pattern);
|
|
9989
10009
|
}
|
|
9990
|
-
exports.isAbsolute =
|
|
10010
|
+
exports.isAbsolute = isAbsolute6;
|
|
9991
10011
|
}
|
|
9992
10012
|
});
|
|
9993
10013
|
|
|
@@ -11071,41 +11091,41 @@ var require_queue = __commonJS({
|
|
|
11071
11091
|
queue.drained = drained;
|
|
11072
11092
|
return queue;
|
|
11073
11093
|
function push(value) {
|
|
11074
|
-
var p19 = new Promise(function(
|
|
11094
|
+
var p19 = new Promise(function(resolve47, reject) {
|
|
11075
11095
|
pushCb(value, function(err, result) {
|
|
11076
11096
|
if (err) {
|
|
11077
11097
|
reject(err);
|
|
11078
11098
|
return;
|
|
11079
11099
|
}
|
|
11080
|
-
|
|
11100
|
+
resolve47(result);
|
|
11081
11101
|
});
|
|
11082
11102
|
});
|
|
11083
11103
|
p19.catch(noop);
|
|
11084
11104
|
return p19;
|
|
11085
11105
|
}
|
|
11086
11106
|
function unshift(value) {
|
|
11087
|
-
var p19 = new Promise(function(
|
|
11107
|
+
var p19 = new Promise(function(resolve47, reject) {
|
|
11088
11108
|
unshiftCb(value, function(err, result) {
|
|
11089
11109
|
if (err) {
|
|
11090
11110
|
reject(err);
|
|
11091
11111
|
return;
|
|
11092
11112
|
}
|
|
11093
|
-
|
|
11113
|
+
resolve47(result);
|
|
11094
11114
|
});
|
|
11095
11115
|
});
|
|
11096
11116
|
p19.catch(noop);
|
|
11097
11117
|
return p19;
|
|
11098
11118
|
}
|
|
11099
11119
|
function drained() {
|
|
11100
|
-
var p19 = new Promise(function(
|
|
11120
|
+
var p19 = new Promise(function(resolve47) {
|
|
11101
11121
|
process.nextTick(function() {
|
|
11102
11122
|
if (queue.idle()) {
|
|
11103
|
-
|
|
11123
|
+
resolve47();
|
|
11104
11124
|
} else {
|
|
11105
11125
|
var previousDrain = queue.drain;
|
|
11106
11126
|
queue.drain = function() {
|
|
11107
11127
|
if (typeof previousDrain === "function") previousDrain();
|
|
11108
|
-
|
|
11128
|
+
resolve47();
|
|
11109
11129
|
queue.drain = previousDrain;
|
|
11110
11130
|
};
|
|
11111
11131
|
}
|
|
@@ -11591,9 +11611,9 @@ var require_stream3 = __commonJS({
|
|
|
11591
11611
|
});
|
|
11592
11612
|
}
|
|
11593
11613
|
_getStat(filepath) {
|
|
11594
|
-
return new Promise((
|
|
11614
|
+
return new Promise((resolve47, reject) => {
|
|
11595
11615
|
this._stat(filepath, this._fsStatSettings, (error, stats) => {
|
|
11596
|
-
return error === null ?
|
|
11616
|
+
return error === null ? resolve47(stats) : reject(error);
|
|
11597
11617
|
});
|
|
11598
11618
|
});
|
|
11599
11619
|
}
|
|
@@ -11617,10 +11637,10 @@ var require_async5 = __commonJS({
|
|
|
11617
11637
|
this._readerStream = new stream_1.default(this._settings);
|
|
11618
11638
|
}
|
|
11619
11639
|
dynamic(root, options) {
|
|
11620
|
-
return new Promise((
|
|
11640
|
+
return new Promise((resolve47, reject) => {
|
|
11621
11641
|
this._walkAsync(root, options, (error, entries) => {
|
|
11622
11642
|
if (error === null) {
|
|
11623
|
-
|
|
11643
|
+
resolve47(entries);
|
|
11624
11644
|
} else {
|
|
11625
11645
|
reject(error);
|
|
11626
11646
|
}
|
|
@@ -11630,10 +11650,10 @@ var require_async5 = __commonJS({
|
|
|
11630
11650
|
async static(patterns, options) {
|
|
11631
11651
|
const entries = [];
|
|
11632
11652
|
const stream = this._readerStream.static(patterns, options);
|
|
11633
|
-
return new Promise((
|
|
11653
|
+
return new Promise((resolve47, reject) => {
|
|
11634
11654
|
stream.once("error", reject);
|
|
11635
11655
|
stream.on("data", (entry) => entries.push(entry));
|
|
11636
|
-
stream.once("end", () =>
|
|
11656
|
+
stream.once("end", () => resolve47(entries));
|
|
11637
11657
|
});
|
|
11638
11658
|
}
|
|
11639
11659
|
};
|
|
@@ -16598,15 +16618,15 @@ function resolveHooksDir() {
|
|
|
16598
16618
|
}
|
|
16599
16619
|
return "node_modules/@massu/core/dist/hooks";
|
|
16600
16620
|
}
|
|
16601
|
-
function massuRuntimeDir(version) {
|
|
16602
|
-
return resolve9(
|
|
16621
|
+
function massuRuntimeDir(version, home = homedir7()) {
|
|
16622
|
+
return resolve9(home, ".massu", "runtime", version);
|
|
16603
16623
|
}
|
|
16604
|
-
function massuRuntimeCliPath(version) {
|
|
16605
|
-
return resolve9(massuRuntimeDir(version), "node_modules", "@massu", "core", "dist", "cli.js");
|
|
16624
|
+
function massuRuntimeCliPath(version, home = homedir7()) {
|
|
16625
|
+
return resolve9(massuRuntimeDir(version, home), "node_modules", "@massu", "core", "dist", "cli.js");
|
|
16606
16626
|
}
|
|
16607
|
-
function resolveMassuRuntimeCli(version) {
|
|
16627
|
+
function resolveMassuRuntimeCli(version, home = homedir7()) {
|
|
16608
16628
|
if (process.env.MASSU_NO_NODE_DIRECT === "1") return null;
|
|
16609
|
-
const cli = massuRuntimeCliPath(version);
|
|
16629
|
+
const cli = massuRuntimeCliPath(version, home);
|
|
16610
16630
|
if (!existsSync19(cli)) return null;
|
|
16611
16631
|
try {
|
|
16612
16632
|
execFileSync2(process.execPath, [cli, "--version"], { stdio: "ignore", timeout: 2e4 });
|
|
@@ -16615,10 +16635,10 @@ function resolveMassuRuntimeCli(version) {
|
|
|
16615
16635
|
return null;
|
|
16616
16636
|
}
|
|
16617
16637
|
}
|
|
16618
|
-
function materializeMassuRuntime(version) {
|
|
16638
|
+
function materializeMassuRuntime(version, home = homedir7()) {
|
|
16619
16639
|
if (process.env.MASSU_NO_NODE_DIRECT === "1") return false;
|
|
16620
|
-
if (resolveMassuRuntimeCli(version) !== null) return true;
|
|
16621
|
-
const dir = massuRuntimeDir(version);
|
|
16640
|
+
if (resolveMassuRuntimeCli(version, home) !== null) return true;
|
|
16641
|
+
const dir = massuRuntimeDir(version, home);
|
|
16622
16642
|
try {
|
|
16623
16643
|
mkdirSync10(dir, { recursive: true });
|
|
16624
16644
|
execFileSync2(
|
|
@@ -16629,10 +16649,10 @@ function materializeMassuRuntime(version) {
|
|
|
16629
16649
|
} catch {
|
|
16630
16650
|
return false;
|
|
16631
16651
|
}
|
|
16632
|
-
return resolveMassuRuntimeCli(version) !== null;
|
|
16652
|
+
return resolveMassuRuntimeCli(version, home) !== null;
|
|
16633
16653
|
}
|
|
16634
|
-
function massuShimPath() {
|
|
16635
|
-
return resolve9(
|
|
16654
|
+
function massuShimPath(home = homedir7()) {
|
|
16655
|
+
return resolve9(home, ".massu", "bin", "massu-hook");
|
|
16636
16656
|
}
|
|
16637
16657
|
function massuShimBody() {
|
|
16638
16658
|
return `#!/bin/sh
|
|
@@ -16657,10 +16677,10 @@ fi
|
|
|
16657
16677
|
exec npx -y "@massu/core@\${MASSU_VERSION}" "$@"
|
|
16658
16678
|
`;
|
|
16659
16679
|
}
|
|
16660
|
-
function installMassuShim() {
|
|
16680
|
+
function installMassuShim(home = homedir7()) {
|
|
16661
16681
|
if (process.env.MASSU_NO_NODE_DIRECT === "1") return false;
|
|
16662
16682
|
if (process.platform === "win32") return false;
|
|
16663
|
-
const shim = massuShimPath();
|
|
16683
|
+
const shim = massuShimPath(home);
|
|
16664
16684
|
try {
|
|
16665
16685
|
mkdirSync10(dirname12(shim), { recursive: true });
|
|
16666
16686
|
writeFileSync5(shim, massuShimBody(), "utf-8");
|
|
@@ -16676,10 +16696,10 @@ function installMassuShim() {
|
|
|
16676
16696
|
return status === 2;
|
|
16677
16697
|
}
|
|
16678
16698
|
}
|
|
16679
|
-
function resolveMassuShim() {
|
|
16699
|
+
function resolveMassuShim(home = homedir7()) {
|
|
16680
16700
|
if (process.env.MASSU_NO_NODE_DIRECT === "1") return null;
|
|
16681
16701
|
if (process.platform === "win32") return null;
|
|
16682
|
-
const shim = massuShimPath();
|
|
16702
|
+
const shim = massuShimPath(home);
|
|
16683
16703
|
if (!existsSync19(shim)) return null;
|
|
16684
16704
|
try {
|
|
16685
16705
|
execFileSync2(shim, [], { stdio: "ignore", timeout: 1e4 });
|
|
@@ -16695,9 +16715,8 @@ function hookCmd(version, hookName, shim) {
|
|
|
16695
16715
|
function getHookTimeout(name) {
|
|
16696
16716
|
return HOOK_TIMEOUTS[name] ?? 5;
|
|
16697
16717
|
}
|
|
16698
|
-
function buildHooksConfig(_hooksDir) {
|
|
16718
|
+
function buildHooksConfig(_hooksDir, shim = resolveMassuShim()) {
|
|
16699
16719
|
const version = getInstallerVersion();
|
|
16700
|
-
const shim = resolveMassuShim();
|
|
16701
16720
|
return {
|
|
16702
16721
|
SessionStart: [
|
|
16703
16722
|
{
|
|
@@ -18154,7 +18173,7 @@ function checkHookFiles(projectRoot) {
|
|
|
18154
18173
|
}
|
|
18155
18174
|
async function checkHookExecution(projectRoot, opts = {}) {
|
|
18156
18175
|
const { spawnSync: spawnSync6 } = await import("child_process");
|
|
18157
|
-
const { mkdtempSync: mkdtempSync2, copyFileSync: copyFileSync4, rmSync:
|
|
18176
|
+
const { mkdtempSync: mkdtempSync2, copyFileSync: copyFileSync4, rmSync: rmSync13 } = await import("fs");
|
|
18158
18177
|
const { tmpdir: tmpdir2 } = await import("os");
|
|
18159
18178
|
const node = opts.node ?? process.execPath;
|
|
18160
18179
|
const timeoutMs = opts.timeoutMs ?? 1e4;
|
|
@@ -18208,7 +18227,7 @@ async function checkHookExecution(projectRoot, opts = {}) {
|
|
|
18208
18227
|
};
|
|
18209
18228
|
} finally {
|
|
18210
18229
|
try {
|
|
18211
|
-
|
|
18230
|
+
rmSync13(tmpCwd, { recursive: true, force: true });
|
|
18212
18231
|
} catch {
|
|
18213
18232
|
}
|
|
18214
18233
|
}
|
|
@@ -20429,8 +20448,8 @@ function renderRegion(content, lines) {
|
|
|
20429
20448
|
${lines.join("\n")}
|
|
20430
20449
|
` : "\n";
|
|
20431
20450
|
if (state.kind === "absent") {
|
|
20432
|
-
const
|
|
20433
|
-
return `${content}${
|
|
20451
|
+
const sep4 = content.endsWith("\n") ? "" : "\n";
|
|
20452
|
+
return `${content}${sep4}
|
|
20434
20453
|
${BEGIN_SENTINEL}${body}${END_SENTINEL}
|
|
20435
20454
|
`;
|
|
20436
20455
|
}
|
|
@@ -20573,7 +20592,7 @@ import { createHash as createHash8 } from "crypto";
|
|
|
20573
20592
|
function renderMemoryFiles(db, candidates, opts) {
|
|
20574
20593
|
const config = opts.config ?? resolveMemoryFilesConfig();
|
|
20575
20594
|
if (!config.renderEnabled) {
|
|
20576
|
-
return EMPTY("render_disabled");
|
|
20595
|
+
return EMPTY("render_disabled", candidates.length);
|
|
20577
20596
|
}
|
|
20578
20597
|
const dryRun = opts.dryRun === true;
|
|
20579
20598
|
const now = opts.now ?? Date.now();
|
|
@@ -20582,7 +20601,7 @@ function renderMemoryFiles(db, candidates, opts) {
|
|
|
20582
20601
|
});
|
|
20583
20602
|
const { memoryDir } = opts;
|
|
20584
20603
|
if (!existsSync33(memoryDir)) {
|
|
20585
|
-
return EMPTY("no_memory_dir");
|
|
20604
|
+
return EMPTY("no_memory_dir", candidates.length);
|
|
20586
20605
|
}
|
|
20587
20606
|
try {
|
|
20588
20607
|
return withMemoryIndexLock(
|
|
@@ -20591,10 +20610,10 @@ function renderMemoryFiles(db, candidates, opts) {
|
|
|
20591
20610
|
);
|
|
20592
20611
|
} catch (err) {
|
|
20593
20612
|
if (err instanceof MemoryIndexLockBusy) {
|
|
20594
|
-
return EMPTY("lock_busy");
|
|
20613
|
+
return EMPTY("lock_busy", candidates.length);
|
|
20595
20614
|
}
|
|
20596
20615
|
audit("memory_file_render_refused", { reason: "unexpected_error", error: String(err) });
|
|
20597
|
-
return EMPTY("error");
|
|
20616
|
+
return EMPTY("error", candidates.length);
|
|
20598
20617
|
}
|
|
20599
20618
|
}
|
|
20600
20619
|
function renderLocked(db, candidates, o) {
|
|
@@ -20604,11 +20623,14 @@ function renderLocked(db, candidates, o) {
|
|
|
20604
20623
|
try {
|
|
20605
20624
|
tombstones = readTombstones(memoryDir);
|
|
20606
20625
|
} catch {
|
|
20607
|
-
return EMPTY("tombstone_ledger_unreadable");
|
|
20626
|
+
return EMPTY("tombstone_ledger_unreadable", candidates.length);
|
|
20608
20627
|
}
|
|
20609
20628
|
const ownerOf = relPathOwnerLookup(db);
|
|
20610
20629
|
const planned = [];
|
|
20611
|
-
|
|
20630
|
+
let unchanged = 0;
|
|
20631
|
+
let capped = 0;
|
|
20632
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
20633
|
+
const c2 = candidates[i];
|
|
20612
20634
|
if (!isLocalOrigin(c2.origin)) {
|
|
20613
20635
|
refusals.push({ name: c2.name, reason: "non_local_origin", detail: c2.origin || "unknown" });
|
|
20614
20636
|
audit("memory_file_render_refused", { name: c2.name, reason: "non_local_origin" });
|
|
@@ -20664,7 +20686,10 @@ function renderLocked(db, candidates, o) {
|
|
|
20664
20686
|
continue;
|
|
20665
20687
|
}
|
|
20666
20688
|
const next = composeFile(c2, home);
|
|
20667
|
-
if (next !== null && next === existing)
|
|
20689
|
+
if (next !== null && next === existing) {
|
|
20690
|
+
unchanged++;
|
|
20691
|
+
continue;
|
|
20692
|
+
}
|
|
20668
20693
|
}
|
|
20669
20694
|
const content = composeFile(c2, home);
|
|
20670
20695
|
if (content === null) {
|
|
@@ -20672,9 +20697,24 @@ function renderLocked(db, candidates, o) {
|
|
|
20672
20697
|
continue;
|
|
20673
20698
|
}
|
|
20674
20699
|
planned.push({ c: c2, relPath, absPath, content });
|
|
20675
|
-
if (planned.length >= config.renderMaxFilesPerSession)
|
|
20700
|
+
if (planned.length >= config.renderMaxFilesPerSession) {
|
|
20701
|
+
capped = candidates.length - (i + 1);
|
|
20702
|
+
break;
|
|
20703
|
+
}
|
|
20704
|
+
}
|
|
20705
|
+
const accounted = planned.length + refusals.length + unchanged + capped;
|
|
20706
|
+
if (accounted !== candidates.length) {
|
|
20707
|
+
audit("memory_file_render_refused", {
|
|
20708
|
+
reason: "accounting_leak",
|
|
20709
|
+
considered: candidates.length,
|
|
20710
|
+
accounted
|
|
20711
|
+
});
|
|
20712
|
+
throw new RenderAccountingLeak(
|
|
20713
|
+
`render did not account for every candidate: considered=${candidates.length} but accounted=${accounted} (planned=${planned.length} refused=${refusals.length} unchanged=${unchanged} capped=${capped}). A candidate was dropped without a bucket.`
|
|
20714
|
+
);
|
|
20676
20715
|
}
|
|
20677
20716
|
const indexLines = buildIndexLines(db, memoryDir, planned, config);
|
|
20717
|
+
const books = { considered: candidates.length, unchanged, capped };
|
|
20678
20718
|
if (dryRun) {
|
|
20679
20719
|
return {
|
|
20680
20720
|
enabled: true,
|
|
@@ -20682,11 +20722,20 @@ function renderLocked(db, candidates, o) {
|
|
|
20682
20722
|
written: planned.map((p19) => p19.relPath),
|
|
20683
20723
|
refusals,
|
|
20684
20724
|
indexLines,
|
|
20685
|
-
bytesWritten: 0
|
|
20725
|
+
bytesWritten: 0,
|
|
20726
|
+
...books
|
|
20686
20727
|
};
|
|
20687
20728
|
}
|
|
20688
20729
|
if (planned.length === 0 && indexLines.length === 0) {
|
|
20689
|
-
return {
|
|
20730
|
+
return {
|
|
20731
|
+
enabled: true,
|
|
20732
|
+
dryRun: false,
|
|
20733
|
+
written: [],
|
|
20734
|
+
refusals,
|
|
20735
|
+
indexLines: [],
|
|
20736
|
+
bytesWritten: 0,
|
|
20737
|
+
...books
|
|
20738
|
+
};
|
|
20690
20739
|
}
|
|
20691
20740
|
if (!hasFreshBackup(memoryDir, home)) {
|
|
20692
20741
|
try {
|
|
@@ -20696,7 +20745,7 @@ function renderLocked(db, candidates, o) {
|
|
|
20696
20745
|
reason: "backup_failed",
|
|
20697
20746
|
error: err instanceof BackupError ? err.message : String(err)
|
|
20698
20747
|
});
|
|
20699
|
-
return EMPTY("backup_failed");
|
|
20748
|
+
return EMPTY("backup_failed", candidates.length);
|
|
20700
20749
|
}
|
|
20701
20750
|
}
|
|
20702
20751
|
const indexPath = o.indexPath ?? join19(memoryDir, "MEMORY.md");
|
|
@@ -20764,7 +20813,10 @@ function renderLocked(db, candidates, o) {
|
|
|
20764
20813
|
errors: restored.errors
|
|
20765
20814
|
});
|
|
20766
20815
|
return {
|
|
20767
|
-
...EMPTY("rollback_incomplete"),
|
|
20816
|
+
...EMPTY("rollback_incomplete", candidates.length),
|
|
20817
|
+
// The books were balanced before the write began; keep them rather than zeroing.
|
|
20818
|
+
unchanged,
|
|
20819
|
+
capped,
|
|
20768
20820
|
enabled: true,
|
|
20769
20821
|
refusals: [
|
|
20770
20822
|
...refusals,
|
|
@@ -20773,9 +20825,9 @@ function renderLocked(db, candidates, o) {
|
|
|
20773
20825
|
};
|
|
20774
20826
|
}
|
|
20775
20827
|
audit("memory_file_render_refused", { reason: "write_failed", error: String(err) });
|
|
20776
|
-
return { ...EMPTY("write_failed"), enabled: true, refusals };
|
|
20828
|
+
return { ...EMPTY("write_failed", candidates.length), enabled: true, refusals, unchanged, capped };
|
|
20777
20829
|
}
|
|
20778
|
-
return { enabled: true, dryRun: false, written, refusals, indexLines, bytesWritten };
|
|
20830
|
+
return { enabled: true, dryRun: false, written, refusals, indexLines, bytesWritten, ...books };
|
|
20779
20831
|
}
|
|
20780
20832
|
function sha2562(s) {
|
|
20781
20833
|
return createHash8("sha256").update(s, "utf8").digest("hex");
|
|
@@ -20844,7 +20896,7 @@ function buildIndexLines(db, memoryDir, planned, config) {
|
|
|
20844
20896
|
function oneLine(s) {
|
|
20845
20897
|
return s.replace(/[\r\n]+/g, " ").slice(0, 120);
|
|
20846
20898
|
}
|
|
20847
|
-
var EMPTY;
|
|
20899
|
+
var RenderAccountingLeak, EMPTY;
|
|
20848
20900
|
var init_memory_renderer = __esm({
|
|
20849
20901
|
"src/memory-renderer.ts"() {
|
|
20850
20902
|
"use strict";
|
|
@@ -20859,14 +20911,22 @@ var init_memory_renderer = __esm({
|
|
|
20859
20911
|
init_memory_index_region();
|
|
20860
20912
|
init_memory_files_config();
|
|
20861
20913
|
init_memory_file_ingest();
|
|
20862
|
-
|
|
20914
|
+
RenderAccountingLeak = class extends Error {
|
|
20915
|
+
};
|
|
20916
|
+
EMPTY = (reason, considered = 0) => ({
|
|
20863
20917
|
enabled: false,
|
|
20864
20918
|
dryRun: false,
|
|
20865
20919
|
written: [],
|
|
20866
20920
|
refusals: [],
|
|
20867
20921
|
indexLines: [],
|
|
20868
20922
|
bytesWritten: 0,
|
|
20869
|
-
skippedReason: reason
|
|
20923
|
+
skippedReason: reason,
|
|
20924
|
+
// An early gate refused, so every candidate was dropped — by that ONE named reason.
|
|
20925
|
+
// Reporting `considered` here is what distinguishes "nothing to do" from "walked away
|
|
20926
|
+
// from 59 rows because the lock was busy".
|
|
20927
|
+
considered,
|
|
20928
|
+
unchanged: 0,
|
|
20929
|
+
capped: 0
|
|
20870
20930
|
});
|
|
20871
20931
|
}
|
|
20872
20932
|
});
|
|
@@ -21057,25 +21117,46 @@ var init_memory_render_cli = __esm({
|
|
|
21057
21117
|
function loadRenderCandidates(db) {
|
|
21058
21118
|
const cfg = resolveMemoryFilesConfig();
|
|
21059
21119
|
const typePlaceholders = RENDERABLE_MEMORY_TYPES.map(() => "?").join(", ");
|
|
21120
|
+
const WHERE = `WHERE importance >= ?
|
|
21121
|
+
AND COALESCE(expired_at_epoch, 0) = 0
|
|
21122
|
+
AND type IN (${typePlaceholders})`;
|
|
21123
|
+
const { population } = db.prepare(`SELECT COUNT(*) AS population FROM observations ${WHERE}`).get(cfg.renderMinImportance, ...RENDERABLE_MEMORY_TYPES);
|
|
21060
21124
|
const rows = db.prepare(
|
|
21061
21125
|
`SELECT id, title, detail, importance, COALESCE(origin, 'local') AS origin
|
|
21062
21126
|
FROM observations
|
|
21063
|
-
|
|
21064
|
-
AND COALESCE(expired_at_epoch, 0) = 0
|
|
21065
|
-
AND type IN (${typePlaceholders})
|
|
21127
|
+
${WHERE}
|
|
21066
21128
|
ORDER BY importance DESC, created_at_epoch DESC
|
|
21067
|
-
LIMIT
|
|
21129
|
+
LIMIT ${CANDIDATE_WINDOW}`
|
|
21068
21130
|
).all(cfg.renderMinImportance, ...RENDERABLE_MEMORY_TYPES);
|
|
21069
|
-
|
|
21070
|
-
|
|
21071
|
-
|
|
21072
|
-
|
|
21073
|
-
|
|
21074
|
-
|
|
21075
|
-
|
|
21076
|
-
|
|
21131
|
+
const reingest = rows.filter((r2) => r2.title.startsWith("[memory-file]"));
|
|
21132
|
+
const kept = rows.filter((r2) => !r2.title.startsWith("[memory-file]"));
|
|
21133
|
+
const excluded = reingest.length ? [{ reason: EXCLUSION_MEMORY_FILE_REINGEST, count: reingest.length }] : [];
|
|
21134
|
+
const ledger = {
|
|
21135
|
+
population,
|
|
21136
|
+
windowed: rows.length,
|
|
21137
|
+
truncatedByWindow: Math.max(0, population - rows.length),
|
|
21138
|
+
excluded,
|
|
21139
|
+
returned: kept.length
|
|
21140
|
+
};
|
|
21141
|
+
const accounted = ledger.returned + ledger.truncatedByWindow + excluded.reduce((n, e2) => n + e2.count, 0);
|
|
21142
|
+
if (accounted !== population) {
|
|
21143
|
+
throw new CandidateAccountingLeak(
|
|
21144
|
+
`candidate ledger does not balance: population=${population} but accounted=${accounted} (returned=${ledger.returned} truncated=${ledger.truncatedByWindow} excluded=${JSON.stringify(excluded)}). A row was dropped without a bucket.`
|
|
21145
|
+
);
|
|
21146
|
+
}
|
|
21147
|
+
return {
|
|
21148
|
+
ledger,
|
|
21149
|
+
candidates: kept.map((r2) => ({
|
|
21150
|
+
observationId: r2.id,
|
|
21151
|
+
name: r2.title,
|
|
21152
|
+
title: r2.title,
|
|
21153
|
+
body: r2.detail ?? "",
|
|
21154
|
+
importance: r2.importance,
|
|
21155
|
+
origin: r2.origin
|
|
21156
|
+
}))
|
|
21157
|
+
};
|
|
21077
21158
|
}
|
|
21078
|
-
var RENDERABLE_MEMORY_TYPES;
|
|
21159
|
+
var RENDERABLE_MEMORY_TYPES, CANDIDATE_WINDOW, EXCLUSION_MEMORY_FILE_REINGEST, CandidateAccountingLeak;
|
|
21079
21160
|
var init_memory_render_candidates = __esm({
|
|
21080
21161
|
"src/memory-render-candidates.ts"() {
|
|
21081
21162
|
"use strict";
|
|
@@ -21086,6 +21167,10 @@ var init_memory_render_candidates = __esm({
|
|
|
21086
21167
|
"incident_near_miss",
|
|
21087
21168
|
"cr_violation"
|
|
21088
21169
|
];
|
|
21170
|
+
CANDIDATE_WINDOW = 50;
|
|
21171
|
+
EXCLUSION_MEMORY_FILE_REINGEST = "memory_file_reingest";
|
|
21172
|
+
CandidateAccountingLeak = class extends Error {
|
|
21173
|
+
};
|
|
21089
21174
|
}
|
|
21090
21175
|
});
|
|
21091
21176
|
|
|
@@ -21165,13 +21250,13 @@ function pruneNoiseObservations(db, opts = {}) {
|
|
|
21165
21250
|
const counts = countNoise(db);
|
|
21166
21251
|
if (dryRun || counts.total === 0) return { counts, expired: 0, dryRun };
|
|
21167
21252
|
const nowSec = opts.now ?? Math.floor(Date.now() / 1e3);
|
|
21168
|
-
const
|
|
21253
|
+
const nowIso2 = new Date(nowSec * 1e3).toISOString();
|
|
21169
21254
|
const res = db.prepare(
|
|
21170
21255
|
`UPDATE observations
|
|
21171
21256
|
SET expired_at = ?, expired_at_epoch = ?, valid_to = ?, valid_to_epoch = ?
|
|
21172
21257
|
WHERE COALESCE(expired_at_epoch, 0) = 0
|
|
21173
21258
|
AND ((${DA_PREDICATE}) OR (${FT_DECISION_PREDICATE}) OR (${DB_PREDICATE}))`
|
|
21174
|
-
).run(
|
|
21259
|
+
).run(nowIso2, nowSec, nowIso2, nowSec);
|
|
21175
21260
|
return { counts, expired: res.changes, dryRun: false };
|
|
21176
21261
|
}
|
|
21177
21262
|
var DA_PREDICATE, FT_DECISION_PREDICATE, DB_PREDICATE;
|
|
@@ -21216,7 +21301,7 @@ async function runMemoryRenderCli(sub, args2) {
|
|
|
21216
21301
|
};
|
|
21217
21302
|
}
|
|
21218
21303
|
const cfg = resolveMemoryFilesConfig();
|
|
21219
|
-
const candidates = loadRenderCandidates(db);
|
|
21304
|
+
const { candidates, ledger } = loadRenderCandidates(db);
|
|
21220
21305
|
const result = renderMemoryFiles(db, candidates, {
|
|
21221
21306
|
memoryDir,
|
|
21222
21307
|
dryRun: true,
|
|
@@ -21231,6 +21316,19 @@ async function runMemoryRenderCli(sub, args2) {
|
|
|
21231
21316
|
}
|
|
21232
21317
|
lines.push(`Massu would write ${result.written.length} file(s):`);
|
|
21233
21318
|
for (const f2 of result.written) lines.push(` ${f2}`);
|
|
21319
|
+
lines.push("", "Where every row went:");
|
|
21320
|
+
lines.push(` candidate population (type+importance+expiry) : ${ledger.population}`);
|
|
21321
|
+
if (ledger.truncatedByWindow > 0) {
|
|
21322
|
+
lines.push(` truncated by the row window : ${ledger.truncatedByWindow}`);
|
|
21323
|
+
}
|
|
21324
|
+
for (const e2 of ledger.excluded) {
|
|
21325
|
+
lines.push(` excluded: ${e2.reason.padEnd(30)} : ${e2.count}`);
|
|
21326
|
+
}
|
|
21327
|
+
lines.push(` -> reached the renderer : ${ledger.returned}`);
|
|
21328
|
+
lines.push(
|
|
21329
|
+
` renderer: considered ${result.considered} = written ${result.written.length} + refused ${result.refusals.length} + unchanged ${result.unchanged} + capped ${result.capped}`
|
|
21330
|
+
);
|
|
21331
|
+
lines.push(` skipped: ${result.skippedReason ?? "(none)"}`);
|
|
21234
21332
|
if (result.indexLines.length > 0) {
|
|
21235
21333
|
lines.push("", "MEMORY.md \u2014 inside the massu:learned region only:");
|
|
21236
21334
|
for (const l2 of result.indexLines) lines.push(` ${l2}`);
|
|
@@ -22817,8 +22915,8 @@ function writeCustomDestination(opts, candidate, slug, targets) {
|
|
|
22817
22915
|
const rendered = renderTemplate2(entry.template, vars);
|
|
22818
22916
|
if (!existsSync38(dirname19(destPath))) mkdirSync17(dirname19(destPath), { recursive: true });
|
|
22819
22917
|
const pre = existsSync38(destPath) ? readFileSync29(destPath, "utf-8") : "";
|
|
22820
|
-
const
|
|
22821
|
-
writeFileSync11(destPath, pre +
|
|
22918
|
+
const sep4 = pre && !pre.endsWith("\n") ? "\n" : "";
|
|
22919
|
+
writeFileSync11(destPath, pre + sep4 + rendered + (rendered.endsWith("\n") ? "" : "\n"), "utf-8");
|
|
22822
22920
|
}
|
|
22823
22921
|
function dismissRuleCandidate(db, opts) {
|
|
22824
22922
|
if (!CANDIDATE_ID_PATTERN.test(opts.candidateId)) {
|
|
@@ -22871,8 +22969,8 @@ function dismissRuleCandidate(db, opts) {
|
|
|
22871
22969
|
const dismissedLog = join24(projectRoot, ".massu", "rule-candidates", ".dismissed.jsonl");
|
|
22872
22970
|
if (!existsSync38(dirname19(dismissedLog))) mkdirSync17(dirname19(dismissedLog), { recursive: true });
|
|
22873
22971
|
const pre = existsSync38(dismissedLog) ? readFileSync29(dismissedLog, "utf-8") : "";
|
|
22874
|
-
const
|
|
22875
|
-
writeFileSync11(dismissedLog, pre +
|
|
22972
|
+
const sep4 = pre && !pre.endsWith("\n") ? "\n" : "";
|
|
22973
|
+
writeFileSync11(dismissedLog, pre + sep4 + JSON.stringify({
|
|
22876
22974
|
id: opts.candidateId,
|
|
22877
22975
|
prompt_hash: candidate.prompt_hash,
|
|
22878
22976
|
score: candidate.score,
|
|
@@ -22903,8 +23001,8 @@ function appendFailureLog(projectRoot, entry) {
|
|
|
22903
23001
|
if (!existsSync38(dir)) mkdirSync17(dir, { recursive: true });
|
|
22904
23002
|
const logPath = join24(dir, ".failures.jsonl");
|
|
22905
23003
|
const pre = existsSync38(logPath) ? readFileSync29(logPath, "utf-8") : "";
|
|
22906
|
-
const
|
|
22907
|
-
writeFileSync11(logPath, pre +
|
|
23004
|
+
const sep4 = pre && !pre.endsWith("\n") ? "\n" : "";
|
|
23005
|
+
writeFileSync11(logPath, pre + sep4 + JSON.stringify(entry) + "\n", "utf-8");
|
|
22908
23006
|
} catch {
|
|
22909
23007
|
}
|
|
22910
23008
|
}
|
|
@@ -23389,7 +23487,7 @@ async function pullInstalledPackRules(db, opts = {}) {
|
|
|
23389
23487
|
}
|
|
23390
23488
|
const packs = parsePacks(envelope.packs_json);
|
|
23391
23489
|
const result = { ...ZERO };
|
|
23392
|
-
const
|
|
23490
|
+
const nowIso2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
23393
23491
|
for (const pack of packs) {
|
|
23394
23492
|
if (!isValidInstalledPack(pack)) continue;
|
|
23395
23493
|
for (const rawRule of pack.rules) {
|
|
@@ -23406,7 +23504,7 @@ async function pullInstalledPackRules(db, opts = {}) {
|
|
|
23406
23504
|
result.skipped += 1;
|
|
23407
23505
|
continue;
|
|
23408
23506
|
}
|
|
23409
|
-
materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack, signedOrgId,
|
|
23507
|
+
materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack, signedOrgId, nowIso2, opts.home ?? homedir20());
|
|
23410
23508
|
result.materialized += 1;
|
|
23411
23509
|
}
|
|
23412
23510
|
}
|
|
@@ -23453,7 +23551,7 @@ function alreadyApplied(db, promptHash) {
|
|
|
23453
23551
|
return false;
|
|
23454
23552
|
}
|
|
23455
23553
|
}
|
|
23456
|
-
function materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack, orgId,
|
|
23554
|
+
function materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack, orgId, nowIso2, home) {
|
|
23457
23555
|
const promptText = `${rule.title}: ${rule.description}`.replace(/\n+/g, " ").slice(0, 200) || `pack rule ${promptHash}`;
|
|
23458
23556
|
const draftText = rule.pattern ?? rule.check ?? rule.description;
|
|
23459
23557
|
const hardened = isExecutableDestination(rule.destination);
|
|
@@ -23469,7 +23567,7 @@ function materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack
|
|
|
23469
23567
|
score: 0,
|
|
23470
23568
|
signals: [],
|
|
23471
23569
|
prior_turn_files: [],
|
|
23472
|
-
timestamp:
|
|
23570
|
+
timestamp: nowIso2,
|
|
23473
23571
|
session_id: `pack:${pack.slug}`,
|
|
23474
23572
|
// Provenance (P2-002/P2-003): the applier's team-origin / hardened apply-gate
|
|
23475
23573
|
// keys on this. `origin:'pack'` rides the SAME gate as a team origin
|
|
@@ -23481,7 +23579,7 @@ function materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack
|
|
|
23481
23579
|
origin: "pack",
|
|
23482
23580
|
org_id: orgId,
|
|
23483
23581
|
promoted_by: `pack:${pack.slug}`,
|
|
23484
|
-
promoted_at:
|
|
23582
|
+
promoted_at: nowIso2,
|
|
23485
23583
|
signature_verified: true,
|
|
23486
23584
|
...applyMac ? { apply_mac: applyMac } : {},
|
|
23487
23585
|
pack_slug: pack.slug,
|
|
@@ -25289,12 +25387,102 @@ var init_gitToplevel = __esm({
|
|
|
25289
25387
|
}
|
|
25290
25388
|
});
|
|
25291
25389
|
|
|
25390
|
+
// src/watch/canary.ts
|
|
25391
|
+
import { mkdirSync as mkdirSync20, rmSync as rmSync10, writeFileSync as writeFileSync15 } from "fs";
|
|
25392
|
+
import { isAbsolute as isAbsolute3, relative as relative7, resolve as resolve20, sep as sep2 } from "path";
|
|
25393
|
+
function toRelative(projectRoot, p19) {
|
|
25394
|
+
const raw = String(p19);
|
|
25395
|
+
const rel = isAbsolute3(raw) ? relative7(projectRoot, raw) : raw;
|
|
25396
|
+
return rel.split(sep2).join("/");
|
|
25397
|
+
}
|
|
25398
|
+
function isCanaryPath(projectRoot, p19) {
|
|
25399
|
+
const rel = toRelative(projectRoot, p19);
|
|
25400
|
+
return rel === CANARY_DIR_REL || rel.startsWith(`${CANARY_DIR_REL}/`);
|
|
25401
|
+
}
|
|
25402
|
+
function canaryAwareIgnore(ignore, projectRoot) {
|
|
25403
|
+
const withoutMassu = ignore.filter((g2) => g2 !== "**/.massu/**");
|
|
25404
|
+
const massuButNotCanary = (p19) => {
|
|
25405
|
+
if (isCanaryPath(projectRoot, p19)) return false;
|
|
25406
|
+
const rel = toRelative(projectRoot, p19);
|
|
25407
|
+
return rel === ".massu" || rel.startsWith(".massu/") || rel.includes("/.massu/");
|
|
25408
|
+
};
|
|
25409
|
+
return [...withoutMassu, massuButNotCanary];
|
|
25410
|
+
}
|
|
25411
|
+
function createWatcherCanary(opts) {
|
|
25412
|
+
const projectRoot = resolve20(opts.projectRoot);
|
|
25413
|
+
const now = opts.now ?? Date.now;
|
|
25414
|
+
const filePath = resolve20(projectRoot, CANARY_FILE_REL);
|
|
25415
|
+
let outstanding = false;
|
|
25416
|
+
let echoed = false;
|
|
25417
|
+
let consecutiveDead = 0;
|
|
25418
|
+
let armedAt = 0;
|
|
25419
|
+
return {
|
|
25420
|
+
dirRel: CANARY_DIR_REL,
|
|
25421
|
+
filePath,
|
|
25422
|
+
get consecutiveDead() {
|
|
25423
|
+
return consecutiveDead;
|
|
25424
|
+
},
|
|
25425
|
+
arm() {
|
|
25426
|
+
if (outstanding) return "armed";
|
|
25427
|
+
try {
|
|
25428
|
+
mkdirSync20(resolve20(projectRoot, CANARY_DIR_REL), { recursive: true });
|
|
25429
|
+
writeFileSync15(filePath, `${now()}
|
|
25430
|
+
`, "utf-8");
|
|
25431
|
+
} catch {
|
|
25432
|
+
outstanding = false;
|
|
25433
|
+
echoed = false;
|
|
25434
|
+
return "unwritable";
|
|
25435
|
+
}
|
|
25436
|
+
outstanding = true;
|
|
25437
|
+
echoed = false;
|
|
25438
|
+
armedAt = now();
|
|
25439
|
+
return "armed";
|
|
25440
|
+
},
|
|
25441
|
+
observe(path) {
|
|
25442
|
+
if (!isCanaryPath(projectRoot, path)) return false;
|
|
25443
|
+
if (outstanding) echoed = true;
|
|
25444
|
+
return true;
|
|
25445
|
+
},
|
|
25446
|
+
evaluate() {
|
|
25447
|
+
if (!outstanding) return "idle";
|
|
25448
|
+
if (now() - armedAt < CANARY_MIN_GRACE_MS) return "idle";
|
|
25449
|
+
outstanding = false;
|
|
25450
|
+
if (echoed) {
|
|
25451
|
+
consecutiveDead = 0;
|
|
25452
|
+
return "alive";
|
|
25453
|
+
}
|
|
25454
|
+
consecutiveDead += 1;
|
|
25455
|
+
return "dead";
|
|
25456
|
+
},
|
|
25457
|
+
cleanup() {
|
|
25458
|
+
try {
|
|
25459
|
+
rmSync10(filePath, { force: true });
|
|
25460
|
+
} catch {
|
|
25461
|
+
}
|
|
25462
|
+
}
|
|
25463
|
+
};
|
|
25464
|
+
}
|
|
25465
|
+
var CANARY_DIR_REL, CANARY_FILE_REL, CANARY_DEAD_STRIKES_BEFORE_REBUILD, CANARY_MIN_GRACE_MS, CANARY_DEAD_LOG, CANARY_REBUILD_LOG, CANARY_UNWRITABLE_LOG, CANARY_RECOVERED_LOG;
|
|
25466
|
+
var init_canary = __esm({
|
|
25467
|
+
"src/watch/canary.ts"() {
|
|
25468
|
+
"use strict";
|
|
25469
|
+
CANARY_DIR_REL = ".massu/watch-canary";
|
|
25470
|
+
CANARY_FILE_REL = `${CANARY_DIR_REL}/liveness`;
|
|
25471
|
+
CANARY_DEAD_STRIKES_BEFORE_REBUILD = 2;
|
|
25472
|
+
CANARY_MIN_GRACE_MS = 2500;
|
|
25473
|
+
CANARY_DEAD_LOG = "[massu] watcher liveness canary NOT echoed";
|
|
25474
|
+
CANARY_REBUILD_LOG = "[massu] rebuilding file watcher";
|
|
25475
|
+
CANARY_UNWRITABLE_LOG = "[massu] watcher liveness canary could not be written";
|
|
25476
|
+
CANARY_RECOVERED_LOG = "[massu] file watcher recovered";
|
|
25477
|
+
}
|
|
25478
|
+
});
|
|
25479
|
+
|
|
25292
25480
|
// src/watch/lockfile-detector.ts
|
|
25293
25481
|
import { existsSync as existsSync45, statSync as statSync15 } from "fs";
|
|
25294
|
-
import { resolve as
|
|
25482
|
+
import { resolve as resolve21 } from "path";
|
|
25295
25483
|
function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WINDOW_MS) {
|
|
25296
25484
|
for (const lf of KNOWN_LOCKFILES) {
|
|
25297
|
-
const p19 =
|
|
25485
|
+
const p19 = resolve21(projectRoot, lf);
|
|
25298
25486
|
if (!existsSync45(p19)) continue;
|
|
25299
25487
|
try {
|
|
25300
25488
|
const stat = statSync15(p19);
|
|
@@ -25308,7 +25496,7 @@ function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WIN
|
|
|
25308
25496
|
function gitMidOperation(projectRoot) {
|
|
25309
25497
|
const sentinels = ["MERGE_HEAD", "REBASE_HEAD", "CHERRY_PICK_HEAD", "rebase-apply", "rebase-merge"];
|
|
25310
25498
|
for (const s of sentinels) {
|
|
25311
|
-
if (existsSync45(
|
|
25499
|
+
if (existsSync45(resolve21(projectRoot, ".git", s))) return true;
|
|
25312
25500
|
}
|
|
25313
25501
|
return false;
|
|
25314
25502
|
}
|
|
@@ -25507,13 +25695,13 @@ var init_paths = __esm({
|
|
|
25507
25695
|
});
|
|
25508
25696
|
|
|
25509
25697
|
// src/watch/state.ts
|
|
25510
|
-
import { closeSync as closeSync5, existsSync as existsSync46, fsyncSync as fsyncSync5, mkdirSync as
|
|
25511
|
-
import { dirname as dirname21, resolve as
|
|
25698
|
+
import { closeSync as closeSync5, existsSync as existsSync46, fsyncSync as fsyncSync5, mkdirSync as mkdirSync21, openSync as openSync5, readFileSync as readFileSync34, renameSync as renameSync5, rmSync as rmSync11, writeFileSync as writeFileSync16, writeSync as writeSync3 } from "fs";
|
|
25699
|
+
import { dirname as dirname21, resolve as resolve22 } from "path";
|
|
25512
25700
|
function watchStatePath(projectRoot) {
|
|
25513
|
-
return
|
|
25701
|
+
return resolve22(projectRoot, ".massu", "watch-state.json");
|
|
25514
25702
|
}
|
|
25515
25703
|
function backupStatePath(projectRoot) {
|
|
25516
|
-
return
|
|
25704
|
+
return resolve22(projectRoot, ".massu", "watch-state.v0.bak.json");
|
|
25517
25705
|
}
|
|
25518
25706
|
function readState(projectRoot) {
|
|
25519
25707
|
const path = watchStatePath(projectRoot);
|
|
@@ -25560,14 +25748,14 @@ function readState(projectRoot) {
|
|
|
25560
25748
|
}
|
|
25561
25749
|
function archiveCorrupt(projectRoot, content) {
|
|
25562
25750
|
const bak = backupStatePath(projectRoot);
|
|
25563
|
-
|
|
25564
|
-
|
|
25751
|
+
mkdirSync21(dirname21(bak), { recursive: true });
|
|
25752
|
+
writeFileSync16(bak, content, "utf-8");
|
|
25565
25753
|
}
|
|
25566
25754
|
function writeStateAtomic(projectRoot, state) {
|
|
25567
25755
|
const path = watchStatePath(projectRoot);
|
|
25568
25756
|
writeStateAtomicCounter = writeStateAtomicCounter + 1 >>> 0;
|
|
25569
25757
|
const tmp = `${path}.${process.pid}.${writeStateAtomicCounter}.tmp`;
|
|
25570
|
-
|
|
25758
|
+
mkdirSync21(dirname21(path), { recursive: true });
|
|
25571
25759
|
let renamed = false;
|
|
25572
25760
|
try {
|
|
25573
25761
|
const fd = openSync5(tmp, "w");
|
|
@@ -25583,7 +25771,7 @@ function writeStateAtomic(projectRoot, state) {
|
|
|
25583
25771
|
} finally {
|
|
25584
25772
|
if (!renamed && existsSync46(tmp)) {
|
|
25585
25773
|
try {
|
|
25586
|
-
|
|
25774
|
+
rmSync11(tmp, { force: true });
|
|
25587
25775
|
} catch {
|
|
25588
25776
|
}
|
|
25589
25777
|
}
|
|
@@ -25626,6 +25814,8 @@ var init_state = __esm({
|
|
|
25626
25814
|
|
|
25627
25815
|
// src/watch/daemon.ts
|
|
25628
25816
|
import * as chokidar from "chokidar";
|
|
25817
|
+
import { mkdirSync as mkdirSync22 } from "fs";
|
|
25818
|
+
import { resolve as resolve23 } from "path";
|
|
25629
25819
|
function readDaemonConfig(projectRoot) {
|
|
25630
25820
|
const cfg = getConfig();
|
|
25631
25821
|
const w2 = cfg.watch ?? {};
|
|
@@ -25659,6 +25849,10 @@ async function startDaemon(projectRoot, hooks) {
|
|
|
25659
25849
|
let tickTimer = null;
|
|
25660
25850
|
let stopped = false;
|
|
25661
25851
|
let runningRefresh = false;
|
|
25852
|
+
let canary = null;
|
|
25853
|
+
let watchSpec = null;
|
|
25854
|
+
let rebuilding = false;
|
|
25855
|
+
let watcherReady = false;
|
|
25662
25856
|
function clearDebounce() {
|
|
25663
25857
|
if (ctx.debounceTimer) {
|
|
25664
25858
|
clearTimeout(ctx.debounceTimer);
|
|
@@ -25765,6 +25959,110 @@ async function startDaemon(projectRoot, hooks) {
|
|
|
25765
25959
|
await watcher.close();
|
|
25766
25960
|
watcher = null;
|
|
25767
25961
|
}
|
|
25962
|
+
if (canary) {
|
|
25963
|
+
canary.cleanup();
|
|
25964
|
+
canary = null;
|
|
25965
|
+
}
|
|
25966
|
+
}
|
|
25967
|
+
function createWatcherInstance() {
|
|
25968
|
+
const spec = watchSpec;
|
|
25969
|
+
if (!spec) throw new Error("watch spec not derived before createWatcherInstance()");
|
|
25970
|
+
const w2 = chokidar.watch(spec.watch, {
|
|
25971
|
+
cwd: cfg.projectRoot,
|
|
25972
|
+
ignored: spec.ignored,
|
|
25973
|
+
ignoreInitial: true,
|
|
25974
|
+
persistent: true,
|
|
25975
|
+
awaitWriteFinish: false
|
|
25976
|
+
});
|
|
25977
|
+
w2.on("ready", () => {
|
|
25978
|
+
watcherReady = true;
|
|
25979
|
+
});
|
|
25980
|
+
w2.on("all", (event, path) => {
|
|
25981
|
+
if (canary?.observe(path)) return;
|
|
25982
|
+
if (!STACK_RELEVANT_EVENTS.has(event)) return;
|
|
25983
|
+
pushEvent(path);
|
|
25984
|
+
});
|
|
25985
|
+
w2.on("error", (err) => {
|
|
25986
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
25987
|
+
writeStderr(`[massu] chokidar error: ${msg}
|
|
25988
|
+
`);
|
|
25989
|
+
try {
|
|
25990
|
+
updateState(cfg.projectRoot, { lastError: `chokidar: ${msg}` });
|
|
25991
|
+
} catch {
|
|
25992
|
+
}
|
|
25993
|
+
});
|
|
25994
|
+
return w2;
|
|
25995
|
+
}
|
|
25996
|
+
function armCanary() {
|
|
25997
|
+
if (!canary || stopped) return;
|
|
25998
|
+
if (canary.arm() === "unwritable") {
|
|
25999
|
+
writeStderr(
|
|
26000
|
+
`${CANARY_UNWRITABLE_LOG} (${canary.filePath}); watcher liveness is UNKNOWN for this interval
|
|
26001
|
+
`
|
|
26002
|
+
);
|
|
26003
|
+
}
|
|
26004
|
+
}
|
|
26005
|
+
function isPowerOfTwo(n) {
|
|
26006
|
+
return n > 0 && (n & n - 1) === 0;
|
|
26007
|
+
}
|
|
26008
|
+
async function rebuildWatcher() {
|
|
26009
|
+
if (rebuilding || stopped || !watchSpec) return;
|
|
26010
|
+
rebuilding = true;
|
|
26011
|
+
try {
|
|
26012
|
+
const old = watcher;
|
|
26013
|
+
watcher = null;
|
|
26014
|
+
if (old) {
|
|
26015
|
+
try {
|
|
26016
|
+
await old.close();
|
|
26017
|
+
} catch {
|
|
26018
|
+
}
|
|
26019
|
+
}
|
|
26020
|
+
if (stopped) return;
|
|
26021
|
+
watcherReady = false;
|
|
26022
|
+
watcher = createWatcherInstance();
|
|
26023
|
+
} catch (err) {
|
|
26024
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
26025
|
+
writeStderr(`[massu] watcher rebuild FAILED: ${msg}
|
|
26026
|
+
`);
|
|
26027
|
+
} finally {
|
|
26028
|
+
rebuilding = false;
|
|
26029
|
+
}
|
|
26030
|
+
}
|
|
26031
|
+
function checkWatcherLiveness() {
|
|
26032
|
+
if (!canary || !watcher || rebuilding || !watcherReady) return;
|
|
26033
|
+
const priorDead = canary.consecutiveDead;
|
|
26034
|
+
const verdict = canary.evaluate();
|
|
26035
|
+
if (verdict === "dead") {
|
|
26036
|
+
const streak = canary.consecutiveDead;
|
|
26037
|
+
if (!isPowerOfTwo(streak)) return armCanary();
|
|
26038
|
+
writeStderr(
|
|
26039
|
+
`${CANARY_DEAD_LOG} after ${streak} consecutive interval(s): the watcher reported ready and emitted no error, but did not deliver its own canary write (${canary.filePath}). Treating it as DEAD and reconciling so changes are not silently missed.
|
|
26040
|
+
`
|
|
26041
|
+
);
|
|
26042
|
+
try {
|
|
26043
|
+
updateState(cfg.projectRoot, {
|
|
26044
|
+
lastError: `watcher liveness canary not echoed for ${streak} consecutive interval(s)`
|
|
26045
|
+
});
|
|
26046
|
+
} catch {
|
|
26047
|
+
}
|
|
26048
|
+
void forceReconciliation();
|
|
26049
|
+
if (streak >= CANARY_DEAD_STRIKES_BEFORE_REBUILD) {
|
|
26050
|
+
writeStderr(`${CANARY_REBUILD_LOG} after ${streak} dead interval(s) (close + re-watch)
|
|
26051
|
+
`);
|
|
26052
|
+
void rebuildWatcher();
|
|
26053
|
+
return;
|
|
26054
|
+
}
|
|
26055
|
+
return armCanary();
|
|
26056
|
+
}
|
|
26057
|
+
if (verdict === "alive" && priorDead > 0) {
|
|
26058
|
+
writeStderr(`${CANARY_RECOVERED_LOG} after ${priorDead} dead interval(s)
|
|
26059
|
+
`);
|
|
26060
|
+
try {
|
|
26061
|
+
updateState(cfg.projectRoot, { lastError: null });
|
|
26062
|
+
} catch {
|
|
26063
|
+
}
|
|
26064
|
+
}
|
|
26065
|
+
armCanary();
|
|
25768
26066
|
}
|
|
25769
26067
|
function tick() {
|
|
25770
26068
|
if (stopped) return;
|
|
@@ -25781,6 +26079,7 @@ async function startDaemon(projectRoot, hooks) {
|
|
|
25781
26079
|
`);
|
|
25782
26080
|
void forceReconciliation();
|
|
25783
26081
|
}
|
|
26082
|
+
checkWatcherLiveness();
|
|
25784
26083
|
}
|
|
25785
26084
|
if (!hooks.noWatcher) {
|
|
25786
26085
|
const cfgYaml = getConfig();
|
|
@@ -25805,23 +26104,16 @@ async function startDaemon(projectRoot, hooks) {
|
|
|
25805
26104
|
`[massu] watch surface: ${countLabel} files (cap ${cap}, opted-in: ${optedIn}, scan ${surfaceMs}ms, scope: ${globs.effectiveScope})
|
|
25806
26105
|
`
|
|
25807
26106
|
);
|
|
25808
|
-
|
|
25809
|
-
|
|
25810
|
-
|
|
25811
|
-
|
|
25812
|
-
|
|
25813
|
-
|
|
25814
|
-
|
|
25815
|
-
|
|
25816
|
-
|
|
25817
|
-
|
|
25818
|
-
writeStderr(`[massu] chokidar error: ${msg}
|
|
25819
|
-
`);
|
|
25820
|
-
try {
|
|
25821
|
-
updateState(cfg.projectRoot, { lastError: `chokidar: ${msg}` });
|
|
25822
|
-
} catch {
|
|
25823
|
-
}
|
|
25824
|
-
});
|
|
26107
|
+
canary = createWatcherCanary({ projectRoot: cfg.projectRoot, now });
|
|
26108
|
+
try {
|
|
26109
|
+
mkdirSync22(resolve23(cfg.projectRoot, canary.dirRel), { recursive: true });
|
|
26110
|
+
} catch {
|
|
26111
|
+
}
|
|
26112
|
+
watchSpec = {
|
|
26113
|
+
watch: [...globs.watch, canary.dirRel],
|
|
26114
|
+
ignored: canaryAwareIgnore(globs.ignore, cfg.projectRoot)
|
|
26115
|
+
};
|
|
26116
|
+
watcher = createWatcherInstance();
|
|
25825
26117
|
}
|
|
25826
26118
|
resetConfig();
|
|
25827
26119
|
try {
|
|
@@ -25833,15 +26125,16 @@ async function startDaemon(projectRoot, hooks) {
|
|
|
25833
26125
|
});
|
|
25834
26126
|
} catch {
|
|
25835
26127
|
}
|
|
25836
|
-
tickTimer = setInterval(tick, TICK_INTERVAL_MS);
|
|
26128
|
+
tickTimer = setInterval(tick, hooks.tickIntervalMs ?? TICK_INTERVAL_MS);
|
|
25837
26129
|
if (typeof tickTimer.unref === "function") tickTimer.unref();
|
|
25838
26130
|
return { pushEvent, flushNow, stop, forceReconciliation };
|
|
25839
26131
|
}
|
|
25840
|
-
var STORM_WINDOW_MS, DEEP_STORM_WINDOW_MS, STORM_WAIT_MS, DEEP_STORM_WAIT_MS, TICK_INTERVAL_MS, TICK_GAP_THRESHOLD_MS;
|
|
26132
|
+
var STORM_WINDOW_MS, DEEP_STORM_WINDOW_MS, STORM_WAIT_MS, DEEP_STORM_WAIT_MS, TICK_INTERVAL_MS, TICK_GAP_THRESHOLD_MS, STACK_RELEVANT_EVENTS;
|
|
25841
26133
|
var init_daemon = __esm({
|
|
25842
26134
|
"src/watch/daemon.ts"() {
|
|
25843
26135
|
"use strict";
|
|
25844
26136
|
init_config();
|
|
26137
|
+
init_canary();
|
|
25845
26138
|
init_lockfile_detector();
|
|
25846
26139
|
init_paths();
|
|
25847
26140
|
init_state();
|
|
@@ -25851,6 +26144,7 @@ var init_daemon = __esm({
|
|
|
25851
26144
|
DEEP_STORM_WAIT_MS = 12e4;
|
|
25852
26145
|
TICK_INTERVAL_MS = 1e4;
|
|
25853
26146
|
TICK_GAP_THRESHOLD_MS = 3e4;
|
|
26147
|
+
STACK_RELEVANT_EVENTS = /* @__PURE__ */ new Set(["add", "change", "unlink"]);
|
|
25854
26148
|
}
|
|
25855
26149
|
});
|
|
25856
26150
|
|
|
@@ -25863,8 +26157,8 @@ __export(watch_exports, {
|
|
|
25863
26157
|
runWatch: () => runWatch
|
|
25864
26158
|
});
|
|
25865
26159
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
25866
|
-
import { basename as basename8, dirname as dirname22, resolve as
|
|
25867
|
-
import { appendFileSync as appendFileSync4, existsSync as existsSync47, mkdirSync as
|
|
26160
|
+
import { basename as basename8, dirname as dirname22, resolve as resolve24 } from "path";
|
|
26161
|
+
import { appendFileSync as appendFileSync4, existsSync as existsSync47, mkdirSync as mkdirSync23, readFileSync as readFileSync35 } from "fs";
|
|
25868
26162
|
function parseFlags(args2) {
|
|
25869
26163
|
const out = {
|
|
25870
26164
|
foreground: false,
|
|
@@ -25889,7 +26183,7 @@ function parseFlags(args2) {
|
|
|
25889
26183
|
}
|
|
25890
26184
|
function findClaudeBg() {
|
|
25891
26185
|
const home = process.env.HOME ?? "";
|
|
25892
|
-
const fixed = home ?
|
|
26186
|
+
const fixed = home ? resolve24(home, ".claude", "bin", "claude-bg") : null;
|
|
25893
26187
|
if (fixed && existsSync47(fixed)) return fixed;
|
|
25894
26188
|
const which = spawnSync5("which", ["claude-bg"], { encoding: "utf-8" });
|
|
25895
26189
|
if (which.status === 0 && which.stdout) {
|
|
@@ -26033,7 +26327,7 @@ async function runForeground(root) {
|
|
|
26033
26327
|
}
|
|
26034
26328
|
throw err;
|
|
26035
26329
|
}
|
|
26036
|
-
return new Promise((
|
|
26330
|
+
return new Promise((resolve47) => {
|
|
26037
26331
|
const shutdown = async () => {
|
|
26038
26332
|
if (stopped) return;
|
|
26039
26333
|
stopped = true;
|
|
@@ -26043,7 +26337,7 @@ async function runForeground(root) {
|
|
|
26043
26337
|
process.chdir(priorCwd);
|
|
26044
26338
|
} catch {
|
|
26045
26339
|
}
|
|
26046
|
-
|
|
26340
|
+
resolve47({ exitCode: 0 });
|
|
26047
26341
|
};
|
|
26048
26342
|
process.on("SIGINT", () => {
|
|
26049
26343
|
void shutdown();
|
|
@@ -26104,12 +26398,12 @@ async function runOnQuiescent(projectRoot) {
|
|
|
26104
26398
|
);
|
|
26105
26399
|
}
|
|
26106
26400
|
function refreshLogPath(projectRoot) {
|
|
26107
|
-
return
|
|
26401
|
+
return resolve24(projectRoot, ".massu", "refresh-log.jsonl");
|
|
26108
26402
|
}
|
|
26109
26403
|
function appendRefreshLog(projectRoot, event) {
|
|
26110
26404
|
const path = refreshLogPath(projectRoot);
|
|
26111
26405
|
try {
|
|
26112
|
-
|
|
26406
|
+
mkdirSync23(dirname22(path), { recursive: true });
|
|
26113
26407
|
appendFileSync4(path, JSON.stringify(event) + "\n", "utf-8");
|
|
26114
26408
|
} catch {
|
|
26115
26409
|
}
|
|
@@ -26195,10 +26489,10 @@ import {
|
|
|
26195
26489
|
closeSync as closeSync6,
|
|
26196
26490
|
existsSync as existsSync48,
|
|
26197
26491
|
fsyncSync as fsyncSync6,
|
|
26198
|
-
mkdirSync as
|
|
26492
|
+
mkdirSync as mkdirSync24,
|
|
26199
26493
|
openSync as openSync6,
|
|
26200
26494
|
renameSync as renameSync6,
|
|
26201
|
-
rmSync as
|
|
26495
|
+
rmSync as rmSync12,
|
|
26202
26496
|
statSync as statSync16,
|
|
26203
26497
|
writeSync as writeSync4
|
|
26204
26498
|
} from "node:fs";
|
|
@@ -26212,7 +26506,7 @@ function atomicWrite(path, content, opts = {}) {
|
|
|
26212
26506
|
if (opts.ensureParentDirMode !== void 0) {
|
|
26213
26507
|
mkdirOpts.mode = opts.ensureParentDirMode;
|
|
26214
26508
|
}
|
|
26215
|
-
|
|
26509
|
+
mkdirSync24(parentDir, mkdirOpts);
|
|
26216
26510
|
}
|
|
26217
26511
|
const buf = typeof content === "string" ? Buffer.from(content, "utf-8") : content;
|
|
26218
26512
|
const openMode = opts.mode ?? 420;
|
|
@@ -26231,7 +26525,7 @@ function atomicWrite(path, content, opts = {}) {
|
|
|
26231
26525
|
} catch (err) {
|
|
26232
26526
|
if (existsSync48(tmpPath)) {
|
|
26233
26527
|
try {
|
|
26234
|
-
|
|
26528
|
+
rmSync12(tmpPath, { force: true });
|
|
26235
26529
|
} catch {
|
|
26236
26530
|
}
|
|
26237
26531
|
}
|
|
@@ -26580,13 +26874,13 @@ var init_fetcher = __esm({
|
|
|
26580
26874
|
// src/security/manifest-cache.ts
|
|
26581
26875
|
import { existsSync as existsSync49, readFileSync as readFileSync36, statSync as statSync17 } from "node:fs";
|
|
26582
26876
|
import { homedir as homedir21 } from "node:os";
|
|
26583
|
-
import { resolve as
|
|
26877
|
+
import { resolve as resolve25 } from "node:path";
|
|
26584
26878
|
import { z as z5 } from "zod";
|
|
26585
26879
|
function defaultCachePaths() {
|
|
26586
|
-
const dir =
|
|
26880
|
+
const dir = resolve25(homedir21(), ".massu");
|
|
26587
26881
|
return {
|
|
26588
|
-
cachePath:
|
|
26589
|
-
lockPath:
|
|
26882
|
+
cachePath: resolve25(dir, "adapter-manifest.json"),
|
|
26883
|
+
lockPath: resolve25(dir, ".adapter-manifest.lock")
|
|
26590
26884
|
};
|
|
26591
26885
|
}
|
|
26592
26886
|
function loadCachedManifest(paths = defaultCachePaths()) {
|
|
@@ -26786,13 +27080,13 @@ var init_adapter_origin = __esm({
|
|
|
26786
27080
|
// src/security/local-fingerprint.ts
|
|
26787
27081
|
import { existsSync as existsSync50, readFileSync as readFileSync37, lstatSync as lstatSync5 } from "node:fs";
|
|
26788
27082
|
import { homedir as homedir22 } from "node:os";
|
|
26789
|
-
import { resolve as
|
|
27083
|
+
import { resolve as resolve26, isAbsolute as isAbsolute4 } from "node:path";
|
|
26790
27084
|
import { createHash as createHash14 } from "node:crypto";
|
|
26791
27085
|
import { z as z6 } from "zod";
|
|
26792
27086
|
function computeLocalFingerprint(localPaths, projectRoot) {
|
|
26793
27087
|
const tuples = [];
|
|
26794
27088
|
for (const p19 of localPaths) {
|
|
26795
|
-
const abs =
|
|
27089
|
+
const abs = isAbsolute4(p19) ? p19 : resolve26(projectRoot, p19);
|
|
26796
27090
|
let contentTag;
|
|
26797
27091
|
try {
|
|
26798
27092
|
const lst = lstatSync5(abs);
|
|
@@ -26864,7 +27158,7 @@ var init_local_fingerprint = __esm({
|
|
|
26864
27158
|
"use strict";
|
|
26865
27159
|
init_atomic_write();
|
|
26866
27160
|
init_manifest_schema();
|
|
26867
|
-
FINGERPRINT_PATH =
|
|
27161
|
+
FINGERPRINT_PATH = resolve26(homedir22(), ".massu", "adapters-local-fingerprint.json");
|
|
26868
27162
|
FingerprintSentinelSchema = z6.object({
|
|
26869
27163
|
fingerprint: z6.string().regex(/^[0-9a-f]{64}$/),
|
|
26870
27164
|
source: z6.enum(["cli", "cli-resync"]),
|
|
@@ -26881,9 +27175,9 @@ var init_local_fingerprint = __esm({
|
|
|
26881
27175
|
|
|
26882
27176
|
// src/security/install-tracking.ts
|
|
26883
27177
|
import { readFileSync as readFileSync38, readdirSync as readdirSync17, lstatSync as lstatSync6, existsSync as existsSync51 } from "node:fs";
|
|
26884
|
-
import { join as join28, relative as
|
|
27178
|
+
import { join as join28, relative as relative8, sep as sep3 } from "node:path";
|
|
26885
27179
|
import { homedir as homedir23 } from "node:os";
|
|
26886
|
-
import { resolve as
|
|
27180
|
+
import { resolve as resolve27 } from "node:path";
|
|
26887
27181
|
import { createHash as createHash15 } from "node:crypto";
|
|
26888
27182
|
import { z as z7 } from "zod";
|
|
26889
27183
|
function containsHiddenDirs(packageDir) {
|
|
@@ -26926,7 +27220,7 @@ function sha256OfDir(dir, opts = {}) {
|
|
|
26926
27220
|
`sha256OfDir: file ${absPath} exceeds maxFileBytes (${lst.size} > ${maxFileBytes}); adapter packages should not ship files this large.`
|
|
26927
27221
|
);
|
|
26928
27222
|
}
|
|
26929
|
-
const rel =
|
|
27223
|
+
const rel = relative8(dir, absPath).split(sep3).join("/");
|
|
26930
27224
|
files.push({ relativePath: rel, absPath });
|
|
26931
27225
|
}
|
|
26932
27226
|
}
|
|
@@ -27010,7 +27304,7 @@ var init_install_tracking = __esm({
|
|
|
27010
27304
|
"src/security/install-tracking.ts"() {
|
|
27011
27305
|
"use strict";
|
|
27012
27306
|
init_atomic_write();
|
|
27013
|
-
INSTALLED_MANIFEST_PATH =
|
|
27307
|
+
INSTALLED_MANIFEST_PATH = resolve27(homedir23(), ".massu", "adapter-manifest-installed.json");
|
|
27014
27308
|
DEFAULT_MAX_FILE_BYTES = 64 * 1024 * 1024;
|
|
27015
27309
|
EXCLUDED_DIR_NAMES = /* @__PURE__ */ new Set([".git", "node_modules", ".cache", ".tmp"]);
|
|
27016
27310
|
InstallEntrySchema = z7.object({
|
|
@@ -27034,10 +27328,10 @@ var init_install_tracking = __esm({
|
|
|
27034
27328
|
|
|
27035
27329
|
// src/detect/adapters/discover.ts
|
|
27036
27330
|
import { existsSync as existsSync52, readdirSync as readdirSync18, readFileSync as readFileSync39, lstatSync as lstatSync7 } from "node:fs";
|
|
27037
|
-
import { resolve as
|
|
27331
|
+
import { resolve as resolve28, isAbsolute as isAbsolute5 } from "node:path";
|
|
27038
27332
|
import { z as z8 } from "zod";
|
|
27039
27333
|
function walkNodeModules(projectRoot, warnings) {
|
|
27040
|
-
const nodeModulesDir =
|
|
27334
|
+
const nodeModulesDir = resolve28(projectRoot, "node_modules");
|
|
27041
27335
|
if (!existsSync52(nodeModulesDir)) {
|
|
27042
27336
|
return [];
|
|
27043
27337
|
}
|
|
@@ -27051,7 +27345,7 @@ function walkNodeModules(projectRoot, warnings) {
|
|
|
27051
27345
|
}
|
|
27052
27346
|
for (const entry of topLevelEntries) {
|
|
27053
27347
|
if (entry.startsWith(".")) continue;
|
|
27054
|
-
const entryPath =
|
|
27348
|
+
const entryPath = resolve28(nodeModulesDir, entry);
|
|
27055
27349
|
let entryStat;
|
|
27056
27350
|
try {
|
|
27057
27351
|
entryStat = lstatSync7(entryPath);
|
|
@@ -27073,7 +27367,7 @@ function walkNodeModules(projectRoot, warnings) {
|
|
|
27073
27367
|
continue;
|
|
27074
27368
|
}
|
|
27075
27369
|
for (const sub of scopedEntries) {
|
|
27076
|
-
const subPath =
|
|
27370
|
+
const subPath = resolve28(entryPath, sub);
|
|
27077
27371
|
let subStat;
|
|
27078
27372
|
try {
|
|
27079
27373
|
subStat = lstatSync7(subPath);
|
|
@@ -27092,7 +27386,7 @@ function walkNodeModules(projectRoot, warnings) {
|
|
|
27092
27386
|
return candidates;
|
|
27093
27387
|
}
|
|
27094
27388
|
function tryReadAdapterPackage(packageDir, warnings) {
|
|
27095
|
-
const pkgJsonPath =
|
|
27389
|
+
const pkgJsonPath = resolve28(packageDir, "package.json");
|
|
27096
27390
|
if (!existsSync52(pkgJsonPath)) return null;
|
|
27097
27391
|
let raw;
|
|
27098
27392
|
try {
|
|
@@ -27230,7 +27524,7 @@ function discoverAdapters(opts) {
|
|
|
27230
27524
|
const localSet = new Set(opts.configLocalPaths);
|
|
27231
27525
|
for (const localPath of opts.configLocalPaths) {
|
|
27232
27526
|
if (seenIds.has(localPath)) continue;
|
|
27233
|
-
const absPath =
|
|
27527
|
+
const absPath = isAbsolute5(localPath) ? localPath : resolve28(opts.projectRoot, localPath);
|
|
27234
27528
|
if (!existsSync52(absPath)) {
|
|
27235
27529
|
warnings.push(
|
|
27236
27530
|
`local adapter file not found: ${localPath} (resolved to ${absPath}). Remove via: massu adapters remove-local ${localPath}`
|
|
@@ -27314,7 +27608,7 @@ __export(adapters_exports, {
|
|
|
27314
27608
|
runAdaptersSearch: () => runAdaptersSearch
|
|
27315
27609
|
});
|
|
27316
27610
|
import { existsSync as existsSync53, readFileSync as readFileSync40 } from "node:fs";
|
|
27317
|
-
import { resolve as
|
|
27611
|
+
import { resolve as resolve29 } from "node:path";
|
|
27318
27612
|
import { parseDocument } from "yaml";
|
|
27319
27613
|
async function handleAdaptersSubcommand(args2) {
|
|
27320
27614
|
const sub = args2[0];
|
|
@@ -27581,7 +27875,7 @@ function mutateLocalArray(mutator, command) {
|
|
|
27581
27875
|
);
|
|
27582
27876
|
return { exitCode: 2 };
|
|
27583
27877
|
}
|
|
27584
|
-
const yamlPath =
|
|
27878
|
+
const yamlPath = resolve29(projectRoot, "massu.config.yaml");
|
|
27585
27879
|
if (!existsSync53(yamlPath)) {
|
|
27586
27880
|
process.stderr.write(
|
|
27587
27881
|
`${command}: massu.config.yaml not found at ${yamlPath}. Run \`massu init\` first.
|
|
@@ -27612,7 +27906,7 @@ function mutateLocalArray(mutator, command) {
|
|
|
27612
27906
|
if (next === null) {
|
|
27613
27907
|
return { exitCode: 0 };
|
|
27614
27908
|
}
|
|
27615
|
-
const lockPath =
|
|
27909
|
+
const lockPath = resolve29(projectRoot, ".massu", "adapters-local-mutate.lock");
|
|
27616
27910
|
return withFileLockSync(lockPath, () => {
|
|
27617
27911
|
doc.setIn(["adapters", "local"], next);
|
|
27618
27912
|
const newYaml = doc.toString();
|
|
@@ -27681,7 +27975,7 @@ async function runAdaptersInstall(args2) {
|
|
|
27681
27975
|
`);
|
|
27682
27976
|
return { exitCode: 1 };
|
|
27683
27977
|
}
|
|
27684
|
-
const packageDir =
|
|
27978
|
+
const packageDir = resolve29(projectRoot, "node_modules", ...packageName.split("/"));
|
|
27685
27979
|
if (!existsSync53(packageDir)) {
|
|
27686
27980
|
process.stderr.write(
|
|
27687
27981
|
`install: ${packageName} is not installed in node_modules. Run \`npm install ${packageName}\` first.
|
|
@@ -27689,7 +27983,7 @@ async function runAdaptersInstall(args2) {
|
|
|
27689
27983
|
);
|
|
27690
27984
|
return { exitCode: 1 };
|
|
27691
27985
|
}
|
|
27692
|
-
const pkgJsonPath =
|
|
27986
|
+
const pkgJsonPath = resolve29(packageDir, "package.json");
|
|
27693
27987
|
if (!existsSync53(pkgJsonPath)) {
|
|
27694
27988
|
process.stderr.write(`install: ${packageName} has no package.json at ${pkgJsonPath}
|
|
27695
27989
|
`);
|
|
@@ -27816,7 +28110,7 @@ async function runAdaptersResign(_args) {
|
|
|
27816
28110
|
removeInstalledManifestEntry(name);
|
|
27817
28111
|
continue;
|
|
27818
28112
|
}
|
|
27819
|
-
const packageDir =
|
|
28113
|
+
const packageDir = resolve29(projectRoot, "node_modules", ...name.split("/"));
|
|
27820
28114
|
if (!existsSync53(packageDir)) {
|
|
27821
28115
|
removed++;
|
|
27822
28116
|
warnings.push(`${name}@${entry.version}: not present in node_modules \u2014 REMOVED from sidecar`);
|
|
@@ -27915,7 +28209,7 @@ var init_sql_table_names = __esm({
|
|
|
27915
28209
|
|
|
27916
28210
|
// src/db.ts
|
|
27917
28211
|
import { dirname as dirname24, join as join29 } from "path";
|
|
27918
|
-
import { existsSync as existsSync54, mkdirSync as
|
|
28212
|
+
import { existsSync as existsSync54, mkdirSync as mkdirSync25, readdirSync as readdirSync19, statSync as statSync18 } from "fs";
|
|
27919
28213
|
function getCodeGraphDb() {
|
|
27920
28214
|
const dbPath = getResolvedPaths().codegraphDbPath;
|
|
27921
28215
|
assertCodegraphUsable(dbPath);
|
|
@@ -27927,7 +28221,7 @@ function getDataDb() {
|
|
|
27927
28221
|
const dbPath = getResolvedPaths().dataDbPath;
|
|
27928
28222
|
const dir = dirname24(dbPath);
|
|
27929
28223
|
if (!existsSync54(dir)) {
|
|
27930
|
-
|
|
28224
|
+
mkdirSync25(dir, { recursive: true });
|
|
27931
28225
|
}
|
|
27932
28226
|
const db = openDatabase2(dbPath);
|
|
27933
28227
|
db.pragma("journal_mode = WAL");
|
|
@@ -28181,12 +28475,23 @@ function initDataSchema(db) {
|
|
|
28181
28475
|
END;
|
|
28182
28476
|
`);
|
|
28183
28477
|
}
|
|
28478
|
+
function codegraphIndexedAtToEpochMs(raw) {
|
|
28479
|
+
const ceiling = Date.now() + CODEGRAPH_TS_SKEW_MS;
|
|
28480
|
+
const asMs = raw;
|
|
28481
|
+
const asSeconds = raw * 1e3;
|
|
28482
|
+
if (asMs >= CODEGRAPH_TS_FLOOR_MS && asMs <= ceiling) return asMs;
|
|
28483
|
+
if (asSeconds >= CODEGRAPH_TS_FLOOR_MS && asSeconds <= ceiling) return asSeconds;
|
|
28484
|
+
const describe = (ms) => Number.isFinite(ms) && Math.abs(ms) <= 864e13 ? new Date(ms).toISOString() : "out of representable range";
|
|
28485
|
+
throw new Error(
|
|
28486
|
+
`CodeGraph files.indexed_at=${raw} is implausible as both milliseconds (${describe(asMs)}) and seconds (${describe(asSeconds)}). Expected a timestamp between ${new Date(CODEGRAPH_TS_FLOOR_MS).toISOString()} and now. The CodeGraph engine's timestamp format may have changed \u2014 do not guess the unit.`
|
|
28487
|
+
);
|
|
28488
|
+
}
|
|
28184
28489
|
function isDataStale(dataDb, codegraphDb) {
|
|
28185
28490
|
const lastBuild = dataDb.prepare(`SELECT value FROM ${t("meta")} WHERE key = 'last_build_time'`).get();
|
|
28186
28491
|
if (!lastBuild) return true;
|
|
28187
28492
|
const latestIndexed = codegraphDb.prepare("SELECT MAX(indexed_at) as latest FROM files").get();
|
|
28188
28493
|
if (!latestIndexed?.latest) return true;
|
|
28189
|
-
return latestIndexed.latest
|
|
28494
|
+
return codegraphIndexedAtToEpochMs(latestIndexed.latest) > new Date(lastBuild.value).getTime();
|
|
28190
28495
|
}
|
|
28191
28496
|
function updateBuildTimestamp(dataDb) {
|
|
28192
28497
|
dataDb.prepare(`INSERT OR REPLACE INTO ${t("meta")} (key, value) VALUES ('last_build_time', ?)`).run((/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -28216,6 +28521,7 @@ function isPythonDataStale(dataDb, pythonRoot) {
|
|
|
28216
28521
|
function updatePythonBuildTimestamp(dataDb) {
|
|
28217
28522
|
dataDb.prepare(`INSERT OR REPLACE INTO ${t("py_meta")} (key, value) VALUES ('last_build_time', ?)`).run((/* @__PURE__ */ new Date()).toISOString());
|
|
28218
28523
|
}
|
|
28524
|
+
var CODEGRAPH_TS_FLOOR_MS, CODEGRAPH_TS_SKEW_MS;
|
|
28219
28525
|
var init_db2 = __esm({
|
|
28220
28526
|
"src/db.ts"() {
|
|
28221
28527
|
"use strict";
|
|
@@ -28224,14 +28530,16 @@ var init_db2 = __esm({
|
|
|
28224
28530
|
init_config();
|
|
28225
28531
|
init_sql_table_names();
|
|
28226
28532
|
init_preflight();
|
|
28533
|
+
CODEGRAPH_TS_FLOOR_MS = 15778368e5;
|
|
28534
|
+
CODEGRAPH_TS_SKEW_MS = 864e5;
|
|
28227
28535
|
}
|
|
28228
28536
|
});
|
|
28229
28537
|
|
|
28230
28538
|
// src/security-utils.ts
|
|
28231
|
-
import { resolve as
|
|
28539
|
+
import { resolve as resolve30, normalize } from "path";
|
|
28232
28540
|
function ensureWithinRoot(filePath, projectRoot) {
|
|
28233
|
-
const resolvedRoot =
|
|
28234
|
-
const resolvedPath =
|
|
28541
|
+
const resolvedRoot = resolve30(projectRoot);
|
|
28542
|
+
const resolvedPath = resolve30(resolvedRoot, filePath);
|
|
28235
28543
|
const normalizedPath = normalize(resolvedPath);
|
|
28236
28544
|
const normalizedRoot = normalize(resolvedRoot);
|
|
28237
28545
|
if (!normalizedPath.startsWith(normalizedRoot + "/") && normalizedPath !== normalizedRoot) {
|
|
@@ -28303,9 +28611,125 @@ var init_rules = __esm({
|
|
|
28303
28611
|
}
|
|
28304
28612
|
});
|
|
28305
28613
|
|
|
28614
|
+
// src/lib/source-layout.ts
|
|
28615
|
+
function normalizeDir(raw) {
|
|
28616
|
+
if (typeof raw !== "string") return null;
|
|
28617
|
+
const posix = raw.replace(/\\/g, "/");
|
|
28618
|
+
if (posix.startsWith("/") || /^[A-Za-z]:\//.test(posix)) return null;
|
|
28619
|
+
const parts = posix.split("/").filter((p19) => p19 !== "" && p19 !== ".");
|
|
28620
|
+
if (parts.some((p19) => p19 === "..")) return null;
|
|
28621
|
+
if (parts.length === 0) return ".";
|
|
28622
|
+
return parts.join("/");
|
|
28623
|
+
}
|
|
28624
|
+
function declaredLanguageSourceDirs() {
|
|
28625
|
+
const languages = getConfig().framework.languages;
|
|
28626
|
+
if (!languages) return [];
|
|
28627
|
+
const out = [];
|
|
28628
|
+
for (const entry of Object.values(languages)) {
|
|
28629
|
+
if (Array.isArray(entry.source_dirs)) out.push(...entry.source_dirs);
|
|
28630
|
+
}
|
|
28631
|
+
return out;
|
|
28632
|
+
}
|
|
28633
|
+
function getSourceLayout() {
|
|
28634
|
+
const config = getConfig();
|
|
28635
|
+
const paths = config.paths;
|
|
28636
|
+
const declared = [
|
|
28637
|
+
paths.source,
|
|
28638
|
+
...paths.monorepo_roots ?? [],
|
|
28639
|
+
...declaredLanguageSourceDirs()
|
|
28640
|
+
];
|
|
28641
|
+
const normalized = declared.map(normalizeDir).filter((d2) => d2 !== null);
|
|
28642
|
+
const includesRoot = normalized.includes(".");
|
|
28643
|
+
const sourceDirs = includesRoot ? [] : [...new Set(normalized)].sort();
|
|
28644
|
+
const source = normalizeDir(paths.source) ?? ".";
|
|
28645
|
+
const under = (child) => source === "." ? child : `${source}/${child}`;
|
|
28646
|
+
return {
|
|
28647
|
+
sourceDirs,
|
|
28648
|
+
includesRoot,
|
|
28649
|
+
extensions: getResolvedPaths().extensions,
|
|
28650
|
+
pagesDir: normalizeDir(paths.pages ?? under("app")) ?? under("app"),
|
|
28651
|
+
componentsDir: normalizeDir(paths.components ?? under("components")) ?? under("components")
|
|
28652
|
+
};
|
|
28653
|
+
}
|
|
28654
|
+
function escapeLike(literal) {
|
|
28655
|
+
return literal.replace(/[\\%_]/g, (m3) => `\\${m3}`);
|
|
28656
|
+
}
|
|
28657
|
+
function assertIdentifier(column) {
|
|
28658
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(column)) {
|
|
28659
|
+
throw new Error(`source-layout: refusing to interpolate '${column}' as a SQL column name`);
|
|
28660
|
+
}
|
|
28661
|
+
return column;
|
|
28662
|
+
}
|
|
28663
|
+
function likeAny(column, patterns) {
|
|
28664
|
+
if (patterns.length === 0) return { sql: "1=0", params: [] };
|
|
28665
|
+
const col = assertIdentifier(column);
|
|
28666
|
+
const sql = patterns.map(() => `${col} LIKE ? ESCAPE '\\'`).join(" OR ");
|
|
28667
|
+
return { sql: `(${sql})`, params: patterns };
|
|
28668
|
+
}
|
|
28669
|
+
function sourceDirPredicate(column = "path", layout = getSourceLayout()) {
|
|
28670
|
+
if (layout.includesRoot) return { sql: `(${assertIdentifier(column)} IS NOT NULL)`, params: [] };
|
|
28671
|
+
return likeAny(column, layout.sourceDirs.map((d2) => `${escapeLike(d2)}/%`));
|
|
28672
|
+
}
|
|
28673
|
+
function sourceFilePredicate(column = "path", layout = getSourceLayout()) {
|
|
28674
|
+
const dirs = sourceDirPredicate(column, layout);
|
|
28675
|
+
const exts = likeAny(column, layout.extensions.map((e2) => `%${escapeLike(e2)}`));
|
|
28676
|
+
return {
|
|
28677
|
+
sql: `${dirs.sql} AND ${exts.sql}`,
|
|
28678
|
+
params: [...dirs.params, ...exts.params]
|
|
28679
|
+
};
|
|
28680
|
+
}
|
|
28681
|
+
function pagesPredicate(layout = getSourceLayout()) {
|
|
28682
|
+
const dir = escapeLike(layout.pagesDir);
|
|
28683
|
+
return {
|
|
28684
|
+
sql: `(path LIKE ? ESCAPE '\\' OR path = ?)`,
|
|
28685
|
+
params: [`${dir}/%/page.tsx`, `${layout.pagesDir}/page.tsx`]
|
|
28686
|
+
};
|
|
28687
|
+
}
|
|
28688
|
+
function componentsPredicate(layout = getSourceLayout()) {
|
|
28689
|
+
return {
|
|
28690
|
+
sql: `path LIKE ? ESCAPE '\\'`,
|
|
28691
|
+
params: [`${escapeLike(layout.componentsDir)}/%`]
|
|
28692
|
+
};
|
|
28693
|
+
}
|
|
28694
|
+
function isUnderSourceDir(path, layout = getSourceLayout()) {
|
|
28695
|
+
if (normalizeDir(path) === null) return false;
|
|
28696
|
+
if (layout.includesRoot) return true;
|
|
28697
|
+
return layout.sourceDirs.some((d2) => path.startsWith(`${d2}/`));
|
|
28698
|
+
}
|
|
28699
|
+
function assertSourceCandidateSet(codegraphDb, builder, layout = getSourceLayout()) {
|
|
28700
|
+
const present = codegraphDb.prepare("SELECT COUNT(*) AS n FROM files").get().n;
|
|
28701
|
+
if (present === 0) return;
|
|
28702
|
+
const pred = sourceDirPredicate("path", layout);
|
|
28703
|
+
const matched = codegraphDb.prepare(`SELECT COUNT(*) AS n FROM files WHERE ${pred.sql}`).get(...pred.params).n;
|
|
28704
|
+
if (matched > 0) return;
|
|
28705
|
+
const prefixes = codegraphDb.prepare(
|
|
28706
|
+
`SELECT CASE WHEN instr(path, '/') > 0 THEN substr(path, 1, instr(path, '/') - 1) ELSE '<root>' END AS prefix,
|
|
28707
|
+
COUNT(*) AS n
|
|
28708
|
+
FROM files GROUP BY prefix ORDER BY n DESC LIMIT 20`
|
|
28709
|
+
).all();
|
|
28710
|
+
const declared = layout.includesRoot ? "<project root>" : layout.sourceDirs.join(", ") || "<none>";
|
|
28711
|
+
const found = prefixes.map((p19) => `${p19.prefix} (${p19.n})`).join(", ") || "<none>";
|
|
28712
|
+
throw new CandidateSetContractError(
|
|
28713
|
+
`${builder}: declared source dirs match 0 of ${present} indexed files. Declared: ${declared}. Present in CodeGraph: ${found}. Declare the real layout in massu.config.yaml (paths.source, paths.monorepo_roots, or framework.languages.<lang>.source_dirs) \u2014 an index built from an empty candidate set is indistinguishable from a project with no imports.`
|
|
28714
|
+
);
|
|
28715
|
+
}
|
|
28716
|
+
var CandidateSetContractError;
|
|
28717
|
+
var init_source_layout = __esm({
|
|
28718
|
+
"src/lib/source-layout.ts"() {
|
|
28719
|
+
"use strict";
|
|
28720
|
+
init_config();
|
|
28721
|
+
CandidateSetContractError = class extends Error {
|
|
28722
|
+
constructor(message) {
|
|
28723
|
+
super(message);
|
|
28724
|
+
this.name = "CandidateSetContractError";
|
|
28725
|
+
}
|
|
28726
|
+
};
|
|
28727
|
+
}
|
|
28728
|
+
});
|
|
28729
|
+
|
|
28306
28730
|
// src/import-resolver.ts
|
|
28307
28731
|
import { readFileSync as readFileSync41, existsSync as existsSync55, statSync as statSync19 } from "fs";
|
|
28308
|
-
import { resolve as
|
|
28732
|
+
import { resolve as resolve31, dirname as dirname25, join as join30 } from "path";
|
|
28309
28733
|
function parseImports(source) {
|
|
28310
28734
|
const imports = [];
|
|
28311
28735
|
const lines = source.split("\n");
|
|
@@ -28361,24 +28785,24 @@ function resolveImportPath(specifier, fromFile) {
|
|
|
28361
28785
|
let basePath;
|
|
28362
28786
|
if (specifier.startsWith("@/")) {
|
|
28363
28787
|
const paths = getResolvedPaths();
|
|
28364
|
-
basePath =
|
|
28788
|
+
basePath = resolve31(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
|
|
28365
28789
|
} else {
|
|
28366
|
-
basePath =
|
|
28790
|
+
basePath = resolve31(dirname25(fromFile), specifier);
|
|
28367
28791
|
}
|
|
28368
28792
|
if (existsSync55(basePath) && !isDirectory(basePath)) {
|
|
28369
|
-
return
|
|
28793
|
+
return toRelative2(basePath);
|
|
28370
28794
|
}
|
|
28371
28795
|
const resolvedPaths = getResolvedPaths();
|
|
28372
28796
|
for (const ext of resolvedPaths.extensions) {
|
|
28373
28797
|
const withExt = basePath + ext;
|
|
28374
28798
|
if (existsSync55(withExt)) {
|
|
28375
|
-
return
|
|
28799
|
+
return toRelative2(withExt);
|
|
28376
28800
|
}
|
|
28377
28801
|
}
|
|
28378
28802
|
for (const indexFile of resolvedPaths.indexFiles) {
|
|
28379
28803
|
const indexPath = join30(basePath, indexFile);
|
|
28380
28804
|
if (existsSync55(indexPath)) {
|
|
28381
|
-
return
|
|
28805
|
+
return toRelative2(indexPath);
|
|
28382
28806
|
}
|
|
28383
28807
|
}
|
|
28384
28808
|
return null;
|
|
@@ -28390,7 +28814,7 @@ function isDirectory(path) {
|
|
|
28390
28814
|
return false;
|
|
28391
28815
|
}
|
|
28392
28816
|
}
|
|
28393
|
-
function
|
|
28817
|
+
function toRelative2(absPath) {
|
|
28394
28818
|
const root = getProjectRoot();
|
|
28395
28819
|
if (absPath.startsWith(root)) {
|
|
28396
28820
|
return absPath.slice(root.length + 1);
|
|
@@ -28398,7 +28822,15 @@ function toRelative(absPath) {
|
|
|
28398
28822
|
return absPath;
|
|
28399
28823
|
}
|
|
28400
28824
|
function buildImportIndex(dataDb, codegraphDb) {
|
|
28401
|
-
|
|
28825
|
+
assertSourceCandidateSet(codegraphDb, "buildImportIndex");
|
|
28826
|
+
const filesPredicate = sourceFilePredicate();
|
|
28827
|
+
const FILE_SCAN_LIMIT = 2e5;
|
|
28828
|
+
const files = codegraphDb.prepare(`SELECT path FROM files WHERE ${filesPredicate.sql} LIMIT ${FILE_SCAN_LIMIT}`).all(...filesPredicate.params);
|
|
28829
|
+
if (files.length === FILE_SCAN_LIMIT) {
|
|
28830
|
+
throw new Error(
|
|
28831
|
+
`buildImportIndex: candidate set hit the ${FILE_SCAN_LIMIT}-row scan limit, so the import index would be built from a truncated file list. Narrow the declared source dirs in massu.config.yaml, or raise FILE_SCAN_LIMIT deliberately.`
|
|
28832
|
+
);
|
|
28833
|
+
}
|
|
28402
28834
|
dataDb.exec(`DELETE FROM ${t("imports")}`);
|
|
28403
28835
|
const insertStmt = dataDb.prepare(
|
|
28404
28836
|
`INSERT INTO ${t("imports")} (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)`
|
|
@@ -28413,7 +28845,7 @@ function buildImportIndex(dataDb, codegraphDb) {
|
|
|
28413
28845
|
const batchSize = 500;
|
|
28414
28846
|
let batch = [];
|
|
28415
28847
|
for (const file of files) {
|
|
28416
|
-
const absPath = ensureWithinRoot(
|
|
28848
|
+
const absPath = ensureWithinRoot(resolve31(projectRoot, file.path), projectRoot);
|
|
28417
28849
|
if (!existsSync55(absPath)) continue;
|
|
28418
28850
|
let source;
|
|
28419
28851
|
try {
|
|
@@ -28450,12 +28882,13 @@ var init_import_resolver = __esm({
|
|
|
28450
28882
|
init_config();
|
|
28451
28883
|
init_security_utils();
|
|
28452
28884
|
init_sql_table_names();
|
|
28885
|
+
init_source_layout();
|
|
28453
28886
|
}
|
|
28454
28887
|
});
|
|
28455
28888
|
|
|
28456
28889
|
// src/trpc-index.ts
|
|
28457
28890
|
import { readFileSync as readFileSync42, existsSync as existsSync56, readdirSync as readdirSync20 } from "fs";
|
|
28458
|
-
import { resolve as
|
|
28891
|
+
import { resolve as resolve32, join as join31 } from "path";
|
|
28459
28892
|
function parseRootRouter() {
|
|
28460
28893
|
const paths = getResolvedPaths();
|
|
28461
28894
|
const rootPath = paths.rootRouterPath;
|
|
@@ -28470,7 +28903,7 @@ function parseRootRouter() {
|
|
|
28470
28903
|
while ((match = importRegex.exec(source)) !== null) {
|
|
28471
28904
|
const variable = match[1];
|
|
28472
28905
|
let filePath = match[2];
|
|
28473
|
-
const fullPath =
|
|
28906
|
+
const fullPath = resolve32(paths.routersDir, filePath);
|
|
28474
28907
|
for (const ext of [".ts", ".tsx", ""]) {
|
|
28475
28908
|
const candidate = fullPath + ext;
|
|
28476
28909
|
const routersRelPath = getConfig().paths.routers ?? "src/server/api/routers";
|
|
@@ -28498,7 +28931,7 @@ function parseRootRouter() {
|
|
|
28498
28931
|
return mappings;
|
|
28499
28932
|
}
|
|
28500
28933
|
function extractProcedures(routerFilePath) {
|
|
28501
|
-
const absPath =
|
|
28934
|
+
const absPath = resolve32(getProjectRoot(), routerFilePath);
|
|
28502
28935
|
if (!existsSync56(absPath)) return [];
|
|
28503
28936
|
const source = readFileSync42(absPath, "utf-8");
|
|
28504
28937
|
const procedures = [];
|
|
@@ -28523,9 +28956,9 @@ function findUICallSites(routerKey, procedureName) {
|
|
|
28523
28956
|
const root = getProjectRoot();
|
|
28524
28957
|
const src = config.paths.source;
|
|
28525
28958
|
const searchDirs = [
|
|
28526
|
-
|
|
28527
|
-
|
|
28528
|
-
|
|
28959
|
+
resolve32(root, config.paths.pages ?? src + "/app"),
|
|
28960
|
+
resolve32(root, config.paths.components ?? src + "/components"),
|
|
28961
|
+
resolve32(root, config.paths.hooks ?? src + "/hooks")
|
|
28529
28962
|
];
|
|
28530
28963
|
const searchPattern = `api.${routerKey}.${procedureName}`;
|
|
28531
28964
|
for (const dir of searchDirs) {
|
|
@@ -28608,9 +29041,11 @@ var init_trpc_index = __esm({
|
|
|
28608
29041
|
|
|
28609
29042
|
// src/page-deps.ts
|
|
28610
29043
|
import { readFileSync as readFileSync43, existsSync as existsSync57 } from "fs";
|
|
28611
|
-
import { resolve as
|
|
29044
|
+
import { resolve as resolve33 } from "path";
|
|
28612
29045
|
function deriveRoute(pageFile) {
|
|
28613
|
-
|
|
29046
|
+
const pagesDir = getSourceLayout().pagesDir;
|
|
29047
|
+
const prefix3 = pagesDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
29048
|
+
let route = pageFile.replace(new RegExp(`^${prefix3}`), "").replace(/\/page\.tsx?$/, "").replace(/\/page\.jsx?$/, "");
|
|
28614
29049
|
return route || "/";
|
|
28615
29050
|
}
|
|
28616
29051
|
function derivePortal(route) {
|
|
@@ -28637,7 +29072,7 @@ function traceImports(startFile, dataDb, visited, components, hooks, maxDepth =
|
|
|
28637
29072
|
if (target.includes("/hooks/") || target.match(/use[A-Z]/)) {
|
|
28638
29073
|
hooks.add(target);
|
|
28639
29074
|
}
|
|
28640
|
-
if (target
|
|
29075
|
+
if (isUnderSourceDir(target)) {
|
|
28641
29076
|
traceImports(target, dataDb, visited, components, hooks, maxDepth - 1);
|
|
28642
29077
|
}
|
|
28643
29078
|
}
|
|
@@ -28646,7 +29081,7 @@ function findRouterCalls(files) {
|
|
|
28646
29081
|
const routers = /* @__PURE__ */ new Set();
|
|
28647
29082
|
const projectRoot = getProjectRoot();
|
|
28648
29083
|
for (const file of files) {
|
|
28649
|
-
const absPath = ensureWithinRoot(
|
|
29084
|
+
const absPath = ensureWithinRoot(resolve33(projectRoot, file), projectRoot);
|
|
28650
29085
|
if (!existsSync57(absPath)) continue;
|
|
28651
29086
|
try {
|
|
28652
29087
|
const source = readFileSync43(absPath, "utf-8");
|
|
@@ -28667,7 +29102,7 @@ function findTablesFromRouters(routerNames, dataDb) {
|
|
|
28667
29102
|
`SELECT DISTINCT router_file FROM ${t("trpc_procedures")} WHERE router_name = ? LIMIT 10000`
|
|
28668
29103
|
).all(routerName);
|
|
28669
29104
|
for (const proc of procs) {
|
|
28670
|
-
const absPath = ensureWithinRoot(
|
|
29105
|
+
const absPath = ensureWithinRoot(resolve33(getProjectRoot(), proc.router_file), getProjectRoot());
|
|
28671
29106
|
if (!existsSync57(absPath)) continue;
|
|
28672
29107
|
try {
|
|
28673
29108
|
const source = readFileSync43(absPath, "utf-8");
|
|
@@ -28685,10 +29120,12 @@ function findTablesFromRouters(routerNames, dataDb) {
|
|
|
28685
29120
|
return [...tables];
|
|
28686
29121
|
}
|
|
28687
29122
|
function buildPageDeps(dataDb, codegraphDb) {
|
|
29123
|
+
assertSourceCandidateSet(codegraphDb, "buildPageDeps");
|
|
28688
29124
|
dataDb.exec(`DELETE FROM ${t("page_deps")}`);
|
|
29125
|
+
const pagesPred = pagesPredicate();
|
|
28689
29126
|
const pages = codegraphDb.prepare(
|
|
28690
|
-
|
|
28691
|
-
).all();
|
|
29127
|
+
`SELECT path FROM files WHERE ${pagesPred.sql} LIMIT 10000`
|
|
29128
|
+
).all(...pagesPred.params);
|
|
28692
29129
|
const insertStmt = dataDb.prepare(
|
|
28693
29130
|
`INSERT INTO ${t("page_deps")} (page_file, route, portal, components, hooks, routers, tables_touched) VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
28694
29131
|
);
|
|
@@ -28783,6 +29220,7 @@ var init_page_deps = __esm({
|
|
|
28783
29220
|
init_config();
|
|
28784
29221
|
init_security_utils();
|
|
28785
29222
|
init_sql_table_names();
|
|
29223
|
+
init_source_layout();
|
|
28786
29224
|
}
|
|
28787
29225
|
});
|
|
28788
29226
|
|
|
@@ -28801,7 +29239,7 @@ function buildMiddlewareTree(dataDb) {
|
|
|
28801
29239
|
`SELECT target_file FROM ${t("imports")} WHERE source_file = ? LIMIT 10000`
|
|
28802
29240
|
).all(current);
|
|
28803
29241
|
for (const imp of imports) {
|
|
28804
|
-
if (!visited.has(imp.target_file) && imp.target_file
|
|
29242
|
+
if (!visited.has(imp.target_file) && isUnderSourceDir(imp.target_file)) {
|
|
28805
29243
|
visited.add(imp.target_file);
|
|
28806
29244
|
queue.push(imp.target_file);
|
|
28807
29245
|
}
|
|
@@ -28825,6 +29263,7 @@ var init_middleware_tree = __esm({
|
|
|
28825
29263
|
"use strict";
|
|
28826
29264
|
init_config();
|
|
28827
29265
|
init_sql_table_names();
|
|
29266
|
+
init_source_layout();
|
|
28828
29267
|
}
|
|
28829
29268
|
});
|
|
28830
29269
|
|
|
@@ -28882,12 +29321,11 @@ function globMatchSimple(name, pattern) {
|
|
|
28882
29321
|
}
|
|
28883
29322
|
function findCrossDomainImports(dataDb) {
|
|
28884
29323
|
const domains = getDomains();
|
|
28885
|
-
const
|
|
28886
|
-
const
|
|
28887
|
-
const srcPattern = srcPrefix + "/%";
|
|
29324
|
+
const srcSource = sourceDirPredicate("source_file");
|
|
29325
|
+
const srcTarget = sourceDirPredicate("target_file");
|
|
28888
29326
|
const imports = dataDb.prepare(
|
|
28889
|
-
`SELECT source_file, target_file FROM ${t("imports")} WHERE
|
|
28890
|
-
).all(
|
|
29327
|
+
`SELECT source_file, target_file FROM ${t("imports")} WHERE ${srcSource.sql} AND ${srcTarget.sql} LIMIT 10000`
|
|
29328
|
+
).all(...srcSource.params, ...srcTarget.params);
|
|
28891
29329
|
const crossings = [];
|
|
28892
29330
|
for (const imp of imports) {
|
|
28893
29331
|
const sourceDomain = classifyFile(imp.source_file);
|
|
@@ -28912,10 +29350,9 @@ function getFilesInDomain(dataDb, codegraphDb, domainName) {
|
|
|
28912
29350
|
const config = getConfig();
|
|
28913
29351
|
const domain = domains.find((d2) => d2.name === domainName);
|
|
28914
29352
|
if (!domain) return { routers: [], pages: [], components: [] };
|
|
28915
|
-
const srcPrefix = config.paths.source;
|
|
28916
29353
|
const routersPath = config.paths.routers ?? "src/server/api/routers";
|
|
28917
|
-
const
|
|
28918
|
-
const allFiles = codegraphDb.prepare(
|
|
29354
|
+
const srcPred = sourceDirPredicate();
|
|
29355
|
+
const allFiles = codegraphDb.prepare(`SELECT path FROM files WHERE ${srcPred.sql} LIMIT 200000`).all(...srcPred.params);
|
|
28919
29356
|
const routers = [];
|
|
28920
29357
|
const pages = [];
|
|
28921
29358
|
const components = [];
|
|
@@ -28938,6 +29375,7 @@ var init_domains = __esm({
|
|
|
28938
29375
|
init_rules();
|
|
28939
29376
|
init_config();
|
|
28940
29377
|
init_sql_table_names();
|
|
29378
|
+
init_source_layout();
|
|
28941
29379
|
}
|
|
28942
29380
|
});
|
|
28943
29381
|
|
|
@@ -29223,11 +29661,11 @@ var init_import_parser = __esm({
|
|
|
29223
29661
|
|
|
29224
29662
|
// src/python/import-resolver.ts
|
|
29225
29663
|
import { readFileSync as readFileSync45, existsSync as existsSync59, readdirSync as readdirSync22 } from "fs";
|
|
29226
|
-
import { resolve as
|
|
29664
|
+
import { resolve as resolve35, join as join33, relative as relative9, dirname as dirname26 } from "path";
|
|
29227
29665
|
function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
|
|
29228
29666
|
const projectRoot = getProjectRoot();
|
|
29229
29667
|
if (level > 0) {
|
|
29230
|
-
let baseDir = dirname26(
|
|
29668
|
+
let baseDir = dirname26(resolve35(projectRoot, fromFile));
|
|
29231
29669
|
for (let i = 1; i < level; i++) {
|
|
29232
29670
|
baseDir = dirname26(baseDir);
|
|
29233
29671
|
}
|
|
@@ -29239,18 +29677,18 @@ function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
|
|
|
29239
29677
|
return tryResolvePythonPath(baseDir, projectRoot);
|
|
29240
29678
|
}
|
|
29241
29679
|
const parts = module.split(".");
|
|
29242
|
-
const candidate = join33(
|
|
29680
|
+
const candidate = join33(resolve35(projectRoot, pythonRoot), ...parts);
|
|
29243
29681
|
return tryResolvePythonPath(candidate, projectRoot);
|
|
29244
29682
|
}
|
|
29245
29683
|
function tryResolvePythonPath(basePath, projectRoot) {
|
|
29246
29684
|
if (existsSync59(basePath + ".py")) {
|
|
29247
|
-
return
|
|
29685
|
+
return relative9(projectRoot, basePath + ".py");
|
|
29248
29686
|
}
|
|
29249
29687
|
if (existsSync59(join33(basePath, "__init__.py"))) {
|
|
29250
|
-
return
|
|
29688
|
+
return relative9(projectRoot, join33(basePath, "__init__.py"));
|
|
29251
29689
|
}
|
|
29252
29690
|
if (basePath.endsWith(".py") && existsSync59(basePath)) {
|
|
29253
|
-
return
|
|
29691
|
+
return relative9(projectRoot, basePath);
|
|
29254
29692
|
}
|
|
29255
29693
|
return null;
|
|
29256
29694
|
}
|
|
@@ -29272,7 +29710,7 @@ function walkPythonFiles(dir, excludeDirs) {
|
|
|
29272
29710
|
}
|
|
29273
29711
|
function buildPythonImportIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
|
|
29274
29712
|
const projectRoot = getProjectRoot();
|
|
29275
|
-
const absRoot =
|
|
29713
|
+
const absRoot = resolve35(projectRoot, pythonRoot);
|
|
29276
29714
|
dataDb.exec(`DELETE FROM ${t("py_imports")}`);
|
|
29277
29715
|
const insertStmt = dataDb.prepare(
|
|
29278
29716
|
`INSERT INTO ${t("py_imports")} (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)`
|
|
@@ -29286,7 +29724,7 @@ function buildPythonImportIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__"
|
|
|
29286
29724
|
});
|
|
29287
29725
|
const batch = [];
|
|
29288
29726
|
for (const absFile of files) {
|
|
29289
|
-
const relFile =
|
|
29727
|
+
const relFile = relative9(projectRoot, absFile);
|
|
29290
29728
|
let source;
|
|
29291
29729
|
try {
|
|
29292
29730
|
source = readFileSync45(absFile, "utf-8");
|
|
@@ -29557,7 +29995,7 @@ var init_route_parser = __esm({
|
|
|
29557
29995
|
|
|
29558
29996
|
// src/python/route-indexer.ts
|
|
29559
29997
|
import { readFileSync as readFileSync46, readdirSync as readdirSync23 } from "fs";
|
|
29560
|
-
import { join as join34, relative as
|
|
29998
|
+
import { join as join34, relative as relative10 } from "path";
|
|
29561
29999
|
function walkPyFiles(dir, excludeDirs) {
|
|
29562
30000
|
const files = [];
|
|
29563
30001
|
try {
|
|
@@ -29586,7 +30024,7 @@ function buildPythonRouteIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__",
|
|
|
29586
30024
|
let count = 0;
|
|
29587
30025
|
dataDb.transaction(() => {
|
|
29588
30026
|
for (const absFile of files) {
|
|
29589
|
-
const relFile =
|
|
30027
|
+
const relFile = relative10(projectRoot, absFile);
|
|
29590
30028
|
let source;
|
|
29591
30029
|
try {
|
|
29592
30030
|
source = readFileSync46(absFile, "utf-8");
|
|
@@ -29802,7 +30240,7 @@ var init_model_parser = __esm({
|
|
|
29802
30240
|
|
|
29803
30241
|
// src/python/model-indexer.ts
|
|
29804
30242
|
import { readFileSync as readFileSync47, readdirSync as readdirSync24 } from "fs";
|
|
29805
|
-
import { join as join35, relative as
|
|
30243
|
+
import { join as join35, relative as relative11 } from "path";
|
|
29806
30244
|
function walkPyFiles2(dir, excludeDirs) {
|
|
29807
30245
|
const files = [];
|
|
29808
30246
|
try {
|
|
@@ -29834,7 +30272,7 @@ function buildPythonModelIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__",
|
|
|
29834
30272
|
let count = 0;
|
|
29835
30273
|
dataDb.transaction(() => {
|
|
29836
30274
|
for (const absFile of files) {
|
|
29837
|
-
const relFile =
|
|
30275
|
+
const relFile = relative11(projectRoot, absFile);
|
|
29838
30276
|
let source;
|
|
29839
30277
|
try {
|
|
29840
30278
|
source = readFileSync47(absFile, "utf-8");
|
|
@@ -30099,7 +30537,7 @@ var init_migration_parser = __esm({
|
|
|
30099
30537
|
|
|
30100
30538
|
// src/python/migration-indexer.ts
|
|
30101
30539
|
import { readFileSync as readFileSync48, readdirSync as readdirSync25 } from "fs";
|
|
30102
|
-
import { join as join36, relative as
|
|
30540
|
+
import { join as join36, relative as relative12 } from "path";
|
|
30103
30541
|
function buildPythonMigrationIndex(dataDb, alembicDir) {
|
|
30104
30542
|
const projectRoot = getProjectRoot();
|
|
30105
30543
|
const absDir = join36(projectRoot, alembicDir);
|
|
@@ -30135,7 +30573,7 @@ function buildPythonMigrationIndex(dataDb, alembicDir) {
|
|
|
30135
30573
|
rows.push({
|
|
30136
30574
|
revision: parsed.revision,
|
|
30137
30575
|
downRevision: parsed.downRevision,
|
|
30138
|
-
file:
|
|
30576
|
+
file: relative12(projectRoot, absFile),
|
|
30139
30577
|
description: parsed.description,
|
|
30140
30578
|
operations: JSON.stringify(parsed.operations)
|
|
30141
30579
|
});
|
|
@@ -30160,7 +30598,7 @@ var init_migration_indexer = __esm({
|
|
|
30160
30598
|
|
|
30161
30599
|
// src/python/coupling-detector.ts
|
|
30162
30600
|
import { readFileSync as readFileSync49, readdirSync as readdirSync26 } from "fs";
|
|
30163
|
-
import { join as join37, relative as
|
|
30601
|
+
import { join as join37, relative as relative13 } from "path";
|
|
30164
30602
|
function buildPythonCouplingIndex(dataDb) {
|
|
30165
30603
|
const projectRoot = getProjectRoot();
|
|
30166
30604
|
const config = getConfig();
|
|
@@ -30193,7 +30631,7 @@ function buildPythonCouplingIndex(dataDb) {
|
|
|
30193
30631
|
];
|
|
30194
30632
|
dataDb.transaction(() => {
|
|
30195
30633
|
for (const absFile of frontendFiles) {
|
|
30196
|
-
const relFile =
|
|
30634
|
+
const relFile = relative13(projectRoot, absFile);
|
|
30197
30635
|
let source;
|
|
30198
30636
|
try {
|
|
30199
30637
|
source = readFileSync49(absFile, "utf-8");
|
|
@@ -30603,7 +31041,7 @@ var init_memory_tools = __esm({
|
|
|
30603
31041
|
|
|
30604
31042
|
// src/docs-tools.ts
|
|
30605
31043
|
import { readFileSync as readFileSync50, existsSync as existsSync60 } from "fs";
|
|
30606
|
-
import { resolve as
|
|
31044
|
+
import { resolve as resolve36, basename as basename9 } from "path";
|
|
30607
31045
|
function p4(baseName) {
|
|
30608
31046
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
30609
31047
|
}
|
|
@@ -30731,9 +31169,9 @@ function extractFrontmatter(content) {
|
|
|
30731
31169
|
}
|
|
30732
31170
|
function extractProcedureNames(routerPath) {
|
|
30733
31171
|
const root = getProjectRoot();
|
|
30734
|
-
const absPath = ensureWithinRoot(
|
|
31172
|
+
const absPath = ensureWithinRoot(resolve36(getResolvedPaths().srcDir, "..", routerPath), root);
|
|
30735
31173
|
if (!existsSync60(absPath)) {
|
|
30736
|
-
const altPath = ensureWithinRoot(
|
|
31174
|
+
const altPath = ensureWithinRoot(resolve36(getResolvedPaths().srcDir, "../server/api/routers", basename9(routerPath)), root);
|
|
30737
31175
|
if (!existsSync60(altPath)) return [];
|
|
30738
31176
|
return extractProcedureNamesFromContent(readFileSync50(altPath, "utf-8"));
|
|
30739
31177
|
}
|
|
@@ -30777,7 +31215,7 @@ function handleDocsAudit(args2) {
|
|
|
30777
31215
|
for (const [mappingId, triggeringFiles] of affectedMappings) {
|
|
30778
31216
|
const mapping = docsMap.mappings.find((m3) => m3.id === mappingId);
|
|
30779
31217
|
if (!mapping) continue;
|
|
30780
|
-
const helpPagePath = ensureWithinRoot(
|
|
31218
|
+
const helpPagePath = ensureWithinRoot(resolve36(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
|
|
30781
31219
|
if (!existsSync60(helpPagePath)) {
|
|
30782
31220
|
results.push({
|
|
30783
31221
|
helpPage: mapping.helpPage,
|
|
@@ -30830,7 +31268,7 @@ function handleDocsAudit(args2) {
|
|
|
30830
31268
|
});
|
|
30831
31269
|
for (const [guideName, parentId] of Object.entries(docsMap.userGuideInheritance.examples)) {
|
|
30832
31270
|
if (parentId === mappingId) {
|
|
30833
|
-
const guidePath = ensureWithinRoot(
|
|
31271
|
+
const guidePath = ensureWithinRoot(resolve36(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
|
|
30834
31272
|
if (existsSync60(guidePath)) {
|
|
30835
31273
|
const guideContent = readFileSync50(guidePath, "utf-8");
|
|
30836
31274
|
const guideFrontmatter = extractFrontmatter(guideContent);
|
|
@@ -30865,7 +31303,7 @@ function handleDocsCoverage(args2) {
|
|
|
30865
31303
|
const gaps = [];
|
|
30866
31304
|
const mappings = filterDomain ? docsMap.mappings.filter((m3) => m3.id === filterDomain) : docsMap.mappings;
|
|
30867
31305
|
for (const mapping of mappings) {
|
|
30868
|
-
const helpPagePath = ensureWithinRoot(
|
|
31306
|
+
const helpPagePath = ensureWithinRoot(resolve36(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
|
|
30869
31307
|
const exists = existsSync60(helpPagePath);
|
|
30870
31308
|
let hasContent = false;
|
|
30871
31309
|
let lineCount = 0;
|
|
@@ -31214,7 +31652,7 @@ var init_observability_tools = __esm({
|
|
|
31214
31652
|
|
|
31215
31653
|
// src/sentinel-db.ts
|
|
31216
31654
|
import { existsSync as existsSync61 } from "fs";
|
|
31217
|
-
import { resolve as
|
|
31655
|
+
import { resolve as resolve37 } from "path";
|
|
31218
31656
|
function parsePortalScope(raw) {
|
|
31219
31657
|
if (!raw) return [];
|
|
31220
31658
|
try {
|
|
@@ -31450,22 +31888,22 @@ function validateFeatures(db, domainFilter) {
|
|
|
31450
31888
|
const missingProcedures = [];
|
|
31451
31889
|
const missingPages = [];
|
|
31452
31890
|
for (const comp of components) {
|
|
31453
|
-
const absPath =
|
|
31891
|
+
const absPath = resolve37(PROJECT_ROOT, comp.component_file);
|
|
31454
31892
|
if (!existsSync61(absPath)) {
|
|
31455
31893
|
missingComponents.push(comp.component_file);
|
|
31456
31894
|
}
|
|
31457
31895
|
}
|
|
31458
31896
|
for (const proc of procedures) {
|
|
31459
|
-
const routerPath =
|
|
31897
|
+
const routerPath = resolve37(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
|
|
31460
31898
|
if (!existsSync61(routerPath)) {
|
|
31461
31899
|
missingProcedures.push({ router: proc.router_name, procedure: proc.procedure_name });
|
|
31462
31900
|
}
|
|
31463
31901
|
}
|
|
31464
31902
|
for (const page of pages) {
|
|
31465
31903
|
const routeToPath = page.page_route.replace(/^\/(portal-[^/]+\/)?/, "src/app/").replace(/\/$/, "") + "/page.tsx";
|
|
31466
|
-
const absPath =
|
|
31904
|
+
const absPath = resolve37(PROJECT_ROOT, routeToPath);
|
|
31467
31905
|
if (page.page_route.startsWith("/") && !existsSync61(absPath)) {
|
|
31468
|
-
const altPath =
|
|
31906
|
+
const altPath = resolve37(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
|
|
31469
31907
|
if (!existsSync61(altPath)) {
|
|
31470
31908
|
missingPages.push(page.page_route);
|
|
31471
31909
|
}
|
|
@@ -31993,7 +32431,7 @@ var init_sentinel_tools = __esm({
|
|
|
31993
32431
|
|
|
31994
32432
|
// src/sentinel-scanner.ts
|
|
31995
32433
|
import { readFileSync as readFileSync51, existsSync as existsSync62, readdirSync as readdirSync27, statSync as statSync20 } from "fs";
|
|
31996
|
-
import { resolve as
|
|
32434
|
+
import { resolve as resolve38, join as join38, basename as basename10, dirname as dirname27, relative as relative14 } from "path";
|
|
31997
32435
|
function inferDomain(filePath) {
|
|
31998
32436
|
const domains = getConfig().domains;
|
|
31999
32437
|
const path = filePath.toLowerCase();
|
|
@@ -32124,7 +32562,7 @@ function scanComponentExports(dataDb) {
|
|
|
32124
32562
|
const projectRoot = getProjectRoot();
|
|
32125
32563
|
const componentsBase = config.paths.components ?? config.paths.source + "/components";
|
|
32126
32564
|
const componentDirs = [];
|
|
32127
|
-
const basePath =
|
|
32565
|
+
const basePath = resolve38(projectRoot, componentsBase);
|
|
32128
32566
|
if (existsSync62(basePath)) {
|
|
32129
32567
|
try {
|
|
32130
32568
|
const entries = readdirSync27(basePath, { withFileTypes: true });
|
|
@@ -32137,11 +32575,11 @@ function scanComponentExports(dataDb) {
|
|
|
32137
32575
|
}
|
|
32138
32576
|
}
|
|
32139
32577
|
for (const dir of componentDirs) {
|
|
32140
|
-
const absDir =
|
|
32578
|
+
const absDir = resolve38(projectRoot, dir);
|
|
32141
32579
|
if (!existsSync62(absDir)) continue;
|
|
32142
32580
|
const files = walkDir2(absDir).filter((f2) => f2.endsWith(".tsx") || f2.endsWith(".ts"));
|
|
32143
32581
|
for (const file of files) {
|
|
32144
|
-
const relPath =
|
|
32582
|
+
const relPath = relative14(projectRoot, file);
|
|
32145
32583
|
const source = readFileSync51(file, "utf-8");
|
|
32146
32584
|
const annotations = parseFeatureAnnotations(source);
|
|
32147
32585
|
if (annotations.length > 0) {
|
|
@@ -33722,7 +34160,7 @@ var init_security_scorer = __esm({
|
|
|
33722
34160
|
|
|
33723
34161
|
// src/dependency-scorer.ts
|
|
33724
34162
|
import { existsSync as existsSync65, readFileSync as readFileSync54 } from "fs";
|
|
33725
|
-
import { resolve as
|
|
34163
|
+
import { resolve as resolve39 } from "path";
|
|
33726
34164
|
function p13(baseName) {
|
|
33727
34165
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
33728
34166
|
}
|
|
@@ -33754,7 +34192,7 @@ function calculateDepRisk(factors) {
|
|
|
33754
34192
|
return Math.min(100, risk);
|
|
33755
34193
|
}
|
|
33756
34194
|
function getInstalledPackages(projectRoot) {
|
|
33757
|
-
const pkgPath =
|
|
34195
|
+
const pkgPath = resolve39(projectRoot, "package.json");
|
|
33758
34196
|
if (!existsSync65(pkgPath)) return /* @__PURE__ */ new Map();
|
|
33759
34197
|
try {
|
|
33760
34198
|
const pkg = JSON.parse(readFileSync54(pkgPath, "utf-8"));
|
|
@@ -34364,7 +34802,7 @@ var init_regression_detector = __esm({
|
|
|
34364
34802
|
// src/knowledge-indexer.ts
|
|
34365
34803
|
import { createHash as createHash16 } from "crypto";
|
|
34366
34804
|
import { readFileSync as readFileSync55, readdirSync as readdirSync28, statSync as statSync21, existsSync as existsSync66 } from "fs";
|
|
34367
|
-
import { resolve as
|
|
34805
|
+
import { resolve as resolve40, relative as relative15, basename as basename11, extname as extname2 } from "path";
|
|
34368
34806
|
function getKnowledgePaths() {
|
|
34369
34807
|
const resolved = getResolvedPaths();
|
|
34370
34808
|
const config = getConfig();
|
|
@@ -34392,7 +34830,7 @@ function discoverMarkdownFiles(baseDir) {
|
|
|
34392
34830
|
try {
|
|
34393
34831
|
const entries = readdirSync28(dir, { withFileTypes: true });
|
|
34394
34832
|
for (const entry of entries) {
|
|
34395
|
-
const fullPath =
|
|
34833
|
+
const fullPath = resolve40(dir, entry.name);
|
|
34396
34834
|
if (entry.isDirectory()) {
|
|
34397
34835
|
if (entry.name === "archive" && dir.includes("session-state")) continue;
|
|
34398
34836
|
if (entry.name === "archive" && dir.includes("status")) continue;
|
|
@@ -34412,7 +34850,7 @@ function categorizeFile(filePath) {
|
|
|
34412
34850
|
const paths = getKnowledgePaths();
|
|
34413
34851
|
if (filePath.startsWith(paths.plansDir)) return "plan";
|
|
34414
34852
|
if (filePath.startsWith(paths.docsDir)) {
|
|
34415
|
-
const relFromDocs =
|
|
34853
|
+
const relFromDocs = relative15(paths.docsDir, filePath).replace(/\\/g, "/").toLowerCase();
|
|
34416
34854
|
if (relFromDocs.startsWith("plans/")) return "plan";
|
|
34417
34855
|
if (relFromDocs.includes("architecture")) return "architecture";
|
|
34418
34856
|
if (relFromDocs.includes("security")) return "security";
|
|
@@ -34428,7 +34866,7 @@ function categorizeFile(filePath) {
|
|
|
34428
34866
|
}
|
|
34429
34867
|
const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
|
|
34430
34868
|
if (filePath.includes(`${claudeDirName}/projects/`) && filePath.includes("/memory/")) return "memory";
|
|
34431
|
-
const rel =
|
|
34869
|
+
const rel = relative15(paths.claudeDir, filePath).replace(/\\/g, "/");
|
|
34432
34870
|
const firstDir = rel.split("/")[0];
|
|
34433
34871
|
const knownCategories = getConfig().conventions?.knowledgeCategories ?? [
|
|
34434
34872
|
"patterns",
|
|
@@ -34681,7 +35119,7 @@ function indexAllKnowledge(db) {
|
|
|
34681
35119
|
files.push(...docsFiles);
|
|
34682
35120
|
}
|
|
34683
35121
|
const now = /* @__PURE__ */ new Date();
|
|
34684
|
-
const
|
|
35122
|
+
const nowIso2 = now.toISOString();
|
|
34685
35123
|
const nowEpoch = now.getTime();
|
|
34686
35124
|
const transaction = db.transaction(() => {
|
|
34687
35125
|
db.exec("DROP TRIGGER IF EXISTS kc_fts_delete");
|
|
@@ -34721,11 +35159,11 @@ function indexAllKnowledge(db) {
|
|
|
34721
35159
|
if (!existsSync66(filePath)) continue;
|
|
34722
35160
|
const content = readFileSync55(filePath, "utf-8");
|
|
34723
35161
|
const hash = hashContent2(content);
|
|
34724
|
-
const relPath = filePath.startsWith(paths.claudeDir) ?
|
|
35162
|
+
const relPath = filePath.startsWith(paths.claudeDir) ? relative15(paths.claudeDir, filePath) : filePath.startsWith(paths.plansDir) ? "plans/" + relative15(paths.plansDir, filePath) : filePath.startsWith(paths.docsDir) ? "docs/" + relative15(paths.docsDir, filePath) : filePath.startsWith(paths.memoryDir) ? `memory/${relative15(paths.memoryDir, filePath)}` : basename11(filePath);
|
|
34725
35163
|
const category = categorizeFile(filePath);
|
|
34726
35164
|
const title = extractTitle(content, filePath);
|
|
34727
35165
|
const description = extractDescription2(content);
|
|
34728
|
-
const result = insertDoc.run(relPath, category, title, description, hash,
|
|
35166
|
+
const result = insertDoc.run(relPath, category, title, description, hash, nowIso2, nowEpoch);
|
|
34729
35167
|
const docId = result.lastInsertRowid;
|
|
34730
35168
|
stats.filesIndexed++;
|
|
34731
35169
|
const sections = parseSections(content, filePath);
|
|
@@ -34886,8 +35324,8 @@ var init_knowledge_indexer = __esm({
|
|
|
34886
35324
|
});
|
|
34887
35325
|
|
|
34888
35326
|
// src/knowledge-tools.ts
|
|
34889
|
-
import { readFileSync as readFileSync56, writeFileSync as
|
|
34890
|
-
import { resolve as
|
|
35327
|
+
import { readFileSync as readFileSync56, writeFileSync as writeFileSync17, appendFileSync as appendFileSync5, readdirSync as readdirSync29 } from "fs";
|
|
35328
|
+
import { resolve as resolve41, basename as basename12 } from "path";
|
|
34891
35329
|
function p16(baseName) {
|
|
34892
35330
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
34893
35331
|
}
|
|
@@ -34905,10 +35343,19 @@ function ensureKnowledgeIndexed(db) {
|
|
|
34905
35343
|
try {
|
|
34906
35344
|
indexIfStale(db);
|
|
34907
35345
|
return true;
|
|
34908
|
-
} catch {
|
|
35346
|
+
} catch (err) {
|
|
35347
|
+
recordHookFailure("knowledge-tools:ensure-indexed", err, {
|
|
35348
|
+
note: "knowledge index refresh failed; dispatch continued against the existing index"
|
|
35349
|
+
});
|
|
34909
35350
|
return false;
|
|
34910
35351
|
}
|
|
34911
35352
|
}
|
|
35353
|
+
function withStaleIndexWarning(result) {
|
|
35354
|
+
const banner = "\u26A0\uFE0F KNOWLEDGE INDEX NOT REFRESHED \u2014 the re-index for this call FAILED, so the results below come from the last successfully built index and may be out of date. The error was recorded to the hook-failure log under `knowledge-tools:ensure-indexed`.";
|
|
35355
|
+
return {
|
|
35356
|
+
content: [{ type: "text", text: banner }, ...result.content]
|
|
35357
|
+
};
|
|
35358
|
+
}
|
|
34912
35359
|
function hasData(db) {
|
|
34913
35360
|
const row = db.prepare("SELECT COUNT(*) as cnt FROM knowledge_documents").get();
|
|
34914
35361
|
return (row?.cnt ?? 0) > 0;
|
|
@@ -35089,8 +35536,15 @@ function isKnowledgeTool(name) {
|
|
|
35089
35536
|
const baseName = name.startsWith(pfx) ? name.slice(pfx.length) : name;
|
|
35090
35537
|
return KNOWLEDGE_BASE_NAMES.has(baseName);
|
|
35091
35538
|
}
|
|
35092
|
-
function handleKnowledgeToolCall(name, args2, db) {
|
|
35093
|
-
|
|
35539
|
+
function handleKnowledgeToolCall(name, args2, db, freshness) {
|
|
35540
|
+
if (freshness === "use-existing") {
|
|
35541
|
+
return dispatchKnowledgeTool(name, args2, db);
|
|
35542
|
+
}
|
|
35543
|
+
const indexed = ensureKnowledgeIndexed(db);
|
|
35544
|
+
const result = dispatchKnowledgeTool(name, args2, db);
|
|
35545
|
+
return indexed ? result : withStaleIndexWarning(result);
|
|
35546
|
+
}
|
|
35547
|
+
function dispatchKnowledgeTool(name, args2, db) {
|
|
35094
35548
|
if (!hasData(db)) {
|
|
35095
35549
|
return text15("No knowledge indexed yet. The knowledge DB will be populated automatically on next MCP server restart.");
|
|
35096
35550
|
}
|
|
@@ -35626,7 +36080,7 @@ function handleCorrect(db, args2) {
|
|
|
35626
36080
|
if (!wrong || !correction || !rule) {
|
|
35627
36081
|
return text15("Error: wrong, correction, and rule are all required.");
|
|
35628
36082
|
}
|
|
35629
|
-
const correctionsPath =
|
|
36083
|
+
const correctionsPath = resolve41(getResolvedPaths().memoryDir, "corrections.md");
|
|
35630
36084
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
35631
36085
|
const title = rule.slice(0, 60);
|
|
35632
36086
|
const entry = formatCorrectionEntry({
|
|
@@ -35646,7 +36100,7 @@ function handleCorrect(db, args2) {
|
|
|
35646
36100
|
if (archiveIdx > 0) {
|
|
35647
36101
|
const before = existing.slice(0, archiveIdx);
|
|
35648
36102
|
const after = existing.slice(archiveIdx);
|
|
35649
|
-
|
|
36103
|
+
writeFileSync17(correctionsPath, before + entry + after);
|
|
35650
36104
|
} else {
|
|
35651
36105
|
appendFileSync5(correctionsPath, entry);
|
|
35652
36106
|
}
|
|
@@ -35960,6 +36414,7 @@ var init_knowledge_tools = __esm({
|
|
|
35960
36414
|
init_db2();
|
|
35961
36415
|
init_memory_db();
|
|
35962
36416
|
init_sql_table_names();
|
|
36417
|
+
init_hook_failure_signal();
|
|
35963
36418
|
KNOWLEDGE_BASE_NAMES = /* @__PURE__ */ new Set([
|
|
35964
36419
|
"knowledge_search",
|
|
35965
36420
|
"knowledge_rule",
|
|
@@ -36596,7 +37051,7 @@ var init_framework_supports = __esm({
|
|
|
36596
37051
|
|
|
36597
37052
|
// src/tools.ts
|
|
36598
37053
|
import { readFileSync as readFileSync57, existsSync as existsSync68 } from "fs";
|
|
36599
|
-
import { resolve as
|
|
37054
|
+
import { resolve as resolve42, basename as basename13 } from "path";
|
|
36600
37055
|
function prefix2() {
|
|
36601
37056
|
return getConfig().toolPrefix;
|
|
36602
37057
|
}
|
|
@@ -36631,7 +37086,7 @@ function ensureIndexes(dataDb, codegraphDb, force = false) {
|
|
|
36631
37086
|
if (config.python?.root) {
|
|
36632
37087
|
const pythonRoot = config.python.root;
|
|
36633
37088
|
const excludeDirs = config.python.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
|
|
36634
|
-
if (force || isPythonDataStale(dataDb,
|
|
37089
|
+
if (force || isPythonDataStale(dataDb, resolve42(getProjectRoot(), pythonRoot))) {
|
|
36635
37090
|
const pyImports = buildPythonImportIndex(dataDb, pythonRoot, excludeDirs);
|
|
36636
37091
|
results.push(`Python imports: ${pyImports}`);
|
|
36637
37092
|
const pyRoutes = buildPythonRouteIndex(dataDb, pythonRoot, excludeDirs);
|
|
@@ -36854,7 +37309,7 @@ async function handleToolCall(name, args2, dataDb, codegraphDb, memoryDbCache, k
|
|
|
36854
37309
|
return withMemDb((memDb) => handleRegressionToolCall(name, args2, memDb));
|
|
36855
37310
|
}
|
|
36856
37311
|
if (isKnowledgeTool(name)) {
|
|
36857
|
-
return withKnowledgeDb((kdb) => handleKnowledgeToolCall(name, args2, kdb));
|
|
37312
|
+
return withKnowledgeDb((kdb) => handleKnowledgeToolCall(name, args2, kdb, "ensure-fresh"));
|
|
36858
37313
|
}
|
|
36859
37314
|
if (isPythonTool(name)) {
|
|
36860
37315
|
const pyDataDb = assertDataDb(dataDb, name);
|
|
@@ -37014,7 +37469,7 @@ function handleContext(file, dataDb, codegraphDb) {
|
|
|
37014
37469
|
try {
|
|
37015
37470
|
const resolvedPaths = getResolvedPaths();
|
|
37016
37471
|
const root = getProjectRoot();
|
|
37017
|
-
const absFilePath = ensureWithinRoot(
|
|
37472
|
+
const absFilePath = ensureWithinRoot(resolve42(resolvedPaths.srcDir, "..", file), root);
|
|
37018
37473
|
if (existsSync68(absFilePath)) {
|
|
37019
37474
|
const fileContent = readFileSync57(absFilePath, "utf-8").slice(0, 3e3);
|
|
37020
37475
|
const keywords = [];
|
|
@@ -37253,9 +37708,10 @@ function handleCouplingCheck(args2, dataDb, codegraphDb) {
|
|
|
37253
37708
|
lines.push("### Uncoupled Procedures: 0 (PASS)");
|
|
37254
37709
|
lines.push("");
|
|
37255
37710
|
}
|
|
37711
|
+
const pagesPred = pagesPredicate();
|
|
37256
37712
|
const allPages = codegraphDb.prepare(
|
|
37257
|
-
|
|
37258
|
-
).all();
|
|
37713
|
+
`SELECT path FROM files WHERE ${pagesPred.sql} LIMIT 10000`
|
|
37714
|
+
).all(...pagesPred.params);
|
|
37259
37715
|
const pageImports = /* @__PURE__ */ new Set();
|
|
37260
37716
|
for (const page of allPages) {
|
|
37261
37717
|
const imports = dataDb.prepare(
|
|
@@ -37268,9 +37724,10 @@ function handleCouplingCheck(args2, dataDb, codegraphDb) {
|
|
|
37268
37724
|
let componentFiles;
|
|
37269
37725
|
if (stagedFiles) {
|
|
37270
37726
|
const placeholders = stagedFiles.map(() => "?").join(",");
|
|
37727
|
+
const componentsPred = componentsPredicate();
|
|
37271
37728
|
componentFiles = codegraphDb.prepare(
|
|
37272
|
-
`SELECT path FROM files WHERE
|
|
37273
|
-
).all(...stagedFiles);
|
|
37729
|
+
`SELECT path FROM files WHERE ${componentsPred.sql} AND path IN (${placeholders}) LIMIT 10000`
|
|
37730
|
+
).all(...componentsPred.params, ...stagedFiles);
|
|
37274
37731
|
} else {
|
|
37275
37732
|
componentFiles = [];
|
|
37276
37733
|
}
|
|
@@ -37453,7 +37910,7 @@ function handleSchema(args2) {
|
|
|
37453
37910
|
lines.push("Checking all column references against Prisma schema...");
|
|
37454
37911
|
lines.push("");
|
|
37455
37912
|
const projectRoot = getProjectRoot();
|
|
37456
|
-
const absPath = ensureWithinRoot(
|
|
37913
|
+
const absPath = ensureWithinRoot(resolve42(projectRoot, file), projectRoot);
|
|
37457
37914
|
if (!existsSync68(absPath)) {
|
|
37458
37915
|
return text17(`File not found: ${file}`);
|
|
37459
37916
|
}
|
|
@@ -37532,6 +37989,7 @@ var init_tools = __esm({
|
|
|
37532
37989
|
init_framework_supports();
|
|
37533
37990
|
init_license();
|
|
37534
37991
|
init_sql_table_names();
|
|
37992
|
+
init_source_layout();
|
|
37535
37993
|
}
|
|
37536
37994
|
});
|
|
37537
37995
|
|
|
@@ -37890,7 +38348,7 @@ var init_startup_health = __esm({
|
|
|
37890
38348
|
// src/server.ts
|
|
37891
38349
|
var server_exports = {};
|
|
37892
38350
|
import { readFileSync as readFileSync58 } from "fs";
|
|
37893
|
-
import { resolve as
|
|
38351
|
+
import { resolve as resolve43, dirname as dirname28 } from "path";
|
|
37894
38352
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
37895
38353
|
function pruneMemoryOnStartup() {
|
|
37896
38354
|
try {
|
|
@@ -37938,7 +38396,7 @@ var init_server = __esm({
|
|
|
37938
38396
|
__dirname5 = dirname28(fileURLToPath6(import.meta.url));
|
|
37939
38397
|
PKG_VERSION = (() => {
|
|
37940
38398
|
try {
|
|
37941
|
-
const pkg = JSON.parse(readFileSync58(
|
|
38399
|
+
const pkg = JSON.parse(readFileSync58(resolve43(__dirname5, "..", "package.json"), "utf-8"));
|
|
37942
38400
|
return pkg.version ?? "0.0.0";
|
|
37943
38401
|
} catch {
|
|
37944
38402
|
return "0.0.0";
|
|
@@ -38241,12 +38699,12 @@ var config_upgrade_exports = {};
|
|
|
38241
38699
|
__export(config_upgrade_exports, {
|
|
38242
38700
|
runConfigUpgrade: () => runConfigUpgrade
|
|
38243
38701
|
});
|
|
38244
|
-
import { existsSync as existsSync69, readFileSync as readFileSync59, writeFileSync as
|
|
38245
|
-
import { resolve as
|
|
38702
|
+
import { existsSync as existsSync69, readFileSync as readFileSync59, writeFileSync as writeFileSync18, copyFileSync as copyFileSync3, unlinkSync as unlinkSync6 } from "fs";
|
|
38703
|
+
import { resolve as resolve44 } from "path";
|
|
38246
38704
|
import { parse as parseYaml6 } from "yaml";
|
|
38247
38705
|
async function runConfigUpgrade(opts = {}) {
|
|
38248
38706
|
const cwd = opts.cwd ?? process.cwd();
|
|
38249
|
-
const configPath =
|
|
38707
|
+
const configPath = resolve44(cwd, "massu.config.yaml");
|
|
38250
38708
|
const bakPath = `${configPath}.bak`;
|
|
38251
38709
|
const log = opts.silent ? () => {
|
|
38252
38710
|
} : (s) => process.stdout.write(s);
|
|
@@ -38300,7 +38758,7 @@ async function runConfigUpgrade(opts = {}) {
|
|
|
38300
38758
|
};
|
|
38301
38759
|
try {
|
|
38302
38760
|
const original = readFileSync59(configPath, "utf-8");
|
|
38303
|
-
|
|
38761
|
+
writeFileSync18(bakPath, original, "utf-8");
|
|
38304
38762
|
} catch (e2) {
|
|
38305
38763
|
const message = `Failed to write backup: ${e2 instanceof Error ? e2.message : String(e2)}`;
|
|
38306
38764
|
err(message + "\n");
|
|
@@ -38334,7 +38792,7 @@ __export(config_check_drift_exports, {
|
|
|
38334
38792
|
runConfigCheckDrift: () => runConfigCheckDrift
|
|
38335
38793
|
});
|
|
38336
38794
|
import { existsSync as existsSync70, readFileSync as readFileSync60 } from "fs";
|
|
38337
|
-
import { resolve as
|
|
38795
|
+
import { resolve as resolve45 } from "path";
|
|
38338
38796
|
import { parse as parseYaml7 } from "yaml";
|
|
38339
38797
|
function renderChanges(changes) {
|
|
38340
38798
|
if (changes.length === 0) return "(none)\n";
|
|
@@ -38342,7 +38800,7 @@ function renderChanges(changes) {
|
|
|
38342
38800
|
}
|
|
38343
38801
|
async function runConfigCheckDrift(opts = {}) {
|
|
38344
38802
|
const cwd = opts.cwd ?? process.cwd();
|
|
38345
|
-
const configPath =
|
|
38803
|
+
const configPath = resolve45(cwd, "massu.config.yaml");
|
|
38346
38804
|
const log = opts.silent ? () => {
|
|
38347
38805
|
} : (s) => process.stdout.write(s);
|
|
38348
38806
|
const err = opts.silent ? () => {
|
|
@@ -38429,7 +38887,7 @@ var init_config_check_drift = __esm({
|
|
|
38429
38887
|
|
|
38430
38888
|
// src/cli.ts
|
|
38431
38889
|
import { readFileSync as readFileSync61 } from "fs";
|
|
38432
|
-
import { resolve as
|
|
38890
|
+
import { resolve as resolve46, dirname as dirname29 } from "path";
|
|
38433
38891
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
38434
38892
|
|
|
38435
38893
|
// src/lib/node-bootstrap.ts
|
|
@@ -38931,7 +39389,7 @@ Examples:
|
|
|
38931
39389
|
}
|
|
38932
39390
|
function printVersion() {
|
|
38933
39391
|
try {
|
|
38934
|
-
const pkg = JSON.parse(readFileSync61(
|
|
39392
|
+
const pkg = JSON.parse(readFileSync61(resolve46(__dirname6, "../package.json"), "utf-8"));
|
|
38935
39393
|
console.log(`massu v${pkg.version}`);
|
|
38936
39394
|
} catch {
|
|
38937
39395
|
console.log("massu v0.1.0");
|