@danielblomma/cortex-mcp 0.4.5 → 0.6.5
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/README.md +38 -42
- package/bin/cortex.mjs +32 -60
- package/package.json +15 -3
- package/scaffold/.context/ontology.cypher +47 -0
- package/scaffold/.githooks/post-commit +14 -0
- package/scaffold/.githooks/post-rewrite +23 -0
- package/scaffold/mcp/package-lock.json +16 -16
- package/scaffold/mcp/package.json +4 -1
- package/scaffold/mcp/src/contextEntities.ts +311 -0
- package/scaffold/mcp/src/defaults.ts +6 -0
- package/scaffold/mcp/src/embed.ts +163 -37
- package/scaffold/mcp/src/frontmatter.ts +39 -0
- package/scaffold/mcp/src/graph.ts +253 -130
- package/scaffold/mcp/src/graphMetrics.ts +12 -0
- package/scaffold/mcp/src/impactPresentation.ts +202 -0
- package/scaffold/mcp/src/impactRanking.ts +237 -0
- package/scaffold/mcp/src/impactResponse.ts +47 -0
- package/scaffold/mcp/src/impactResults.ts +173 -0
- package/scaffold/mcp/src/impactSeed.ts +33 -0
- package/scaffold/mcp/src/impactTraversal.ts +83 -0
- package/scaffold/mcp/src/jsonl.ts +34 -0
- package/scaffold/mcp/src/loadGraph.ts +345 -86
- package/scaffold/mcp/src/paths.ts +17 -1
- package/scaffold/mcp/src/presets.ts +137 -0
- package/scaffold/mcp/src/relatedResponse.ts +30 -0
- package/scaffold/mcp/src/relatedTraversal.ts +101 -0
- package/scaffold/mcp/src/rules.ts +27 -0
- package/scaffold/mcp/src/search.ts +186 -455
- package/scaffold/mcp/src/searchCore.ts +274 -0
- package/scaffold/mcp/src/searchResults.ts +133 -0
- package/scaffold/mcp/src/server.ts +95 -3
- package/scaffold/mcp/src/types.ts +82 -3
- package/scaffold/scripts/context.sh +12 -46
- package/scaffold/scripts/dashboard.mjs +797 -0
- package/scaffold/scripts/dashboard.sh +13 -0
- package/scaffold/scripts/ingest.mjs +2227 -59
- package/scaffold/scripts/install-git-hooks.sh +3 -1
- package/scaffold/scripts/memory-compile.mjs +232 -0
- package/scaffold/scripts/memory-compile.sh +20 -0
- package/scaffold/scripts/memory-lint.mjs +375 -0
- package/scaffold/scripts/memory-lint.sh +20 -0
- package/scaffold/scripts/parsers/config.mjs +178 -0
- package/scaffold/scripts/parsers/cpp.mjs +316 -0
- package/scaffold/scripts/parsers/dotnet/VbNetParser/Program.cs +374 -0
- package/scaffold/scripts/parsers/dotnet/VbNetParser/VbNetParser.csproj +13 -0
- package/scaffold/scripts/parsers/javascript/ast.mjs +61 -0
- package/scaffold/scripts/parsers/javascript/calls.mjs +53 -0
- package/scaffold/scripts/parsers/javascript/chunks.mjs +388 -0
- package/scaffold/scripts/parsers/javascript/imports.mjs +162 -0
- package/scaffold/scripts/parsers/javascript/patterns.mjs +82 -0
- package/scaffold/scripts/parsers/javascript/scope-analysis.mjs +3 -0
- package/scaffold/scripts/parsers/javascript/scope-builder.mjs +305 -0
- package/scaffold/scripts/parsers/javascript/scope-resolver.mjs +82 -0
- package/scaffold/scripts/parsers/javascript.mjs +27 -350
- package/scaffold/scripts/parsers/resources.mjs +166 -0
- package/scaffold/scripts/parsers/rust.mjs +515 -0
- package/scaffold/scripts/parsers/sql.mjs +137 -0
- package/scaffold/scripts/parsers/vbnet.mjs +143 -0
- package/scaffold/scripts/status.sh +0 -7
- package/scaffold/scripts/capture-note.sh +0 -55
- package/scaffold/scripts/plan-state-engine.cjs +0 -310
- package/scaffold/scripts/plan-state.sh +0 -71
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import ryugraph, { type Connection, type QueryResult } from "ryugraph";
|
|
4
|
+
import ryugraph, { type Connection, type PreparedStatement, type QueryResult, type RyuValue } from "ryugraph";
|
|
5
|
+
import { readJsonl, asString, asNumber, asBoolean } from "./jsonl.js";
|
|
6
|
+
import type { JsonObject } from "./types.js";
|
|
5
7
|
|
|
6
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
9
|
const __dirname = path.dirname(__filename);
|
|
@@ -10,9 +12,19 @@ const CONTEXT_DIR = path.join(REPO_ROOT, ".context");
|
|
|
10
12
|
const CACHE_DIR = path.join(CONTEXT_DIR, "cache");
|
|
11
13
|
const DB_PATH = path.join(CONTEXT_DIR, "db", "graph.ryu");
|
|
12
14
|
const ONTOLOGY_PATH = path.join(CONTEXT_DIR, "ontology.cypher");
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
const BATCH_SIZE = 50;
|
|
16
|
+
|
|
17
|
+
async function executeBatch(
|
|
18
|
+
conn: Connection,
|
|
19
|
+
statement: PreparedStatement,
|
|
20
|
+
items: Record<string, RyuValue>[],
|
|
21
|
+
batchSize = BATCH_SIZE
|
|
22
|
+
): Promise<void> {
|
|
23
|
+
for (let i = 0; i < items.length; i += batchSize) {
|
|
24
|
+
const batch = items.slice(i, i + batchSize);
|
|
25
|
+
await Promise.all(batch.map((item) => conn.execute(statement, item)));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
16
28
|
|
|
17
29
|
type FileEntity = {
|
|
18
30
|
id: string;
|
|
@@ -57,12 +69,44 @@ type ChunkEntity = {
|
|
|
57
69
|
kind: string;
|
|
58
70
|
signature: string;
|
|
59
71
|
body: string;
|
|
72
|
+
description: string;
|
|
60
73
|
start_line: number;
|
|
61
74
|
end_line: number;
|
|
62
75
|
language: string;
|
|
76
|
+
exported: boolean;
|
|
63
77
|
checksum: string;
|
|
64
78
|
updated_at: string;
|
|
79
|
+
source_of_truth: boolean;
|
|
65
80
|
trust_level: number;
|
|
81
|
+
status: string;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
type ModuleEntity = {
|
|
85
|
+
id: string;
|
|
86
|
+
path: string;
|
|
87
|
+
name: string;
|
|
88
|
+
summary: string;
|
|
89
|
+
file_count: number;
|
|
90
|
+
exported_symbols: string;
|
|
91
|
+
updated_at: string;
|
|
92
|
+
source_of_truth: boolean;
|
|
93
|
+
trust_level: number;
|
|
94
|
+
status: string;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
type ProjectEntity = {
|
|
98
|
+
id: string;
|
|
99
|
+
path: string;
|
|
100
|
+
name: string;
|
|
101
|
+
kind: string;
|
|
102
|
+
language: string;
|
|
103
|
+
target_framework: string;
|
|
104
|
+
summary: string;
|
|
105
|
+
file_count: number;
|
|
106
|
+
updated_at: string;
|
|
107
|
+
source_of_truth: boolean;
|
|
108
|
+
trust_level: number;
|
|
109
|
+
status: string;
|
|
66
110
|
};
|
|
67
111
|
|
|
68
112
|
type Relation = {
|
|
@@ -83,38 +127,6 @@ type ImportRelation = {
|
|
|
83
127
|
import_name: string;
|
|
84
128
|
};
|
|
85
129
|
|
|
86
|
-
function asString(value: JsonValue | undefined, fallback = ""): string {
|
|
87
|
-
return typeof value === "string" ? value : fallback;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function asNumber(value: JsonValue | undefined, fallback = 0): number {
|
|
91
|
-
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function asBoolean(value: JsonValue | undefined, fallback = false): boolean {
|
|
95
|
-
return typeof value === "boolean" ? value : fallback;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function readJsonl(filePath: string): JsonObject[] {
|
|
99
|
-
if (!fs.existsSync(filePath)) {
|
|
100
|
-
return [];
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return fs
|
|
104
|
-
.readFileSync(filePath, "utf8")
|
|
105
|
-
.split(/\r?\n/)
|
|
106
|
-
.map((line) => line.trim())
|
|
107
|
-
.filter(Boolean)
|
|
108
|
-
.map((line) => {
|
|
109
|
-
try {
|
|
110
|
-
return JSON.parse(line) as JsonObject;
|
|
111
|
-
} catch {
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
})
|
|
115
|
-
.filter((value): value is JsonObject => value !== null);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
130
|
function readEntityFile(fileName: string): JsonObject[] {
|
|
119
131
|
return readJsonl(path.join(CACHE_DIR, fileName));
|
|
120
132
|
}
|
|
@@ -206,17 +218,71 @@ function parseChunks(raw: JsonObject[]): ChunkEntity[] {
|
|
|
206
218
|
kind: asString(item.kind, "function"),
|
|
207
219
|
signature: asString(item.signature),
|
|
208
220
|
body: asString(item.body),
|
|
221
|
+
description: asString(item.description),
|
|
209
222
|
start_line: asNumber(item.start_line, 0),
|
|
210
223
|
end_line: asNumber(item.end_line, 0),
|
|
211
224
|
language: asString(item.language, "javascript"),
|
|
225
|
+
exported: asBoolean(item.exported, false),
|
|
212
226
|
checksum: asString(item.checksum),
|
|
213
227
|
updated_at: asString(item.updated_at),
|
|
214
|
-
|
|
228
|
+
source_of_truth: asBoolean(item.source_of_truth, true),
|
|
229
|
+
trust_level: asNumber(item.trust_level, 80),
|
|
230
|
+
status: asString(item.status, "active")
|
|
215
231
|
};
|
|
216
232
|
})
|
|
217
233
|
.filter((value): value is ChunkEntity => value !== null);
|
|
218
234
|
}
|
|
219
235
|
|
|
236
|
+
function parseModules(raw: JsonObject[]): ModuleEntity[] {
|
|
237
|
+
return raw
|
|
238
|
+
.map((item) => {
|
|
239
|
+
const id = asString(item.id);
|
|
240
|
+
if (!id) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
id,
|
|
246
|
+
path: asString(item.path),
|
|
247
|
+
name: asString(item.name),
|
|
248
|
+
summary: asString(item.summary),
|
|
249
|
+
file_count: asNumber(item.file_count, 0),
|
|
250
|
+
exported_symbols: asString(item.exported_symbols),
|
|
251
|
+
updated_at: asString(item.updated_at),
|
|
252
|
+
source_of_truth: asBoolean(item.source_of_truth, false),
|
|
253
|
+
trust_level: asNumber(item.trust_level, 75),
|
|
254
|
+
status: asString(item.status, "active")
|
|
255
|
+
};
|
|
256
|
+
})
|
|
257
|
+
.filter((value): value is ModuleEntity => value !== null);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function parseProjects(raw: JsonObject[]): ProjectEntity[] {
|
|
261
|
+
return raw
|
|
262
|
+
.map((item) => {
|
|
263
|
+
const id = asString(item.id);
|
|
264
|
+
if (!id) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
id,
|
|
270
|
+
path: asString(item.path),
|
|
271
|
+
name: asString(item.name),
|
|
272
|
+
kind: asString(item.kind, "project"),
|
|
273
|
+
language: asString(item.language, "dotnet"),
|
|
274
|
+
target_framework: asString(item.target_framework),
|
|
275
|
+
summary: asString(item.summary),
|
|
276
|
+
file_count: asNumber(item.file_count, 0),
|
|
277
|
+
updated_at: asString(item.updated_at),
|
|
278
|
+
source_of_truth: asBoolean(item.source_of_truth, false),
|
|
279
|
+
trust_level: asNumber(item.trust_level, 80),
|
|
280
|
+
status: asString(item.status, "active")
|
|
281
|
+
};
|
|
282
|
+
})
|
|
283
|
+
.filter((value): value is ProjectEntity => value !== null);
|
|
284
|
+
}
|
|
285
|
+
|
|
220
286
|
function parseRelations(fileName: string, noteField: string): Relation[] {
|
|
221
287
|
const raw = readEntityFile(fileName);
|
|
222
288
|
return raw
|
|
@@ -335,21 +401,36 @@ async function ensureRequiredFiles(): Promise<void> {
|
|
|
335
401
|
}
|
|
336
402
|
}
|
|
337
403
|
|
|
338
|
-
function
|
|
339
|
-
const
|
|
404
|
+
function warnIfOptionalFilesMissing(): void {
|
|
405
|
+
const optionalFiles = [
|
|
340
406
|
"entities.chunk.jsonl",
|
|
341
407
|
"relations.defines.jsonl",
|
|
342
408
|
"relations.calls.jsonl",
|
|
343
|
-
"relations.
|
|
409
|
+
"relations.calls_sql.jsonl",
|
|
410
|
+
"relations.uses_config_key.jsonl",
|
|
411
|
+
"relations.uses_resource_key.jsonl",
|
|
412
|
+
"relations.uses_setting_key.jsonl",
|
|
413
|
+
"relations.imports.jsonl",
|
|
414
|
+
"entities.module.jsonl",
|
|
415
|
+
"entities.project.jsonl",
|
|
416
|
+
"relations.contains.jsonl",
|
|
417
|
+
"relations.contains_module.jsonl",
|
|
418
|
+
"relations.exports.jsonl",
|
|
419
|
+
"relations.includes_file.jsonl",
|
|
420
|
+
"relations.references_project.jsonl",
|
|
421
|
+
"relations.uses_resource.jsonl",
|
|
422
|
+
"relations.uses_setting.jsonl",
|
|
423
|
+
"relations.uses_config.jsonl",
|
|
424
|
+
"relations.transforms_config.jsonl"
|
|
344
425
|
];
|
|
345
426
|
|
|
346
|
-
const missing =
|
|
427
|
+
const missing = optionalFiles.filter((fileName) => !fs.existsSync(path.join(CACHE_DIR, fileName)));
|
|
347
428
|
if (missing.length === 0) {
|
|
348
429
|
return;
|
|
349
430
|
}
|
|
350
431
|
|
|
351
432
|
console.warn(
|
|
352
|
-
`[graph-load] warning: missing optional
|
|
433
|
+
`[graph-load] warning: missing optional files (${missing.join(", ")}); continuing without those nodes/relations`
|
|
353
434
|
);
|
|
354
435
|
}
|
|
355
436
|
|
|
@@ -358,7 +439,7 @@ async function main(): Promise<void> {
|
|
|
358
439
|
const reset = !args.has("--no-reset");
|
|
359
440
|
|
|
360
441
|
await ensureRequiredFiles();
|
|
361
|
-
|
|
442
|
+
warnIfOptionalFilesMissing();
|
|
362
443
|
|
|
363
444
|
if (reset) {
|
|
364
445
|
fs.rmSync(DB_PATH, { recursive: true, force: true });
|
|
@@ -371,15 +452,33 @@ async function main(): Promise<void> {
|
|
|
371
452
|
const ontologyStatements = parseOntologyStatements(fs.readFileSync(ONTOLOGY_PATH, "utf8"));
|
|
372
453
|
await executeStatements(conn, ontologyStatements);
|
|
373
454
|
|
|
455
|
+
// Delete all relations first, then all nodes, to avoid orphaned edges
|
|
374
456
|
await conn.query("MATCH (a:ADR)-[r:SUPERSEDES]->(b:ADR) DELETE r;");
|
|
375
457
|
await conn.query("MATCH (f:File)-[i:IMPLEMENTS]->(r:Rule) DELETE i;");
|
|
376
458
|
await conn.query("MATCH (r:Rule)-[c:CONSTRAINS]->(f:File) DELETE c;");
|
|
377
459
|
await conn.query("MATCH (f:File)-[d:DEFINES]->(c:Chunk) DELETE d;");
|
|
378
460
|
await conn.query("MATCH (c1:Chunk)-[ca:CALLS]->(c2:Chunk) DELETE ca;");
|
|
379
461
|
await conn.query("MATCH (c:Chunk)-[im:IMPORTS]->(f:File) DELETE im;");
|
|
462
|
+
await conn.query("MATCH (f:File)-[cs:CALLS_SQL]->(c:Chunk) DELETE cs;");
|
|
463
|
+
await conn.query("MATCH (f:File)-[uck:USES_CONFIG_KEY]->(c:Chunk) DELETE uck;");
|
|
464
|
+
await conn.query("MATCH (f:File)-[urk:USES_RESOURCE_KEY]->(c:Chunk) DELETE urk;");
|
|
465
|
+
await conn.query("MATCH (f:File)-[usk:USES_SETTING_KEY]->(c:Chunk) DELETE usk;");
|
|
466
|
+
await conn.query("MATCH (m:Module)-[co:CONTAINS]->(f:File) DELETE co;");
|
|
467
|
+
await conn.query("MATCH (m1:Module)-[cm:CONTAINS_MODULE]->(m2:Module) DELETE cm;");
|
|
468
|
+
await conn.query("MATCH (m:Module)-[ex:EXPORTS]->(c:Chunk) DELETE ex;");
|
|
469
|
+
await conn.query("MATCH (p:Project)-[inc:INCLUDES_FILE]->(f:File) DELETE inc;");
|
|
470
|
+
await conn.query("MATCH (p1:Project)-[rp:REFERENCES_PROJECT]->(p2:Project) DELETE rp;");
|
|
471
|
+
await conn.query("MATCH (f1:File)-[ur:USES_RESOURCE]->(f2:File) DELETE ur;");
|
|
472
|
+
await conn.query("MATCH (f1:File)-[us:USES_SETTING]->(f2:File) DELETE us;");
|
|
473
|
+
await conn.query("MATCH (f1:File)-[uc:USES_CONFIG]->(f2:File) DELETE uc;");
|
|
474
|
+
await conn.query("MATCH (f1:File)-[tc:TRANSFORMS_CONFIG]->(f2:File) DELETE tc;");
|
|
475
|
+
|
|
476
|
+
// Now delete all nodes
|
|
380
477
|
await conn.query("MATCH (n:ADR) DELETE n;");
|
|
381
478
|
await conn.query("MATCH (n:Rule) DELETE n;");
|
|
382
479
|
await conn.query("MATCH (n:Chunk) DELETE n;");
|
|
480
|
+
await conn.query("MATCH (n:Module) DELETE n;");
|
|
481
|
+
await conn.query("MATCH (n:Project) DELETE n;");
|
|
383
482
|
await conn.query("MATCH (n:File) DELETE n;");
|
|
384
483
|
|
|
385
484
|
const fileEntities = parseFiles(readEntityFile("entities.file.jsonl"));
|
|
@@ -391,7 +490,22 @@ async function main(): Promise<void> {
|
|
|
391
490
|
const supersedes = parseRelations("relations.supersedes.jsonl", "reason");
|
|
392
491
|
const defines = parseSimpleRelations("relations.defines.jsonl");
|
|
393
492
|
const calls = parseCallRelations("relations.calls.jsonl");
|
|
493
|
+
const callsSql = parseRelations("relations.calls_sql.jsonl", "note");
|
|
494
|
+
const usesConfigKey = parseRelations("relations.uses_config_key.jsonl", "note");
|
|
495
|
+
const usesResourceKey = parseRelations("relations.uses_resource_key.jsonl", "note");
|
|
496
|
+
const usesSettingKey = parseRelations("relations.uses_setting_key.jsonl", "note");
|
|
394
497
|
const imports = parseImportRelations("relations.imports.jsonl");
|
|
498
|
+
const moduleEntities = parseModules(readEntityFile("entities.module.jsonl"));
|
|
499
|
+
const projectEntities = parseProjects(readEntityFile("entities.project.jsonl"));
|
|
500
|
+
const contains = parseSimpleRelations("relations.contains.jsonl");
|
|
501
|
+
const containsModule = parseSimpleRelations("relations.contains_module.jsonl");
|
|
502
|
+
const exports = parseSimpleRelations("relations.exports.jsonl");
|
|
503
|
+
const includesFile = parseSimpleRelations("relations.includes_file.jsonl");
|
|
504
|
+
const referencesProject = parseRelations("relations.references_project.jsonl", "note");
|
|
505
|
+
const usesResource = parseRelations("relations.uses_resource.jsonl", "note");
|
|
506
|
+
const usesSetting = parseRelations("relations.uses_setting.jsonl", "note");
|
|
507
|
+
const usesConfig = parseRelations("relations.uses_config.jsonl", "note");
|
|
508
|
+
const transformsConfig = parseRelations("relations.transforms_config.jsonl", "note");
|
|
395
509
|
|
|
396
510
|
const insertFile = await conn.prepare(`
|
|
397
511
|
CREATE (f:File {
|
|
@@ -443,12 +557,16 @@ async function main(): Promise<void> {
|
|
|
443
557
|
kind: $kind,
|
|
444
558
|
signature: $signature,
|
|
445
559
|
body: $body,
|
|
560
|
+
description: $description,
|
|
446
561
|
start_line: $start_line,
|
|
447
562
|
end_line: $end_line,
|
|
448
563
|
language: $language,
|
|
564
|
+
exported: $exported,
|
|
449
565
|
checksum: $checksum,
|
|
450
566
|
updated_at: $updated_at,
|
|
451
|
-
|
|
567
|
+
source_of_truth: $source_of_truth,
|
|
568
|
+
trust_level: $trust_level,
|
|
569
|
+
status: $status
|
|
452
570
|
});
|
|
453
571
|
`);
|
|
454
572
|
|
|
@@ -482,55 +600,135 @@ async function main(): Promise<void> {
|
|
|
482
600
|
CREATE (c)-[:IMPORTS {import_name: $import_name}]->(f);
|
|
483
601
|
`);
|
|
484
602
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
603
|
+
const insertCallsSql = await conn.prepare(`
|
|
604
|
+
MATCH (f:File {id: $from}), (c:Chunk {id: $to})
|
|
605
|
+
CREATE (f)-[:CALLS_SQL {note: $note}]->(c);
|
|
606
|
+
`);
|
|
488
607
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
608
|
+
const insertUsesConfigKey = await conn.prepare(`
|
|
609
|
+
MATCH (f:File {id: $from}), (c:Chunk {id: $to})
|
|
610
|
+
CREATE (f)-[:USES_CONFIG_KEY {note: $note}]->(c);
|
|
611
|
+
`);
|
|
612
|
+
|
|
613
|
+
const insertUsesResourceKey = await conn.prepare(`
|
|
614
|
+
MATCH (f:File {id: $from}), (c:Chunk {id: $to})
|
|
615
|
+
CREATE (f)-[:USES_RESOURCE_KEY {note: $note}]->(c);
|
|
616
|
+
`);
|
|
617
|
+
|
|
618
|
+
const insertUsesSettingKey = await conn.prepare(`
|
|
619
|
+
MATCH (f:File {id: $from}), (c:Chunk {id: $to})
|
|
620
|
+
CREATE (f)-[:USES_SETTING_KEY {note: $note}]->(c);
|
|
621
|
+
`);
|
|
622
|
+
|
|
623
|
+
const insertModule = await conn.prepare(`
|
|
624
|
+
CREATE (m:Module {
|
|
625
|
+
id: $id,
|
|
626
|
+
path: $path,
|
|
627
|
+
name: $name,
|
|
628
|
+
summary: $summary,
|
|
629
|
+
file_count: $file_count,
|
|
630
|
+
exported_symbols: $exported_symbols,
|
|
631
|
+
updated_at: $updated_at,
|
|
632
|
+
source_of_truth: $source_of_truth,
|
|
633
|
+
trust_level: $trust_level,
|
|
634
|
+
status: $status
|
|
500
635
|
});
|
|
501
|
-
|
|
636
|
+
`);
|
|
502
637
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
638
|
+
const insertProject = await conn.prepare(`
|
|
639
|
+
CREATE (p:Project {
|
|
640
|
+
id: $id,
|
|
641
|
+
path: $path,
|
|
642
|
+
name: $name,
|
|
643
|
+
kind: $kind,
|
|
644
|
+
language: $language,
|
|
645
|
+
target_framework: $target_framework,
|
|
646
|
+
summary: $summary,
|
|
647
|
+
file_count: $file_count,
|
|
648
|
+
updated_at: $updated_at,
|
|
649
|
+
source_of_truth: $source_of_truth,
|
|
650
|
+
trust_level: $trust_level,
|
|
651
|
+
status: $status
|
|
652
|
+
});
|
|
653
|
+
`);
|
|
506
654
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
655
|
+
const insertContains = await conn.prepare(`
|
|
656
|
+
MATCH (m:Module {id: $from}), (f:File {id: $to})
|
|
657
|
+
CREATE (m)-[:CONTAINS]->(f);
|
|
658
|
+
`);
|
|
510
659
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
660
|
+
const insertContainsModule = await conn.prepare(`
|
|
661
|
+
MATCH (m1:Module {id: $from}), (m2:Module {id: $to})
|
|
662
|
+
CREATE (m1)-[:CONTAINS_MODULE]->(m2);
|
|
663
|
+
`);
|
|
514
664
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
665
|
+
const insertExports = await conn.prepare(`
|
|
666
|
+
MATCH (m:Module {id: $from}), (c:Chunk {id: $to})
|
|
667
|
+
CREATE (m)-[:EXPORTS]->(c);
|
|
668
|
+
`);
|
|
518
669
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
670
|
+
const insertIncludesFile = await conn.prepare(`
|
|
671
|
+
MATCH (p:Project {id: $from}), (f:File {id: $to})
|
|
672
|
+
CREATE (p)-[:INCLUDES_FILE]->(f);
|
|
673
|
+
`);
|
|
522
674
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
675
|
+
const insertReferencesProject = await conn.prepare(`
|
|
676
|
+
MATCH (p1:Project {id: $from}), (p2:Project {id: $to})
|
|
677
|
+
CREATE (p1)-[:REFERENCES_PROJECT {note: $note}]->(p2);
|
|
678
|
+
`);
|
|
526
679
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
680
|
+
const insertUsesResource = await conn.prepare(`
|
|
681
|
+
MATCH (f1:File {id: $from}), (f2:File {id: $to})
|
|
682
|
+
CREATE (f1)-[:USES_RESOURCE {note: $note}]->(f2);
|
|
683
|
+
`);
|
|
530
684
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
685
|
+
const insertUsesSetting = await conn.prepare(`
|
|
686
|
+
MATCH (f1:File {id: $from}), (f2:File {id: $to})
|
|
687
|
+
CREATE (f1)-[:USES_SETTING {note: $note}]->(f2);
|
|
688
|
+
`);
|
|
689
|
+
|
|
690
|
+
const insertUsesConfig = await conn.prepare(`
|
|
691
|
+
MATCH (f1:File {id: $from}), (f2:File {id: $to})
|
|
692
|
+
CREATE (f1)-[:USES_CONFIG {note: $note}]->(f2);
|
|
693
|
+
`);
|
|
694
|
+
|
|
695
|
+
const insertTransformsConfig = await conn.prepare(`
|
|
696
|
+
MATCH (f1:File {id: $from}), (f2:File {id: $to})
|
|
697
|
+
CREATE (f1)-[:TRANSFORMS_CONFIG {note: $note}]->(f2);
|
|
698
|
+
`);
|
|
699
|
+
|
|
700
|
+
// Insert all nodes first (batched for performance)
|
|
701
|
+
await executeBatch(conn, insertFile, fileEntities);
|
|
702
|
+
await executeBatch(conn, insertRule, ruleEntities.map((e) => ({
|
|
703
|
+
id: e.id, title: e.title, body: e.body, scope: e.scope,
|
|
704
|
+
priority: e.priority, updated_at: e.updated_at,
|
|
705
|
+
source_of_truth: e.source_of_truth, trust_level: e.trust_level, status: e.status
|
|
706
|
+
})));
|
|
707
|
+
await executeBatch(conn, insertAdr, adrEntities);
|
|
708
|
+
await executeBatch(conn, insertChunk, chunkEntities);
|
|
709
|
+
await executeBatch(conn, insertModule, moduleEntities);
|
|
710
|
+
await executeBatch(conn, insertProject, projectEntities);
|
|
711
|
+
|
|
712
|
+
// Insert all edges (nodes must exist first)
|
|
713
|
+
await executeBatch(conn, insertDefines, defines);
|
|
714
|
+
await executeBatch(conn, insertCalls, calls);
|
|
715
|
+
await executeBatch(conn, insertImports, imports);
|
|
716
|
+
await executeBatch(conn, insertCallsSql, callsSql);
|
|
717
|
+
await executeBatch(conn, insertUsesConfigKey, usesConfigKey);
|
|
718
|
+
await executeBatch(conn, insertUsesResourceKey, usesResourceKey);
|
|
719
|
+
await executeBatch(conn, insertUsesSettingKey, usesSettingKey);
|
|
720
|
+
await executeBatch(conn, insertConstrains, constrains);
|
|
721
|
+
await executeBatch(conn, insertImplements, implementsEdges);
|
|
722
|
+
await executeBatch(conn, insertSupersedes, supersedes);
|
|
723
|
+
await executeBatch(conn, insertContains, contains);
|
|
724
|
+
await executeBatch(conn, insertContainsModule, containsModule);
|
|
725
|
+
await executeBatch(conn, insertExports, exports);
|
|
726
|
+
await executeBatch(conn, insertIncludesFile, includesFile);
|
|
727
|
+
await executeBatch(conn, insertReferencesProject, referencesProject);
|
|
728
|
+
await executeBatch(conn, insertUsesResource, usesResource);
|
|
729
|
+
await executeBatch(conn, insertUsesSetting, usesSetting);
|
|
730
|
+
await executeBatch(conn, insertUsesConfig, usesConfig);
|
|
731
|
+
await executeBatch(conn, insertTransformsConfig, transformsConfig);
|
|
534
732
|
|
|
535
733
|
const fileCount = await rows(await conn.query("MATCH (f:File) RETURN count(*) AS count;"));
|
|
536
734
|
const ruleCount = await rows(await conn.query("MATCH (r:Rule) RETURN count(*) AS count;"));
|
|
@@ -554,6 +752,49 @@ async function main(): Promise<void> {
|
|
|
554
752
|
const importsCount = await rows(
|
|
555
753
|
await conn.query("MATCH (:Chunk)-[im:IMPORTS]->(:File) RETURN count(im) AS count;")
|
|
556
754
|
);
|
|
755
|
+
const callsSqlCount = await rows(
|
|
756
|
+
await conn.query("MATCH (:File)-[cs:CALLS_SQL]->(:Chunk) RETURN count(cs) AS count;")
|
|
757
|
+
);
|
|
758
|
+
const usesConfigKeyCount = await rows(
|
|
759
|
+
await conn.query("MATCH (:File)-[uck:USES_CONFIG_KEY]->(:Chunk) RETURN count(uck) AS count;")
|
|
760
|
+
);
|
|
761
|
+
const usesResourceKeyCount = await rows(
|
|
762
|
+
await conn.query("MATCH (:File)-[urk:USES_RESOURCE_KEY]->(:Chunk) RETURN count(urk) AS count;")
|
|
763
|
+
);
|
|
764
|
+
const usesSettingKeyCount = await rows(
|
|
765
|
+
await conn.query("MATCH (:File)-[usk:USES_SETTING_KEY]->(:Chunk) RETURN count(usk) AS count;")
|
|
766
|
+
);
|
|
767
|
+
const moduleCount = await rows(await conn.query("MATCH (m:Module) RETURN count(*) AS count;"));
|
|
768
|
+
const projectCount = await rows(await conn.query("MATCH (p:Project) RETURN count(*) AS count;"));
|
|
769
|
+
const containsCount = await rows(
|
|
770
|
+
await conn.query("MATCH (:Module)-[co:CONTAINS]->(:File) RETURN count(co) AS count;")
|
|
771
|
+
);
|
|
772
|
+
const containsModuleCount = await rows(
|
|
773
|
+
await conn.query("MATCH (:Module)-[cm:CONTAINS_MODULE]->(:Module) RETURN count(cm) AS count;")
|
|
774
|
+
);
|
|
775
|
+
const exportsCount = await rows(
|
|
776
|
+
await conn.query("MATCH (:Module)-[ex:EXPORTS]->(:Chunk) RETURN count(ex) AS count;")
|
|
777
|
+
);
|
|
778
|
+
const includesFileCount = await rows(
|
|
779
|
+
await conn.query("MATCH (:Project)-[inc:INCLUDES_FILE]->(:File) RETURN count(inc) AS count;")
|
|
780
|
+
);
|
|
781
|
+
const referencesProjectCount = await rows(
|
|
782
|
+
await conn.query(
|
|
783
|
+
"MATCH (:Project)-[rp:REFERENCES_PROJECT]->(:Project) RETURN count(rp) AS count;"
|
|
784
|
+
)
|
|
785
|
+
);
|
|
786
|
+
const usesResourceCount = await rows(
|
|
787
|
+
await conn.query("MATCH (:File)-[ur:USES_RESOURCE]->(:File) RETURN count(ur) AS count;")
|
|
788
|
+
);
|
|
789
|
+
const usesSettingCount = await rows(
|
|
790
|
+
await conn.query("MATCH (:File)-[us:USES_SETTING]->(:File) RETURN count(us) AS count;")
|
|
791
|
+
);
|
|
792
|
+
const usesConfigCount = await rows(
|
|
793
|
+
await conn.query("MATCH (:File)-[uc:USES_CONFIG]->(:File) RETURN count(uc) AS count;")
|
|
794
|
+
);
|
|
795
|
+
const transformsConfigCount = await rows(
|
|
796
|
+
await conn.query("MATCH (:File)-[tc:TRANSFORMS_CONFIG]->(:File) RETURN count(tc) AS count;")
|
|
797
|
+
);
|
|
557
798
|
|
|
558
799
|
const summary = {
|
|
559
800
|
generated_at: new Date().toISOString(),
|
|
@@ -568,7 +809,22 @@ async function main(): Promise<void> {
|
|
|
568
809
|
supersedes: Number(supersedesCount[0]?.count ?? 0),
|
|
569
810
|
defines: Number(definesCount[0]?.count ?? 0),
|
|
570
811
|
calls: Number(callsCount[0]?.count ?? 0),
|
|
571
|
-
imports: Number(importsCount[0]?.count ?? 0)
|
|
812
|
+
imports: Number(importsCount[0]?.count ?? 0),
|
|
813
|
+
calls_sql: Number(callsSqlCount[0]?.count ?? 0),
|
|
814
|
+
uses_config_key: Number(usesConfigKeyCount[0]?.count ?? 0),
|
|
815
|
+
uses_resource_key: Number(usesResourceKeyCount[0]?.count ?? 0),
|
|
816
|
+
uses_setting_key: Number(usesSettingKeyCount[0]?.count ?? 0),
|
|
817
|
+
modules: Number(moduleCount[0]?.count ?? 0),
|
|
818
|
+
projects: Number(projectCount[0]?.count ?? 0),
|
|
819
|
+
contains: Number(containsCount[0]?.count ?? 0),
|
|
820
|
+
contains_module: Number(containsModuleCount[0]?.count ?? 0),
|
|
821
|
+
exports: Number(exportsCount[0]?.count ?? 0),
|
|
822
|
+
includes_file: Number(includesFileCount[0]?.count ?? 0),
|
|
823
|
+
references_project: Number(referencesProjectCount[0]?.count ?? 0),
|
|
824
|
+
uses_resource: Number(usesResourceCount[0]?.count ?? 0),
|
|
825
|
+
uses_setting: Number(usesSettingCount[0]?.count ?? 0),
|
|
826
|
+
uses_config: Number(usesConfigCount[0]?.count ?? 0),
|
|
827
|
+
transforms_config: Number(transformsConfigCount[0]?.count ?? 0)
|
|
572
828
|
}
|
|
573
829
|
};
|
|
574
830
|
|
|
@@ -577,13 +833,16 @@ async function main(): Promise<void> {
|
|
|
577
833
|
|
|
578
834
|
console.log(`[graph-load] db_path=${DB_PATH}`);
|
|
579
835
|
console.log(
|
|
580
|
-
`[graph-load] files=${summary.counts.files} rules=${summary.counts.rules} adrs=${summary.counts.adrs} chunks=${summary.counts.chunks}`
|
|
836
|
+
`[graph-load] files=${summary.counts.files} rules=${summary.counts.rules} adrs=${summary.counts.adrs} chunks=${summary.counts.chunks} modules=${summary.counts.modules} projects=${summary.counts.projects}`
|
|
581
837
|
);
|
|
582
838
|
console.log(
|
|
583
839
|
`[graph-load] rels constrains=${summary.counts.constrains} implements=${summary.counts.implements} supersedes=${summary.counts.supersedes}`
|
|
584
840
|
);
|
|
585
841
|
console.log(
|
|
586
|
-
`[graph-load] rels defines=${summary.counts.defines} calls=${summary.counts.calls} imports=${summary.counts.imports}`
|
|
842
|
+
`[graph-load] rels defines=${summary.counts.defines} calls=${summary.counts.calls} imports=${summary.counts.imports} calls_sql=${summary.counts.calls_sql} uses_config_key=${summary.counts.uses_config_key} uses_resource_key=${summary.counts.uses_resource_key} uses_setting_key=${summary.counts.uses_setting_key}`
|
|
843
|
+
);
|
|
844
|
+
console.log(
|
|
845
|
+
`[graph-load] rels contains=${summary.counts.contains} contains_module=${summary.counts.contains_module} exports=${summary.counts.exports} includes_file=${summary.counts.includes_file} references_project=${summary.counts.references_project} uses_resource=${summary.counts.uses_resource} uses_setting=${summary.counts.uses_setting} uses_config=${summary.counts.uses_config} transforms_config=${summary.counts.transforms_config}`
|
|
587
846
|
);
|
|
588
847
|
console.log(`[graph-load] manifest=${summaryPath}`);
|
|
589
848
|
|
|
@@ -24,11 +24,27 @@ export const PATHS = {
|
|
|
24
24
|
adrEntities: path.join(CACHE_DIR, "entities.adr.jsonl"),
|
|
25
25
|
ruleEntities: path.join(CACHE_DIR, "entities.rule.jsonl"),
|
|
26
26
|
chunkEntities: path.join(CACHE_DIR, "entities.chunk.jsonl"),
|
|
27
|
+
projectEntities: path.join(CACHE_DIR, "entities.project.jsonl"),
|
|
27
28
|
constrainsRelations: path.join(CACHE_DIR, "relations.constrains.jsonl"),
|
|
28
29
|
implementsRelations: path.join(CACHE_DIR, "relations.implements.jsonl"),
|
|
29
30
|
supersedesRelations: path.join(CACHE_DIR, "relations.supersedes.jsonl"),
|
|
30
31
|
callsRelations: path.join(CACHE_DIR, "relations.calls.jsonl"),
|
|
31
|
-
|
|
32
|
+
callsSqlRelations: path.join(CACHE_DIR, "relations.calls_sql.jsonl"),
|
|
33
|
+
usesConfigKeyRelations: path.join(CACHE_DIR, "relations.uses_config_key.jsonl"),
|
|
34
|
+
usesResourceKeyRelations: path.join(CACHE_DIR, "relations.uses_resource_key.jsonl"),
|
|
35
|
+
usesSettingKeyRelations: path.join(CACHE_DIR, "relations.uses_setting_key.jsonl"),
|
|
36
|
+
definesRelations: path.join(CACHE_DIR, "relations.defines.jsonl"),
|
|
37
|
+
importsRelations: path.join(CACHE_DIR, "relations.imports.jsonl"),
|
|
38
|
+
moduleEntities: path.join(CACHE_DIR, "entities.module.jsonl"),
|
|
39
|
+
containsRelations: path.join(CACHE_DIR, "relations.contains.jsonl"),
|
|
40
|
+
containsModuleRelations: path.join(CACHE_DIR, "relations.contains_module.jsonl"),
|
|
41
|
+
exportsRelations: path.join(CACHE_DIR, "relations.exports.jsonl"),
|
|
42
|
+
includesFileRelations: path.join(CACHE_DIR, "relations.includes_file.jsonl"),
|
|
43
|
+
referencesProjectRelations: path.join(CACHE_DIR, "relations.references_project.jsonl"),
|
|
44
|
+
usesResourceRelations: path.join(CACHE_DIR, "relations.uses_resource.jsonl"),
|
|
45
|
+
usesSettingRelations: path.join(CACHE_DIR, "relations.uses_setting.jsonl"),
|
|
46
|
+
usesConfigRelations: path.join(CACHE_DIR, "relations.uses_config.jsonl"),
|
|
47
|
+
transformsConfigRelations: path.join(CACHE_DIR, "relations.transforms_config.jsonl")
|
|
32
48
|
};
|
|
33
49
|
|
|
34
50
|
export const DEFAULT_RANKING: RankingWeights = {
|