@codragraph/cli 2.1.5 → 2.1.6
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 +18 -13
- package/dist/cli/analyze.d.ts +9 -4
- package/dist/cli/analyze.js +37 -13
- package/dist/cli/index.js +9 -3
- package/dist/cli/status.d.ts +1 -1
- package/dist/cli/status.js +8 -0
- package/dist/cli/tool.d.ts +10 -2
- package/dist/cli/tool.js +27 -6
- package/dist/core/adaptive-profile.d.ts +52 -0
- package/dist/core/adaptive-profile.js +180 -0
- package/dist/core/cgdb/cgdb-adapter.d.ts +34 -5
- package/dist/core/cgdb/cgdb-adapter.js +418 -5
- package/dist/core/cgdb/pool-adapter.js +1 -1
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +3 -1
- package/dist/core/ingestion/pipeline-phases/structure.js +19 -3
- package/dist/core/ingestion/pipeline.d.ts +10 -0
- package/dist/core/run-analyze.d.ts +27 -2
- package/dist/core/run-analyze.js +598 -27
- package/dist/core/search/bm25-index.d.ts +19 -0
- package/dist/core/search/bm25-index.js +68 -29
- package/dist/mcp/local/local-backend.js +6 -3
- package/dist/storage/repo-manager.d.ts +29 -0
- package/dist/web/assets/__vite-browser-external-BIHI7g3E.js +1 -0
- package/dist/web/assets/agent-DcdaQnmu.js +1104 -0
- package/dist/web/assets/architectureDiagram-UL44E2DR-DFSpa3Hb.js +36 -0
- package/dist/web/assets/blockDiagram-7IZFK4PR-DlFaxH1b.js +132 -0
- package/dist/web/assets/{c4Diagram-DFAF54RM-C4Hl3J2U.js → c4Diagram-Y2BXMSZH-BjJ_Yrim.js} +1 -1
- package/dist/web/assets/{chunk-7RZVMHOQ-BitYcNVR.js → chunk-3SSMPTDK-KGZSzG3Y.js} +1 -1
- package/dist/web/assets/{chunk-TBF5ZNIQ-DL5stGM1.js → chunk-6764PJDD-p1sGJgVm.js} +1 -1
- package/dist/web/assets/{chunk-KSICW3F5-BYzvDLNI.js → chunk-AZZRMDJM-DIDkQA4V.js} +1 -1
- package/dist/web/assets/{chunk-AEOMTBSW-BgTIXPsY.js → chunk-JQRUD6KW-DAwg-yCU.js} +1 -1
- package/dist/web/assets/chunk-KRXBNO2N-ChVO_XdS.js +1 -0
- package/dist/web/assets/chunk-LCXTWHL2-DGYdb_Eh.js +231 -0
- package/dist/web/assets/{chunk-O5ABG6QK-dHwHzA6n.js → chunk-LII3EMHJ-Bzh9SNgD.js} +1 -1
- package/dist/web/assets/chunk-RG4AUYOV-Bcl7U_IV.js +206 -0
- package/dist/web/assets/{chunk-TU3PZOEN-RLyvLcv-.js → chunk-T5OCTHI4-CZYMg5sc.js} +1 -1
- package/dist/web/assets/chunk-W44A43WB-REOI67PN.js +13 -0
- package/dist/web/assets/{chunk-RWUO3TPN-BgRTY0_k.js → chunk-ZXARS5L4-BfFdV1tf.js} +1 -1
- package/dist/web/assets/classDiagram-KGZ6W3CR-B-qkKMYi.js +1 -0
- package/dist/web/assets/classDiagram-v2-72OJOZXJ-B-qkKMYi.js +1 -0
- package/dist/web/assets/{cose-bilkent-PNC4W37J-DVhePRYg.js → cose-bilkent-UX7MHV2Q-D6vANJGG.js} +1 -1
- package/dist/web/assets/dagre-ND4H6XIP-BiHe5Lal.js +4 -0
- package/dist/web/assets/diagram-3NCE3AQN-CEutBCOW.js +43 -0
- package/dist/web/assets/diagram-GF46GFSD-CZns6HPQ.js +24 -0
- package/dist/web/assets/diagram-HNR7UZ2L-Vz8fE5of.js +3 -0
- package/dist/web/assets/diagram-QXG6HAR7-D60HKZ_y.js +24 -0
- package/dist/web/assets/diagram-WEQXMOUZ-vGAf1p3E.js +10 -0
- package/dist/web/assets/{erDiagram-GCSMX5X6-C3dhDFA8.js → erDiagram-L5TCEMPS-DZaplJA6.js} +5 -5
- package/dist/web/assets/{flowDiagram-OTCZ4VVT-CWSFWmhr.js → flowDiagram-H6V6AXG4-BqUqeAsI.js} +9 -9
- package/dist/web/assets/ganttDiagram-JCBTUEKG-XEB6H-0G.js +292 -0
- package/dist/web/assets/gitGraphDiagram-S2ZK5IYY-7G50u1Cd.js +106 -0
- package/dist/web/assets/index-B5WxtMpv.js +1415 -0
- package/dist/web/assets/infoDiagram-3YFTVSEB-Cut_rzaf.js +2 -0
- package/dist/web/assets/{ishikawaDiagram-YMYX4NHK-DUoJvNP2.js → ishikawaDiagram-BNXS4ZKH-B4DGfGi3.js} +3 -3
- package/dist/web/assets/{journeyDiagram-SO5T7YLQ-RMFPNNqz.js → journeyDiagram-M6C3CM3L-BBFhsL3E.js} +1 -1
- package/dist/web/assets/{kanban-definition-LJHFXRCJ-BzpDs1K9.js → kanban-definition-75IXJCU3-DarGRyn3.js} +4 -4
- package/dist/web/assets/{katex-GD7MH7QM-DBQvrix-.js → katex-K3KEBU37-W5XTYMhr.js} +1 -1
- package/dist/web/assets/mindmap-definition-2TDM6QVE-BgeczIJM.js +96 -0
- package/dist/web/assets/pieDiagram-CU6KROY3-Kkoo-Noq.js +30 -0
- package/dist/web/assets/quadrantDiagram-VICAPDV7-CDQFeRWN.js +7 -0
- package/dist/web/assets/{requirementDiagram-M5DCFWZL-DLHOVTSv.js → requirementDiagram-JXO7QTGE-Cz9-XnkA.js} +2 -2
- package/dist/web/assets/sankeyDiagram-URQDO5SZ-CU26z0n7.js +40 -0
- package/dist/web/assets/sequenceDiagram-VS2MUI6T-OGK1FLOt.js +162 -0
- package/dist/web/assets/stateDiagram-7D4R322I-DJ9brq0U.js +1 -0
- package/dist/web/assets/stateDiagram-v2-36443NZ5-DhJ4Ky-7.js +1 -0
- package/dist/web/assets/{timeline-definition-5SPVSISX-TRSDRgPw.js → timeline-definition-O6YCAMPW-XZvnjqTT.js} +4 -4
- package/dist/web/assets/{vennDiagram-IE5QUKF5-DNy7HRBM.js → vennDiagram-MWXL3ELB-CJUssEjA.js} +6 -6
- package/dist/web/assets/wardley-L42UT6IY-5TKZOOLJ-DZr11zBG.js +173 -0
- package/dist/web/assets/wardleyDiagram-CUQ6CDDI-C276iqrN.js +78 -0
- package/dist/web/assets/{xychartDiagram-ZHJ5623Y-Dr9r7a35.js → xychartDiagram-N2JHSOCM-B9-uCZyP.js} +4 -4
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/vendor/node_modules/node-addon-api/node_addon_api_except.stamp +0 -0
- package/dist/web/assets/agent-D5lb0zXz.js +0 -1089
- package/dist/web/assets/architectureDiagram-EMZXCZ2Q-CZtc99v_.js +0 -36
- package/dist/web/assets/blockDiagram-IGV67L2C-BtoUp-6Y.js +0 -132
- package/dist/web/assets/chunk-3GS5O3IE-DkUjU0WD.js +0 -231
- package/dist/web/assets/chunk-3YCYZ6SJ-CQkVgT_z.js +0 -1
- package/dist/web/assets/chunk-H3VCZNTA-Cx5XV_aC.js +0 -13
- package/dist/web/assets/chunk-HN6EAY2L-BBnyTNdB.js +0 -1
- package/dist/web/assets/chunk-PK6DOVAG-CvsEnugt.js +0 -206
- package/dist/web/assets/classDiagram-PPOCWD7C-DTr8QIOf.js +0 -1
- package/dist/web/assets/classDiagram-v2-23LJLIIU-DTr8QIOf.js +0 -1
- package/dist/web/assets/dagre-E77IOHMT-Dzx0A6ZU.js +0 -4
- package/dist/web/assets/diagram-H7BISOXX-CC9pRew1.js +0 -43
- package/dist/web/assets/diagram-JC5VWROH-Bau_i9tf.js +0 -24
- package/dist/web/assets/diagram-LXUTUG65-D9_FM2Gt.js +0 -10
- package/dist/web/assets/diagram-WEHSV5V5-BMlayouL.js +0 -24
- package/dist/web/assets/ganttDiagram-MUNLMDZQ-D3a67Yol.js +0 -292
- package/dist/web/assets/gitGraphDiagram-3HKGZ4G3-7jmry-vM.js +0 -106
- package/dist/web/assets/index-BgeqpYgd.js +0 -1415
- package/dist/web/assets/infoDiagram-MN7RKWGX-G7lhP0Ib.js +0 -2
- package/dist/web/assets/mindmap-definition-2EUWGEK5-Bk0O4roa.js +0 -96
- package/dist/web/assets/pieDiagram-3IATQBI2-DKU7kpgS.js +0 -30
- package/dist/web/assets/quadrantDiagram-E256RVCF-BY0TGWCS.js +0 -7
- package/dist/web/assets/sankeyDiagram-L3NBLAOT-DVMj5rX2.js +0 -10
- package/dist/web/assets/sequenceDiagram-ZOUHS735-CJC73bV-.js +0 -157
- package/dist/web/assets/stateDiagram-MLPALWAM-BCFyESls.js +0 -1
- package/dist/web/assets/stateDiagram-v2-B5LQ5ZB2-DahzzIca.js +0 -1
- package/dist/web/assets/wardley-RL74JXVD-BCRCBASE-B-eZEzf9.js +0 -161
- package/dist/web/assets/wardleyDiagram-XU3VSMPF-BP-r1xzR.js +0 -20
|
@@ -60,6 +60,37 @@ export declare const loadGraphToCgdb: (graph: KnowledgeGraph, repoPath: string,
|
|
|
60
60
|
skippedRels: number;
|
|
61
61
|
warnings: string[];
|
|
62
62
|
}>;
|
|
63
|
+
export interface FileGraphPatchResult {
|
|
64
|
+
replacedFiles: number;
|
|
65
|
+
deletedNodeIds: number;
|
|
66
|
+
insertedRels: number;
|
|
67
|
+
restoredRels: number;
|
|
68
|
+
prunedFolders: number;
|
|
69
|
+
}
|
|
70
|
+
export interface FileGraphPatchOptions {
|
|
71
|
+
compress?: ContentEncoding;
|
|
72
|
+
/** Map old relative paths to their new relative paths for rename remapping. */
|
|
73
|
+
pathAliases?: ReadonlyMap<string, string> | Record<string, string>;
|
|
74
|
+
}
|
|
75
|
+
export interface FileGraphReplacementResult {
|
|
76
|
+
deletedNodes: number;
|
|
77
|
+
insertedRels: number;
|
|
78
|
+
}
|
|
79
|
+
export interface GlobalGraphLayerReplaceResult {
|
|
80
|
+
deletedGlobalNodes: number;
|
|
81
|
+
insertedRels: number;
|
|
82
|
+
}
|
|
83
|
+
export interface LoadKnowledgeGraphFromCgdbOptions {
|
|
84
|
+
includeGlobal?: boolean;
|
|
85
|
+
}
|
|
86
|
+
export declare const applyFileGraphPatchToCgdb: (graph: KnowledgeGraph, repoPath: string, storagePath: string, filePaths: readonly string[], onProgress?: CgdbProgressCallback, options?: FileGraphPatchOptions) => Promise<FileGraphPatchResult>;
|
|
87
|
+
export declare const replaceFileScopedGraphInCgdb: (graph: KnowledgeGraph, repoPath: string, storagePath: string, onProgress?: CgdbProgressCallback, options?: {
|
|
88
|
+
compress?: ContentEncoding;
|
|
89
|
+
}) => Promise<FileGraphReplacementResult>;
|
|
90
|
+
export declare const replaceGlobalGraphLayersInCgdb: (graph: KnowledgeGraph, repoPath: string, storagePath: string, onProgress?: CgdbProgressCallback, options?: {
|
|
91
|
+
compress?: ContentEncoding;
|
|
92
|
+
}) => Promise<GlobalGraphLayerReplaceResult>;
|
|
93
|
+
export declare const loadKnowledgeGraphFromCgdb: (options?: LoadKnowledgeGraphFromCgdbOptions) => Promise<KnowledgeGraph>;
|
|
63
94
|
/**
|
|
64
95
|
* Insert a single node to LadybugDB
|
|
65
96
|
* @param label - Node type (File, Function, Class, etc.)
|
|
@@ -143,12 +174,10 @@ export declare const loadVectorExtension: () => Promise<void>;
|
|
|
143
174
|
*/
|
|
144
175
|
export declare const createFTSIndex: (tableName: string, indexName: string, properties: string[], stemmer?: string) => Promise<void>;
|
|
145
176
|
/**
|
|
146
|
-
*
|
|
177
|
+
* Create an FTS index if needed, caching the fact in-process.
|
|
147
178
|
*
|
|
148
|
-
* Used by
|
|
149
|
-
*
|
|
150
|
-
* the cost is moved to the first `query`/`context` call in a session,
|
|
151
|
-
* where it's amortised across many lookups.
|
|
179
|
+
* Used by analyze to warm persisted keyword indexes while the DB is writable,
|
|
180
|
+
* and by direct core search as a defensive fallback for older indexes.
|
|
152
181
|
*
|
|
153
182
|
* Safe to call repeatedly — the in-process Set guarantees only the first
|
|
154
183
|
* call hits LadybugDB. `closeCgdb` clears the cache so re-init starts fresh.
|
|
@@ -5,6 +5,7 @@ import { once } from 'events';
|
|
|
5
5
|
import { finished } from 'stream/promises';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import cgdb from '@ladybugdb/core';
|
|
8
|
+
import { createKnowledgeGraph } from '../graph/graph.js';
|
|
8
9
|
import { NODE_TABLES, REL_TABLE_NAME, SCHEMA_QUERIES, EMBEDDING_TABLE_NAME, STALE_HASH_SENTINEL, } from './schema.js';
|
|
9
10
|
import { streamAllCSVsToDisk } from './csv-generator.js';
|
|
10
11
|
/**
|
|
@@ -432,6 +433,419 @@ export const loadGraphToCgdb = async (graph, repoPath, storagePath, onProgress,
|
|
|
432
433
|
catch { }
|
|
433
434
|
return { success: true, insertedRels, skippedRels, warnings };
|
|
434
435
|
};
|
|
436
|
+
const FILE_SCOPED_NODE_TABLES = NODE_TABLES.filter((table) => table !== 'Community' && table !== 'Process' && table !== 'FeatureCluster');
|
|
437
|
+
const GLOBAL_NODE_TABLES = new Set(['Community', 'Process', 'FeatureCluster']);
|
|
438
|
+
const PRESERVABLE_OUTGOING_REL_TYPES = new Set([
|
|
439
|
+
'MEMBER_OF',
|
|
440
|
+
'STEP_IN_PROCESS',
|
|
441
|
+
'ENTRY_POINT_OF',
|
|
442
|
+
'FEATURE_MEMBER_OF',
|
|
443
|
+
]);
|
|
444
|
+
const rowString = (row, named, positional) => String(row[named] ?? row[positional] ?? '');
|
|
445
|
+
const rowNumber = (row, named, positional, fallback) => {
|
|
446
|
+
const value = Number(row[named] ?? row[positional] ?? fallback);
|
|
447
|
+
return Number.isFinite(value) ? value : fallback;
|
|
448
|
+
};
|
|
449
|
+
const nodeLabelForId = (nodeId) => {
|
|
450
|
+
if (nodeId.startsWith('comm_'))
|
|
451
|
+
return 'Community';
|
|
452
|
+
if (nodeId.startsWith('proc_'))
|
|
453
|
+
return 'Process';
|
|
454
|
+
return nodeId.split(':')[0] || 'CodeElement';
|
|
455
|
+
};
|
|
456
|
+
const fileScopedNodeSignature = (label, name, filePath) => {
|
|
457
|
+
if (!name || !filePath)
|
|
458
|
+
return null;
|
|
459
|
+
return `${label}\0${name}\0${filePath}`;
|
|
460
|
+
};
|
|
461
|
+
const normalizePathAliases = (aliases) => {
|
|
462
|
+
if (!aliases)
|
|
463
|
+
return new Map();
|
|
464
|
+
if (aliases instanceof Map) {
|
|
465
|
+
return new Map([...aliases].map(([from, to]) => [from.replace(/\\/g, '/'), to.replace(/\\/g, '/')]));
|
|
466
|
+
}
|
|
467
|
+
return new Map(Object.entries(aliases).map(([from, to]) => [
|
|
468
|
+
from.replace(/\\/g, '/'),
|
|
469
|
+
String(to).replace(/\\/g, '/'),
|
|
470
|
+
]));
|
|
471
|
+
};
|
|
472
|
+
const remapFilePath = (filePath, aliases) => {
|
|
473
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
474
|
+
return aliases.get(normalized) ?? normalized;
|
|
475
|
+
};
|
|
476
|
+
const collectNodeIdsForFiles = async (filePaths) => {
|
|
477
|
+
const nodeIds = new Set();
|
|
478
|
+
if (filePaths.length === 0)
|
|
479
|
+
return nodeIds;
|
|
480
|
+
for (const table of FILE_SCOPED_NODE_TABLES) {
|
|
481
|
+
for (const filePath of filePaths) {
|
|
482
|
+
try {
|
|
483
|
+
const rows = await executePrepared(`MATCH (n:${escapeTableName(table)}) WHERE n.filePath = $filePath RETURN n.id AS id`, { filePath });
|
|
484
|
+
for (const row of rows) {
|
|
485
|
+
const id = rowString(row, 'id', 0);
|
|
486
|
+
if (id)
|
|
487
|
+
nodeIds.add(id);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
catch {
|
|
491
|
+
/* table may be absent in old indexes */
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return nodeIds;
|
|
496
|
+
};
|
|
497
|
+
const buildPatchNodeLookup = (graph, filePaths) => {
|
|
498
|
+
const ids = new Set();
|
|
499
|
+
const signatureBuckets = new Map();
|
|
500
|
+
for (const node of graph.iterNodes()) {
|
|
501
|
+
const filePath = node.properties?.filePath;
|
|
502
|
+
if (typeof filePath === 'string' && filePaths.has(filePath)) {
|
|
503
|
+
ids.add(node.id);
|
|
504
|
+
const signature = fileScopedNodeSignature(node.label, typeof node.properties?.name === 'string' ? node.properties.name : undefined, filePath);
|
|
505
|
+
if (signature) {
|
|
506
|
+
let bucket = signatureBuckets.get(signature);
|
|
507
|
+
if (bucket === undefined) {
|
|
508
|
+
bucket = new Set();
|
|
509
|
+
signatureBuckets.set(signature, bucket);
|
|
510
|
+
}
|
|
511
|
+
bucket.add(node.id);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
const signatureToId = new Map();
|
|
516
|
+
for (const [signature, nodeIds] of signatureBuckets) {
|
|
517
|
+
if (nodeIds.size === 1) {
|
|
518
|
+
signatureToId.set(signature, [...nodeIds][0]);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return { ids, signatureToId };
|
|
522
|
+
};
|
|
523
|
+
const prunePatchGraphRelationships = (graph, patchNodeIds) => {
|
|
524
|
+
for (const rel of [...graph.iterRelationships()]) {
|
|
525
|
+
if (!patchNodeIds.has(rel.sourceId) && !patchNodeIds.has(rel.targetId)) {
|
|
526
|
+
graph.removeRelationship(rel.id);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
const collectPreservedRelationships = async (oldNodeIds) => {
|
|
531
|
+
const preserved = [];
|
|
532
|
+
for (const nodeId of oldNodeIds) {
|
|
533
|
+
try {
|
|
534
|
+
const incoming = await executePrepared(`MATCH (a)-[r:${REL_TABLE_NAME}]->(b) WHERE b.id = $nodeId RETURN a.id AS sourceId, b.id AS targetId, r.type AS type, r.confidence AS confidence, r.reason AS reason, r.step AS step, a.name AS sourceName, a.filePath AS sourceFilePath, b.name AS targetName, b.filePath AS targetFilePath`, { nodeId });
|
|
535
|
+
for (const row of incoming) {
|
|
536
|
+
const sourceId = rowString(row, 'sourceId', 0);
|
|
537
|
+
const targetId = rowString(row, 'targetId', 1);
|
|
538
|
+
if (!sourceId || !targetId || oldNodeIds.has(sourceId))
|
|
539
|
+
continue;
|
|
540
|
+
preserved.push({
|
|
541
|
+
sourceId,
|
|
542
|
+
targetId,
|
|
543
|
+
type: rowString(row, 'type', 2),
|
|
544
|
+
confidence: rowNumber(row, 'confidence', 3, 1),
|
|
545
|
+
reason: rowString(row, 'reason', 4),
|
|
546
|
+
step: rowNumber(row, 'step', 5, 0),
|
|
547
|
+
sourceName: rowString(row, 'sourceName', 6),
|
|
548
|
+
sourceFilePath: rowString(row, 'sourceFilePath', 7),
|
|
549
|
+
targetName: rowString(row, 'targetName', 8),
|
|
550
|
+
targetFilePath: rowString(row, 'targetFilePath', 9),
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
catch {
|
|
555
|
+
/* best-effort preservation */
|
|
556
|
+
}
|
|
557
|
+
try {
|
|
558
|
+
const outgoing = await executePrepared(`MATCH (a)-[r:${REL_TABLE_NAME}]->(b) WHERE a.id = $nodeId RETURN a.id AS sourceId, b.id AS targetId, r.type AS type, r.confidence AS confidence, r.reason AS reason, r.step AS step, a.name AS sourceName, a.filePath AS sourceFilePath, b.name AS targetName, b.filePath AS targetFilePath`, { nodeId });
|
|
559
|
+
for (const row of outgoing) {
|
|
560
|
+
const sourceId = rowString(row, 'sourceId', 0);
|
|
561
|
+
const targetId = rowString(row, 'targetId', 1);
|
|
562
|
+
const type = rowString(row, 'type', 2);
|
|
563
|
+
if (!sourceId ||
|
|
564
|
+
!targetId ||
|
|
565
|
+
oldNodeIds.has(targetId) ||
|
|
566
|
+
!PRESERVABLE_OUTGOING_REL_TYPES.has(type)) {
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
preserved.push({
|
|
570
|
+
sourceId,
|
|
571
|
+
targetId,
|
|
572
|
+
type,
|
|
573
|
+
confidence: rowNumber(row, 'confidence', 3, 1),
|
|
574
|
+
reason: rowString(row, 'reason', 4),
|
|
575
|
+
step: rowNumber(row, 'step', 5, 0),
|
|
576
|
+
sourceName: rowString(row, 'sourceName', 6),
|
|
577
|
+
sourceFilePath: rowString(row, 'sourceFilePath', 7),
|
|
578
|
+
targetName: rowString(row, 'targetName', 8),
|
|
579
|
+
targetFilePath: rowString(row, 'targetFilePath', 9),
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
catch {
|
|
584
|
+
/* best-effort preservation */
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return preserved;
|
|
588
|
+
};
|
|
589
|
+
const deleteEmbeddingsForNodeIds = async (nodeIds) => {
|
|
590
|
+
await executeWithReusedStatement(`MATCH (e:${EMBEDDING_TABLE_NAME} {nodeId: $nodeId}) DELETE e`, [...nodeIds].map((nodeId) => ({ nodeId })));
|
|
591
|
+
};
|
|
592
|
+
const deleteFileScopedNodes = async (filePaths) => {
|
|
593
|
+
for (const table of FILE_SCOPED_NODE_TABLES) {
|
|
594
|
+
for (const filePath of filePaths) {
|
|
595
|
+
try {
|
|
596
|
+
await executePrepared(`MATCH (n:${escapeTableName(table)}) WHERE n.filePath = $filePath DETACH DELETE n`, { filePath });
|
|
597
|
+
}
|
|
598
|
+
catch (err) {
|
|
599
|
+
if (!isMissingColumnOrTableError(err instanceof Error ? err.message : String(err))) {
|
|
600
|
+
throw err;
|
|
601
|
+
}
|
|
602
|
+
/* table may be absent in old indexes */
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
const restorePreservedRelationships = async (relationships, patchLookup, pathAliases) => {
|
|
608
|
+
let restored = 0;
|
|
609
|
+
const seen = new Set();
|
|
610
|
+
for (const rel of relationships) {
|
|
611
|
+
const sourceSignature = fileScopedNodeSignature(nodeLabelForId(rel.sourceId), rel.sourceName, remapFilePath(rel.sourceFilePath, pathAliases));
|
|
612
|
+
const targetSignature = fileScopedNodeSignature(nodeLabelForId(rel.targetId), rel.targetName, remapFilePath(rel.targetFilePath, pathAliases));
|
|
613
|
+
const resolvedSourceId = (patchLookup.ids.has(rel.sourceId) ? rel.sourceId : undefined) ??
|
|
614
|
+
(sourceSignature ? patchLookup.signatureToId.get(sourceSignature) : undefined) ??
|
|
615
|
+
rel.sourceId;
|
|
616
|
+
const resolvedTargetId = (patchLookup.ids.has(rel.targetId) ? rel.targetId : undefined) ??
|
|
617
|
+
(targetSignature ? patchLookup.signatureToId.get(targetSignature) : undefined) ??
|
|
618
|
+
rel.targetId;
|
|
619
|
+
const patchSideSurvives = patchLookup.ids.has(resolvedSourceId) || patchLookup.ids.has(resolvedTargetId);
|
|
620
|
+
if (!patchSideSurvives)
|
|
621
|
+
continue;
|
|
622
|
+
const key = `${resolvedSourceId}\0${rel.type}\0${resolvedTargetId}\0${rel.step}`;
|
|
623
|
+
if (seen.has(key))
|
|
624
|
+
continue;
|
|
625
|
+
seen.add(key);
|
|
626
|
+
const sourceLabel = escapeTableName(nodeLabelForId(resolvedSourceId));
|
|
627
|
+
const targetLabel = escapeTableName(nodeLabelForId(resolvedTargetId));
|
|
628
|
+
try {
|
|
629
|
+
const existingRows = await executePrepared(`MATCH (a:${sourceLabel})-[r:${REL_TABLE_NAME}]->(b:${targetLabel}) WHERE a.id = $sourceId AND b.id = $targetId AND r.type = $type AND r.step = $step RETURN count(r) AS cnt`, { ...rel, sourceId: resolvedSourceId, targetId: resolvedTargetId });
|
|
630
|
+
const existing = rowNumber(existingRows?.[0] ?? {}, 'cnt', 0, 0);
|
|
631
|
+
if (existing > 0)
|
|
632
|
+
continue;
|
|
633
|
+
await executePrepared(`MATCH (a:${sourceLabel} {id: $sourceId}), (b:${targetLabel} {id: $targetId}) CREATE (a)-[:${REL_TABLE_NAME} {type: $type, confidence: $confidence, reason: $reason, step: $step}]->(b)`, { ...rel, sourceId: resolvedSourceId, targetId: resolvedTargetId });
|
|
634
|
+
restored++;
|
|
635
|
+
}
|
|
636
|
+
catch {
|
|
637
|
+
/* source or target disappeared; skip */
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
return restored;
|
|
641
|
+
};
|
|
642
|
+
export const applyFileGraphPatchToCgdb = async (graph, repoPath, storagePath, filePaths, onProgress, options) => {
|
|
643
|
+
if (!conn) {
|
|
644
|
+
throw new Error('LadybugDB not initialized. Call initCgdb first.');
|
|
645
|
+
}
|
|
646
|
+
const normalizedFiles = [...new Set(filePaths.map((p) => p.replace(/\\/g, '/')))];
|
|
647
|
+
const pathAliases = normalizePathAliases(options?.pathAliases);
|
|
648
|
+
const patchFileSet = new Set(normalizedFiles);
|
|
649
|
+
const oldNodeIds = await collectNodeIdsForFiles(normalizedFiles);
|
|
650
|
+
const preservedRels = await collectPreservedRelationships(oldNodeIds);
|
|
651
|
+
await loadFTSExtension();
|
|
652
|
+
await deleteEmbeddingsForNodeIds(oldNodeIds);
|
|
653
|
+
await deleteFileScopedNodes(normalizedFiles);
|
|
654
|
+
const patchLookup = buildPatchNodeLookup(graph, patchFileSet);
|
|
655
|
+
prunePatchGraphRelationships(graph, patchLookup.ids);
|
|
656
|
+
const loadResult = graph.nodeCount > 0
|
|
657
|
+
? await loadGraphToCgdb(graph, repoPath, storagePath, onProgress, options)
|
|
658
|
+
: { insertedRels: 0 };
|
|
659
|
+
const restoredRels = await restorePreservedRelationships(preservedRels, patchLookup, pathAliases);
|
|
660
|
+
const prunedFolders = await pruneEmptyFoldersFromCgdb();
|
|
661
|
+
return {
|
|
662
|
+
replacedFiles: normalizedFiles.length,
|
|
663
|
+
deletedNodeIds: oldNodeIds.size,
|
|
664
|
+
insertedRels: loadResult.insertedRels,
|
|
665
|
+
restoredRels,
|
|
666
|
+
prunedFolders,
|
|
667
|
+
};
|
|
668
|
+
};
|
|
669
|
+
const deleteEmbeddingsForAllNodes = async () => {
|
|
670
|
+
try {
|
|
671
|
+
await executeQuery(`MATCH (e:${EMBEDDING_TABLE_NAME}) DELETE e`);
|
|
672
|
+
}
|
|
673
|
+
catch {
|
|
674
|
+
/* table may be absent in old indexes */
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
const deleteAllFileScopedNodes = async () => {
|
|
678
|
+
let deleted = 0;
|
|
679
|
+
for (const table of FILE_SCOPED_NODE_TABLES) {
|
|
680
|
+
try {
|
|
681
|
+
const rows = await executeQuery(`MATCH (n:${escapeTableName(table)}) RETURN count(n) AS cnt`);
|
|
682
|
+
deleted += Number(rows?.[0]?.cnt ?? rows?.[0]?.[0] ?? 0);
|
|
683
|
+
}
|
|
684
|
+
catch (err) {
|
|
685
|
+
if (!isMissingColumnOrTableError(err instanceof Error ? err.message : String(err))) {
|
|
686
|
+
throw err;
|
|
687
|
+
}
|
|
688
|
+
/* table may be absent in old indexes */
|
|
689
|
+
}
|
|
690
|
+
try {
|
|
691
|
+
await executeQuery(`MATCH (n:${escapeTableName(table)}) DETACH DELETE n`);
|
|
692
|
+
}
|
|
693
|
+
catch (err) {
|
|
694
|
+
if (!isMissingColumnOrTableError(err instanceof Error ? err.message : String(err))) {
|
|
695
|
+
throw err;
|
|
696
|
+
}
|
|
697
|
+
/* table may be absent in old indexes */
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
return deleted;
|
|
701
|
+
};
|
|
702
|
+
export const replaceFileScopedGraphInCgdb = async (graph, repoPath, storagePath, onProgress, options) => {
|
|
703
|
+
if (!conn) {
|
|
704
|
+
throw new Error('LadybugDB not initialized. Call initCgdb first.');
|
|
705
|
+
}
|
|
706
|
+
await loadFTSExtension();
|
|
707
|
+
await deleteEmbeddingsForAllNodes();
|
|
708
|
+
const deletedNodes = await deleteAllFileScopedNodes();
|
|
709
|
+
const loadResult = graph.nodeCount > 0
|
|
710
|
+
? await loadGraphToCgdb(graph, repoPath, storagePath, onProgress, options)
|
|
711
|
+
: { insertedRels: 0 };
|
|
712
|
+
return { deletedNodes, insertedRels: loadResult.insertedRels };
|
|
713
|
+
};
|
|
714
|
+
export const replaceGlobalGraphLayersInCgdb = async (graph, repoPath, storagePath, onProgress, options) => {
|
|
715
|
+
if (!conn) {
|
|
716
|
+
throw new Error('LadybugDB not initialized. Call initCgdb first.');
|
|
717
|
+
}
|
|
718
|
+
await loadFTSExtension();
|
|
719
|
+
let deletedGlobalNodes = 0;
|
|
720
|
+
for (const table of GLOBAL_NODE_TABLES) {
|
|
721
|
+
try {
|
|
722
|
+
const rows = await executeQuery(`MATCH (n:${escapeTableName(table)}) RETURN count(n) AS cnt`);
|
|
723
|
+
deletedGlobalNodes += Number(rows?.[0]?.cnt ?? rows?.[0]?.[0] ?? 0);
|
|
724
|
+
}
|
|
725
|
+
catch {
|
|
726
|
+
/* table may be absent in old indexes */
|
|
727
|
+
}
|
|
728
|
+
try {
|
|
729
|
+
await executeQuery(`MATCH (n:${escapeTableName(table)}) DETACH DELETE n`);
|
|
730
|
+
}
|
|
731
|
+
catch {
|
|
732
|
+
/* table may be absent in old indexes */
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
const loadResult = graph.nodeCount > 0
|
|
736
|
+
? await loadGraphToCgdb(graph, repoPath, storagePath, onProgress, options)
|
|
737
|
+
: { insertedRels: 0 };
|
|
738
|
+
return { deletedGlobalNodes, insertedRels: loadResult.insertedRels };
|
|
739
|
+
};
|
|
740
|
+
const unwrapNodeResult = (raw) => {
|
|
741
|
+
if (!raw || typeof raw !== 'object')
|
|
742
|
+
return null;
|
|
743
|
+
const row = raw;
|
|
744
|
+
const candidate = row.n ?? row[0];
|
|
745
|
+
return candidate && typeof candidate === 'object' && !Array.isArray(candidate)
|
|
746
|
+
? candidate
|
|
747
|
+
: null;
|
|
748
|
+
};
|
|
749
|
+
const nodePropertiesFromCgdbRow = (row) => {
|
|
750
|
+
const properties = {
|
|
751
|
+
name: String(row.name ?? ''),
|
|
752
|
+
filePath: String(row.filePath ?? ''),
|
|
753
|
+
};
|
|
754
|
+
for (const [key, value] of Object.entries(row)) {
|
|
755
|
+
if (key === 'id' || key === '_id' || key === '_label')
|
|
756
|
+
continue;
|
|
757
|
+
properties[key] = value;
|
|
758
|
+
}
|
|
759
|
+
return properties;
|
|
760
|
+
};
|
|
761
|
+
export const loadKnowledgeGraphFromCgdb = async (options = {}) => {
|
|
762
|
+
if (!conn) {
|
|
763
|
+
throw new Error('LadybugDB not initialized. Call initCgdb first.');
|
|
764
|
+
}
|
|
765
|
+
const includeGlobal = options.includeGlobal ?? false;
|
|
766
|
+
const graph = createKnowledgeGraph();
|
|
767
|
+
for (const table of NODE_TABLES) {
|
|
768
|
+
if (!includeGlobal && GLOBAL_NODE_TABLES.has(table))
|
|
769
|
+
continue;
|
|
770
|
+
try {
|
|
771
|
+
const rows = await executeQuery(`MATCH (n:${escapeTableName(table)}) RETURN n`);
|
|
772
|
+
for (const raw of rows) {
|
|
773
|
+
const node = unwrapNodeResult(raw);
|
|
774
|
+
const id = node ? String(node.id ?? '') : '';
|
|
775
|
+
if (!node || !id)
|
|
776
|
+
continue;
|
|
777
|
+
graph.addNode({
|
|
778
|
+
id,
|
|
779
|
+
label: table,
|
|
780
|
+
properties: nodePropertiesFromCgdbRow(node),
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
catch {
|
|
785
|
+
/* table may be absent in old indexes */
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
try {
|
|
789
|
+
const rows = await executeQuery(`MATCH (a)-[r:${REL_TABLE_NAME}]->(b) RETURN a.id AS sourceId, b.id AS targetId, r.type AS type, r.confidence AS confidence, r.reason AS reason, r.step AS step`);
|
|
790
|
+
let relIndex = 0;
|
|
791
|
+
for (const row of rows) {
|
|
792
|
+
const sourceId = rowString(row, 'sourceId', 0);
|
|
793
|
+
const targetId = rowString(row, 'targetId', 1);
|
|
794
|
+
if (!sourceId || !targetId)
|
|
795
|
+
continue;
|
|
796
|
+
if (!includeGlobal) {
|
|
797
|
+
const sourceLabel = nodeLabelForId(sourceId);
|
|
798
|
+
const targetLabel = nodeLabelForId(targetId);
|
|
799
|
+
if (GLOBAL_NODE_TABLES.has(sourceLabel) || GLOBAL_NODE_TABLES.has(targetLabel))
|
|
800
|
+
continue;
|
|
801
|
+
}
|
|
802
|
+
const type = rowString(row, 'type', 2);
|
|
803
|
+
graph.addRelationship({
|
|
804
|
+
id: `cgdb:${relIndex++}:${sourceId}->${type}->${targetId}`,
|
|
805
|
+
sourceId,
|
|
806
|
+
targetId,
|
|
807
|
+
type: type,
|
|
808
|
+
confidence: rowNumber(row, 'confidence', 3, 1),
|
|
809
|
+
reason: rowString(row, 'reason', 4),
|
|
810
|
+
step: rowNumber(row, 'step', 5, 0),
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
catch {
|
|
815
|
+
/* relationships may be absent in old indexes */
|
|
816
|
+
}
|
|
817
|
+
return graph;
|
|
818
|
+
};
|
|
819
|
+
const pruneEmptyFoldersFromCgdb = async () => {
|
|
820
|
+
let folderRows = [];
|
|
821
|
+
let fileRows = [];
|
|
822
|
+
try {
|
|
823
|
+
folderRows = await executeQuery('MATCH (f:Folder) RETURN f.id AS id, f.filePath AS filePath');
|
|
824
|
+
fileRows = await executeQuery('MATCH (f:File) RETURN f.filePath AS filePath');
|
|
825
|
+
}
|
|
826
|
+
catch {
|
|
827
|
+
return 0;
|
|
828
|
+
}
|
|
829
|
+
const filePaths = fileRows.map((row) => rowString(row, 'filePath', 0)).filter(Boolean);
|
|
830
|
+
let pruned = 0;
|
|
831
|
+
for (const row of folderRows) {
|
|
832
|
+
const id = rowString(row, 'id', 0);
|
|
833
|
+
const folderPath = rowString(row, 'filePath', 1);
|
|
834
|
+
if (!id || !folderPath)
|
|
835
|
+
continue;
|
|
836
|
+
const hasDescendantFile = filePaths.some((filePath) => filePath.startsWith(`${folderPath}/`));
|
|
837
|
+
if (hasDescendantFile)
|
|
838
|
+
continue;
|
|
839
|
+
try {
|
|
840
|
+
await executePrepared('MATCH (f:Folder {id: $id}) DETACH DELETE f', { id });
|
|
841
|
+
pruned++;
|
|
842
|
+
}
|
|
843
|
+
catch {
|
|
844
|
+
/* best-effort cleanup */
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
return pruned;
|
|
848
|
+
};
|
|
435
849
|
// LadybugDB default ESCAPE is '\' (backslash), but our CSV uses RFC 4180 escaping ("" for literal quotes).
|
|
436
850
|
// Source code content is full of backslashes which confuse the auto-detection.
|
|
437
851
|
// We MUST explicitly set ESCAPE='"' to use RFC 4180 escaping, and disable auto_detect to prevent
|
|
@@ -1254,12 +1668,10 @@ export const createFTSIndex = async (tableName, indexName, properties, stemmer =
|
|
|
1254
1668
|
}
|
|
1255
1669
|
};
|
|
1256
1670
|
/**
|
|
1257
|
-
*
|
|
1671
|
+
* Create an FTS index if needed, caching the fact in-process.
|
|
1258
1672
|
*
|
|
1259
|
-
* Used by
|
|
1260
|
-
*
|
|
1261
|
-
* the cost is moved to the first `query`/`context` call in a session,
|
|
1262
|
-
* where it's amortised across many lookups.
|
|
1673
|
+
* Used by analyze to warm persisted keyword indexes while the DB is writable,
|
|
1674
|
+
* and by direct core search as a defensive fallback for older indexes.
|
|
1263
1675
|
*
|
|
1264
1676
|
* Safe to call repeatedly — the in-process Set guarantees only the first
|
|
1265
1677
|
* call hits LadybugDB. `closeCgdb` clears the cache so re-init starts fresh.
|
|
@@ -1327,6 +1739,7 @@ export const dropFTSIndex = async (tableName, indexName) => {
|
|
|
1327
1739
|
if (!conn) {
|
|
1328
1740
|
throw new Error('LadybugDB not initialized. Call initCgdb first.');
|
|
1329
1741
|
}
|
|
1742
|
+
await loadFTSExtension();
|
|
1330
1743
|
try {
|
|
1331
1744
|
await conn.query(`CALL DROP_FTS_INDEX('${tableName}', '${indexName}')`);
|
|
1332
1745
|
}
|
|
@@ -586,7 +586,7 @@ function isBenignLabelScanError(error) {
|
|
|
586
586
|
const message = error.message.toLowerCase();
|
|
587
587
|
return (message.includes('cannot find property') ||
|
|
588
588
|
message.includes('does not have property') ||
|
|
589
|
-
message.includes('property') && message.includes('not found'));
|
|
589
|
+
(message.includes('property') && message.includes('not found')));
|
|
590
590
|
}
|
|
591
591
|
export const executeQuery = async (repoId, cypher) => {
|
|
592
592
|
const entry = pool.get(repoId);
|
|
@@ -119,7 +119,9 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
119
119
|
workerUrl = pathToFileURL(distWorker);
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
-
workerPool = createWorkerPool(workerUrl
|
|
122
|
+
workerPool = createWorkerPool(workerUrl, options?.workerPoolSize, {
|
|
123
|
+
subBatchSize: options?.workerSubBatchSize,
|
|
124
|
+
});
|
|
123
125
|
}
|
|
124
126
|
catch (err) {
|
|
125
127
|
console.warn('Worker pool creation failed, using sequential fallback:', err.message);
|
|
@@ -20,16 +20,32 @@ export const structurePhase = {
|
|
|
20
20
|
message: 'Analyzing project structure...',
|
|
21
21
|
stats: { filesProcessed: 0, totalFiles, nodesCreated: ctx.graph.nodeCount },
|
|
22
22
|
});
|
|
23
|
-
|
|
23
|
+
const focusSet = ctx.options?.focusPaths
|
|
24
|
+
? new Set(ctx.options.focusPaths.map((p) => p.replace(/\\/g, '/')))
|
|
25
|
+
: null;
|
|
26
|
+
const graphPaths = focusSet ? allPaths.filter((p) => focusSet.has(p)) : allPaths;
|
|
27
|
+
processStructure(ctx.graph, graphPaths);
|
|
24
28
|
ctx.onProgress({
|
|
25
29
|
phase: 'structure',
|
|
26
30
|
percent: 20,
|
|
27
31
|
message: 'Project structure analyzed',
|
|
28
|
-
stats: {
|
|
32
|
+
stats: {
|
|
33
|
+
filesProcessed: graphPaths.length,
|
|
34
|
+
totalFiles: focusSet ? graphPaths.length : totalFiles,
|
|
35
|
+
nodesCreated: ctx.graph.nodeCount,
|
|
36
|
+
},
|
|
29
37
|
});
|
|
30
38
|
// Build the set once here so cobol, markdown, and cross-file propagation
|
|
31
39
|
// can all reuse it instead of re-materializing `new Set(allPaths)` each.
|
|
32
40
|
const allPathSet = new Set(allPaths);
|
|
33
|
-
|
|
41
|
+
const focusedScannedFiles = focusSet
|
|
42
|
+
? scannedFiles.filter((f) => focusSet.has(f.path))
|
|
43
|
+
: scannedFiles;
|
|
44
|
+
return {
|
|
45
|
+
scannedFiles: focusedScannedFiles,
|
|
46
|
+
allPaths,
|
|
47
|
+
allPathSet,
|
|
48
|
+
totalFiles: focusSet ? focusedScannedFiles.length : totalFiles,
|
|
49
|
+
};
|
|
34
50
|
},
|
|
35
51
|
};
|
|
@@ -25,6 +25,16 @@ export interface PipelineOptions {
|
|
|
25
25
|
featureClusterRepo?: string;
|
|
26
26
|
/** Indexed source commit written onto FeatureCluster metadata. */
|
|
27
27
|
lastIndexedCommit?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Optional focused indexing set for incremental file analysis. The walker
|
|
30
|
+
* still scans the repo so import resolution sees every path, but graph
|
|
31
|
+
* writes and content parsing are limited to these relative paths.
|
|
32
|
+
*/
|
|
33
|
+
focusPaths?: readonly string[];
|
|
34
|
+
/** Adaptive analyze worker pool size. */
|
|
35
|
+
workerPoolSize?: number;
|
|
36
|
+
/** Adaptive analyze max files per worker message. */
|
|
37
|
+
workerSubBatchSize?: number;
|
|
28
38
|
/**
|
|
29
39
|
* @internal Test-only override for worker-pool gating thresholds.
|
|
30
40
|
* When unset, production defaults apply (15 files OR 512 KB total bytes).
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { type RepoMeta } from '../storage/repo-manager.js';
|
|
12
12
|
import type { ContentEncoding } from '@codragraph/graphstore';
|
|
13
|
+
import { type AnalyzeProfileOption, type CompressionOption, type EmbeddingMode } from './adaptive-profile.js';
|
|
13
14
|
export interface AnalyzeCallbacks {
|
|
14
15
|
onProgress: (phase: string, percent: number, message: string) => void;
|
|
15
16
|
onLog?: (message: string) => void;
|
|
@@ -23,6 +24,8 @@ export interface AnalyzeOptions {
|
|
|
23
24
|
*/
|
|
24
25
|
force?: boolean;
|
|
25
26
|
embeddings?: boolean;
|
|
27
|
+
profile?: AnalyzeProfileOption;
|
|
28
|
+
embeddingMode?: EmbeddingMode;
|
|
26
29
|
skipGit?: boolean;
|
|
27
30
|
/** Skip AGENTS.md and CLAUDE.md codragraph block updates. */
|
|
28
31
|
skipAgentsMd?: boolean;
|
|
@@ -54,7 +57,7 @@ export interface AnalyzeOptions {
|
|
|
54
57
|
* that wrote the rows). Readers on older Node will get a clear
|
|
55
58
|
* forward-compat error rather than silently bad content.
|
|
56
59
|
*/
|
|
57
|
-
compress?:
|
|
60
|
+
compress?: CompressionOption;
|
|
58
61
|
}
|
|
59
62
|
export interface AnalyzeResult {
|
|
60
63
|
repoName: string;
|
|
@@ -91,7 +94,29 @@ export declare const isGeneratedAgentContextPath: (filePath: string) => boolean;
|
|
|
91
94
|
export declare const isGraphContentPath: (filePath: string) => boolean;
|
|
92
95
|
export declare const changedPathAffectsGraph: (change: AnalyzeChangedPath) => boolean;
|
|
93
96
|
export declare const getGraphRelevantChangedPaths: (changes: readonly AnalyzeChangedPath[]) => AnalyzeChangedPath[];
|
|
94
|
-
export
|
|
97
|
+
export interface IncrementalFilePatchPlan {
|
|
98
|
+
eligible: boolean;
|
|
99
|
+
reason: string;
|
|
100
|
+
replacePaths: string[];
|
|
101
|
+
currentPaths: string[];
|
|
102
|
+
fileCountDelta: number;
|
|
103
|
+
/**
|
|
104
|
+
* True when a change can affect resolver/global structure broadly
|
|
105
|
+
* (config/ignore/package). The incremental path still avoids deleting the
|
|
106
|
+
* whole DB, but it replaces every file-scoped node from a fresh full scan.
|
|
107
|
+
*/
|
|
108
|
+
replaceAllFileScoped: boolean;
|
|
109
|
+
/** Old path -> new path aliases used to reconnect external edges across renames. */
|
|
110
|
+
pathAliases: Record<string, string>;
|
|
111
|
+
}
|
|
112
|
+
export declare const isPatchableIncrementalPath: (filePath: string) => boolean;
|
|
113
|
+
export declare const buildIncrementalFilePatchPlan: (changes: readonly AnalyzeChangedPath[], _options?: {
|
|
114
|
+
limit?: number;
|
|
115
|
+
}) => IncrementalFilePatchPlan;
|
|
116
|
+
export declare const getAnalyzeConfigRebuildReason: (existingMeta: Pick<RepoMeta, "compress" | "searchIndexes" | "stats">, options: {
|
|
117
|
+
compress?: ContentEncoding;
|
|
118
|
+
embeddings?: boolean;
|
|
119
|
+
}) => string | null;
|
|
95
120
|
/**
|
|
96
121
|
* Run the full CodraGraph analysis pipeline.
|
|
97
122
|
*
|