codedev-mcp 3.2.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/CHANGELOG.md +90 -0
- package/LICENSE +21 -0
- package/README.md +760 -0
- package/dist/analyzers/api-contract.d.ts +46 -0
- package/dist/analyzers/api-contract.d.ts.map +1 -0
- package/dist/analyzers/api-contract.js +319 -0
- package/dist/analyzers/api-contract.js.map +1 -0
- package/dist/analyzers/architecture.d.ts +37 -0
- package/dist/analyzers/architecture.d.ts.map +1 -0
- package/dist/analyzers/architecture.js +149 -0
- package/dist/analyzers/architecture.js.map +1 -0
- package/dist/analyzers/branch-compare.d.ts +46 -0
- package/dist/analyzers/branch-compare.d.ts.map +1 -0
- package/dist/analyzers/branch-compare.js +128 -0
- package/dist/analyzers/branch-compare.js.map +1 -0
- package/dist/analyzers/cicd.d.ts +42 -0
- package/dist/analyzers/cicd.d.ts.map +1 -0
- package/dist/analyzers/cicd.js +237 -0
- package/dist/analyzers/cicd.js.map +1 -0
- package/dist/analyzers/codebase.d.ts +64 -0
- package/dist/analyzers/codebase.d.ts.map +1 -0
- package/dist/analyzers/codebase.js +354 -0
- package/dist/analyzers/codebase.js.map +1 -0
- package/dist/analyzers/complexity-heatmap.d.ts +50 -0
- package/dist/analyzers/complexity-heatmap.d.ts.map +1 -0
- package/dist/analyzers/complexity-heatmap.js +156 -0
- package/dist/analyzers/complexity-heatmap.js.map +1 -0
- package/dist/analyzers/context-pack.d.ts +43 -0
- package/dist/analyzers/context-pack.d.ts.map +1 -0
- package/dist/analyzers/context-pack.js +232 -0
- package/dist/analyzers/context-pack.js.map +1 -0
- package/dist/analyzers/coverage.d.ts +70 -0
- package/dist/analyzers/coverage.d.ts.map +1 -0
- package/dist/analyzers/coverage.js +313 -0
- package/dist/analyzers/coverage.js.map +1 -0
- package/dist/analyzers/db-schema.d.ts +55 -0
- package/dist/analyzers/db-schema.d.ts.map +1 -0
- package/dist/analyzers/db-schema.js +237 -0
- package/dist/analyzers/db-schema.js.map +1 -0
- package/dist/analyzers/dead-code.d.ts +34 -0
- package/dist/analyzers/dead-code.d.ts.map +1 -0
- package/dist/analyzers/dead-code.js +131 -0
- package/dist/analyzers/dead-code.js.map +1 -0
- package/dist/analyzers/dep-vuln.d.ts +36 -0
- package/dist/analyzers/dep-vuln.d.ts.map +1 -0
- package/dist/analyzers/dep-vuln.js +342 -0
- package/dist/analyzers/dep-vuln.js.map +1 -0
- package/dist/analyzers/docs.d.ts +47 -0
- package/dist/analyzers/docs.d.ts.map +1 -0
- package/dist/analyzers/docs.js +473 -0
- package/dist/analyzers/docs.js.map +1 -0
- package/dist/analyzers/git.d.ts +115 -0
- package/dist/analyzers/git.d.ts.map +1 -0
- package/dist/analyzers/git.js +214 -0
- package/dist/analyzers/git.js.map +1 -0
- package/dist/analyzers/iac.d.ts +39 -0
- package/dist/analyzers/iac.d.ts.map +1 -0
- package/dist/analyzers/iac.js +233 -0
- package/dist/analyzers/iac.js.map +1 -0
- package/dist/analyzers/impact.d.ts +51 -0
- package/dist/analyzers/impact.d.ts.map +1 -0
- package/dist/analyzers/impact.js +235 -0
- package/dist/analyzers/impact.js.map +1 -0
- package/dist/analyzers/monorepo.d.ts +36 -0
- package/dist/analyzers/monorepo.d.ts.map +1 -0
- package/dist/analyzers/monorepo.js +233 -0
- package/dist/analyzers/monorepo.js.map +1 -0
- package/dist/analyzers/notebook.d.ts +53 -0
- package/dist/analyzers/notebook.d.ts.map +1 -0
- package/dist/analyzers/notebook.js +149 -0
- package/dist/analyzers/notebook.js.map +1 -0
- package/dist/analyzers/perf-profile.d.ts +39 -0
- package/dist/analyzers/perf-profile.d.ts.map +1 -0
- package/dist/analyzers/perf-profile.js +222 -0
- package/dist/analyzers/perf-profile.js.map +1 -0
- package/dist/analyzers/scaffold.d.ts +46 -0
- package/dist/analyzers/scaffold.d.ts.map +1 -0
- package/dist/analyzers/scaffold.js +313 -0
- package/dist/analyzers/scaffold.js.map +1 -0
- package/dist/analyzers/security.d.ts +42 -0
- package/dist/analyzers/security.d.ts.map +1 -0
- package/dist/analyzers/security.js +281 -0
- package/dist/analyzers/security.js.map +1 -0
- package/dist/analyzers/symbols.d.ts +49 -0
- package/dist/analyzers/symbols.d.ts.map +1 -0
- package/dist/analyzers/symbols.js +212 -0
- package/dist/analyzers/symbols.js.map +1 -0
- package/dist/analyzers/tree-sitter.d.ts +71 -0
- package/dist/analyzers/tree-sitter.d.ts.map +1 -0
- package/dist/analyzers/tree-sitter.js +333 -0
- package/dist/analyzers/tree-sitter.js.map +1 -0
- package/dist/analyzers/type-flow.d.ts +39 -0
- package/dist/analyzers/type-flow.d.ts.map +1 -0
- package/dist/analyzers/type-flow.js +75 -0
- package/dist/analyzers/type-flow.js.map +1 -0
- package/dist/cache/memory-cache.d.ts +130 -0
- package/dist/cache/memory-cache.d.ts.map +1 -0
- package/dist/cache/memory-cache.js +273 -0
- package/dist/cache/memory-cache.js.map +1 -0
- package/dist/config.d.ts +32 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +57 -0
- package/dist/config.js.map +1 -0
- package/dist/constants/instructions.d.ts +2 -0
- package/dist/constants/instructions.d.ts.map +1 -0
- package/dist/constants/instructions.js +82 -0
- package/dist/constants/instructions.js.map +1 -0
- package/dist/db/connection.d.ts +12 -0
- package/dist/db/connection.d.ts.map +1 -0
- package/dist/db/connection.js +34 -0
- package/dist/db/connection.js.map +1 -0
- package/dist/db/json-store.d.ts +111 -0
- package/dist/db/json-store.d.ts.map +1 -0
- package/dist/db/json-store.js +201 -0
- package/dist/db/json-store.js.map +1 -0
- package/dist/db/sqlite-store.d.ts +153 -0
- package/dist/db/sqlite-store.d.ts.map +1 -0
- package/dist/db/sqlite-store.js +388 -0
- package/dist/db/sqlite-store.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +116 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/health.d.ts +35 -0
- package/dist/resources/health.d.ts.map +1 -0
- package/dist/resources/health.js +81 -0
- package/dist/resources/health.js.map +1 -0
- package/dist/schemas/output-schemas.d.ts +517 -0
- package/dist/schemas/output-schemas.d.ts.map +1 -0
- package/dist/schemas/output-schemas.js +296 -0
- package/dist/schemas/output-schemas.js.map +1 -0
- package/dist/search/fast-search.d.ts +90 -0
- package/dist/search/fast-search.d.ts.map +1 -0
- package/dist/search/fast-search.js +387 -0
- package/dist/search/fast-search.js.map +1 -0
- package/dist/search/semantic.d.ts +26 -0
- package/dist/search/semantic.d.ts.map +1 -0
- package/dist/search/semantic.js +458 -0
- package/dist/search/semantic.js.map +1 -0
- package/dist/tools/analysis.d.ts +7 -0
- package/dist/tools/analysis.d.ts.map +1 -0
- package/dist/tools/analysis.js +491 -0
- package/dist/tools/analysis.js.map +1 -0
- package/dist/tools/architecture.d.ts +7 -0
- package/dist/tools/architecture.d.ts.map +1 -0
- package/dist/tools/architecture.js +176 -0
- package/dist/tools/architecture.js.map +1 -0
- package/dist/tools/devops.d.ts +7 -0
- package/dist/tools/devops.d.ts.map +1 -0
- package/dist/tools/devops.js +179 -0
- package/dist/tools/devops.js.map +1 -0
- package/dist/tools/docs.d.ts +7 -0
- package/dist/tools/docs.d.ts.map +1 -0
- package/dist/tools/docs.js +102 -0
- package/dist/tools/docs.js.map +1 -0
- package/dist/tools/git.d.ts +7 -0
- package/dist/tools/git.d.ts.map +1 -0
- package/dist/tools/git.js +475 -0
- package/dist/tools/git.js.map +1 -0
- package/dist/tools/nav.d.ts +7 -0
- package/dist/tools/nav.d.ts.map +1 -0
- package/dist/tools/nav.js +275 -0
- package/dist/tools/nav.js.map +1 -0
- package/dist/tools/notebook.d.ts +7 -0
- package/dist/tools/notebook.d.ts.map +1 -0
- package/dist/tools/notebook.js +102 -0
- package/dist/tools/notebook.js.map +1 -0
- package/dist/tools/performance.d.ts +7 -0
- package/dist/tools/performance.d.ts.map +1 -0
- package/dist/tools/performance.js +59 -0
- package/dist/tools/performance.js.map +1 -0
- package/dist/tools/quality.d.ts +7 -0
- package/dist/tools/quality.d.ts.map +1 -0
- package/dist/tools/quality.js +279 -0
- package/dist/tools/quality.js.map +1 -0
- package/dist/tools/scaffold.d.ts +7 -0
- package/dist/tools/scaffold.d.ts.map +1 -0
- package/dist/tools/scaffold.js +80 -0
- package/dist/tools/scaffold.js.map +1 -0
- package/dist/tools/search.d.ts +7 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +308 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/security.d.ts +7 -0
- package/dist/tools/security.d.ts.map +1 -0
- package/dist/tools/security.js +138 -0
- package/dist/tools/security.js.map +1 -0
- package/dist/utils/analytics.d.ts +69 -0
- package/dist/utils/analytics.d.ts.map +1 -0
- package/dist/utils/analytics.js +144 -0
- package/dist/utils/analytics.js.map +1 -0
- package/dist/utils/concurrency.d.ts +43 -0
- package/dist/utils/concurrency.d.ts.map +1 -0
- package/dist/utils/concurrency.js +78 -0
- package/dist/utils/concurrency.js.map +1 -0
- package/dist/utils/fallback.d.ts +52 -0
- package/dist/utils/fallback.d.ts.map +1 -0
- package/dist/utils/fallback.js +137 -0
- package/dist/utils/fallback.js.map +1 -0
- package/dist/utils/git-hooks.d.ts +24 -0
- package/dist/utils/git-hooks.d.ts.map +1 -0
- package/dist/utils/git-hooks.js +108 -0
- package/dist/utils/git-hooks.js.map +1 -0
- package/dist/utils/languages.d.ts +72 -0
- package/dist/utils/languages.d.ts.map +1 -0
- package/dist/utils/languages.js +463 -0
- package/dist/utils/languages.js.map +1 -0
- package/dist/utils/logger.d.ts +13 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +34 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/plugins.d.ts +105 -0
- package/dist/utils/plugins.d.ts.map +1 -0
- package/dist/utils/plugins.js +325 -0
- package/dist/utils/plugins.js.map +1 -0
- package/dist/utils/security.d.ts +17 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +48 -0
- package/dist/utils/security.js.map +1 -0
- package/dist/utils/streaming.d.ts +56 -0
- package/dist/utils/streaming.d.ts.map +1 -0
- package/dist/utils/streaming.js +95 -0
- package/dist/utils/streaming.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/mcp.json +100 -0
- package/package.json +89 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Symbol Extraction & Code Analysis
|
|
3
|
+
* Language-agnostic symbol detection for 40+ languages
|
|
4
|
+
*/
|
|
5
|
+
export interface Symbol {
|
|
6
|
+
name: string;
|
|
7
|
+
kind: 'function' | 'class' | 'interface' | 'type' | 'constant' | 'export';
|
|
8
|
+
line: number;
|
|
9
|
+
language: string;
|
|
10
|
+
signature?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface FileAnalysis {
|
|
13
|
+
path: string;
|
|
14
|
+
language: string;
|
|
15
|
+
size: number;
|
|
16
|
+
lines: number;
|
|
17
|
+
codeLines: number;
|
|
18
|
+
commentLines: number;
|
|
19
|
+
blankLines: number;
|
|
20
|
+
symbols: Symbol[];
|
|
21
|
+
imports: string[];
|
|
22
|
+
/** Rough cyclomatic complexity estimate */
|
|
23
|
+
complexity: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Extract all symbols from a file.
|
|
27
|
+
* @param filePath - Path to the source file
|
|
28
|
+
* @returns Array of symbols found in the file
|
|
29
|
+
*/
|
|
30
|
+
export declare function extractSymbols(filePath: string): Promise<Symbol[]>;
|
|
31
|
+
/**
|
|
32
|
+
* Extract imports from a file.
|
|
33
|
+
* @param filePath - Path to the source file
|
|
34
|
+
* @returns Array of imported module names
|
|
35
|
+
*/
|
|
36
|
+
export declare function extractImports(filePath: string): Promise<string[]>;
|
|
37
|
+
/**
|
|
38
|
+
* Full file analysis.
|
|
39
|
+
* @param filePath - Path to the source file
|
|
40
|
+
* @returns Complete file analysis with symbols, imports, and metrics
|
|
41
|
+
*/
|
|
42
|
+
export declare function analyzeFile(filePath: string): Promise<FileAnalysis>;
|
|
43
|
+
/**
|
|
44
|
+
* Generate a concise outline of a file's structure.
|
|
45
|
+
* @param analysis - The file analysis result
|
|
46
|
+
* @returns Formatted string outline of the file
|
|
47
|
+
*/
|
|
48
|
+
export declare function formatFileOutline(analysis: FileAnalysis): string;
|
|
49
|
+
//# sourceMappingURL=symbols.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbols.d.ts","sourceRoot":"","sources":["../../src/analyzers/symbols.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAC;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAiDxE;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAexE;AAyFD;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAsBzE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAgChE"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Symbol Extraction & Code Analysis
|
|
3
|
+
* Language-agnostic symbol detection for 40+ languages
|
|
4
|
+
*/
|
|
5
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
6
|
+
import { detectLanguage, getSymbolPatterns, getImportPatterns, getCommentStyle } from '../utils/languages.js';
|
|
7
|
+
/**
|
|
8
|
+
* Extract all symbols from a file.
|
|
9
|
+
* @param filePath - Path to the source file
|
|
10
|
+
* @returns Array of symbols found in the file
|
|
11
|
+
*/
|
|
12
|
+
export async function extractSymbols(filePath) {
|
|
13
|
+
const content = await readFile(filePath, 'utf-8');
|
|
14
|
+
const language = detectLanguage(filePath);
|
|
15
|
+
const patterns = getSymbolPatterns(language);
|
|
16
|
+
const symbols = [];
|
|
17
|
+
const lines = content.split('\n');
|
|
18
|
+
const kinds = [
|
|
19
|
+
{ key: 'functions', kind: 'function' },
|
|
20
|
+
{ key: 'classes', kind: 'class' },
|
|
21
|
+
{ key: 'interfaces', kind: 'interface' },
|
|
22
|
+
{ key: 'types', kind: 'type' },
|
|
23
|
+
{ key: 'constants', kind: 'constant' },
|
|
24
|
+
{ key: 'exports', kind: 'export' },
|
|
25
|
+
];
|
|
26
|
+
for (const { key, kind } of kinds) {
|
|
27
|
+
for (const pattern of patterns[key]) {
|
|
28
|
+
// Reset regex state
|
|
29
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
30
|
+
let match;
|
|
31
|
+
while ((match = regex.exec(content)) !== null) {
|
|
32
|
+
const name = match[1];
|
|
33
|
+
if (!name || name.length < 2)
|
|
34
|
+
continue;
|
|
35
|
+
// Deduplicate
|
|
36
|
+
if (symbols.some((s) => s.name === name && s.kind === kind))
|
|
37
|
+
continue;
|
|
38
|
+
// Find line number
|
|
39
|
+
const beforeMatch = content.slice(0, match.index);
|
|
40
|
+
const line = beforeMatch.split('\n').length;
|
|
41
|
+
// Get signature (the full matched line)
|
|
42
|
+
const lineContent = lines[line - 1]?.trim() || '';
|
|
43
|
+
symbols.push({
|
|
44
|
+
name,
|
|
45
|
+
kind,
|
|
46
|
+
line,
|
|
47
|
+
language,
|
|
48
|
+
// Truncate long lines
|
|
49
|
+
signature: lineContent.slice(0, 200),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Sort by line number
|
|
55
|
+
symbols.sort((a, b) => a.line - b.line);
|
|
56
|
+
return symbols;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Extract imports from a file.
|
|
60
|
+
* @param filePath - Path to the source file
|
|
61
|
+
* @returns Array of imported module names
|
|
62
|
+
*/
|
|
63
|
+
export async function extractImports(filePath) {
|
|
64
|
+
const content = await readFile(filePath, 'utf-8');
|
|
65
|
+
const language = detectLanguage(filePath);
|
|
66
|
+
const patterns = getImportPatterns(language);
|
|
67
|
+
const imports = new Set();
|
|
68
|
+
for (const pattern of patterns) {
|
|
69
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
70
|
+
let match;
|
|
71
|
+
while ((match = regex.exec(content)) !== null) {
|
|
72
|
+
if (match[1])
|
|
73
|
+
imports.add(match[1]);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return [...imports];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Count code, comment, and blank lines.
|
|
80
|
+
* @param content - The file content
|
|
81
|
+
* @param language - The programming language
|
|
82
|
+
* @returns Line counts by type
|
|
83
|
+
*/
|
|
84
|
+
function countLineTypes(content, language) {
|
|
85
|
+
const lines = content.split('\n');
|
|
86
|
+
const style = getCommentStyle(language);
|
|
87
|
+
let code = 0, comment = 0, blank = 0;
|
|
88
|
+
let inMultiLineComment = false;
|
|
89
|
+
for (const line of lines) {
|
|
90
|
+
const trimmed = line.trim();
|
|
91
|
+
if (trimmed === '') {
|
|
92
|
+
blank++;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (inMultiLineComment) {
|
|
96
|
+
comment++;
|
|
97
|
+
if (style.multiEnd && trimmed.includes(style.multiEnd)) {
|
|
98
|
+
inMultiLineComment = false;
|
|
99
|
+
}
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (style.multiStart && trimmed.startsWith(style.multiStart)) {
|
|
103
|
+
comment++;
|
|
104
|
+
if (!style.multiEnd || !trimmed.includes(style.multiEnd, style.multiStart.length)) {
|
|
105
|
+
inMultiLineComment = true;
|
|
106
|
+
}
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (style.single && trimmed.startsWith(style.single)) {
|
|
110
|
+
comment++;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
code++;
|
|
114
|
+
}
|
|
115
|
+
return { code, comment, blank };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Rough cyclomatic complexity estimate.
|
|
119
|
+
* @param content - The file content
|
|
120
|
+
* @returns Estimated cyclomatic complexity score
|
|
121
|
+
*/
|
|
122
|
+
function estimateComplexity(content) {
|
|
123
|
+
// Count decision points
|
|
124
|
+
const decisionKeywords = [
|
|
125
|
+
/\bif\b/g,
|
|
126
|
+
/\belse\s+if\b/g,
|
|
127
|
+
/\belif\b/g,
|
|
128
|
+
/\bfor\b/g,
|
|
129
|
+
/\bwhile\b/g,
|
|
130
|
+
/\bdo\b/g,
|
|
131
|
+
/\bswitch\b/g,
|
|
132
|
+
/\bcase\b/g,
|
|
133
|
+
/\bmatch\b/g,
|
|
134
|
+
/\bcatch\b/g,
|
|
135
|
+
/\bexcept\b/g,
|
|
136
|
+
/\brescue\b/g,
|
|
137
|
+
/\?\?/g,
|
|
138
|
+
/\?\./g,
|
|
139
|
+
/&&/g,
|
|
140
|
+
/\|\|/g,
|
|
141
|
+
/\bwhen\b/g,
|
|
142
|
+
/\bguard\b/g,
|
|
143
|
+
];
|
|
144
|
+
// Base complexity
|
|
145
|
+
let complexity = 1;
|
|
146
|
+
for (const pattern of decisionKeywords) {
|
|
147
|
+
const matches = content.match(pattern);
|
|
148
|
+
if (matches)
|
|
149
|
+
complexity += matches.length;
|
|
150
|
+
}
|
|
151
|
+
return complexity;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Full file analysis.
|
|
155
|
+
* @param filePath - Path to the source file
|
|
156
|
+
* @returns Complete file analysis with symbols, imports, and metrics
|
|
157
|
+
*/
|
|
158
|
+
export async function analyzeFile(filePath) {
|
|
159
|
+
const content = await readFile(filePath, 'utf-8');
|
|
160
|
+
const fileStat = await stat(filePath);
|
|
161
|
+
const language = detectLanguage(filePath);
|
|
162
|
+
const lines = content.split('\n').length;
|
|
163
|
+
const lineCounts = countLineTypes(content, language);
|
|
164
|
+
const symbols = await extractSymbols(filePath);
|
|
165
|
+
const imports = await extractImports(filePath);
|
|
166
|
+
const complexity = estimateComplexity(content);
|
|
167
|
+
return {
|
|
168
|
+
path: filePath,
|
|
169
|
+
language,
|
|
170
|
+
size: fileStat.size,
|
|
171
|
+
lines,
|
|
172
|
+
codeLines: lineCounts.code,
|
|
173
|
+
commentLines: lineCounts.comment,
|
|
174
|
+
blankLines: lineCounts.blank,
|
|
175
|
+
symbols,
|
|
176
|
+
imports,
|
|
177
|
+
complexity,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Generate a concise outline of a file's structure.
|
|
182
|
+
* @param analysis - The file analysis result
|
|
183
|
+
* @returns Formatted string outline of the file
|
|
184
|
+
*/
|
|
185
|
+
export function formatFileOutline(analysis) {
|
|
186
|
+
const parts = [];
|
|
187
|
+
parts.push(`📄 ${analysis.path}`);
|
|
188
|
+
parts.push(` Language: ${analysis.language} | Lines: ${analysis.lines} (code: ${analysis.codeLines}, comments: ${analysis.commentLines}) | Complexity: ${analysis.complexity}`);
|
|
189
|
+
if (analysis.imports.length > 0) {
|
|
190
|
+
parts.push(` 📦 Imports: ${analysis.imports.join(', ')}`);
|
|
191
|
+
}
|
|
192
|
+
const grouped = {};
|
|
193
|
+
for (const sym of analysis.symbols) {
|
|
194
|
+
if (!grouped[sym.kind])
|
|
195
|
+
grouped[sym.kind] = [];
|
|
196
|
+
grouped[sym.kind].push(sym);
|
|
197
|
+
}
|
|
198
|
+
const icons = {
|
|
199
|
+
class: '🏗️',
|
|
200
|
+
interface: '📋',
|
|
201
|
+
type: '🔷',
|
|
202
|
+
function: 'ƒ',
|
|
203
|
+
constant: '🔒',
|
|
204
|
+
export: '📤',
|
|
205
|
+
};
|
|
206
|
+
for (const [kind, syms] of Object.entries(grouped)) {
|
|
207
|
+
const icon = icons[kind] || '•';
|
|
208
|
+
parts.push(` ${icon} ${kind}s: ${syms.map((s) => `${s.name}:${s.line}`).join(', ')}`);
|
|
209
|
+
}
|
|
210
|
+
return parts.join('\n');
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=symbols.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbols.js","sourceRoot":"","sources":["../../src/analyzers/symbols.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree-sitter AST parsing for accurate code analysis.
|
|
3
|
+
* Uses web-tree-sitter (WASM-based, no native compilation).
|
|
4
|
+
* Falls back to regex-based parsing if grammars unavailable.
|
|
5
|
+
*
|
|
6
|
+
* Supported languages: TypeScript, JavaScript, Python, Go, Java, Rust, C/C++
|
|
7
|
+
*/
|
|
8
|
+
export interface ASTSymbol {
|
|
9
|
+
name: string;
|
|
10
|
+
type: 'function' | 'class' | 'method' | 'interface' | 'enum' | 'variable' | 'type' | 'module' | 'struct' | 'trait';
|
|
11
|
+
startLine: number;
|
|
12
|
+
endLine: number;
|
|
13
|
+
exported: boolean;
|
|
14
|
+
params?: string[];
|
|
15
|
+
returnType?: string;
|
|
16
|
+
docComment?: string;
|
|
17
|
+
/** Methods inside classes, etc. */
|
|
18
|
+
children?: ASTSymbol[];
|
|
19
|
+
}
|
|
20
|
+
export interface CallGraphEntry {
|
|
21
|
+
caller: string;
|
|
22
|
+
callee: string;
|
|
23
|
+
file: string;
|
|
24
|
+
line: number;
|
|
25
|
+
}
|
|
26
|
+
export interface ScopeInfo {
|
|
27
|
+
name: string;
|
|
28
|
+
type: string;
|
|
29
|
+
startLine: number;
|
|
30
|
+
endLine: number;
|
|
31
|
+
variables: string[];
|
|
32
|
+
children: ScopeInfo[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Initialize tree-sitter. Call once at startup.
|
|
36
|
+
* @returns True if initialization succeeded
|
|
37
|
+
*/
|
|
38
|
+
export declare function initTreeSitter(): Promise<boolean>;
|
|
39
|
+
/**
|
|
40
|
+
* Check if tree-sitter is available and ready.
|
|
41
|
+
* @returns True if tree-sitter has been initialized
|
|
42
|
+
*/
|
|
43
|
+
export declare function isTreeSitterReady(): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Get supported languages for tree-sitter.
|
|
46
|
+
* @returns Array of supported language names
|
|
47
|
+
*/
|
|
48
|
+
export declare function getTreeSitterLanguages(): string[];
|
|
49
|
+
/**
|
|
50
|
+
* Parse a file into an AST and extract symbols.
|
|
51
|
+
* Falls back to null if tree-sitter isn't available for this language.
|
|
52
|
+
* @param filePath - Path to the source file
|
|
53
|
+
* @param language - The programming language
|
|
54
|
+
* @returns Array of AST symbols or null if parsing unavailable
|
|
55
|
+
*/
|
|
56
|
+
export declare function parseAST(filePath: string, language: string): Promise<ASTSymbol[] | null>;
|
|
57
|
+
/**
|
|
58
|
+
* Extract call graph from a file -- which functions call which.
|
|
59
|
+
* @param filePath - Path to the source file
|
|
60
|
+
* @param language - The programming language
|
|
61
|
+
* @returns Array of call graph entries or null if parsing unavailable
|
|
62
|
+
*/
|
|
63
|
+
export declare function extractCallGraph(filePath: string, language: string): Promise<CallGraphEntry[] | null>;
|
|
64
|
+
/**
|
|
65
|
+
* Extract scope information from a file.
|
|
66
|
+
* @param filePath - Path to the source file
|
|
67
|
+
* @param language - The programming language
|
|
68
|
+
* @returns Array of scope information or null if parsing unavailable
|
|
69
|
+
*/
|
|
70
|
+
export declare function extractScopes(filePath: string, language: string): Promise<ScopeInfo[] | null>;
|
|
71
|
+
//# sourceMappingURL=tree-sitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-sitter.d.ts","sourceRoot":"","sources":["../../src/analyzers/tree-sitter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA6CH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;IACnH,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,QAAQ,CAAC,EAAE,SAAS,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,SAAS,EAAE,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAavD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAEjD;AAsCD;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,CAoB9F;AA4GD;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,GAAG,IAAI,CAAC,CAwD3G;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,CAmEnG"}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree-sitter AST parsing for accurate code analysis.
|
|
3
|
+
* Uses web-tree-sitter (WASM-based, no native compilation).
|
|
4
|
+
* Falls back to regex-based parsing if grammars unavailable.
|
|
5
|
+
*
|
|
6
|
+
* Supported languages: TypeScript, JavaScript, Python, Go, Java, Rust, C/C++
|
|
7
|
+
*/
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { readFile } from 'node:fs/promises';
|
|
10
|
+
// Dynamic import for web-tree-sitter (may not be available)
|
|
11
|
+
let Parser = null;
|
|
12
|
+
let treeSitterReady = false;
|
|
13
|
+
const loadedLanguages = new Map();
|
|
14
|
+
// Grammar file locations (downloaded on first use)
|
|
15
|
+
const GRAMMAR_URLS = {
|
|
16
|
+
typescript: 'tree-sitter-typescript.wasm',
|
|
17
|
+
javascript: 'tree-sitter-javascript.wasm',
|
|
18
|
+
python: 'tree-sitter-python.wasm',
|
|
19
|
+
go: 'tree-sitter-go.wasm',
|
|
20
|
+
java: 'tree-sitter-java.wasm',
|
|
21
|
+
rust: 'tree-sitter-rust.wasm',
|
|
22
|
+
c: 'tree-sitter-c.wasm',
|
|
23
|
+
cpp: 'tree-sitter-cpp.wasm',
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Initialize tree-sitter. Call once at startup.
|
|
27
|
+
* @returns True if initialization succeeded
|
|
28
|
+
*/
|
|
29
|
+
export async function initTreeSitter() {
|
|
30
|
+
try {
|
|
31
|
+
const TreeSitter = await import('web-tree-sitter');
|
|
32
|
+
const TreeSitterModule = TreeSitter.default || TreeSitter;
|
|
33
|
+
// Type assertion needed because web-tree-sitter types don't match our interface exactly
|
|
34
|
+
Parser = TreeSitterModule;
|
|
35
|
+
await Parser.init();
|
|
36
|
+
treeSitterReady = true;
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
treeSitterReady = false;
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Check if tree-sitter is available and ready.
|
|
46
|
+
* @returns True if tree-sitter has been initialized
|
|
47
|
+
*/
|
|
48
|
+
export function isTreeSitterReady() {
|
|
49
|
+
return treeSitterReady;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get supported languages for tree-sitter.
|
|
53
|
+
* @returns Array of supported language names
|
|
54
|
+
*/
|
|
55
|
+
export function getTreeSitterLanguages() {
|
|
56
|
+
return Object.keys(GRAMMAR_URLS);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Load a language grammar. Returns null if not available.
|
|
60
|
+
* @param language - The language name to load
|
|
61
|
+
* @returns The loaded language grammar or null
|
|
62
|
+
*/
|
|
63
|
+
async function loadLanguage(language) {
|
|
64
|
+
if (!treeSitterReady || !Parser)
|
|
65
|
+
return null;
|
|
66
|
+
if (loadedLanguages.has(language))
|
|
67
|
+
return loadedLanguages.get(language);
|
|
68
|
+
const grammarFile = GRAMMAR_URLS[language];
|
|
69
|
+
if (!grammarFile)
|
|
70
|
+
return null;
|
|
71
|
+
try {
|
|
72
|
+
// Try loading from node_modules or local cache
|
|
73
|
+
const possiblePaths = [
|
|
74
|
+
path.join(process.cwd(), 'grammars', grammarFile),
|
|
75
|
+
path.join(process.cwd(), '.codedev-mcp', 'grammars', grammarFile),
|
|
76
|
+
path.join(process.env.HOME || '', '.codedev-mcp', 'grammars', grammarFile),
|
|
77
|
+
];
|
|
78
|
+
for (const p of possiblePaths) {
|
|
79
|
+
try {
|
|
80
|
+
const lang = await Parser.Language.load(p);
|
|
81
|
+
loadedLanguages.set(language, lang);
|
|
82
|
+
return lang;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Parse a file into an AST and extract symbols.
|
|
96
|
+
* Falls back to null if tree-sitter isn't available for this language.
|
|
97
|
+
* @param filePath - Path to the source file
|
|
98
|
+
* @param language - The programming language
|
|
99
|
+
* @returns Array of AST symbols or null if parsing unavailable
|
|
100
|
+
*/
|
|
101
|
+
export async function parseAST(filePath, language) {
|
|
102
|
+
const lang = await loadLanguage(language);
|
|
103
|
+
if (!lang || !Parser)
|
|
104
|
+
return null;
|
|
105
|
+
try {
|
|
106
|
+
const content = await readFile(filePath, 'utf-8');
|
|
107
|
+
const parser = new Parser();
|
|
108
|
+
parser.setLanguage(lang);
|
|
109
|
+
const tree = parser.parse(content);
|
|
110
|
+
const symbols = [];
|
|
111
|
+
extractSymbolsFromNode(tree.rootNode, symbols, content, language);
|
|
112
|
+
parser.delete();
|
|
113
|
+
tree.delete();
|
|
114
|
+
return symbols;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Recursively extract symbols from AST nodes.
|
|
122
|
+
* @param node - The current AST node
|
|
123
|
+
* @param symbols - Accumulator for found symbols
|
|
124
|
+
* @param source - The full source code
|
|
125
|
+
* @param language - The programming language
|
|
126
|
+
* @param depth - Current recursion depth
|
|
127
|
+
*/
|
|
128
|
+
function extractSymbolsFromNode(node, symbols, source, language, depth = 0) {
|
|
129
|
+
// Prevent infinite recursion
|
|
130
|
+
if (depth > 10)
|
|
131
|
+
return;
|
|
132
|
+
const symbolTypes = {
|
|
133
|
+
function_declaration: 'function',
|
|
134
|
+
function_definition: 'function',
|
|
135
|
+
method_definition: 'method',
|
|
136
|
+
method_declaration: 'method',
|
|
137
|
+
class_declaration: 'class',
|
|
138
|
+
class_definition: 'class',
|
|
139
|
+
interface_declaration: 'interface',
|
|
140
|
+
type_alias_declaration: 'type',
|
|
141
|
+
enum_declaration: 'enum',
|
|
142
|
+
struct_item: 'struct',
|
|
143
|
+
impl_item: 'module',
|
|
144
|
+
trait_item: 'trait',
|
|
145
|
+
variable_declaration: 'variable',
|
|
146
|
+
lexical_declaration: 'variable',
|
|
147
|
+
};
|
|
148
|
+
const nodeType = symbolTypes[node.type];
|
|
149
|
+
if (nodeType) {
|
|
150
|
+
const nameNode = node.childForFieldName?.('name') ||
|
|
151
|
+
node.children?.find((c) => c.type === 'identifier' || c.type === 'type_identifier');
|
|
152
|
+
if (nameNode) {
|
|
153
|
+
const symbol = {
|
|
154
|
+
name: nameNode.text,
|
|
155
|
+
type: nodeType,
|
|
156
|
+
startLine: node.startPosition.row + 1,
|
|
157
|
+
endLine: node.endPosition.row + 1,
|
|
158
|
+
exported: isExported(node, source),
|
|
159
|
+
children: [],
|
|
160
|
+
};
|
|
161
|
+
// Extract params for functions
|
|
162
|
+
const params = node.childForFieldName?.('parameters') || node.childForFieldName?.('formal_parameters');
|
|
163
|
+
if (params) {
|
|
164
|
+
symbol.params = extractParams(params);
|
|
165
|
+
}
|
|
166
|
+
// Extract return type
|
|
167
|
+
const returnType = node.childForFieldName?.('return_type') || node.childForFieldName?.('result');
|
|
168
|
+
if (returnType) {
|
|
169
|
+
symbol.returnType = returnType.text;
|
|
170
|
+
}
|
|
171
|
+
// Extract doc comment (preceding comment node)
|
|
172
|
+
const prevSibling = node.previousNamedSibling;
|
|
173
|
+
if (prevSibling && (prevSibling.type === 'comment' || prevSibling.type === 'block_comment')) {
|
|
174
|
+
symbol.docComment = prevSibling.text;
|
|
175
|
+
}
|
|
176
|
+
// Extract children (methods inside classes)
|
|
177
|
+
if (nodeType === 'class' || nodeType === 'struct' || nodeType === 'trait') {
|
|
178
|
+
const body = node.childForFieldName?.('body');
|
|
179
|
+
if (body) {
|
|
180
|
+
const childSymbols = [];
|
|
181
|
+
for (const child of body.children || []) {
|
|
182
|
+
extractSymbolsFromNode(child, childSymbols, source, language, depth + 1);
|
|
183
|
+
}
|
|
184
|
+
symbol.children = childSymbols;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
symbols.push(symbol);
|
|
188
|
+
// Don't recurse into the same symbol
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Recurse into children
|
|
193
|
+
for (const child of node.children || []) {
|
|
194
|
+
extractSymbolsFromNode(child, symbols, source, language, depth + 1);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function isExported(node, source) {
|
|
198
|
+
const lineStart = source.lastIndexOf('\n', node.startIndex) + 1;
|
|
199
|
+
const lineText = source.slice(lineStart, node.startIndex + 50);
|
|
200
|
+
return /export\s/.test(lineText) || /^pub\s/.test(lineText);
|
|
201
|
+
}
|
|
202
|
+
function extractParams(paramsNode) {
|
|
203
|
+
return (paramsNode.children || [])
|
|
204
|
+
.filter((c) => c.type !== '(' && c.type !== ')' && c.type !== ',')
|
|
205
|
+
.map((c) => c.text)
|
|
206
|
+
.filter((t) => t.trim().length > 0);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Extract call graph from a file -- which functions call which.
|
|
210
|
+
* @param filePath - Path to the source file
|
|
211
|
+
* @param language - The programming language
|
|
212
|
+
* @returns Array of call graph entries or null if parsing unavailable
|
|
213
|
+
*/
|
|
214
|
+
export async function extractCallGraph(filePath, language) {
|
|
215
|
+
const lang = await loadLanguage(language);
|
|
216
|
+
if (!lang || !Parser)
|
|
217
|
+
return null;
|
|
218
|
+
try {
|
|
219
|
+
const content = await readFile(filePath, 'utf-8');
|
|
220
|
+
const parser = new Parser();
|
|
221
|
+
parser.setLanguage(lang);
|
|
222
|
+
const tree = parser.parse(content);
|
|
223
|
+
const calls = [];
|
|
224
|
+
const funcStack = [];
|
|
225
|
+
function walkForCalls(node) {
|
|
226
|
+
// Track function scope
|
|
227
|
+
const isFuncDef = ['function_declaration', 'function_definition', 'method_definition', 'arrow_function'].includes(node.type);
|
|
228
|
+
if (isFuncDef) {
|
|
229
|
+
const nameNode = node.childForFieldName?.('name');
|
|
230
|
+
if (nameNode)
|
|
231
|
+
funcStack.push(nameNode.text);
|
|
232
|
+
}
|
|
233
|
+
// Detect function calls
|
|
234
|
+
if (node.type === 'call_expression') {
|
|
235
|
+
const funcNode = node.childForFieldName?.('function') || node.children?.[0];
|
|
236
|
+
if (funcNode) {
|
|
237
|
+
const callee = funcNode.text;
|
|
238
|
+
const caller = funcStack.length > 0 ? funcStack[funcStack.length - 1] : '<module>';
|
|
239
|
+
calls.push({
|
|
240
|
+
caller,
|
|
241
|
+
callee,
|
|
242
|
+
file: filePath,
|
|
243
|
+
line: node.startPosition.row + 1,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
for (const child of node.children || []) {
|
|
248
|
+
walkForCalls(child);
|
|
249
|
+
}
|
|
250
|
+
if (isFuncDef && node.childForFieldName?.('name')) {
|
|
251
|
+
funcStack.pop();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
walkForCalls(tree.rootNode);
|
|
255
|
+
parser.delete();
|
|
256
|
+
tree.delete();
|
|
257
|
+
return calls;
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Extract scope information from a file.
|
|
265
|
+
* @param filePath - Path to the source file
|
|
266
|
+
* @param language - The programming language
|
|
267
|
+
* @returns Array of scope information or null if parsing unavailable
|
|
268
|
+
*/
|
|
269
|
+
export async function extractScopes(filePath, language) {
|
|
270
|
+
const lang = await loadLanguage(language);
|
|
271
|
+
if (!lang || !Parser)
|
|
272
|
+
return null;
|
|
273
|
+
try {
|
|
274
|
+
const content = await readFile(filePath, 'utf-8');
|
|
275
|
+
const parser = new Parser();
|
|
276
|
+
parser.setLanguage(lang);
|
|
277
|
+
const tree = parser.parse(content);
|
|
278
|
+
const scopes = [];
|
|
279
|
+
function walkForScopes(node, parent) {
|
|
280
|
+
const scopeTypes = [
|
|
281
|
+
'function_declaration',
|
|
282
|
+
'function_definition',
|
|
283
|
+
'method_definition',
|
|
284
|
+
'class_declaration',
|
|
285
|
+
'class_definition',
|
|
286
|
+
'block',
|
|
287
|
+
'if_statement',
|
|
288
|
+
'for_statement',
|
|
289
|
+
'while_statement',
|
|
290
|
+
'try_statement',
|
|
291
|
+
];
|
|
292
|
+
if (scopeTypes.includes(node.type)) {
|
|
293
|
+
const nameNode = node.childForFieldName?.('name');
|
|
294
|
+
const scope = {
|
|
295
|
+
name: nameNode?.text || `<${node.type}>`,
|
|
296
|
+
type: node.type,
|
|
297
|
+
startLine: node.startPosition.row + 1,
|
|
298
|
+
endLine: node.endPosition.row + 1,
|
|
299
|
+
variables: [],
|
|
300
|
+
children: [],
|
|
301
|
+
};
|
|
302
|
+
// Extract variable declarations within this scope
|
|
303
|
+
for (const child of node.children || []) {
|
|
304
|
+
if (['variable_declaration', 'lexical_declaration', 'assignment'].includes(child.type)) {
|
|
305
|
+
const varName = child.childForFieldName?.('name');
|
|
306
|
+
if (varName)
|
|
307
|
+
scope.variables.push(varName.text);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (parent)
|
|
311
|
+
parent.children.push(scope);
|
|
312
|
+
else
|
|
313
|
+
scopes.push(scope);
|
|
314
|
+
for (const child of node.children || []) {
|
|
315
|
+
walkForScopes(child, scope);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
for (const child of node.children || []) {
|
|
320
|
+
walkForScopes(child, parent);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
walkForScopes(tree.rootNode, null);
|
|
325
|
+
parser.delete();
|
|
326
|
+
tree.delete();
|
|
327
|
+
return scopes;
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
//# sourceMappingURL=tree-sitter.js.map
|