@promptwheel/core 0.7.13 → 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.
- package/dist/codebase-index/ast-analysis.d.ts +57 -0
- package/dist/codebase-index/ast-analysis.d.ts.map +1 -0
- package/dist/codebase-index/ast-analysis.js +325 -0
- package/dist/codebase-index/ast-analysis.js.map +1 -0
- package/dist/codebase-index/ast-cache.d.ts +29 -0
- package/dist/codebase-index/ast-cache.d.ts.map +1 -0
- package/dist/codebase-index/ast-cache.js +75 -0
- package/dist/codebase-index/ast-cache.js.map +1 -0
- package/dist/codebase-index/dead-code.d.ts +41 -0
- package/dist/codebase-index/dead-code.d.ts.map +1 -0
- package/dist/codebase-index/dead-code.js +158 -0
- package/dist/codebase-index/dead-code.js.map +1 -0
- package/dist/codebase-index/format-analysis.d.ts +18 -0
- package/dist/codebase-index/format-analysis.d.ts.map +1 -0
- package/dist/codebase-index/format-analysis.js +191 -0
- package/dist/codebase-index/format-analysis.js.map +1 -0
- package/dist/codebase-index/index.d.ts +7 -3
- package/dist/codebase-index/index.d.ts.map +1 -1
- package/dist/codebase-index/index.js +114 -5
- package/dist/codebase-index/index.js.map +1 -1
- package/dist/codebase-index/shared.d.ts +92 -0
- package/dist/codebase-index/shared.d.ts.map +1 -1
- package/dist/codebase-index/shared.js +137 -1
- package/dist/codebase-index/shared.js.map +1 -1
- package/dist/sectors/shared.d.ts +25 -4
- package/dist/sectors/shared.d.ts.map +1 -1
- package/dist/sectors/shared.js +51 -8
- package/dist/sectors/shared.js.map +1 -1
- package/package.json +13 -1
|
@@ -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":"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"}
|
|
@@ -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"}
|