@promptwheel/core 0.7.14 → 0.7.17

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.
@@ -0,0 +1,57 @@
1
+ /**
2
+ * AST-level codebase analysis via @ast-grep/napi.
3
+ *
4
+ * All functions are dependency-injected: they receive the ast-grep module
5
+ * as a parameter so there's no top-level import. This keeps the module
6
+ * loadable even when @ast-grep/napi is not installed.
7
+ *
8
+ * The ast-grep parse() function is synchronous — no async needed.
9
+ */
10
+ import type { AstAnalysisResult, ExportEntry } from './shared.js';
11
+ /**
12
+ * Minimal interface for the ast-grep/napi module.
13
+ * Avoids importing the actual types so this module works without the dep.
14
+ */
15
+ export interface AstGrepModule {
16
+ parse(lang: AstGrepLang, src: string): AstGrepRoot;
17
+ Lang: Record<string, AstGrepLang>;
18
+ }
19
+ /** Opaque language identifier (the ast-grep Lang enum value). */
20
+ export type AstGrepLang = string;
21
+ /** Minimal SgRoot interface. */
22
+ interface AstGrepRoot {
23
+ root(): AstGrepNode;
24
+ }
25
+ /** Minimal SgNode interface. */
26
+ interface AstGrepNode {
27
+ kind(): string;
28
+ text(): string;
29
+ children(): AstGrepNode[];
30
+ findAll(rule: {
31
+ rule: {
32
+ kind: string;
33
+ };
34
+ }): AstGrepNode[];
35
+ isNamed(): boolean;
36
+ }
37
+ /** Map file extension to ast-grep Lang enum key. Returns null for unsupported extensions. */
38
+ export declare function mapExtensionToLang(ext: string): string | null;
39
+ /**
40
+ * Analyze a single file using AST parsing. Returns import specifiers,
41
+ * export entries, and cyclomatic complexity.
42
+ *
43
+ * Falls back to null on any parse error (caller should use regex fallback).
44
+ */
45
+ export declare function analyzeFileAst(content: string, filePath: string, langKey: string, astGrep: AstGrepModule): AstAnalysisResult | null;
46
+ /** Extract import specifiers from an AST root node. */
47
+ export declare function extractImportsAst(root: AstGrepNode, langKey: string): string[];
48
+ /** Extract exported symbols from an AST root node. JS/TS only for now. */
49
+ export declare function extractExportsAst(root: AstGrepNode, langKey: string): ExportEntry[];
50
+ /**
51
+ * Estimate cyclomatic complexity by counting decision points in the AST.
52
+ * Base complexity is 1 (single path), each decision point adds 1.
53
+ * Also counts && and || in binary expressions as decision points.
54
+ */
55
+ export declare function estimateCyclomaticComplexity(root: AstGrepNode, langKey: string): number;
56
+ export {};
57
+ //# sourceMappingURL=ast-analysis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast-analysis.d.ts","sourceRoot":"","sources":["../../src/codebase-index/ast-analysis.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAMlE;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CAAC;IACnD,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACnC;AAED,iEAAiE;AACjE,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC;AAEjC,gCAAgC;AAChC,UAAU,WAAW;IACnB,IAAI,IAAI,WAAW,CAAC;CACrB;AAED,gCAAgC;AAChC,UAAU,WAAW;IACnB,IAAI,IAAI,MAAM,CAAC;IACf,IAAI,IAAI,MAAM,CAAC;IACf,QAAQ,IAAI,WAAW,EAAE,CAAC;IAC1B,OAAO,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAAG,WAAW,EAAE,CAAC;IACzD,OAAO,IAAI,OAAO,CAAC;CACpB;AAMD,6FAA6F;AAC7F,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyB7D;AAMD;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,aAAa,GACrB,iBAAiB,GAAG,IAAI,CAY1B;AAMD,uDAAuD;AACvD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAmE9E;AAMD,0EAA0E;AAC1E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,CA2EnF;AAmDD;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAqBvF"}
@@ -0,0 +1,325 @@
1
+ /**
2
+ * AST-level codebase analysis via @ast-grep/napi.
3
+ *
4
+ * All functions are dependency-injected: they receive the ast-grep module
5
+ * as a parameter so there's no top-level import. This keeps the module
6
+ * loadable even when @ast-grep/napi is not installed.
7
+ *
8
+ * The ast-grep parse() function is synchronous — no async needed.
9
+ */
10
+ // ---------------------------------------------------------------------------
11
+ // Language mapping
12
+ // ---------------------------------------------------------------------------
13
+ /** Map file extension to ast-grep Lang enum key. Returns null for unsupported extensions. */
14
+ export function mapExtensionToLang(ext) {
15
+ switch (ext) {
16
+ case '.ts': return 'TypeScript';
17
+ case '.tsx': return 'Tsx';
18
+ case '.js':
19
+ case '.jsx':
20
+ case '.mjs':
21
+ case '.cjs':
22
+ return 'JavaScript';
23
+ case '.py': return 'Python';
24
+ case '.go': return 'Go';
25
+ case '.rs': return 'Rust';
26
+ case '.java': return 'Java';
27
+ case '.rb': return 'Ruby';
28
+ case '.c': return 'C';
29
+ case '.cpp':
30
+ case '.hpp':
31
+ return 'Cpp';
32
+ case '.cs': return 'CSharp';
33
+ case '.swift': return 'Swift';
34
+ case '.kt':
35
+ case '.kts':
36
+ return 'Kotlin';
37
+ default: return null;
38
+ }
39
+ }
40
+ // ---------------------------------------------------------------------------
41
+ // Core analysis
42
+ // ---------------------------------------------------------------------------
43
+ /**
44
+ * Analyze a single file using AST parsing. Returns import specifiers,
45
+ * export entries, and cyclomatic complexity.
46
+ *
47
+ * Falls back to null on any parse error (caller should use regex fallback).
48
+ */
49
+ export function analyzeFileAst(content, filePath, langKey, astGrep) {
50
+ try {
51
+ const lang = astGrep.Lang[langKey];
52
+ if (!lang)
53
+ return null;
54
+ const root = astGrep.parse(lang, content).root();
55
+ const imports = extractImportsAst(root, langKey);
56
+ const exports = extractExportsAst(root, langKey);
57
+ const complexity = estimateCyclomaticComplexity(root, langKey);
58
+ return { imports, exports, complexity };
59
+ }
60
+ catch {
61
+ return null; // parse error — caller falls back to regex
62
+ }
63
+ }
64
+ // ---------------------------------------------------------------------------
65
+ // Import extraction
66
+ // ---------------------------------------------------------------------------
67
+ /** Extract import specifiers from an AST root node. */
68
+ export function extractImportsAst(root, langKey) {
69
+ const imports = [];
70
+ if (langKey === 'TypeScript' || langKey === 'Tsx' || langKey === 'JavaScript') {
71
+ // JS/TS: import_statement contains string nodes with the specifier
72
+ const importNodes = findAllByKind(root, 'import_statement');
73
+ for (const node of importNodes) {
74
+ const specifier = extractStringLiteral(node);
75
+ if (specifier)
76
+ imports.push(specifier);
77
+ }
78
+ // Also catch require() calls
79
+ const callNodes = findAllByKind(root, 'call_expression');
80
+ for (const node of callNodes) {
81
+ const text = node.text();
82
+ if (text.startsWith('require(')) {
83
+ const match = text.match(/require\s*\(\s*['"]([^'"]+)['"]\s*\)/);
84
+ if (match?.[1])
85
+ imports.push(match[1]);
86
+ }
87
+ }
88
+ }
89
+ else if (langKey === 'Python') {
90
+ // import_statement, import_from_statement
91
+ for (const kind of ['import_statement', 'import_from_statement']) {
92
+ const nodes = findAllByKind(root, kind);
93
+ for (const node of nodes) {
94
+ const text = node.text();
95
+ // from X import ... → extract X
96
+ const fromMatch = text.match(/from\s+([\w.]+)\s+import/);
97
+ if (fromMatch?.[1]) {
98
+ imports.push(fromMatch[1]);
99
+ continue;
100
+ }
101
+ // import X → extract X
102
+ const importMatch = text.match(/import\s+([\w.]+)/);
103
+ if (importMatch?.[1])
104
+ imports.push(importMatch[1]);
105
+ }
106
+ }
107
+ }
108
+ else if (langKey === 'Go') {
109
+ const nodes = findAllByKind(root, 'import_spec');
110
+ for (const node of nodes) {
111
+ const specifier = extractStringLiteral(node);
112
+ if (specifier)
113
+ imports.push(specifier);
114
+ }
115
+ // Also try import_declaration for single imports
116
+ const declNodes = findAllByKind(root, 'import_declaration');
117
+ for (const node of declNodes) {
118
+ const specifier = extractStringLiteral(node);
119
+ if (specifier)
120
+ imports.push(specifier);
121
+ }
122
+ }
123
+ else if (langKey === 'Rust') {
124
+ const nodes = findAllByKind(root, 'use_declaration');
125
+ for (const node of nodes) {
126
+ const text = node.text().replace(/^use\s+/, '').replace(/;$/, '').trim();
127
+ if (text)
128
+ imports.push(text);
129
+ }
130
+ }
131
+ else if (langKey === 'Java' || langKey === 'Kotlin') {
132
+ const nodes = findAllByKind(root, 'import_declaration');
133
+ for (const node of nodes) {
134
+ const text = node.text().replace(/^import\s+/, '').replace(/^static\s+/, '').replace(/;$/, '').trim();
135
+ if (text)
136
+ imports.push(text);
137
+ }
138
+ }
139
+ else if (langKey === 'Ruby') {
140
+ const callNodes = findAllByKind(root, 'call');
141
+ for (const node of callNodes) {
142
+ const text = node.text();
143
+ const match = text.match(/require(?:_relative)?\s+['"]([^'"]+)['"]/);
144
+ if (match?.[1])
145
+ imports.push(match[1]);
146
+ }
147
+ }
148
+ return imports;
149
+ }
150
+ // ---------------------------------------------------------------------------
151
+ // Export extraction
152
+ // ---------------------------------------------------------------------------
153
+ /** Extract exported symbols from an AST root node. JS/TS only for now. */
154
+ export function extractExportsAst(root, langKey) {
155
+ if (langKey !== 'TypeScript' && langKey !== 'Tsx' && langKey !== 'JavaScript') {
156
+ return [];
157
+ }
158
+ const exports = [];
159
+ const seen = new Set();
160
+ const exportNodes = findAllByKind(root, 'export_statement');
161
+ for (const node of exportNodes) {
162
+ const text = node.text();
163
+ // export default — skip (anonymous)
164
+ if (/^export\s+default\b/.test(text)) {
165
+ if (!seen.has('default')) {
166
+ exports.push({ name: 'default', kind: 'other' });
167
+ seen.add('default');
168
+ }
169
+ continue;
170
+ }
171
+ // export function foo
172
+ const fnMatch = text.match(/export\s+function\s+(\w+)/) ?? text.match(/export\s+async\s+function\s+(\w+)/);
173
+ if (fnMatch?.[1] && !seen.has(fnMatch[1])) {
174
+ exports.push({ name: fnMatch[1], kind: 'function' });
175
+ seen.add(fnMatch[1]);
176
+ continue;
177
+ }
178
+ // export class Foo
179
+ const classMatch = text.match(/export\s+class\s+(\w+)/) ?? text.match(/export\s+abstract\s+class\s+(\w+)/);
180
+ if (classMatch?.[1] && !seen.has(classMatch[1])) {
181
+ exports.push({ name: classMatch[1], kind: 'class' });
182
+ seen.add(classMatch[1]);
183
+ continue;
184
+ }
185
+ // export interface Foo / export type Foo
186
+ const typeMatch = text.match(/export\s+(?:interface|type)\s+(\w+)/);
187
+ if (typeMatch?.[1] && !seen.has(typeMatch[1])) {
188
+ exports.push({ name: typeMatch[1], kind: 'type' });
189
+ seen.add(typeMatch[1]);
190
+ continue;
191
+ }
192
+ // export enum Foo
193
+ const enumMatch = text.match(/export\s+enum\s+(\w+)/) ?? text.match(/export\s+const\s+enum\s+(\w+)/);
194
+ if (enumMatch?.[1] && !seen.has(enumMatch[1])) {
195
+ exports.push({ name: enumMatch[1], kind: 'enum' });
196
+ seen.add(enumMatch[1]);
197
+ continue;
198
+ }
199
+ // export const/let/var foo
200
+ const varMatch = text.match(/export\s+(?:const|let|var)\s+(\w+)/);
201
+ if (varMatch?.[1] && !seen.has(varMatch[1])) {
202
+ exports.push({ name: varMatch[1], kind: 'variable' });
203
+ seen.add(varMatch[1]);
204
+ continue;
205
+ }
206
+ // export { foo, bar } — named re-exports
207
+ const namedMatch = text.match(/export\s*\{([^}]+)\}/);
208
+ if (namedMatch?.[1]) {
209
+ for (const part of namedMatch[1].split(',')) {
210
+ const name = part.trim().split(/\s+as\s+/).pop()?.trim();
211
+ if (name && !seen.has(name)) {
212
+ exports.push({ name, kind: 'other' });
213
+ seen.add(name);
214
+ }
215
+ }
216
+ }
217
+ }
218
+ return exports;
219
+ }
220
+ // ---------------------------------------------------------------------------
221
+ // Cyclomatic complexity
222
+ // ---------------------------------------------------------------------------
223
+ /** Decision-point node kinds per language family. */
224
+ const DECISION_KINDS = {
225
+ js: [
226
+ 'if_statement', 'while_statement', 'for_statement', 'for_in_statement',
227
+ 'switch_case', 'catch_clause', 'ternary_expression',
228
+ ],
229
+ python: [
230
+ 'if_statement', 'while_statement', 'for_statement',
231
+ 'except_clause', 'with_statement',
232
+ ],
233
+ go: [
234
+ 'if_statement', 'for_statement', 'select_statement',
235
+ 'type_switch_statement', 'communication_case',
236
+ ],
237
+ rust: [
238
+ 'if_expression', 'while_expression', 'for_expression',
239
+ 'match_arm', 'if_let_expression',
240
+ ],
241
+ java: [
242
+ 'if_statement', 'while_statement', 'for_statement',
243
+ 'enhanced_for_statement', 'switch_block_statement_group',
244
+ 'catch_clause', 'ternary_expression',
245
+ ],
246
+ };
247
+ function getLangFamily(langKey) {
248
+ switch (langKey) {
249
+ case 'TypeScript':
250
+ case 'Tsx':
251
+ case 'JavaScript':
252
+ return 'js';
253
+ case 'Python':
254
+ return 'python';
255
+ case 'Go':
256
+ return 'go';
257
+ case 'Rust':
258
+ return 'rust';
259
+ case 'Java':
260
+ case 'Kotlin':
261
+ return 'java';
262
+ default:
263
+ return 'js'; // fallback
264
+ }
265
+ }
266
+ /**
267
+ * Estimate cyclomatic complexity by counting decision points in the AST.
268
+ * Base complexity is 1 (single path), each decision point adds 1.
269
+ * Also counts && and || in binary expressions as decision points.
270
+ */
271
+ export function estimateCyclomaticComplexity(root, langKey) {
272
+ const family = getLangFamily(langKey);
273
+ const kinds = DECISION_KINDS[family] ?? DECISION_KINDS.js;
274
+ let complexity = 1; // base path
275
+ for (const kind of kinds) {
276
+ complexity += findAllByKind(root, kind).length;
277
+ }
278
+ // Count logical operators (&&, ||) as additional decision points
279
+ const binaryNodes = findAllByKind(root, 'binary_expression');
280
+ for (const node of binaryNodes) {
281
+ const text = node.text();
282
+ // Only count top-level operator, not nested ones
283
+ if (text.includes('&&') || text.includes('||')) {
284
+ complexity++;
285
+ }
286
+ }
287
+ return complexity;
288
+ }
289
+ // ---------------------------------------------------------------------------
290
+ // Helpers
291
+ // ---------------------------------------------------------------------------
292
+ /** Find all descendant nodes of a given kind using tree traversal. */
293
+ function findAllByKind(root, kind) {
294
+ try {
295
+ return root.findAll({ rule: { kind } });
296
+ }
297
+ catch {
298
+ // Fallback: manual traversal if findAll doesn't support rule object
299
+ const result = [];
300
+ const stack = [root];
301
+ while (stack.length > 0) {
302
+ const node = stack.pop();
303
+ if (node.kind() === kind)
304
+ result.push(node);
305
+ try {
306
+ const children = node.children();
307
+ for (let i = children.length - 1; i >= 0; i--) {
308
+ stack.push(children[i]);
309
+ }
310
+ }
311
+ catch {
312
+ // node.children() not available — skip
313
+ }
314
+ }
315
+ return result;
316
+ }
317
+ }
318
+ /** Extract the first string literal value from a node's children. */
319
+ function extractStringLiteral(node) {
320
+ const text = node.text();
321
+ // Match single or double quoted strings
322
+ const match = text.match(/['"]([^'"]+)['"]/);
323
+ return match?.[1] ?? null;
324
+ }
325
+ //# sourceMappingURL=ast-analysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast-analysis.js","sourceRoot":"","sources":["../../src/codebase-index/ast-analysis.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAkCH,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,6FAA6F;AAC7F,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,KAAK,CAAC,CAAC,OAAO,YAAY,CAAC;QAChC,KAAK,MAAM,CAAC,CAAC,OAAO,KAAK,CAAC;QAC1B,KAAK,KAAK,CAAC;QACX,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM;YACT,OAAO,YAAY,CAAC;QACtB,KAAK,KAAK,CAAC,CAAC,OAAO,QAAQ,CAAC;QAC5B,KAAK,KAAK,CAAC,CAAC,OAAO,IAAI,CAAC;QACxB,KAAK,KAAK,CAAC,CAAC,OAAO,MAAM,CAAC;QAC1B,KAAK,OAAO,CAAC,CAAC,OAAO,MAAM,CAAC;QAC5B,KAAK,KAAK,CAAC,CAAC,OAAO,MAAM,CAAC;QAC1B,KAAK,IAAI,CAAC,CAAC,OAAO,GAAG,CAAC;QACtB,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,KAAK,CAAC,CAAC,OAAO,QAAQ,CAAC;QAC5B,KAAK,QAAQ,CAAC,CAAC,OAAO,OAAO,CAAC;QAC9B,KAAK,KAAK,CAAC;QACX,KAAK,MAAM;YACT,OAAO,QAAQ,CAAC;QAClB,OAAO,CAAC,CAAC,OAAO,IAAI,CAAC;IACvB,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,QAAgB,EAChB,OAAe,EACf,OAAsB;IAEtB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnC,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QACjD,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,UAAU,GAAG,4BAA4B,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC/D,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,CAAC,2CAA2C;IAC1D,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E,uDAAuD;AACvD,MAAM,UAAU,iBAAiB,CAAC,IAAiB,EAAE,OAAe;IAClE,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,IAAI,OAAO,KAAK,YAAY,IAAI,OAAO,KAAK,KAAK,IAAI,OAAO,KAAK,YAAY,EAAE,CAAC;QAC9E,mEAAmE;QACnE,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;QAC5D,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC/B,MAAM,SAAS,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;YAC7C,IAAI,SAAS;gBAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzC,CAAC;QACD,6BAA6B;QAC7B,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;QACzD,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAChC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;gBACjE,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC;oBAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACzC,CAAC;QACH,CAAC;IACH,CAAC;SAAM,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,0CAA0C;QAC1C,KAAK,MAAM,IAAI,IAAI,CAAC,kBAAkB,EAAE,uBAAuB,CAAC,EAAE,CAAC;YACjE,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACxC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;gBACzB,gCAAgC;gBAChC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;gBACzD,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;oBAAC,SAAS;gBAAC,CAAC;gBAC7D,uBAAuB;gBACvB,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;gBACpD,IAAI,WAAW,EAAE,CAAC,CAAC,CAAC;oBAAE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;IACH,CAAC;SAAM,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QACjD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,SAAS,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;YAC7C,IAAI,SAAS;gBAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzC,CAAC;QACD,iDAAiD;QACjD,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;QAC5D,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,MAAM,SAAS,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;YAC7C,IAAI,SAAS;gBAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;SAAM,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;QACrD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YACzE,IAAI,IAAI;gBAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;SAAM,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACtD,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;QACxD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YACtG,IAAI,IAAI;gBAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;SAAM,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC9C,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;YACrE,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC;gBAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E,0EAA0E;AAC1E,MAAM,UAAU,iBAAiB,CAAC,IAAiB,EAAE,OAAe;IAClE,IAAI,OAAO,KAAK,YAAY,IAAI,OAAO,KAAK,KAAK,IAAI,OAAO,KAAK,YAAY,EAAE,CAAC;QAC9E,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;IAC5D,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAEzB,oCAAoC;QACpC,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBACzB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;gBACjD,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACtB,CAAC;YACD,SAAS;QACX,CAAC;QAED,sBAAsB;QACtB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,2BAA2B,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QAC3G,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1C,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;YACrD,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YACrB,SAAS;QACX,CAAC;QAED,mBAAmB;QACnB,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QAC3G,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAChD,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YACrD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;YACxB,SAAS;QACX,CAAC;QAED,yCAAyC;QACzC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACpE,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9C,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YACnD,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;YACvB,SAAS;QACX,CAAC;QAED,kBAAkB;QAClB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,uBAAuB,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACrG,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9C,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YACnD,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;YACvB,SAAS;QACX,CAAC;QAED,2BAA2B;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;QAClE,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5C,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;YACtD,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;YACtB,SAAS;QACX,CAAC;QAED,yCAAyC;QACzC,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACtD,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpB,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC;gBACzD,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC5B,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;oBACtC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACjB,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E,qDAAqD;AACrD,MAAM,cAAc,GAA6B;IAC/C,EAAE,EAAE;QACF,cAAc,EAAE,iBAAiB,EAAE,eAAe,EAAE,kBAAkB;QACtE,aAAa,EAAE,cAAc,EAAE,oBAAoB;KACpD;IACD,MAAM,EAAE;QACN,cAAc,EAAE,iBAAiB,EAAE,eAAe;QAClD,eAAe,EAAE,gBAAgB;KAClC;IACD,EAAE,EAAE;QACF,cAAc,EAAE,eAAe,EAAE,kBAAkB;QACnD,uBAAuB,EAAE,oBAAoB;KAC9C;IACD,IAAI,EAAE;QACJ,eAAe,EAAE,kBAAkB,EAAE,gBAAgB;QACrD,WAAW,EAAE,mBAAmB;KACjC;IACD,IAAI,EAAE;QACJ,cAAc,EAAE,iBAAiB,EAAE,eAAe;QAClD,wBAAwB,EAAE,8BAA8B;QACxD,cAAc,EAAE,oBAAoB;KACrC;CACF,CAAC;AAEF,SAAS,aAAa,CAAC,OAAe;IACpC,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,YAAY,CAAC;QAClB,KAAK,KAAK,CAAC;QACX,KAAK,YAAY;YACf,OAAO,IAAI,CAAC;QACd,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAC;QAClB,KAAK,IAAI;YACP,OAAO,IAAI,CAAC;QACd,KAAK,MAAM;YACT,OAAO,MAAM,CAAC;QAChB,KAAK,MAAM,CAAC;QACZ,KAAK,QAAQ;YACX,OAAO,MAAM,CAAC;QAChB;YACE,OAAO,IAAI,CAAC,CAAC,WAAW;IAC5B,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAAC,IAAiB,EAAE,OAAe;IAC7E,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,CAAC,IAAI,cAAc,CAAC,EAAE,CAAC;IAE1D,IAAI,UAAU,GAAG,CAAC,CAAC,CAAC,YAAY;IAEhC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,UAAU,IAAI,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC;IACjD,CAAC;IAED,iEAAiE;IACjE,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;IAC7D,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACzB,iDAAiD;QACjD,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/C,UAAU,EAAE,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,sEAAsE;AACtE,SAAS,aAAa,CAAC,IAAiB,EAAE,IAAY;IACpD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,oEAAoE;QACpE,MAAM,MAAM,GAAkB,EAAE,CAAC;QACjC,MAAM,KAAK,GAAkB,CAAC,IAAI,CAAC,CAAC;QACpC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,EAAG,CAAC;YAC1B,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI;gBAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5C,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACjC,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;oBAC9C,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,uCAAuC;YACzC,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;AACH,CAAC;AAED,qEAAqE;AACrE,SAAS,oBAAoB,CAAC,IAAiB;IAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACzB,wCAAwC;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAC7C,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Mtime-based per-file AST analysis cache.
3
+ *
4
+ * Stores import/export/complexity results per source file. Invalidated when
5
+ * a file's mtime or size changes. Stale entries (files no longer in module set)
6
+ * are pruned on save.
7
+ *
8
+ * Cache location: .promptwheel/ast-cache.json
9
+ * ~200 bytes per entry → ~200KB for 1000 files.
10
+ */
11
+ import type { ExportEntry } from './shared.js';
12
+ export interface AstCacheEntry {
13
+ mtime: number;
14
+ size: number;
15
+ imports: string[];
16
+ exports: ExportEntry[];
17
+ complexity: number;
18
+ }
19
+ export type AstCache = Record<string, AstCacheEntry>;
20
+ /** Load the AST cache from disk. Returns empty cache if missing or corrupted. */
21
+ export declare function loadAstCache(repoRoot: string): AstCache;
22
+ /**
23
+ * Save the AST cache to disk. Prunes entries for files not in the current
24
+ * module set to prevent unbounded growth.
25
+ */
26
+ export declare function saveAstCache(repoRoot: string, cache: AstCache, currentFiles?: Set<string>): void;
27
+ /** Check if a cache entry is still current for a given file. */
28
+ export declare function isEntryCurrent(entry: AstCacheEntry | undefined, mtime: number, size: number): boolean;
29
+ //# sourceMappingURL=ast-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast-cache.d.ts","sourceRoot":"","sources":["../../src/codebase-index/ast-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAUrD,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAYvD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAwBhG;AAED,gEAAgE;AAChE,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGrG"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Mtime-based per-file AST analysis cache.
3
+ *
4
+ * Stores import/export/complexity results per source file. Invalidated when
5
+ * a file's mtime or size changes. Stale entries (files no longer in module set)
6
+ * are pruned on save.
7
+ *
8
+ * Cache location: .promptwheel/ast-cache.json
9
+ * ~200 bytes per entry → ~200KB for 1000 files.
10
+ */
11
+ import * as fs from 'node:fs';
12
+ import * as path from 'node:path';
13
+ // ---------------------------------------------------------------------------
14
+ // I/O
15
+ // ---------------------------------------------------------------------------
16
+ function getCachePath(repoRoot) {
17
+ return path.join(repoRoot, '.promptwheel', 'ast-cache.json');
18
+ }
19
+ /** Load the AST cache from disk. Returns empty cache if missing or corrupted. */
20
+ export function loadAstCache(repoRoot) {
21
+ try {
22
+ const cachePath = getCachePath(repoRoot);
23
+ if (!fs.existsSync(cachePath))
24
+ return {};
25
+ const raw = fs.readFileSync(cachePath, 'utf-8');
26
+ if (!raw.trim())
27
+ return {};
28
+ const data = JSON.parse(raw);
29
+ if (!data || typeof data !== 'object' || Array.isArray(data))
30
+ return {};
31
+ return data;
32
+ }
33
+ catch {
34
+ return {};
35
+ }
36
+ }
37
+ /**
38
+ * Save the AST cache to disk. Prunes entries for files not in the current
39
+ * module set to prevent unbounded growth.
40
+ */
41
+ export function saveAstCache(repoRoot, cache, currentFiles) {
42
+ const cachePath = getCachePath(repoRoot);
43
+ const tmp = cachePath + '.tmp';
44
+ try {
45
+ // Prune stale entries
46
+ const pruned = {};
47
+ for (const [relPath, entry] of Object.entries(cache)) {
48
+ if (!currentFiles || currentFiles.has(relPath)) {
49
+ pruned[relPath] = entry;
50
+ }
51
+ }
52
+ // Ensure directory exists
53
+ const dir = path.dirname(cachePath);
54
+ if (!fs.existsSync(dir)) {
55
+ fs.mkdirSync(dir, { recursive: true });
56
+ }
57
+ fs.writeFileSync(tmp, JSON.stringify(pruned));
58
+ fs.renameSync(tmp, cachePath);
59
+ }
60
+ catch {
61
+ // Cache write failure is non-fatal
62
+ try {
63
+ if (fs.existsSync(tmp))
64
+ fs.unlinkSync(tmp);
65
+ }
66
+ catch { /* ignore */ }
67
+ }
68
+ }
69
+ /** Check if a cache entry is still current for a given file. */
70
+ export function isEntryCurrent(entry, mtime, size) {
71
+ if (!entry)
72
+ return false;
73
+ return entry.mtime === mtime && entry.size === size;
74
+ }
75
+ //# sourceMappingURL=ast-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast-cache.js","sourceRoot":"","sources":["../../src/codebase-index/ast-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAiBlC,8EAA8E;AAC9E,MAAM;AACN,8EAA8E;AAE9E,SAAS,YAAY,CAAC,QAAgB;IACpC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,gBAAgB,CAAC,CAAC;AAC/D,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAChD,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;YAAE,OAAO,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QACxE,OAAO,IAAgB,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,KAAe,EAAE,YAA0B;IACxF,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,SAAS,GAAG,MAAM,CAAC;IAC/B,IAAI,CAAC;QACH,sBAAsB;QACtB,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,KAAK,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACrD,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC/C,MAAM,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC;YAC1B,CAAC;QACH,CAAC;QAED,0BAA0B;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACpC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9C,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,mCAAmC;QACnC,IAAI,CAAC;YAAC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAC5E,CAAC;AACH,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,cAAc,CAAC,KAAgC,EAAE,KAAa,EAAE,IAAY;IAC1F,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,OAAO,KAAK,CAAC,KAAK,KAAK,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;AACtD,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Dead code detection and structural issue analysis.
3
+ *
4
+ * Cross-module export-import matching to find unused exports and
5
+ * structural anti-patterns. Pure functions — no I/O.
6
+ */
7
+ import type { ModuleEntry, DeadExportEntry, StructuralIssue, ExportEntry } from './shared.js';
8
+ /**
9
+ * Detect potentially dead exports by comparing each module's exported names
10
+ * against what other modules import. Uses name-based matching (no type
11
+ * resolution) — sufficient for high-confidence identification.
12
+ *
13
+ * Returns at most `maxResults` entries to keep prompt token count bounded.
14
+ */
15
+ export declare function detectDeadExports(modules: ModuleEntry[], edges: Record<string, string[]>, exportsByModule: Record<string, ExportEntry[]>, importsByModule: Record<string, string[]>, maxResults?: number): DeadExportEntry[];
16
+ /**
17
+ * Detect structural anti-patterns from dependency graph topology.
18
+ *
19
+ * Patterns detected:
20
+ * - god-module: fan_in > 5 AND file_count > 20
21
+ * - excessive-fan-out: fan_out > 8
22
+ * - barrel-only: 1 file that exists only to re-export
23
+ * - orphan: zero in/out edges, not an entrypoint
24
+ *
25
+ * Returns at most `maxResults` entries.
26
+ */
27
+ export declare function detectStructuralIssues(modules: ModuleEntry[], edges: Record<string, string[]>, reverseEdges: Record<string, string[]>, cycles: string[][], entrypoints?: string[], maxResults?: number): StructuralIssue[];
28
+ export interface CouplingMetrics {
29
+ /** Per-module instability: Ce / (Ca + Ce). High = easily affected by change. */
30
+ instability: Record<string, number>;
31
+ }
32
+ /**
33
+ * Compute Robert C. Martin's instability metric per module.
34
+ * I = Ce / (Ca + Ce) where:
35
+ * - Ca (afferent coupling) = fan_in (who depends on me)
36
+ * - Ce (efferent coupling) = fan_out (who I depend on)
37
+ * - I = 0: maximally stable (many dependents, no dependencies)
38
+ * - I = 1: maximally unstable (no dependents, many dependencies)
39
+ */
40
+ export declare function computeCouplingMetrics(modules: ModuleEntry[], edges: Record<string, string[]>, reverseEdges: Record<string, string[]>): CouplingMetrics;
41
+ //# sourceMappingURL=dead-code.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dead-code.d.ts","sourceRoot":"","sources":["../../src/codebase-index/dead-code.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,eAAe,EACf,WAAW,EACZ,MAAM,aAAa,CAAC;AAMrB;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,WAAW,EAAE,EACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAC/B,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,EAC9C,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACzC,UAAU,SAAK,GACd,eAAe,EAAE,CA6CnB;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,WAAW,EAAE,EACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAC/B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACtC,MAAM,EAAE,MAAM,EAAE,EAAE,EAClB,WAAW,GAAE,MAAM,EAAO,EAC1B,UAAU,SAAK,GACd,eAAe,EAAE,CAkEnB;AAMD,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,WAAW,EAAE,EACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAC/B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACrC,eAAe,CAWjB"}