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.
@@ -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. Extracts ES imports and
12
- * CommonJS requires; falls back silently on unreadable / oversized files.
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
- * Convert an import specifier to a bare package name.
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. */
@@ -1,124 +1,26 @@
1
- import fs from 'node:fs/promises';
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. Extracts ES imports and
61
- * CommonJS requires; falls back silently on unreadable / oversized files.
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
- let scannedFiles = 0;
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 = toPackageName(imp.source);
85
- if (pkg && !NODE_BUILTINS.has(pkg) && !pkg.startsWith('node:')) {
14
+ const pkg = graphToPackageName(imp.source);
15
+ if (pkg)
86
16
  externalPackages.add(pkg);
87
- }
88
17
  }
89
- byFile.set(file.relativePath, specifiers);
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 specifier.split('/')[0];
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":"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"}
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":"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"}
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';