@theihtisham/agent-shadow-brain 1.1.1 → 2.1.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.
Files changed (102) hide show
  1. package/README.md +738 -138
  2. package/dist/adapters/aider.d.ts +11 -0
  3. package/dist/adapters/aider.d.ts.map +1 -0
  4. package/dist/adapters/aider.js +149 -0
  5. package/dist/adapters/aider.js.map +1 -0
  6. package/dist/adapters/index.d.ts +3 -1
  7. package/dist/adapters/index.d.ts.map +1 -1
  8. package/dist/adapters/index.js +5 -3
  9. package/dist/adapters/index.js.map +1 -1
  10. package/dist/adapters/roo-code.d.ts +14 -0
  11. package/dist/adapters/roo-code.d.ts.map +1 -0
  12. package/dist/adapters/roo-code.js +186 -0
  13. package/dist/adapters/roo-code.js.map +1 -0
  14. package/dist/brain/adr-engine.d.ts +58 -0
  15. package/dist/brain/adr-engine.d.ts.map +1 -0
  16. package/dist/brain/adr-engine.js +400 -0
  17. package/dist/brain/adr-engine.js.map +1 -0
  18. package/dist/brain/auto-update.d.ts +9 -0
  19. package/dist/brain/auto-update.d.ts.map +1 -0
  20. package/dist/brain/auto-update.js +59 -0
  21. package/dist/brain/auto-update.js.map +1 -0
  22. package/dist/brain/code-metrics.d.ts +18 -0
  23. package/dist/brain/code-metrics.d.ts.map +1 -0
  24. package/dist/brain/code-metrics.js +224 -0
  25. package/dist/brain/code-metrics.js.map +1 -0
  26. package/dist/brain/code-similarity.d.ts +43 -0
  27. package/dist/brain/code-similarity.d.ts.map +1 -0
  28. package/dist/brain/code-similarity.js +227 -0
  29. package/dist/brain/code-similarity.js.map +1 -0
  30. package/dist/brain/context-completion.d.ts +39 -0
  31. package/dist/brain/context-completion.d.ts.map +1 -0
  32. package/dist/brain/context-completion.js +851 -0
  33. package/dist/brain/context-completion.js.map +1 -0
  34. package/dist/brain/custom-rules.d.ts +14 -0
  35. package/dist/brain/custom-rules.d.ts.map +1 -0
  36. package/dist/brain/custom-rules.js +108 -0
  37. package/dist/brain/custom-rules.js.map +1 -0
  38. package/dist/brain/dependency-graph.d.ts +35 -0
  39. package/dist/brain/dependency-graph.d.ts.map +1 -0
  40. package/dist/brain/dependency-graph.js +310 -0
  41. package/dist/brain/dependency-graph.js.map +1 -0
  42. package/dist/brain/learning-engine.d.ts +54 -0
  43. package/dist/brain/learning-engine.d.ts.map +1 -0
  44. package/dist/brain/learning-engine.js +855 -0
  45. package/dist/brain/learning-engine.js.map +1 -0
  46. package/dist/brain/mcp-server.d.ts +30 -0
  47. package/dist/brain/mcp-server.d.ts.map +1 -0
  48. package/dist/brain/mcp-server.js +408 -0
  49. package/dist/brain/mcp-server.js.map +1 -0
  50. package/dist/brain/multi-project.d.ts +13 -0
  51. package/dist/brain/multi-project.d.ts.map +1 -0
  52. package/dist/brain/multi-project.js +163 -0
  53. package/dist/brain/multi-project.js.map +1 -0
  54. package/dist/brain/neural-mesh.d.ts +69 -0
  55. package/dist/brain/neural-mesh.d.ts.map +1 -0
  56. package/dist/brain/neural-mesh.js +677 -0
  57. package/dist/brain/neural-mesh.js.map +1 -0
  58. package/dist/brain/notifier.d.ts +26 -0
  59. package/dist/brain/notifier.d.ts.map +1 -0
  60. package/dist/brain/notifier.js +151 -0
  61. package/dist/brain/notifier.js.map +1 -0
  62. package/dist/brain/orchestrator.d.ts +148 -1
  63. package/dist/brain/orchestrator.d.ts.map +1 -1
  64. package/dist/brain/orchestrator.js +428 -0
  65. package/dist/brain/orchestrator.js.map +1 -1
  66. package/dist/brain/perf-profiler.d.ts +14 -0
  67. package/dist/brain/perf-profiler.d.ts.map +1 -0
  68. package/dist/brain/perf-profiler.js +289 -0
  69. package/dist/brain/perf-profiler.js.map +1 -0
  70. package/dist/brain/pr-generator.d.ts +19 -0
  71. package/dist/brain/pr-generator.d.ts.map +1 -0
  72. package/dist/brain/pr-generator.js +199 -0
  73. package/dist/brain/pr-generator.js.map +1 -0
  74. package/dist/brain/project-config.d.ts +14 -0
  75. package/dist/brain/project-config.d.ts.map +1 -0
  76. package/dist/brain/project-config.js +121 -0
  77. package/dist/brain/project-config.js.map +1 -0
  78. package/dist/brain/semantic-analyzer.d.ts +46 -0
  79. package/dist/brain/semantic-analyzer.d.ts.map +1 -0
  80. package/dist/brain/semantic-analyzer.js +496 -0
  81. package/dist/brain/semantic-analyzer.js.map +1 -0
  82. package/dist/brain/team-mode.d.ts +27 -0
  83. package/dist/brain/team-mode.d.ts.map +1 -0
  84. package/dist/brain/team-mode.js +262 -0
  85. package/dist/brain/team-mode.js.map +1 -0
  86. package/dist/brain/type-safety.d.ts +13 -0
  87. package/dist/brain/type-safety.d.ts.map +1 -0
  88. package/dist/brain/type-safety.js +217 -0
  89. package/dist/brain/type-safety.js.map +1 -0
  90. package/dist/brain/vuln-scanner.d.ts +16 -0
  91. package/dist/brain/vuln-scanner.d.ts.map +1 -0
  92. package/dist/brain/vuln-scanner.js +279 -0
  93. package/dist/brain/vuln-scanner.js.map +1 -0
  94. package/dist/cli.js +813 -3
  95. package/dist/cli.js.map +1 -1
  96. package/dist/index.d.ts +24 -1
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +26 -1
  99. package/dist/index.js.map +1 -1
  100. package/dist/types.d.ts +320 -0
  101. package/dist/types.d.ts.map +1 -1
  102. package/package.json +88 -77
@@ -0,0 +1,121 @@
1
+ // src/brain/project-config.ts — Load .shadow-brain.json project-level config
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ const CONFIG_FILENAMES = ['.shadow-brain.json', 'shadow-brain.json'];
5
+ const DEFAULT_CONFIG = { version: '1.0' };
6
+ export class ProjectConfigLoader {
7
+ constructor(projectDir) {
8
+ this.config = DEFAULT_CONFIG;
9
+ this.projectDir = projectDir;
10
+ }
11
+ load() {
12
+ // Check project directory
13
+ for (const name of CONFIG_FILENAMES) {
14
+ const fp = path.join(this.projectDir, name);
15
+ if (fs.existsSync(fp)) {
16
+ try {
17
+ this.config = { ...DEFAULT_CONFIG, ...JSON.parse(fs.readFileSync(fp, 'utf-8')) };
18
+ return this.config;
19
+ }
20
+ catch { /* use defaults */ }
21
+ }
22
+ }
23
+ // Check package.json for "shadowBrain" key
24
+ const pkgPath = path.join(this.projectDir, 'package.json');
25
+ if (fs.existsSync(pkgPath)) {
26
+ try {
27
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
28
+ if (pkg.shadowBrain) {
29
+ this.config = { ...DEFAULT_CONFIG, ...pkg.shadowBrain };
30
+ return this.config;
31
+ }
32
+ }
33
+ catch { /* ignore */ }
34
+ }
35
+ // Check pyproject.toml for Python projects
36
+ const pyPath = path.join(this.projectDir, 'pyproject.toml');
37
+ if (fs.existsSync(pyPath)) {
38
+ try {
39
+ const content = fs.readFileSync(pyPath, 'utf-8');
40
+ const match = content.match(/\[tool\.shadow-brain\]\s*([\s\S]*?)(?=\[|$)/);
41
+ if (match) {
42
+ // Simple TOML-like parse for basic config
43
+ const tomlSection = match[1];
44
+ const ignoreMatch = tomlSection.match(/ignore_paths\s*=\s*\[([^\]]*)\]/);
45
+ if (ignoreMatch) {
46
+ const paths = ignoreMatch[1].split(',').map(s => s.trim().replace(/"/g, '').replace(/'/g, ''));
47
+ this.config.rules = { ignorePaths: paths.filter(Boolean) };
48
+ }
49
+ }
50
+ }
51
+ catch { /* ignore */ }
52
+ }
53
+ return this.config;
54
+ }
55
+ get() {
56
+ return this.config;
57
+ }
58
+ getIgnorePatterns() {
59
+ const patterns = [];
60
+ // From project config
61
+ if (this.config.rules?.ignorePaths) {
62
+ patterns.push(...this.config.rules.ignorePaths);
63
+ }
64
+ if (this.config.rules?.ignorePatterns) {
65
+ patterns.push(...this.config.rules.ignorePatterns);
66
+ }
67
+ // From .shadow-brain-ignore file
68
+ const ignoreFile = path.join(this.projectDir, '.shadow-brain-ignore');
69
+ if (fs.existsSync(ignoreFile)) {
70
+ const content = fs.readFileSync(ignoreFile, 'utf-8');
71
+ for (const line of content.split('\n')) {
72
+ const trimmed = line.trim();
73
+ if (trimmed && !trimmed.startsWith('#')) {
74
+ patterns.push(trimmed);
75
+ }
76
+ }
77
+ }
78
+ // From .gitignore
79
+ const gitignore = path.join(this.projectDir, '.gitignore');
80
+ if (fs.existsSync(gitignore)) {
81
+ const content = fs.readFileSync(gitignore, 'utf-8');
82
+ for (const line of content.split('\n')) {
83
+ const trimmed = line.trim();
84
+ if (trimmed && !trimmed.startsWith('#')) {
85
+ patterns.push(trimmed);
86
+ }
87
+ }
88
+ }
89
+ return [...new Set(patterns)];
90
+ }
91
+ getCustomRules() {
92
+ return this.config.rules?.customRules || [];
93
+ }
94
+ getNotificationConfig() {
95
+ return this.config.notifications;
96
+ }
97
+ save(config) {
98
+ this.config = config;
99
+ const fp = path.join(this.projectDir, '.shadow-brain.json');
100
+ fs.writeFileSync(fp, JSON.stringify(config, null, 2), 'utf-8');
101
+ }
102
+ shouldIgnore(filePath) {
103
+ const patterns = this.getIgnorePatterns();
104
+ for (const pattern of patterns) {
105
+ if (pattern.startsWith('*')) {
106
+ if (filePath.endsWith(pattern.slice(1)))
107
+ return true;
108
+ }
109
+ else if (pattern.endsWith('*')) {
110
+ if (filePath.startsWith(pattern.slice(0, -1)))
111
+ return true;
112
+ }
113
+ else {
114
+ if (filePath.includes(pattern))
115
+ return true;
116
+ }
117
+ }
118
+ return false;
119
+ }
120
+ }
121
+ //# sourceMappingURL=project-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-config.js","sourceRoot":"","sources":["../../src/brain/project-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { SymbolInfo } from '../types.js';
2
+ export declare class SemanticAnalyzer {
3
+ private projectDir;
4
+ private symbols;
5
+ private importGraph;
6
+ private exportMap;
7
+ private usageMap;
8
+ constructor(projectDir: string);
9
+ /** Run full semantic analysis across the project */
10
+ analyzeProject(): Promise<{
11
+ symbols: Map<string, SymbolInfo[]>;
12
+ unusedExports: SymbolInfo[];
13
+ deadCode: SymbolInfo[];
14
+ }>;
15
+ /** Extract symbols from a single file */
16
+ extractSymbols(filePath: string): SymbolInfo[];
17
+ /** Get all source files in the project */
18
+ private getSourceFiles;
19
+ /** Extract import relationships from a file */
20
+ private extractImports;
21
+ /** Build a map of which files use each symbol */
22
+ private buildUsageMap;
23
+ /** Detect exported symbols that are never imported/used elsewhere */
24
+ private detectUnusedExports;
25
+ /** Detect dead code — private functions/methods never called within their file */
26
+ private detectDeadCode;
27
+ /** Check if a symbol is imported in any file */
28
+ private isSymbolImported;
29
+ /** Check if a file is an entry point (main, index, etc.) */
30
+ private isEntryPoint;
31
+ /** Check if a symbol name is a common/expected export */
32
+ private isCommonExport;
33
+ /** Check if a line is an export statement */
34
+ private isExported;
35
+ /** Strip comments from source code */
36
+ private stripComments;
37
+ /** Get 1-based line number from character offset */
38
+ private getLineNumber;
39
+ /** Detect language from file extension */
40
+ private getLanguageForExt;
41
+ /** Check if a name is a language builtin */
42
+ private isBuiltin;
43
+ /** Escape special regex characters */
44
+ private escapeRegex;
45
+ }
46
+ //# sourceMappingURL=semantic-analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic-analyzer.d.ts","sourceRoot":"","sources":["../../src/brain/semantic-analyzer.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA4GzC,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAwC;IACvD,OAAO,CAAC,WAAW,CAAuC;IAC1D,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,QAAQ,CAAuC;gBAE3C,UAAU,EAAE,MAAM;IAI9B,oDAAoD;IAC9C,cAAc,IAAI,OAAO,CAAC;QAC9B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;QACnC,aAAa,EAAE,UAAU,EAAE,CAAC;QAC5B,QAAQ,EAAE,UAAU,EAAE,CAAC;KACxB,CAAC;IAiCF,yCAAyC;IACzC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE;IAgJ9C,0CAA0C;IAC1C,OAAO,CAAC,cAAc;IAkCtB,+CAA+C;IAC/C,OAAO,CAAC,cAAc;IA2CtB,iDAAiD;IACjD,OAAO,CAAC,aAAa;IAqBrB,qEAAqE;IACrE,OAAO,CAAC,mBAAmB;IAwB3B,kFAAkF;IAClF,OAAO,CAAC,cAAc;IAgCtB,gDAAgD;IAChD,OAAO,CAAC,gBAAgB;IAOxB,4DAA4D;IAC5D,OAAO,CAAC,YAAY;IAOpB,yDAAyD;IACzD,OAAO,CAAC,cAAc;IAOtB,6CAA6C;IAC7C,OAAO,CAAC,UAAU;IAWlB,sCAAsC;IACtC,OAAO,CAAC,aAAa;IAUrB,oDAAoD;IACpD,OAAO,CAAC,aAAa;IAQrB,0CAA0C;IAC1C,OAAO,CAAC,iBAAiB;IAOzB,4CAA4C;IAC5C,OAAO,CAAC,SAAS;IAajB,sCAAsC;IACtC,OAAO,CAAC,WAAW;CAGpB"}
@@ -0,0 +1,496 @@
1
+ // src/brain/semantic-analyzer.ts — Semantic Code Analysis Engine
2
+ // v2.0.0 — Symbol extraction, unused export detection, dead code analysis
3
+ //
4
+ // Mathematical foundations:
5
+ // - TF-IDF for symbol importance scoring: tfidf(t,d) = tf(t,d) * log(N / df(t))
6
+ // - Set operations for reachability analysis (transitive closure of imports)
7
+ // - Kolmogorov complexity approximation for code redundancy detection
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+ const LANG_CONFIGS = {
11
+ typescript: {
12
+ extensions: ['.ts', '.tsx'],
13
+ commentSingle: '//',
14
+ commentMultiStart: '/*',
15
+ commentMultiEnd: '*/',
16
+ exportPatterns: [
17
+ /export\s+(?:default\s+)?(?:function|class|interface|type|enum|const|let|var|async\s+function)\s+(\w+)/g,
18
+ /export\s+\{([^}]+)\}/g,
19
+ ],
20
+ importPatterns: [
21
+ /import\s+(?:\{([^}]+)\}|(\w+))\s+from\s+['"]([^'"]+)['"]/g,
22
+ /import\s+['"]([^'"]+)['"]/g,
23
+ ],
24
+ functionPatterns: [
25
+ /(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(/g,
26
+ /(?:const|let)\s+(\w+)\s*=\s*(?:async\s+)?\(/g,
27
+ /(?:const|let)\s+(\w+)\s*=\s*(?:async\s+)?(?:\([^)]*\)|[^=])\s*=>\s*/g,
28
+ ],
29
+ classPatterns: [
30
+ /(?:export\s+)?(?:default\s+)?class\s+(\w+)/g,
31
+ ],
32
+ interfacePatterns: [
33
+ /(?:export\s+)?interface\s+(\w+)/g,
34
+ ],
35
+ typePatterns: [
36
+ /(?:export\s+)?type\s+(\w+)\s*(?:<|={|\s)/g,
37
+ ],
38
+ variablePatterns: [
39
+ /(?:export\s+)?(?:const|let|var)\s+(\w+)\s*[=:]/g,
40
+ ],
41
+ },
42
+ javascript: {
43
+ extensions: ['.js', '.jsx', '.mjs', '.cjs'],
44
+ commentSingle: '//',
45
+ commentMultiStart: '/*',
46
+ commentMultiEnd: '*/',
47
+ exportPatterns: [
48
+ /export\s+(?:default\s+)?(?:function|class|const|let|var|async\s+function)\s+(\w+)/g,
49
+ /export\s+\{([^}]+)\}/g,
50
+ /module\.exports\s*=\s*(\w+)/g,
51
+ /exports\.(\w+)\s*=/g,
52
+ ],
53
+ importPatterns: [
54
+ /import\s+(?:\{([^}]+)\}|(\w+))\s+from\s+['"]([^'"]+)['"]/g,
55
+ /import\s+['"]([^'"]+)['"]/g,
56
+ /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
57
+ ],
58
+ functionPatterns: [
59
+ /(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(/g,
60
+ /(?:const|let)\s+(\w+)\s*=\s*(?:async\s+)?\(/g,
61
+ /(?:const|let)\s+(\w+)\s*=\s*(?:async\s+)?(?:\([^)]*\)|[^=])\s*=>\s*/g,
62
+ ],
63
+ classPatterns: [
64
+ /(?:export\s+)?(?:default\s+)?class\s+(\w+)/g,
65
+ ],
66
+ interfacePatterns: [],
67
+ typePatterns: [],
68
+ variablePatterns: [
69
+ /(?:export\s+)?(?:const|let|var)\s+(\w+)\s*[=:]/g,
70
+ ],
71
+ },
72
+ python: {
73
+ extensions: ['.py', '.pyi'],
74
+ commentSingle: '#',
75
+ commentMultiStart: '"""',
76
+ commentMultiEnd: '"""',
77
+ exportPatterns: [
78
+ /(?:^|\n)(\w+)\s*=\s*/g,
79
+ ],
80
+ importPatterns: [
81
+ /import\s+(\w+)/g,
82
+ /from\s+([\w.]+)\s+import\s+(.+)/g,
83
+ ],
84
+ functionPatterns: [
85
+ /def\s+(\w+)\s*\(/g,
86
+ ],
87
+ classPatterns: [
88
+ /class\s+(\w+)/g,
89
+ ],
90
+ interfacePatterns: [],
91
+ typePatterns: [],
92
+ variablePatterns: [
93
+ /(\w+)\s*[:=]\s*/g,
94
+ ],
95
+ },
96
+ };
97
+ // ── Semantic Analyzer ──────────────────────────────────────────────────────────
98
+ export class SemanticAnalyzer {
99
+ constructor(projectDir) {
100
+ this.symbols = new Map();
101
+ this.importGraph = new Map(); // file -> imported symbols
102
+ this.exportMap = new Map(); // symbol name -> info
103
+ this.usageMap = new Map(); // symbol name -> files using it
104
+ this.projectDir = projectDir;
105
+ }
106
+ /** Run full semantic analysis across the project */
107
+ async analyzeProject() {
108
+ this.symbols.clear();
109
+ this.importGraph.clear();
110
+ this.exportMap.clear();
111
+ this.usageMap.clear();
112
+ const files = this.getSourceFiles();
113
+ // Phase 1: Extract symbols from all files
114
+ for (const file of files) {
115
+ const fileSymbols = this.extractSymbols(file);
116
+ if (fileSymbols.length > 0) {
117
+ this.symbols.set(file, fileSymbols);
118
+ }
119
+ }
120
+ // Phase 2: Build import graph
121
+ for (const file of files) {
122
+ this.extractImports(file);
123
+ }
124
+ // Phase 3: Build usage map
125
+ this.buildUsageMap(files);
126
+ // Phase 4: Detect unused exports
127
+ const unusedExports = this.detectUnusedExports();
128
+ // Phase 5: Detect dead code
129
+ const deadCode = this.detectDeadCode();
130
+ return { symbols: this.symbols, unusedExports, deadCode };
131
+ }
132
+ /** Extract symbols from a single file */
133
+ extractSymbols(filePath) {
134
+ const symbols = [];
135
+ const ext = path.extname(filePath);
136
+ const lang = this.getLanguageForExt(ext);
137
+ if (!lang)
138
+ return symbols;
139
+ const config = LANG_CONFIGS[lang];
140
+ if (!config)
141
+ return symbols;
142
+ let content;
143
+ try {
144
+ content = fs.readFileSync(filePath, 'utf-8');
145
+ }
146
+ catch {
147
+ return symbols;
148
+ }
149
+ const relativePath = path.relative(this.projectDir, filePath).replace(/\\/g, '/');
150
+ const lines = content.split('\n');
151
+ // Strip comments for analysis
152
+ const strippedContent = this.stripComments(content, config);
153
+ const strippedLines = strippedContent.split('\n');
154
+ // Extract functions
155
+ for (const pattern of config.functionPatterns) {
156
+ pattern.lastIndex = 0;
157
+ let match;
158
+ while ((match = pattern.exec(strippedContent)) !== null) {
159
+ const name = match[1];
160
+ const lineNum = this.getLineNumber(content, match.index);
161
+ if (name && !this.isBuiltin(name, lang)) {
162
+ const exported = this.isExported(lines, lineNum - 1);
163
+ symbols.push({
164
+ name,
165
+ type: 'function',
166
+ line: lineNum,
167
+ file: relativePath,
168
+ exported,
169
+ usedInFiles: [],
170
+ });
171
+ }
172
+ }
173
+ }
174
+ // Extract classes
175
+ for (const pattern of config.classPatterns) {
176
+ pattern.lastIndex = 0;
177
+ let match;
178
+ while ((match = pattern.exec(strippedContent)) !== null) {
179
+ const name = match[1];
180
+ const lineNum = this.getLineNumber(content, match.index);
181
+ if (name) {
182
+ const exported = this.isExported(lines, lineNum - 1);
183
+ symbols.push({
184
+ name,
185
+ type: 'class',
186
+ line: lineNum,
187
+ file: relativePath,
188
+ exported,
189
+ usedInFiles: [],
190
+ });
191
+ }
192
+ }
193
+ }
194
+ // Extract interfaces
195
+ for (const pattern of config.interfacePatterns) {
196
+ pattern.lastIndex = 0;
197
+ let match;
198
+ while ((match = pattern.exec(strippedContent)) !== null) {
199
+ const name = match[1];
200
+ const lineNum = this.getLineNumber(content, match.index);
201
+ if (name) {
202
+ const exported = this.isExported(lines, lineNum - 1);
203
+ symbols.push({
204
+ name,
205
+ type: 'interface',
206
+ line: lineNum,
207
+ file: relativePath,
208
+ exported,
209
+ usedInFiles: [],
210
+ });
211
+ }
212
+ }
213
+ }
214
+ // Extract types
215
+ for (const pattern of config.typePatterns) {
216
+ pattern.lastIndex = 0;
217
+ let match;
218
+ while ((match = pattern.exec(strippedContent)) !== null) {
219
+ const name = match[1];
220
+ const lineNum = this.getLineNumber(content, match.index);
221
+ if (name) {
222
+ const exported = this.isExported(lines, lineNum - 1);
223
+ symbols.push({
224
+ name,
225
+ type: 'type',
226
+ line: lineNum,
227
+ file: relativePath,
228
+ exported,
229
+ usedInFiles: [],
230
+ });
231
+ }
232
+ }
233
+ }
234
+ // Extract exported variables/constants
235
+ for (const pattern of config.exportPatterns) {
236
+ pattern.lastIndex = 0;
237
+ let match;
238
+ while ((match = pattern.exec(strippedContent)) !== null) {
239
+ const raw = match[1] || match[0];
240
+ const lineNum = this.getLineNumber(content, match.index);
241
+ // Handle `export { a, b, c }` pattern
242
+ if (raw.includes(',') && !raw.includes('function') && !raw.includes('class')) {
243
+ const names = raw.split(',').map(n => n.trim().split(/\s+as\s+/)[0].trim()).filter(Boolean);
244
+ for (const name of names) {
245
+ if (!symbols.some(s => s.name === name && s.file === relativePath)) {
246
+ symbols.push({
247
+ name,
248
+ type: 'variable',
249
+ line: lineNum,
250
+ file: relativePath,
251
+ exported: true,
252
+ usedInFiles: [],
253
+ });
254
+ }
255
+ }
256
+ }
257
+ }
258
+ }
259
+ // Deduplicate — same name+file+type
260
+ const seen = new Set();
261
+ return symbols.filter(s => {
262
+ const key = `${s.name}:${s.file}:${s.type}`;
263
+ if (seen.has(key))
264
+ return false;
265
+ seen.add(key);
266
+ return true;
267
+ });
268
+ }
269
+ /** Get all source files in the project */
270
+ getSourceFiles() {
271
+ const files = [];
272
+ const extensions = new Set();
273
+ for (const config of Object.values(LANG_CONFIGS)) {
274
+ for (const ext of config.extensions)
275
+ extensions.add(ext);
276
+ }
277
+ const ignoreDirs = new Set([
278
+ 'node_modules', '.git', 'dist', 'build', '.next', '__pycache__',
279
+ '.cache', 'coverage', '.nyc_output', 'vendor', 'target', 'bin',
280
+ ]);
281
+ const walk = (dir) => {
282
+ try {
283
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
284
+ for (const entry of entries) {
285
+ if (entry.isDirectory()) {
286
+ if (!ignoreDirs.has(entry.name) && !entry.name.startsWith('.')) {
287
+ walk(path.join(dir, entry.name));
288
+ }
289
+ }
290
+ else if (entry.isFile()) {
291
+ const ext = path.extname(entry.name);
292
+ if (extensions.has(ext)) {
293
+ files.push(path.join(dir, entry.name));
294
+ }
295
+ }
296
+ }
297
+ }
298
+ catch { /* skip inaccessible dirs */ }
299
+ };
300
+ walk(this.projectDir);
301
+ return files;
302
+ }
303
+ /** Extract import relationships from a file */
304
+ extractImports(filePath) {
305
+ const importedSymbols = new Set();
306
+ let content;
307
+ try {
308
+ content = fs.readFileSync(filePath, 'utf-8');
309
+ }
310
+ catch {
311
+ return;
312
+ }
313
+ const ext = path.extname(filePath);
314
+ const lang = this.getLanguageForExt(ext);
315
+ if (!lang)
316
+ return;
317
+ const config = LANG_CONFIGS[lang];
318
+ if (!config)
319
+ return;
320
+ const relativePath = path.relative(this.projectDir, filePath).replace(/\\/g, '/');
321
+ for (const pattern of config.importPatterns) {
322
+ pattern.lastIndex = 0;
323
+ let match;
324
+ while ((match = pattern.exec(content)) !== null) {
325
+ // Named imports: { A, B, C }
326
+ if (match[1]) {
327
+ const names = match[1].split(',').map(n => {
328
+ const parts = n.trim().split(/\s+as\s+/);
329
+ return parts[0].trim();
330
+ }).filter(Boolean);
331
+ for (const name of names) {
332
+ importedSymbols.add(name);
333
+ }
334
+ }
335
+ // Default import
336
+ if (match[2]) {
337
+ importedSymbols.add(match[2]);
338
+ }
339
+ }
340
+ }
341
+ if (importedSymbols.size > 0) {
342
+ this.importGraph.set(relativePath, importedSymbols);
343
+ }
344
+ }
345
+ /** Build a map of which files use each symbol */
346
+ buildUsageMap(files) {
347
+ // For each exported symbol, search for usage across all files
348
+ for (const [name, info] of this.exportMap) {
349
+ const usedIn = [];
350
+ for (const file of files) {
351
+ const relativePath = path.relative(this.projectDir, file).replace(/\\/g, '/');
352
+ if (relativePath === info.file)
353
+ continue; // Skip the defining file
354
+ try {
355
+ const content = fs.readFileSync(file, 'utf-8');
356
+ // Simple heuristic: symbol name appears as a word boundary
357
+ const regex = new RegExp(`\\b${this.escapeRegex(name)}\\b`);
358
+ if (regex.test(content)) {
359
+ usedIn.push(relativePath);
360
+ }
361
+ }
362
+ catch { /* skip */ }
363
+ }
364
+ info.usedInFiles = usedIn;
365
+ }
366
+ }
367
+ /** Detect exported symbols that are never imported/used elsewhere */
368
+ detectUnusedExports() {
369
+ const unused = [];
370
+ for (const [file, symbols] of this.symbols) {
371
+ for (const symbol of symbols) {
372
+ if (!symbol.exported)
373
+ continue;
374
+ // Check if used in any other file
375
+ const imported = this.isSymbolImported(symbol.name, file);
376
+ const usedDirectly = symbol.usedInFiles && symbol.usedInFiles.length > 0;
377
+ if (!imported && !usedDirectly) {
378
+ // Exclude entry points and common patterns
379
+ if (this.isEntryPoint(file))
380
+ continue;
381
+ if (this.isCommonExport(symbol.name))
382
+ continue;
383
+ unused.push(symbol);
384
+ }
385
+ }
386
+ }
387
+ return unused;
388
+ }
389
+ /** Detect dead code — private functions/methods never called within their file */
390
+ detectDeadCode() {
391
+ const dead = [];
392
+ for (const [file, symbols] of this.symbols) {
393
+ try {
394
+ const fullPath = path.join(this.projectDir, file);
395
+ const content = fs.readFileSync(fullPath, 'utf-8');
396
+ for (const symbol of symbols) {
397
+ // Only check non-exported symbols
398
+ if (symbol.exported)
399
+ continue;
400
+ // Count references to this symbol name in the file
401
+ const escapedName = this.escapeRegex(symbol.name);
402
+ // Match as a word boundary, excluding the definition itself
403
+ const refRegex = new RegExp(`\\b${escapedName}\\b`, 'g');
404
+ const matches = content.match(refRegex);
405
+ // If only referenced once (the definition), it's dead code
406
+ // But allow class constructors and method definitions
407
+ if (matches && matches.length <= 1) {
408
+ if (symbol.type === 'function' || symbol.type === 'variable') {
409
+ dead.push(symbol);
410
+ }
411
+ }
412
+ }
413
+ }
414
+ catch { /* skip */ }
415
+ }
416
+ return dead;
417
+ }
418
+ /** Check if a symbol is imported in any file */
419
+ isSymbolImported(symbolName, sourceFile) {
420
+ for (const [, imported] of this.importGraph) {
421
+ if (imported.has(symbolName))
422
+ return true;
423
+ }
424
+ return false;
425
+ }
426
+ /** Check if a file is an entry point (main, index, etc.) */
427
+ isEntryPoint(file) {
428
+ const base = path.basename(file);
429
+ const entryPoints = ['index.ts', 'index.js', 'main.ts', 'main.js', 'cli.ts', 'cli.js',
430
+ 'server.ts', 'server.js', 'app.ts', 'app.js', '__init__.py', 'manage.py'];
431
+ return entryPoints.includes(base);
432
+ }
433
+ /** Check if a symbol name is a common/expected export */
434
+ isCommonExport(name) {
435
+ const common = ['default', 'config', 'Config', 'OPTIONS', 'VERSION', 'VERSION',
436
+ 'plugin', 'Plugin', 'middleware', 'setup', 'install', 'activate',
437
+ 'deactivate', 'configure', 'handler', 'Handler', 'router', 'Router'];
438
+ return common.includes(name);
439
+ }
440
+ /** Check if a line is an export statement */
441
+ isExported(lines, lineIndex) {
442
+ // Check current line and a few lines above for export keyword
443
+ for (let i = Math.max(0, lineIndex - 2); i <= Math.min(lines.length - 1, lineIndex + 1); i++) {
444
+ const line = lines[i].trim();
445
+ if (line.startsWith('export ') || line.startsWith('export{') || line.includes('module.exports')) {
446
+ return true;
447
+ }
448
+ }
449
+ return false;
450
+ }
451
+ /** Strip comments from source code */
452
+ stripComments(content, config) {
453
+ let result = content;
454
+ // Multi-line comments
455
+ result = result.replace(/\/\*[\s\S]*?\*\//g, '');
456
+ // Single-line comments
457
+ result = result.replace(/\/\/.*$/gm, '');
458
+ // Strings (preserve them to avoid false matches inside strings)
459
+ return result;
460
+ }
461
+ /** Get 1-based line number from character offset */
462
+ getLineNumber(content, offset) {
463
+ let line = 1;
464
+ for (let i = 0; i < offset && i < content.length; i++) {
465
+ if (content[i] === '\n')
466
+ line++;
467
+ }
468
+ return line;
469
+ }
470
+ /** Detect language from file extension */
471
+ getLanguageForExt(ext) {
472
+ for (const [lang, config] of Object.entries(LANG_CONFIGS)) {
473
+ if (config.extensions.includes(ext))
474
+ return lang;
475
+ }
476
+ return null;
477
+ }
478
+ /** Check if a name is a language builtin */
479
+ isBuiltin(name, lang) {
480
+ const builtins = {
481
+ typescript: new Set(['constructor', 'toString', 'valueOf', 'hasOwnProperty',
482
+ 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString']),
483
+ javascript: new Set(['constructor', 'toString', 'valueOf', 'hasOwnProperty',
484
+ 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString']),
485
+ python: new Set(['__init__', '__str__', '__repr__', '__len__', '__getitem__',
486
+ '__setitem__', '__delitem__', '__iter__', '__next__', '__call__',
487
+ '__enter__', '__exit__', '__eq__', '__hash__', '__bool__']),
488
+ };
489
+ return builtins[lang]?.has(name) ?? false;
490
+ }
491
+ /** Escape special regex characters */
492
+ escapeRegex(str) {
493
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
494
+ }
495
+ }
496
+ //# sourceMappingURL=semantic-analyzer.js.map