projscan 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -33
- package/dist/cli/index.js +126 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/ast.d.ts +34 -0
- package/dist/core/ast.js +270 -0
- package/dist/core/ast.js.map +1 -0
- package/dist/core/codeGraph.d.ts +36 -0
- package/dist/core/codeGraph.js +167 -0
- package/dist/core/codeGraph.js.map +1 -0
- package/dist/core/hotspotAnalyzer.js +62 -8
- package/dist/core/hotspotAnalyzer.js.map +1 -1
- package/dist/core/importGraph.d.ts +5 -15
- package/dist/core/importGraph.js +12 -110
- package/dist/core/importGraph.js.map +1 -1
- package/dist/core/indexCache.d.ts +12 -0
- package/dist/core/indexCache.js +97 -0
- package/dist/core/indexCache.js.map +1 -0
- package/dist/core/searchIndex.d.ts +54 -0
- package/dist/core/searchIndex.js +268 -0
- package/dist/core/searchIndex.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +30 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tokenBudget.d.ts +25 -0
- package/dist/mcp/tokenBudget.js +89 -0
- package/dist/mcp/tokenBudget.js.map +1 -0
- package/dist/mcp/tools.js +163 -0
- package/dist/mcp/tools.js.map +1 -1
- package/dist/utils/banner.js +6 -5
- package/dist/utils/banner.js.map +1 -1
- package/package.json +4 -2
- package/dist/utils/cache.d.ts +0 -3
- package/dist/utils/cache.js +0 -51
- package/dist/utils/cache.js.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FileEntry } from '../types.js';
|
|
2
|
+
import { toPackageName as graphToPackageName } from './codeGraph.js';
|
|
2
3
|
export interface ImportGraph {
|
|
3
4
|
/** file → set of import specifiers exactly as they appear in source */
|
|
4
5
|
byFile: Map<string, Set<string>>;
|
|
@@ -8,23 +9,12 @@ export interface ImportGraph {
|
|
|
8
9
|
scannedFiles: number;
|
|
9
10
|
}
|
|
10
11
|
/**
|
|
11
|
-
* Walk source files and build an import graph.
|
|
12
|
-
*
|
|
12
|
+
* Walk source files and build an import graph. Now backed by AST-based
|
|
13
|
+
* codeGraph — this function is retained for public API compatibility.
|
|
13
14
|
*/
|
|
14
15
|
export declare function buildImportGraph(rootPath: string, files: FileEntry[]): Promise<ImportGraph>;
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
* Returns null for relative paths ("./", "../", "/"), node: builtins, and bare builtins.
|
|
18
|
-
*
|
|
19
|
-
* 'react' -> 'react'
|
|
20
|
-
* 'react/jsx-runtime' -> 'react'
|
|
21
|
-
* '@types/node' -> '@types/node'
|
|
22
|
-
* '@scope/pkg/deep' -> '@scope/pkg'
|
|
23
|
-
* './local' -> null
|
|
24
|
-
* 'node:fs' -> null
|
|
25
|
-
* 'fs' -> null (node builtin)
|
|
26
|
-
*/
|
|
27
|
-
export declare function toPackageName(specifier: string): string | null;
|
|
16
|
+
/** Convert an import specifier to a bare package name. */
|
|
17
|
+
export declare const toPackageName: typeof graphToPackageName;
|
|
28
18
|
/** Check if a package is referenced by at least one file in the graph. */
|
|
29
19
|
export declare function isPackageUsed(graph: ImportGraph, pkg: string): boolean;
|
|
30
20
|
/** List files that import a given package. */
|
package/dist/core/importGraph.js
CHANGED
|
@@ -1,124 +1,26 @@
|
|
|
1
|
-
import
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { extractImports } from './fileInspector.js';
|
|
4
|
-
const SOURCE_EXTENSIONS = new Set([
|
|
5
|
-
'.ts',
|
|
6
|
-
'.tsx',
|
|
7
|
-
'.js',
|
|
8
|
-
'.jsx',
|
|
9
|
-
'.mjs',
|
|
10
|
-
'.cjs',
|
|
11
|
-
'.mts',
|
|
12
|
-
'.cts',
|
|
13
|
-
]);
|
|
14
|
-
const MAX_FILE_SIZE = 1024 * 1024; // 1 MB — skip giant generated files
|
|
15
|
-
const NODE_BUILTINS = new Set([
|
|
16
|
-
'assert',
|
|
17
|
-
'async_hooks',
|
|
18
|
-
'buffer',
|
|
19
|
-
'child_process',
|
|
20
|
-
'cluster',
|
|
21
|
-
'console',
|
|
22
|
-
'constants',
|
|
23
|
-
'crypto',
|
|
24
|
-
'dgram',
|
|
25
|
-
'dns',
|
|
26
|
-
'domain',
|
|
27
|
-
'events',
|
|
28
|
-
'fs',
|
|
29
|
-
'fs/promises',
|
|
30
|
-
'http',
|
|
31
|
-
'http2',
|
|
32
|
-
'https',
|
|
33
|
-
'inspector',
|
|
34
|
-
'module',
|
|
35
|
-
'net',
|
|
36
|
-
'os',
|
|
37
|
-
'path',
|
|
38
|
-
'perf_hooks',
|
|
39
|
-
'process',
|
|
40
|
-
'punycode',
|
|
41
|
-
'querystring',
|
|
42
|
-
'readline',
|
|
43
|
-
'repl',
|
|
44
|
-
'stream',
|
|
45
|
-
'string_decoder',
|
|
46
|
-
'sys',
|
|
47
|
-
'timers',
|
|
48
|
-
'tls',
|
|
49
|
-
'trace_events',
|
|
50
|
-
'tty',
|
|
51
|
-
'url',
|
|
52
|
-
'util',
|
|
53
|
-
'v8',
|
|
54
|
-
'vm',
|
|
55
|
-
'wasi',
|
|
56
|
-
'worker_threads',
|
|
57
|
-
'zlib',
|
|
58
|
-
]);
|
|
1
|
+
import { buildCodeGraph, toPackageName as graphToPackageName } from './codeGraph.js';
|
|
59
2
|
/**
|
|
60
|
-
* Walk source files and build an import graph.
|
|
61
|
-
*
|
|
3
|
+
* Walk source files and build an import graph. Now backed by AST-based
|
|
4
|
+
* codeGraph — this function is retained for public API compatibility.
|
|
62
5
|
*/
|
|
63
6
|
export async function buildImportGraph(rootPath, files) {
|
|
7
|
+
const code = await buildCodeGraph(rootPath, files);
|
|
64
8
|
const byFile = new Map();
|
|
65
9
|
const externalPackages = new Set();
|
|
66
|
-
|
|
67
|
-
const sourceFiles = files.filter((f) => SOURCE_EXTENSIONS.has(f.extension) && f.sizeBytes <= MAX_FILE_SIZE);
|
|
68
|
-
await Promise.all(sourceFiles.map(async (file) => {
|
|
69
|
-
const abs = path.isAbsolute(file.absolutePath)
|
|
70
|
-
? file.absolutePath
|
|
71
|
-
: path.resolve(rootPath, file.relativePath);
|
|
72
|
-
let content;
|
|
73
|
-
try {
|
|
74
|
-
content = await fs.readFile(abs, 'utf-8');
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
scannedFiles++;
|
|
80
|
-
const imports = extractImports(content);
|
|
10
|
+
for (const [file, entry] of code.files) {
|
|
81
11
|
const specifiers = new Set();
|
|
82
|
-
for (const imp of imports) {
|
|
12
|
+
for (const imp of entry.imports) {
|
|
83
13
|
specifiers.add(imp.source);
|
|
84
|
-
const pkg =
|
|
85
|
-
if (pkg
|
|
14
|
+
const pkg = graphToPackageName(imp.source);
|
|
15
|
+
if (pkg)
|
|
86
16
|
externalPackages.add(pkg);
|
|
87
|
-
}
|
|
88
17
|
}
|
|
89
|
-
byFile.set(file
|
|
90
|
-
}));
|
|
91
|
-
return { byFile, externalPackages, scannedFiles };
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Convert an import specifier to a bare package name.
|
|
95
|
-
* Returns null for relative paths ("./", "../", "/"), node: builtins, and bare builtins.
|
|
96
|
-
*
|
|
97
|
-
* 'react' -> 'react'
|
|
98
|
-
* 'react/jsx-runtime' -> 'react'
|
|
99
|
-
* '@types/node' -> '@types/node'
|
|
100
|
-
* '@scope/pkg/deep' -> '@scope/pkg'
|
|
101
|
-
* './local' -> null
|
|
102
|
-
* 'node:fs' -> null
|
|
103
|
-
* 'fs' -> null (node builtin)
|
|
104
|
-
*/
|
|
105
|
-
export function toPackageName(specifier) {
|
|
106
|
-
if (!specifier)
|
|
107
|
-
return null;
|
|
108
|
-
if (specifier.startsWith('.') || specifier.startsWith('/'))
|
|
109
|
-
return null;
|
|
110
|
-
if (specifier.startsWith('node:'))
|
|
111
|
-
return null;
|
|
112
|
-
if (NODE_BUILTINS.has(specifier))
|
|
113
|
-
return null;
|
|
114
|
-
if (specifier.startsWith('@')) {
|
|
115
|
-
const segments = specifier.split('/');
|
|
116
|
-
if (segments.length < 2)
|
|
117
|
-
return null;
|
|
118
|
-
return `${segments[0]}/${segments[1]}`;
|
|
18
|
+
byFile.set(file, specifiers);
|
|
119
19
|
}
|
|
120
|
-
return
|
|
20
|
+
return { byFile, externalPackages, scannedFiles: code.scannedFiles };
|
|
121
21
|
}
|
|
22
|
+
/** Convert an import specifier to a bare package name. */
|
|
23
|
+
export const toPackageName = graphToPackageName;
|
|
122
24
|
/** Check if a package is referenced by at least one file in the graph. */
|
|
123
25
|
export function isPackageUsed(graph, pkg) {
|
|
124
26
|
return graph.externalPackages.has(pkg);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"importGraph.js","sourceRoot":"","sources":["../../src/core/importGraph.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"importGraph.js","sourceRoot":"","sources":["../../src/core/importGraph.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,aAAa,IAAI,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAWrF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAAgB,EAChB,KAAkB;IAElB,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAEnD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC9C,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;IAE3C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACvC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;QACrC,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAChC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC3B,MAAM,GAAG,GAAG,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC3C,IAAI,GAAG;gBAAE,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrC,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC;AACvE,CAAC;AAED,0DAA0D;AAC1D,MAAM,CAAC,MAAM,aAAa,GAAG,kBAAkB,CAAC;AAEhD,0EAA0E;AAC1E,MAAM,UAAU,aAAa,CAAC,KAAkB,EAAE,GAAW;IAC3D,OAAO,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACzC,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,cAAc,CAAC,KAAkB,EAAE,GAAW;IAC5D,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAC9C,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;YAC9B,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;gBAChC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACf,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CodeGraph } from './codeGraph.js';
|
|
2
|
+
/**
|
|
3
|
+
* Load a previously cached code graph, if present and valid. Returns undefined
|
|
4
|
+
* when there's no cache or the cache is incompatible — caller should rebuild.
|
|
5
|
+
*/
|
|
6
|
+
export declare function loadCachedGraph(rootPath: string): Promise<CodeGraph | undefined>;
|
|
7
|
+
/**
|
|
8
|
+
* Persist the graph. Creates .projscan-cache/ if needed. Swallows errors —
|
|
9
|
+
* caching is best-effort, never blocks a run.
|
|
10
|
+
*/
|
|
11
|
+
export declare function saveCachedGraph(rootPath: string, graph: CodeGraph): Promise<void>;
|
|
12
|
+
export declare function invalidateCache(rootPath: string): Promise<void>;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const CACHE_DIR = '.projscan-cache';
|
|
4
|
+
const CACHE_FILE = 'graph.json';
|
|
5
|
+
const CACHE_VERSION = 1;
|
|
6
|
+
/**
|
|
7
|
+
* Load a previously cached code graph, if present and valid. Returns undefined
|
|
8
|
+
* when there's no cache or the cache is incompatible — caller should rebuild.
|
|
9
|
+
*/
|
|
10
|
+
export async function loadCachedGraph(rootPath) {
|
|
11
|
+
const cachePath = path.join(rootPath, CACHE_DIR, CACHE_FILE);
|
|
12
|
+
let raw;
|
|
13
|
+
try {
|
|
14
|
+
raw = await fs.readFile(cachePath, 'utf-8');
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
let parsed;
|
|
20
|
+
try {
|
|
21
|
+
parsed = JSON.parse(raw);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
if (parsed.version !== CACHE_VERSION)
|
|
27
|
+
return undefined;
|
|
28
|
+
const files = new Map();
|
|
29
|
+
for (const entry of parsed.files) {
|
|
30
|
+
files.set(entry.relativePath, {
|
|
31
|
+
relativePath: entry.relativePath,
|
|
32
|
+
imports: entry.imports,
|
|
33
|
+
exports: entry.exports,
|
|
34
|
+
callSites: entry.callSites,
|
|
35
|
+
lineCount: entry.lineCount,
|
|
36
|
+
mtimeMs: entry.mtimeMs,
|
|
37
|
+
parseOk: entry.parseOk,
|
|
38
|
+
parseReason: entry.parseReason,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// Derived indexes are rebuilt on load — cheap compared to re-parsing.
|
|
42
|
+
// Return a partial graph the caller will rehydrate via buildCodeGraph.
|
|
43
|
+
return {
|
|
44
|
+
files,
|
|
45
|
+
packageImporters: new Map(),
|
|
46
|
+
localImporters: new Map(),
|
|
47
|
+
symbolDefs: new Map(),
|
|
48
|
+
scannedFiles: files.size,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Persist the graph. Creates .projscan-cache/ if needed. Swallows errors —
|
|
53
|
+
* caching is best-effort, never blocks a run.
|
|
54
|
+
*/
|
|
55
|
+
export async function saveCachedGraph(rootPath, graph) {
|
|
56
|
+
const dir = path.join(rootPath, CACHE_DIR);
|
|
57
|
+
try {
|
|
58
|
+
await fs.mkdir(dir, { recursive: true });
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const payload = {
|
|
64
|
+
version: CACHE_VERSION,
|
|
65
|
+
rootPath,
|
|
66
|
+
files: [...graph.files.values()].map((entry) => ({
|
|
67
|
+
relativePath: entry.relativePath,
|
|
68
|
+
imports: entry.imports,
|
|
69
|
+
exports: entry.exports,
|
|
70
|
+
callSites: entry.callSites,
|
|
71
|
+
lineCount: entry.lineCount,
|
|
72
|
+
mtimeMs: entry.mtimeMs,
|
|
73
|
+
parseOk: entry.parseOk,
|
|
74
|
+
parseReason: entry.parseReason,
|
|
75
|
+
})),
|
|
76
|
+
createdAt: new Date().toISOString(),
|
|
77
|
+
};
|
|
78
|
+
try {
|
|
79
|
+
await fs.writeFile(path.join(dir, CACHE_FILE), JSON.stringify(payload), 'utf-8');
|
|
80
|
+
// Ensure users don't accidentally commit the cache.
|
|
81
|
+
const gitignorePath = path.join(dir, '.gitignore');
|
|
82
|
+
await fs.writeFile(gitignorePath, '*\n', 'utf-8');
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// ignore — cache is best-effort
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export async function invalidateCache(rootPath) {
|
|
89
|
+
const cachePath = path.join(rootPath, CACHE_DIR, CACHE_FILE);
|
|
90
|
+
try {
|
|
91
|
+
await fs.unlink(cachePath);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// ignore
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=indexCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexCache.js","sourceRoot":"","sources":["../../src/core/indexCache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { FileEntry } from '../types.js';
|
|
2
|
+
import type { CodeGraph } from './codeGraph.js';
|
|
3
|
+
export interface IndexedFile {
|
|
4
|
+
relativePath: string;
|
|
5
|
+
content: string[];
|
|
6
|
+
symbols: string[];
|
|
7
|
+
pathTokens: string[];
|
|
8
|
+
length: number;
|
|
9
|
+
}
|
|
10
|
+
export interface SearchIndex {
|
|
11
|
+
files: Map<string, IndexedFile>;
|
|
12
|
+
/** token → map<file, count> */
|
|
13
|
+
postings: Map<string, Map<string, number>>;
|
|
14
|
+
/** average document length across indexed files */
|
|
15
|
+
avgDocLength: number;
|
|
16
|
+
/** total number of indexed documents */
|
|
17
|
+
docCount: number;
|
|
18
|
+
}
|
|
19
|
+
export interface SearchHit {
|
|
20
|
+
file: string;
|
|
21
|
+
score: number;
|
|
22
|
+
matched: string[];
|
|
23
|
+
symbolMatch: boolean;
|
|
24
|
+
pathMatch: boolean;
|
|
25
|
+
excerpt: string;
|
|
26
|
+
line: number;
|
|
27
|
+
}
|
|
28
|
+
export interface SearchOptions {
|
|
29
|
+
limit?: number;
|
|
30
|
+
symbolWeight?: number;
|
|
31
|
+
pathWeight?: number;
|
|
32
|
+
}
|
|
33
|
+
export declare function buildSearchIndex(rootPath: string, files: FileEntry[], graph?: CodeGraph): Promise<SearchIndex>;
|
|
34
|
+
export declare function search(index: SearchIndex, query: string, options?: SearchOptions): SearchHit[];
|
|
35
|
+
/**
|
|
36
|
+
* Attach a one-line excerpt to each hit, reading the file to find the first
|
|
37
|
+
* matching line. This is a separate pass to avoid paying the I/O cost when
|
|
38
|
+
* the caller only wants paths (e.g., an agent filtering before fetching).
|
|
39
|
+
*/
|
|
40
|
+
export declare function attachExcerpts(rootPath: string, hits: SearchHit[], queryTokens: string[]): Promise<SearchHit[]>;
|
|
41
|
+
/**
|
|
42
|
+
* Tokenize a string for indexing/querying:
|
|
43
|
+
* - lowercase
|
|
44
|
+
* - split on non-identifier chars
|
|
45
|
+
* - split camelCase and snake_case
|
|
46
|
+
* - drop tokens shorter than 2 chars, stopwords, TS keywords
|
|
47
|
+
* - apply basic stem (drop trailing s / ing / ed)
|
|
48
|
+
*/
|
|
49
|
+
export declare function tokenize(input: string): string[];
|
|
50
|
+
/**
|
|
51
|
+
* Expand a user query into a set of candidate tokens. Same rules as tokenize
|
|
52
|
+
* plus: if the raw query has no hits, try progressively looser tokenization.
|
|
53
|
+
*/
|
|
54
|
+
export declare function expandQuery(query: string): string[];
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight BM25-ranked inverted index over source files.
|
|
5
|
+
*
|
|
6
|
+
* We index three fields per file with different weights:
|
|
7
|
+
* - content (body tokens, BM25 baseline)
|
|
8
|
+
* - symbols (export names — most informative for code search)
|
|
9
|
+
* - path (file path tokens)
|
|
10
|
+
*
|
|
11
|
+
* Scoring:
|
|
12
|
+
* score(file, query) = BM25(content) + 2.0 * hits(symbols) + 0.5 * hits(path)
|
|
13
|
+
*
|
|
14
|
+
* This intentionally beats pure substring matching while staying
|
|
15
|
+
* zero-dependency and fast enough for sub-second queries on 10k-file repos.
|
|
16
|
+
*/
|
|
17
|
+
const MAX_FILE_SIZE = 512 * 1024;
|
|
18
|
+
const STOPWORDS = new Set([
|
|
19
|
+
'the', 'a', 'an', 'and', 'or', 'not', 'of', 'to', 'in', 'is', 'it', 'for', 'on', 'with',
|
|
20
|
+
'this', 'that', 'by', 'as', 'at', 'be', 'are', 'was', 'were', 'has', 'have', 'had',
|
|
21
|
+
]);
|
|
22
|
+
const TS_KEYWORDS = new Set([
|
|
23
|
+
'const', 'let', 'var', 'function', 'return', 'if', 'else', 'while', 'for', 'do', 'break',
|
|
24
|
+
'continue', 'switch', 'case', 'default', 'new', 'class', 'extends', 'implements',
|
|
25
|
+
'interface', 'type', 'enum', 'public', 'private', 'protected', 'static', 'readonly',
|
|
26
|
+
'async', 'await', 'try', 'catch', 'finally', 'throw', 'import', 'export', 'from', 'as',
|
|
27
|
+
'typeof', 'instanceof', 'void', 'null', 'undefined', 'true', 'false', 'this', 'super',
|
|
28
|
+
'yield', 'delete', 'in', 'of', 'any', 'never', 'unknown', 'string', 'number', 'boolean',
|
|
29
|
+
'object', 'symbol', 'bigint',
|
|
30
|
+
]);
|
|
31
|
+
export async function buildSearchIndex(rootPath, files, graph) {
|
|
32
|
+
const indexed = new Map();
|
|
33
|
+
const postings = new Map();
|
|
34
|
+
const parseable = files.filter((f) => f.sizeBytes <= MAX_FILE_SIZE && isIndexable(f.relativePath));
|
|
35
|
+
await Promise.all(parseable.map(async (file) => {
|
|
36
|
+
const abs = path.isAbsolute(file.absolutePath)
|
|
37
|
+
? file.absolutePath
|
|
38
|
+
: path.resolve(rootPath, file.relativePath);
|
|
39
|
+
let content;
|
|
40
|
+
try {
|
|
41
|
+
content = await fs.readFile(abs, 'utf-8');
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const contentTokens = tokenize(content);
|
|
47
|
+
const pathTokens = tokenize(file.relativePath);
|
|
48
|
+
const symbols = (graph?.files.get(file.relativePath)?.exports ?? []).map((e) => e.name.toLowerCase());
|
|
49
|
+
const entry = {
|
|
50
|
+
relativePath: file.relativePath,
|
|
51
|
+
content: contentTokens,
|
|
52
|
+
symbols: symbols.flatMap((s) => tokenize(s)),
|
|
53
|
+
pathTokens,
|
|
54
|
+
length: contentTokens.length,
|
|
55
|
+
};
|
|
56
|
+
indexed.set(file.relativePath, entry);
|
|
57
|
+
// Build postings from content tokens
|
|
58
|
+
const termCounts = new Map();
|
|
59
|
+
for (const tok of contentTokens) {
|
|
60
|
+
termCounts.set(tok, (termCounts.get(tok) ?? 0) + 1);
|
|
61
|
+
}
|
|
62
|
+
for (const [tok, count] of termCounts) {
|
|
63
|
+
if (!postings.has(tok))
|
|
64
|
+
postings.set(tok, new Map());
|
|
65
|
+
postings.get(tok).set(file.relativePath, count);
|
|
66
|
+
}
|
|
67
|
+
}));
|
|
68
|
+
const totalLength = [...indexed.values()].reduce((sum, f) => sum + f.length, 0);
|
|
69
|
+
const avgDocLength = indexed.size > 0 ? totalLength / indexed.size : 1;
|
|
70
|
+
return {
|
|
71
|
+
files: indexed,
|
|
72
|
+
postings,
|
|
73
|
+
avgDocLength,
|
|
74
|
+
docCount: indexed.size,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export function search(index, query, options = {}) {
|
|
78
|
+
const limit = Math.max(1, Math.min(500, options.limit ?? 30));
|
|
79
|
+
const symbolWeight = options.symbolWeight ?? 2.0;
|
|
80
|
+
const pathWeight = options.pathWeight ?? 0.5;
|
|
81
|
+
const queryTokens = expandQuery(query);
|
|
82
|
+
if (queryTokens.length === 0)
|
|
83
|
+
return [];
|
|
84
|
+
// BM25 parameters
|
|
85
|
+
const k1 = 1.5;
|
|
86
|
+
const b = 0.75;
|
|
87
|
+
const scores = new Map();
|
|
88
|
+
for (const qTok of queryTokens) {
|
|
89
|
+
const postings = index.postings.get(qTok);
|
|
90
|
+
if (!postings)
|
|
91
|
+
continue;
|
|
92
|
+
const df = postings.size;
|
|
93
|
+
const idf = Math.log(1 + (index.docCount - df + 0.5) / (df + 0.5));
|
|
94
|
+
for (const [file, tf] of postings) {
|
|
95
|
+
const doc = index.files.get(file);
|
|
96
|
+
if (!doc)
|
|
97
|
+
continue;
|
|
98
|
+
const dl = doc.length;
|
|
99
|
+
const norm = tf * (k1 + 1) / (tf + k1 * (1 - b + b * (dl / index.avgDocLength)));
|
|
100
|
+
const bm25 = idf * norm;
|
|
101
|
+
const existing = scores.get(file);
|
|
102
|
+
if (existing) {
|
|
103
|
+
existing.score += bm25;
|
|
104
|
+
existing.matched.add(qTok);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
scores.set(file, { score: bm25, matched: new Set([qTok]) });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Apply symbol + path boosts
|
|
112
|
+
for (const [file, entry] of index.files) {
|
|
113
|
+
const symbolHits = countHits(entry.symbols, queryTokens);
|
|
114
|
+
const pathHits = countHits(entry.pathTokens, queryTokens);
|
|
115
|
+
if (symbolHits > 0 || pathHits > 0) {
|
|
116
|
+
const current = scores.get(file) ?? { score: 0, matched: new Set() };
|
|
117
|
+
current.score += symbolHits * symbolWeight + pathHits * pathWeight;
|
|
118
|
+
for (const qt of queryTokens) {
|
|
119
|
+
if (entry.symbols.includes(qt) || entry.pathTokens.includes(qt)) {
|
|
120
|
+
current.matched.add(qt);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
scores.set(file, current);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (scores.size === 0)
|
|
127
|
+
return [];
|
|
128
|
+
// Sort by score, take top limit
|
|
129
|
+
const ranked = [...scores.entries()]
|
|
130
|
+
.sort((a, b) => b[1].score - a[1].score)
|
|
131
|
+
.slice(0, limit);
|
|
132
|
+
return ranked.map(([file, info]) => {
|
|
133
|
+
const entry = index.files.get(file);
|
|
134
|
+
const symbolMatch = queryTokens.some((t) => entry.symbols.includes(t));
|
|
135
|
+
const pathMatch = queryTokens.some((t) => entry.pathTokens.includes(t));
|
|
136
|
+
return {
|
|
137
|
+
file,
|
|
138
|
+
score: Math.round(info.score * 100) / 100,
|
|
139
|
+
matched: [...info.matched],
|
|
140
|
+
symbolMatch,
|
|
141
|
+
pathMatch,
|
|
142
|
+
excerpt: '',
|
|
143
|
+
line: 0,
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Attach a one-line excerpt to each hit, reading the file to find the first
|
|
149
|
+
* matching line. This is a separate pass to avoid paying the I/O cost when
|
|
150
|
+
* the caller only wants paths (e.g., an agent filtering before fetching).
|
|
151
|
+
*/
|
|
152
|
+
export async function attachExcerpts(rootPath, hits, queryTokens) {
|
|
153
|
+
const qLower = queryTokens.map((t) => t.toLowerCase());
|
|
154
|
+
return Promise.all(hits.map(async (hit) => {
|
|
155
|
+
const abs = path.resolve(rootPath, hit.file);
|
|
156
|
+
try {
|
|
157
|
+
const content = await fs.readFile(abs, 'utf-8');
|
|
158
|
+
const lines = content.split('\n');
|
|
159
|
+
for (let i = 0; i < lines.length; i++) {
|
|
160
|
+
const lower = lines[i].toLowerCase();
|
|
161
|
+
if (qLower.some((t) => lower.includes(t))) {
|
|
162
|
+
return { ...hit, line: i + 1, excerpt: lines[i].trim().slice(0, 200) };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// ignore
|
|
168
|
+
}
|
|
169
|
+
return hit;
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Tokenize a string for indexing/querying:
|
|
174
|
+
* - lowercase
|
|
175
|
+
* - split on non-identifier chars
|
|
176
|
+
* - split camelCase and snake_case
|
|
177
|
+
* - drop tokens shorter than 2 chars, stopwords, TS keywords
|
|
178
|
+
* - apply basic stem (drop trailing s / ing / ed)
|
|
179
|
+
*/
|
|
180
|
+
export function tokenize(input) {
|
|
181
|
+
const out = [];
|
|
182
|
+
// Split on non-identifier boundaries. Keep original case so we can also
|
|
183
|
+
// split on camelCase boundaries below.
|
|
184
|
+
const rawTokens = input.match(/[A-Za-z0-9_]+/g) ?? [];
|
|
185
|
+
for (const raw of rawTokens) {
|
|
186
|
+
// Split on underscore and camelCase. camelCase: insert a boundary before
|
|
187
|
+
// each uppercase that follows a lowercase or digit (OR before runs of
|
|
188
|
+
// uppercase followed by lowercase to handle acronyms like "XMLParser").
|
|
189
|
+
const camelSplit = raw
|
|
190
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
191
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2');
|
|
192
|
+
const parts = camelSplit.split(/[_\s]+/).filter(Boolean);
|
|
193
|
+
for (const part of parts) {
|
|
194
|
+
// Split embedded digits from letters — e.g. "v1api" → "v", "1", "api"
|
|
195
|
+
const subparts = part.split(/(\d+)/).filter(Boolean);
|
|
196
|
+
for (const sp of subparts) {
|
|
197
|
+
const lower = sp.toLowerCase();
|
|
198
|
+
const stemmed = stem(lower);
|
|
199
|
+
if (!keepToken(stemmed))
|
|
200
|
+
continue;
|
|
201
|
+
out.push(stemmed);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return out;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Expand a user query into a set of candidate tokens. Same rules as tokenize
|
|
209
|
+
* plus: if the raw query has no hits, try progressively looser tokenization.
|
|
210
|
+
*/
|
|
211
|
+
export function expandQuery(query) {
|
|
212
|
+
const tokens = tokenize(query);
|
|
213
|
+
return [...new Set(tokens)];
|
|
214
|
+
}
|
|
215
|
+
function stem(token) {
|
|
216
|
+
if (token.length <= 3)
|
|
217
|
+
return token;
|
|
218
|
+
if (token.endsWith('ing'))
|
|
219
|
+
return token.slice(0, -3);
|
|
220
|
+
if (token.endsWith('ed') && token.length > 4)
|
|
221
|
+
return token.slice(0, -2);
|
|
222
|
+
if (token.endsWith('es') && token.length > 4)
|
|
223
|
+
return token.slice(0, -2);
|
|
224
|
+
if (token.endsWith('s') && !token.endsWith('ss'))
|
|
225
|
+
return token.slice(0, -1);
|
|
226
|
+
return token;
|
|
227
|
+
}
|
|
228
|
+
function keepToken(token) {
|
|
229
|
+
if (token.length < 2)
|
|
230
|
+
return false;
|
|
231
|
+
if (STOPWORDS.has(token))
|
|
232
|
+
return false;
|
|
233
|
+
if (TS_KEYWORDS.has(token))
|
|
234
|
+
return false;
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
function countHits(tokens, query) {
|
|
238
|
+
let count = 0;
|
|
239
|
+
const set = new Set(tokens);
|
|
240
|
+
for (const q of query)
|
|
241
|
+
if (set.has(q))
|
|
242
|
+
count++;
|
|
243
|
+
return count;
|
|
244
|
+
}
|
|
245
|
+
function isIndexable(relativePath) {
|
|
246
|
+
const ext = path.extname(relativePath).toLowerCase();
|
|
247
|
+
// Index source and markup/docs where it's likely useful
|
|
248
|
+
return (ext === '.ts' ||
|
|
249
|
+
ext === '.tsx' ||
|
|
250
|
+
ext === '.js' ||
|
|
251
|
+
ext === '.jsx' ||
|
|
252
|
+
ext === '.mjs' ||
|
|
253
|
+
ext === '.cjs' ||
|
|
254
|
+
ext === '.mts' ||
|
|
255
|
+
ext === '.cts' ||
|
|
256
|
+
ext === '.py' ||
|
|
257
|
+
ext === '.go' ||
|
|
258
|
+
ext === '.rb' ||
|
|
259
|
+
ext === '.java' ||
|
|
260
|
+
ext === '.rs' ||
|
|
261
|
+
ext === '.php' ||
|
|
262
|
+
ext === '.cs' ||
|
|
263
|
+
ext === '.swift' ||
|
|
264
|
+
ext === '.kt' ||
|
|
265
|
+
ext === '.md' ||
|
|
266
|
+
ext === '.mdx');
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=searchIndex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchIndex.js","sourceRoot":"","sources":["../../src/core/searchIndex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B;;;;;;;;;;;;;GAaG;AAEH,MAAM,aAAa,GAAG,GAAG,GAAG,IAAI,CAAC;AAEjC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC;IACxB,KAAK,EAAC,GAAG,EAAC,IAAI,EAAC,KAAK,EAAC,IAAI,EAAC,KAAK,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM;IAC1E,MAAM,EAAC,MAAM,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC,KAAK,EAAC,MAAM,EAAC,KAAK,EAAC,MAAM,EAAC,KAAK;CACxE,CAAC,CAAC;AAEH,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC1B,OAAO,EAAC,KAAK,EAAC,KAAK,EAAC,UAAU,EAAC,QAAQ,EAAC,IAAI,EAAC,MAAM,EAAC,OAAO,EAAC,KAAK,EAAC,IAAI,EAAC,OAAO;IAC9E,UAAU,EAAC,QAAQ,EAAC,MAAM,EAAC,SAAS,EAAC,KAAK,EAAC,OAAO,EAAC,SAAS,EAAC,YAAY;IACzE,WAAW,EAAC,MAAM,EAAC,MAAM,EAAC,QAAQ,EAAC,SAAS,EAAC,WAAW,EAAC,QAAQ,EAAC,UAAU;IAC5E,OAAO,EAAC,OAAO,EAAC,KAAK,EAAC,OAAO,EAAC,SAAS,EAAC,OAAO,EAAC,QAAQ,EAAC,QAAQ,EAAC,MAAM,EAAC,IAAI;IAC7E,QAAQ,EAAC,YAAY,EAAC,MAAM,EAAC,MAAM,EAAC,WAAW,EAAC,MAAM,EAAC,OAAO,EAAC,MAAM,EAAC,OAAO;IAC7E,OAAO,EAAC,QAAQ,EAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC,OAAO,EAAC,SAAS,EAAC,QAAQ,EAAC,QAAQ,EAAC,SAAS;IAC9E,QAAQ,EAAC,QAAQ,EAAC,QAAQ;CAC3B,CAAC,CAAC;AAoCH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAAgB,EAChB,KAAkB,EAClB,KAAiB;IAEjB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC/C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA+B,CAAC;IAExD,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,aAAa,IAAI,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnG,MAAM,OAAO,CAAC,GAAG,CACf,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC;YAC5C,CAAC,CAAC,IAAI,CAAC,YAAY;YACnB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC9C,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QAED,MAAM,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC7E,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CACrB,CAAC;QAEF,MAAM,KAAK,GAAgB;YACzB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,OAAO,EAAE,aAAa;YACtB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC5C,UAAU;YACV,MAAM,EAAE,aAAa,CAAC,MAAM;SAC7B,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QAEtC,qCAAqC;QACrC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC7C,KAAK,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;YAChC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACtD,CAAC;QACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,UAAU,EAAE,CAAC;YACtC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;YACrD,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QACnD,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,WAAW,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAChF,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAEvE,OAAO;QACL,KAAK,EAAE,OAAO;QACd,QAAQ;QACR,YAAY;QACZ,QAAQ,EAAE,OAAO,CAAC,IAAI;KACvB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,MAAM,CACpB,KAAkB,EAClB,KAAa,EACb,UAAyB,EAAE;IAE3B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;IAC9D,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,GAAG,CAAC;IACjD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,GAAG,CAAC;IAE7C,MAAM,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAExC,kBAAkB;IAClB,MAAM,EAAE,GAAG,GAAG,CAAC;IACf,MAAM,CAAC,GAAG,IAAI,CAAC;IAEf,MAAM,MAAM,GAAG,IAAI,GAAG,EAAmD,CAAC;IAE1E,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ;YAAE,SAAS;QACxB,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,GAAG,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;QAEnE,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,QAAQ,EAAE,CAAC;YAClC,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAClC,IAAI,CAAC,GAAG;gBAAE,SAAS;YACnB,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;YACtB,MAAM,IAAI,GAAG,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACjF,MAAM,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC;YAExB,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAClC,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,KAAK,IAAI,IAAI,CAAC;gBACvB,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;IACH,CAAC;IAED,6BAA6B;IAC7B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QACxC,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QACzD,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QAC1D,IAAI,UAAU,GAAG,CAAC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,GAAG,EAAU,EAAE,CAAC;YAC7E,OAAO,CAAC,KAAK,IAAI,UAAU,GAAG,YAAY,GAAG,QAAQ,GAAG,UAAU,CAAC;YACnE,KAAK,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;gBAC7B,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;oBAChE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEjC,gCAAgC;IAChC,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;SACjC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;SACvC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAEnB,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;QACjC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC;QACrC,MAAM,WAAW,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACvE,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACxE,OAAO;YACL,IAAI;YACJ,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG;YACzC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC;YAC1B,WAAW;YACX,SAAS;YACT,OAAO,EAAE,EAAE;YACX,IAAI,EAAE,CAAC;SACR,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,QAAgB,EAChB,IAAiB,EACjB,WAAqB;IAErB,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IACvD,OAAO,OAAO,CAAC,GAAG,CAChB,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACrB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAChD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACtC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;gBACrC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC1C,OAAO,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;gBACzE,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,wEAAwE;IACxE,uCAAuC;IACvC,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;IACtD,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,yEAAyE;QACzE,sEAAsE;QACtE,wEAAwE;QACxE,MAAM,UAAU,GAAG,GAAG;aACnB,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC;aACtC,OAAO,CAAC,uBAAuB,EAAE,OAAO,CAAC,CAAC;QAC7C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,sEAAsE;YACtE,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACrD,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;gBAC1B,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;gBAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC5B,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;oBAAE,SAAS;gBAClC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,IAAI,CAAC,KAAa;IACzB,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACpC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACrD,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACxE,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACxE,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5E,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC9B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACnC,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACzC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,SAAS,CAAC,MAAgB,EAAE,KAAe;IAClD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,KAAK,EAAE,CAAC;IAC/C,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,WAAW,CAAC,YAAoB;IACvC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;IACrD,wDAAwD;IACxD,OAAO,CACL,GAAG,KAAK,KAAK;QACb,GAAG,KAAK,MAAM;QACd,GAAG,KAAK,KAAK;QACb,GAAG,KAAK,MAAM;QACd,GAAG,KAAK,MAAM;QACd,GAAG,KAAK,MAAM;QACd,GAAG,KAAK,MAAM;QACd,GAAG,KAAK,MAAM;QACd,GAAG,KAAK,KAAK;QACb,GAAG,KAAK,KAAK;QACb,GAAG,KAAK,KAAK;QACb,GAAG,KAAK,OAAO;QACf,GAAG,KAAK,KAAK;QACb,GAAG,KAAK,MAAM;QACd,GAAG,KAAK,KAAK;QACb,GAAG,KAAK,QAAQ;QAChB,GAAG,KAAK,KAAK;QACb,GAAG,KAAK,KAAK;QACb,GAAG,KAAK,MAAM,CACf,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,11 @@ export { runAudit, auditFindingsToIssues } from './core/auditRunner.js';
|
|
|
11
11
|
export { previewUpgrade } from './core/upgradePreview.js';
|
|
12
12
|
export { parseCoverage, coverageMap } from './core/coverageParser.js';
|
|
13
13
|
export { joinCoverageWithHotspots } from './core/coverageJoin.js';
|
|
14
|
+
export { parseSource, isParseable } from './core/ast.js';
|
|
15
|
+
export { buildCodeGraph, filesImportingFile, filesImportingPackage, filesDefiningSymbol, exportsOf, importsOf, importersOf, } from './core/codeGraph.js';
|
|
16
|
+
export { loadCachedGraph, saveCachedGraph, invalidateCache } from './core/indexCache.js';
|
|
17
|
+
export { applyBudget, estimateTokens } from './mcp/tokenBudget.js';
|
|
18
|
+
export { buildSearchIndex, search, tokenize, expandQuery, attachExcerpts, } from './core/searchIndex.js';
|
|
14
19
|
export { findDependencyLines } from './utils/packageJsonLocator.js';
|
|
15
20
|
export { parse as parseSemver, compare as compareSemver, drift as semverDrift } from './utils/semver.js';
|
|
16
21
|
export { walkFiles } from './utils/fileWalker.js';
|