archicore 0.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 (118) hide show
  1. package/README.md +530 -0
  2. package/dist/analyzers/dead-code.d.ts +95 -0
  3. package/dist/analyzers/dead-code.js +327 -0
  4. package/dist/analyzers/duplication.d.ts +90 -0
  5. package/dist/analyzers/duplication.js +344 -0
  6. package/dist/analyzers/security.d.ts +79 -0
  7. package/dist/analyzers/security.js +484 -0
  8. package/dist/architecture/index.d.ts +35 -0
  9. package/dist/architecture/index.js +249 -0
  10. package/dist/cli/commands/analyzers.d.ts +6 -0
  11. package/dist/cli/commands/analyzers.js +431 -0
  12. package/dist/cli/commands/export.d.ts +6 -0
  13. package/dist/cli/commands/export.js +78 -0
  14. package/dist/cli/commands/index.d.ts +8 -0
  15. package/dist/cli/commands/index.js +8 -0
  16. package/dist/cli/commands/init.d.ts +26 -0
  17. package/dist/cli/commands/init.js +140 -0
  18. package/dist/cli/commands/interactive.d.ts +7 -0
  19. package/dist/cli/commands/interactive.js +522 -0
  20. package/dist/cli/commands/projects.d.ts +6 -0
  21. package/dist/cli/commands/projects.js +249 -0
  22. package/dist/cli/index.d.ts +7 -0
  23. package/dist/cli/index.js +7 -0
  24. package/dist/cli/ui/box.d.ts +17 -0
  25. package/dist/cli/ui/box.js +62 -0
  26. package/dist/cli/ui/colors.d.ts +49 -0
  27. package/dist/cli/ui/colors.js +86 -0
  28. package/dist/cli/ui/index.d.ts +9 -0
  29. package/dist/cli/ui/index.js +9 -0
  30. package/dist/cli/ui/prompt.d.ts +34 -0
  31. package/dist/cli/ui/prompt.js +122 -0
  32. package/dist/cli/ui/spinner.d.ts +29 -0
  33. package/dist/cli/ui/spinner.js +80 -0
  34. package/dist/cli/ui/table.d.ts +33 -0
  35. package/dist/cli/ui/table.js +84 -0
  36. package/dist/cli/utils/config.d.ts +23 -0
  37. package/dist/cli/utils/config.js +73 -0
  38. package/dist/cli/utils/index.d.ts +6 -0
  39. package/dist/cli/utils/index.js +6 -0
  40. package/dist/cli/utils/session.d.ts +27 -0
  41. package/dist/cli/utils/session.js +117 -0
  42. package/dist/cli.d.ts +8 -0
  43. package/dist/cli.js +295 -0
  44. package/dist/code-index/ast-parser.d.ts +16 -0
  45. package/dist/code-index/ast-parser.js +330 -0
  46. package/dist/code-index/dependency-graph.d.ts +16 -0
  47. package/dist/code-index/dependency-graph.js +161 -0
  48. package/dist/code-index/index.d.ts +44 -0
  49. package/dist/code-index/index.js +124 -0
  50. package/dist/code-index/symbol-extractor.d.ts +13 -0
  51. package/dist/code-index/symbol-extractor.js +150 -0
  52. package/dist/export/index.d.ts +92 -0
  53. package/dist/export/index.js +676 -0
  54. package/dist/github/github-service.d.ts +146 -0
  55. package/dist/github/github-service.js +609 -0
  56. package/dist/impact-engine/index.d.ts +25 -0
  57. package/dist/impact-engine/index.js +284 -0
  58. package/dist/index.d.ts +60 -0
  59. package/dist/index.js +149 -0
  60. package/dist/metrics/index.d.ts +136 -0
  61. package/dist/metrics/index.js +525 -0
  62. package/dist/orchestrator/deepseek-optimizer.d.ts +67 -0
  63. package/dist/orchestrator/deepseek-optimizer.js +320 -0
  64. package/dist/orchestrator/index.d.ts +34 -0
  65. package/dist/orchestrator/index.js +305 -0
  66. package/dist/pr-guardian/index.d.ts +143 -0
  67. package/dist/pr-guardian/index.js +553 -0
  68. package/dist/refactoring/index.d.ts +108 -0
  69. package/dist/refactoring/index.js +580 -0
  70. package/dist/rules-engine/index.d.ts +129 -0
  71. package/dist/rules-engine/index.js +482 -0
  72. package/dist/semantic-memory/embedding-service.d.ts +24 -0
  73. package/dist/semantic-memory/embedding-service.js +120 -0
  74. package/dist/semantic-memory/index.d.ts +45 -0
  75. package/dist/semantic-memory/index.js +206 -0
  76. package/dist/semantic-memory/vector-store.d.ts +27 -0
  77. package/dist/semantic-memory/vector-store.js +166 -0
  78. package/dist/server/index.d.ts +28 -0
  79. package/dist/server/index.js +141 -0
  80. package/dist/server/middleware/api-auth.d.ts +43 -0
  81. package/dist/server/middleware/api-auth.js +256 -0
  82. package/dist/server/routes/admin.d.ts +5 -0
  83. package/dist/server/routes/admin.js +123 -0
  84. package/dist/server/routes/api.d.ts +7 -0
  85. package/dist/server/routes/api.js +362 -0
  86. package/dist/server/routes/auth.d.ts +16 -0
  87. package/dist/server/routes/auth.js +191 -0
  88. package/dist/server/routes/developer.d.ts +8 -0
  89. package/dist/server/routes/developer.js +439 -0
  90. package/dist/server/routes/github.d.ts +7 -0
  91. package/dist/server/routes/github.js +495 -0
  92. package/dist/server/routes/upload.d.ts +7 -0
  93. package/dist/server/routes/upload.js +196 -0
  94. package/dist/server/services/api-key-service.d.ts +81 -0
  95. package/dist/server/services/api-key-service.js +281 -0
  96. package/dist/server/services/auth-service.d.ts +40 -0
  97. package/dist/server/services/auth-service.js +315 -0
  98. package/dist/server/services/project-service.d.ts +123 -0
  99. package/dist/server/services/project-service.js +533 -0
  100. package/dist/server/services/token-service.d.ts +107 -0
  101. package/dist/server/services/token-service.js +416 -0
  102. package/dist/server/services/upload-service.d.ts +93 -0
  103. package/dist/server/services/upload-service.js +464 -0
  104. package/dist/types/api.d.ts +188 -0
  105. package/dist/types/api.js +86 -0
  106. package/dist/types/github.d.ts +335 -0
  107. package/dist/types/github.js +5 -0
  108. package/dist/types/index.d.ts +265 -0
  109. package/dist/types/index.js +32 -0
  110. package/dist/types/user.d.ts +69 -0
  111. package/dist/types/user.js +42 -0
  112. package/dist/utils/file-utils.d.ts +20 -0
  113. package/dist/utils/file-utils.js +163 -0
  114. package/dist/utils/logger.d.ts +17 -0
  115. package/dist/utils/logger.js +41 -0
  116. package/dist/watcher/index.d.ts +125 -0
  117. package/dist/watcher/index.js +397 -0
  118. package/package.json +71 -0
package/dist/cli.js ADDED
@@ -0,0 +1,295 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ArchiCore CLI
4
+ *
5
+ * AI Software Architect - Interactive CLI
6
+ */
7
+ import 'dotenv/config';
8
+ import { Command } from 'commander';
9
+ import { AIArhitector } from './index.js';
10
+ import { Logger, LogLevel } from './utils/logger.js';
11
+ import chalk from 'chalk';
12
+ import ora from 'ora';
13
+ import { writeFile } from 'fs/promises';
14
+ // Import new CLI modules
15
+ import { registerAnalyzerCommands, registerExportCommand, startInteractiveMode, initProject, } from './cli/index.js';
16
+ const program = new Command();
17
+ program
18
+ .name('archicore')
19
+ .description('ArchiCore - AI Software Architect')
20
+ .version('0.1.0');
21
+ // Init command - initialize project in current directory
22
+ program
23
+ .command('init')
24
+ .description('Initialize ArchiCore in current directory')
25
+ .option('-n, --name <name>', 'Project name')
26
+ .action(async (options) => {
27
+ await initProject(process.cwd(), options.name);
28
+ });
29
+ // Register modular commands
30
+ registerAnalyzerCommands(program);
31
+ registerExportCommand(program);
32
+ // Interactive mode (default when no command)
33
+ program
34
+ .command('chat', { isDefault: false })
35
+ .description('Start interactive mode')
36
+ .action(async () => {
37
+ await startInteractiveMode();
38
+ });
39
+ // Legacy commands (keeping for backwards compatibility)
40
+ program
41
+ .command('index')
42
+ .description('Index a project (legacy)')
43
+ .option('-d, --dir <directory>', 'Project directory', process.cwd())
44
+ .option('-v, --verbose', 'Verbose output')
45
+ .action(async (options) => {
46
+ if (options.verbose) {
47
+ Logger.setLevel(LogLevel.DEBUG);
48
+ }
49
+ const spinner = ora('Initializing AIArhitector...').start();
50
+ try {
51
+ const ai = new AIArhitector({
52
+ rootDir: options.dir,
53
+ llm: {
54
+ provider: 'deepseek',
55
+ model: 'deepseek-chat',
56
+ temperature: 0.1,
57
+ maxTokens: 4096
58
+ },
59
+ vectorStore: {
60
+ url: process.env.QDRANT_URL || 'http://localhost:6333',
61
+ apiKey: process.env.QDRANT_API_KEY,
62
+ collectionName: 'aiarhitector'
63
+ }
64
+ });
65
+ spinner.text = 'Indexing project...';
66
+ await ai.initialize();
67
+ const stats = ai.getStatistics();
68
+ spinner.succeed('Project indexed!');
69
+ console.log(chalk.green('\n📊 Statistics:\n'));
70
+ console.log(` Files: ${stats.codeIndex.totalFiles}`);
71
+ console.log(` Symbols: ${stats.codeIndex.totalSymbols}`);
72
+ console.log(` Nodes: ${stats.codeIndex.totalNodes}`);
73
+ console.log(` Edges: ${stats.codeIndex.totalEdges}`);
74
+ console.log(chalk.cyan('\n🔤 Symbols by kind:\n'));
75
+ for (const [kind, count] of Object.entries(stats.codeIndex.symbolsByKind)) {
76
+ console.log(` ${kind}: ${count}`);
77
+ }
78
+ const memStats = await stats.semanticMemory;
79
+ if (memStats) {
80
+ console.log(chalk.magenta('\n🧠 Semantic memory:\n'));
81
+ console.log(` Vectors: ${memStats.vectorsCount}`);
82
+ console.log(` Points: ${memStats.pointsCount}`);
83
+ }
84
+ }
85
+ catch (error) {
86
+ spinner.fail('Indexing error');
87
+ Logger.error('Error', error);
88
+ process.exit(1);
89
+ }
90
+ });
91
+ program
92
+ .command('analyze')
93
+ .description('Analyze impact of changes (legacy)')
94
+ .requiredOption('-d, --description <desc>', 'Change description')
95
+ .option('--files <files>', 'Files (comma-separated)')
96
+ .option('--symbols <symbols>', 'Symbols (comma-separated)')
97
+ .option('--type <type>', 'Change type (add|modify|delete|refactor)', 'modify')
98
+ .option('--output <file>', 'Output file')
99
+ .action(async (options) => {
100
+ const spinner = ora('Analyzing impact...').start();
101
+ try {
102
+ const ai = new AIArhitector({
103
+ rootDir: process.cwd(),
104
+ llm: {
105
+ provider: 'deepseek',
106
+ model: 'deepseek-chat',
107
+ temperature: 0.1,
108
+ maxTokens: 4096
109
+ },
110
+ vectorStore: {
111
+ url: process.env.QDRANT_URL || 'http://localhost:6333',
112
+ apiKey: process.env.QDRANT_API_KEY,
113
+ collectionName: 'aiarhitector'
114
+ }
115
+ });
116
+ spinner.text = 'Loading index...';
117
+ await ai.initialize();
118
+ const change = {
119
+ type: options.type,
120
+ description: options.description,
121
+ files: options.files ? options.files.split(',') : [],
122
+ symbols: options.symbols ? options.symbols.split(',') : []
123
+ };
124
+ spinner.text = 'Analyzing...';
125
+ const impact = await ai.analyzeChange(change);
126
+ spinner.succeed('Analysis complete!');
127
+ console.log(chalk.bold('\n📋 IMPACT ANALYSIS REPORT\n'));
128
+ console.log(chalk.yellow('⚠ AFFECTED COMPONENTS:\n'));
129
+ console.log(` Total: ${impact.affectedNodes.length}`);
130
+ const byLevel = {
131
+ critical: impact.affectedNodes.filter(n => n.impactLevel === 'critical'),
132
+ high: impact.affectedNodes.filter(n => n.impactLevel === 'high'),
133
+ medium: impact.affectedNodes.filter(n => n.impactLevel === 'medium'),
134
+ low: impact.affectedNodes.filter(n => n.impactLevel === 'low')
135
+ };
136
+ console.log(` 🔴 Critical: ${byLevel.critical.length}`);
137
+ console.log(` 🟠 High: ${byLevel.high.length}`);
138
+ console.log(` 🟡 Medium: ${byLevel.medium.length}`);
139
+ console.log(` 🟢 Low: ${byLevel.low.length}`);
140
+ if (byLevel.critical.length > 0) {
141
+ console.log(chalk.red('\n🔴 CRITICAL COMPONENTS:\n'));
142
+ for (const node of byLevel.critical.slice(0, 10)) {
143
+ console.log(` - ${node.name} (${node.filePath})`);
144
+ console.log(` ${node.reason}`);
145
+ }
146
+ }
147
+ console.log(chalk.red('\n⚠ RISKS:\n'));
148
+ for (const risk of impact.risks) {
149
+ console.log(` [${risk.severity.toUpperCase()}] ${risk.description}`);
150
+ if (risk.mitigation) {
151
+ console.log(` 💡 ${risk.mitigation}`);
152
+ }
153
+ console.log('');
154
+ }
155
+ console.log(chalk.blue('\n✅ RECOMMENDATIONS:\n'));
156
+ for (const rec of impact.recommendations) {
157
+ console.log(` [${rec.priority.toUpperCase()}] ${rec.description}`);
158
+ if (rec.details) {
159
+ console.log(` ${rec.details}`);
160
+ }
161
+ console.log('');
162
+ }
163
+ if (options.output) {
164
+ const report = {
165
+ change,
166
+ impact,
167
+ timestamp: new Date().toISOString()
168
+ };
169
+ await writeFile(options.output, JSON.stringify(report, null, 2));
170
+ console.log(chalk.green(`\n✓ Report saved: ${options.output}`));
171
+ }
172
+ }
173
+ catch (error) {
174
+ spinner.fail('Analysis error');
175
+ Logger.error('Error', error);
176
+ process.exit(1);
177
+ }
178
+ });
179
+ program
180
+ .command('search')
181
+ .description('Semantic code search (legacy)')
182
+ .requiredOption('-q, --query <query>', 'Search query')
183
+ .option('-l, --limit <limit>', 'Number of results', '10')
184
+ .action(async (options) => {
185
+ const spinner = ora('Searching...').start();
186
+ try {
187
+ const ai = new AIArhitector({
188
+ rootDir: process.cwd(),
189
+ llm: {
190
+ provider: 'deepseek',
191
+ model: 'deepseek-chat',
192
+ temperature: 0.1,
193
+ maxTokens: 4096
194
+ },
195
+ vectorStore: {
196
+ url: process.env.QDRANT_URL || 'http://localhost:6333',
197
+ apiKey: process.env.QDRANT_API_KEY,
198
+ collectionName: 'aiarhitector'
199
+ }
200
+ });
201
+ await ai.initialize();
202
+ spinner.text = 'Executing search...';
203
+ const results = await ai.searchCode(options.query, parseInt(options.limit));
204
+ spinner.succeed(`Found ${results.length} results`);
205
+ console.log(chalk.bold('\n🔍 SEARCH RESULTS:\n'));
206
+ for (const [index, result] of results.entries()) {
207
+ console.log(chalk.cyan(`${index + 1}. ${result.chunk.metadata.filePath}:${result.chunk.metadata.startLine}`));
208
+ console.log(` Relevance: ${(result.score * 100).toFixed(2)}%`);
209
+ console.log(` ${result.context}\n`);
210
+ }
211
+ }
212
+ catch (error) {
213
+ spinner.fail('Search error');
214
+ Logger.error('Error', error);
215
+ process.exit(1);
216
+ }
217
+ });
218
+ program
219
+ .command('ask')
220
+ .description('Ask about architecture (legacy)')
221
+ .requiredOption('-q, --question <question>', 'Question')
222
+ .action(async (options) => {
223
+ const spinner = ora('Analyzing...').start();
224
+ try {
225
+ const ai = new AIArhitector({
226
+ rootDir: process.cwd(),
227
+ llm: {
228
+ provider: 'deepseek',
229
+ model: 'deepseek-chat',
230
+ temperature: 0.3,
231
+ maxTokens: 4096
232
+ },
233
+ vectorStore: {
234
+ url: process.env.QDRANT_URL || 'http://localhost:6333',
235
+ apiKey: process.env.QDRANT_API_KEY,
236
+ collectionName: 'aiarhitector'
237
+ }
238
+ });
239
+ await ai.initialize();
240
+ spinner.text = 'Getting AI response...';
241
+ const answer = await ai.askQuestion(options.question);
242
+ spinner.succeed('Answer received!');
243
+ console.log(chalk.bold('\n💬 AI ARCHITECT:\n'));
244
+ console.log(answer);
245
+ }
246
+ catch (error) {
247
+ spinner.fail('Error');
248
+ Logger.error('Error', error);
249
+ process.exit(1);
250
+ }
251
+ });
252
+ program
253
+ .command('docs')
254
+ .description('Generate documentation (legacy)')
255
+ .option('-o, --output <file>', 'Output file', 'ARCHITECTURE.md')
256
+ .action(async (options) => {
257
+ const spinner = ora('Generating documentation...').start();
258
+ try {
259
+ const ai = new AIArhitector({
260
+ rootDir: process.cwd(),
261
+ llm: {
262
+ provider: 'deepseek',
263
+ model: 'deepseek-chat',
264
+ temperature: 0.3,
265
+ maxTokens: 8192
266
+ },
267
+ vectorStore: {
268
+ url: process.env.QDRANT_URL || 'http://localhost:6333',
269
+ apiKey: process.env.QDRANT_API_KEY,
270
+ collectionName: 'aiarhitector'
271
+ }
272
+ });
273
+ await ai.initialize();
274
+ spinner.text = 'Generating documentation...';
275
+ const docs = await ai.generateDocumentation();
276
+ await writeFile(options.output, docs);
277
+ spinner.succeed(`Documentation saved: ${options.output}`);
278
+ }
279
+ catch (error) {
280
+ spinner.fail('Documentation generation error');
281
+ Logger.error('Error', error);
282
+ process.exit(1);
283
+ }
284
+ });
285
+ // Handle no command - start interactive mode
286
+ if (process.argv.length === 2) {
287
+ startInteractiveMode().catch((error) => {
288
+ console.error('Failed to start interactive mode:', error);
289
+ process.exit(1);
290
+ });
291
+ }
292
+ else {
293
+ program.parse();
294
+ }
295
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1,16 @@
1
+ import Parser from 'tree-sitter';
2
+ import { ASTNode, Location } from '../types/index.js';
3
+ export declare class ASTParser {
4
+ private parsers;
5
+ constructor();
6
+ private initializeParsers;
7
+ parseFile(filePath: string): Promise<ASTNode | null>;
8
+ private parseWithRegex;
9
+ private inferTypeFromPattern;
10
+ private extractVueScript;
11
+ parseProject(rootDir: string): Promise<Map<string, ASTNode>>;
12
+ private convertToASTNode;
13
+ private extractName;
14
+ extractLocation(node: Parser.SyntaxNode, filePath: string): Location;
15
+ }
16
+ //# sourceMappingURL=ast-parser.d.ts.map
@@ -0,0 +1,330 @@
1
+ import Parser from 'tree-sitter';
2
+ import TypeScript from 'tree-sitter-typescript';
3
+ import JavaScript from 'tree-sitter-javascript';
4
+ import Python from 'tree-sitter-python';
5
+ import { FileUtils } from '../utils/file-utils.js';
6
+ import { Logger } from '../utils/logger.js';
7
+ // Tree-sitter has a ~32KB limit for parsing
8
+ const MAX_TREE_SITTER_SIZE = 30000;
9
+ export class ASTParser {
10
+ parsers;
11
+ constructor() {
12
+ this.parsers = new Map();
13
+ this.initializeParsers();
14
+ }
15
+ initializeParsers() {
16
+ const tsParser = new Parser();
17
+ tsParser.setLanguage(TypeScript.typescript);
18
+ this.parsers.set('typescript', tsParser);
19
+ const jsParser = new Parser();
20
+ jsParser.setLanguage(JavaScript);
21
+ this.parsers.set('javascript', jsParser);
22
+ const pyParser = new Parser();
23
+ pyParser.setLanguage(Python);
24
+ this.parsers.set('python', pyParser);
25
+ Logger.debug('AST parsers initialized');
26
+ }
27
+ async parseFile(filePath) {
28
+ try {
29
+ let content = await FileUtils.readFileContent(filePath);
30
+ let language = FileUtils.getLanguageFromExtension(filePath);
31
+ // Vue SFC: extract <script> section and parse as TS/JS
32
+ if (language === 'vue') {
33
+ const scriptResult = this.extractVueScript(content);
34
+ if (!scriptResult) {
35
+ return null; // No script section found
36
+ }
37
+ content = scriptResult.content;
38
+ language = scriptResult.isTypeScript ? 'typescript' : 'javascript';
39
+ }
40
+ // Validate content before parsing
41
+ if (!content || typeof content !== 'string' || !content.trim()) {
42
+ Logger.warn(`Empty or invalid content in: ${filePath}`);
43
+ return null;
44
+ }
45
+ // For large files, use regex-based fallback
46
+ if (content.length > MAX_TREE_SITTER_SIZE) {
47
+ Logger.debug(`Large file (${content.length} bytes), using regex fallback: ${filePath}`);
48
+ return this.parseWithRegex(content, filePath, language);
49
+ }
50
+ const parser = this.parsers.get(language);
51
+ if (!parser) {
52
+ // Use regex fallback for unsupported languages
53
+ return this.parseWithRegex(content, filePath, language);
54
+ }
55
+ const tree = parser.parse(content);
56
+ return this.convertToASTNode(tree.rootNode, filePath);
57
+ }
58
+ catch (error) {
59
+ // Try regex fallback on parse error
60
+ try {
61
+ const content = await FileUtils.readFileContent(filePath);
62
+ const language = FileUtils.getLanguageFromExtension(filePath);
63
+ Logger.debug(`Tree-sitter failed, using regex fallback: ${filePath}`);
64
+ return this.parseWithRegex(content, filePath, language);
65
+ }
66
+ catch {
67
+ Logger.error(`Failed to parse ${filePath}`, error);
68
+ return null;
69
+ }
70
+ }
71
+ }
72
+ // Regex-based fallback for large files or unsupported languages
73
+ parseWithRegex(content, filePath, language) {
74
+ const children = [];
75
+ const lines = content.split('\n');
76
+ // Patterns for different languages
77
+ const patterns = {
78
+ javascript: [
79
+ /^(?:export\s+)?(?:async\s+)?function\s+(\w+)/,
80
+ /^(?:export\s+)?class\s+(\w+)/,
81
+ /^(?:export\s+)?const\s+(\w+)\s*=/,
82
+ /^(?:export\s+)?let\s+(\w+)\s*=/,
83
+ /(\w+)\s*[=:]\s*(?:async\s+)?\([^)]*\)\s*(?:=>|{)/,
84
+ /(\w+)\s*\([^)]*\)\s*{/
85
+ ],
86
+ typescript: [
87
+ /^(?:export\s+)?(?:async\s+)?function\s+(\w+)/,
88
+ /^(?:export\s+)?class\s+(\w+)/,
89
+ /^(?:export\s+)?interface\s+(\w+)/,
90
+ /^(?:export\s+)?type\s+(\w+)\s*=/,
91
+ /^(?:export\s+)?const\s+(\w+)\s*[=:]/,
92
+ /^(?:export\s+)?enum\s+(\w+)/,
93
+ /(\w+)\s*[=:]\s*(?:async\s+)?\([^)]*\)\s*(?:=>|{)/,
94
+ /(\w+)\s*\([^)]*\)\s*(?::\s*\w+)?\s*{/
95
+ ],
96
+ python: [
97
+ /^(?:async\s+)?def\s+(\w+)/,
98
+ /^class\s+(\w+)/
99
+ ],
100
+ vue: [
101
+ /^(?:export\s+)?(?:async\s+)?function\s+(\w+)/,
102
+ /^(?:export\s+)?const\s+(\w+)\s*=/,
103
+ /(\w+)\s*[=:]\s*(?:async\s+)?\([^)]*\)\s*(?:=>|{)/,
104
+ /(\w+)\s*\([^)]*\)\s*{/
105
+ ],
106
+ go: [
107
+ /^func\s+(?:\([^)]+\)\s+)?(\w+)/,
108
+ /^type\s+(\w+)\s+(?:struct|interface)/
109
+ ],
110
+ rust: [
111
+ /^(?:pub\s+)?(?:async\s+)?fn\s+(\w+)/,
112
+ /^(?:pub\s+)?struct\s+(\w+)/,
113
+ /^(?:pub\s+)?enum\s+(\w+)/,
114
+ /^(?:pub\s+)?trait\s+(\w+)/,
115
+ /^impl(?:<[^>]+>)?\s+(\w+)/
116
+ ],
117
+ java: [
118
+ /^(?:public|private|protected)?\s*(?:static\s+)?(?:final\s+)?class\s+(\w+)/,
119
+ /^(?:public|private|protected)?\s*(?:static\s+)?(?:final\s+)?interface\s+(\w+)/,
120
+ /^(?:public|private|protected)?\s*(?:static\s+)?(?:final\s+)?(?:\w+\s+)+(\w+)\s*\(/
121
+ ],
122
+ php: [
123
+ /^(?:public|private|protected)?\s*(?:static\s+)?function\s+(\w+)/,
124
+ /^class\s+(\w+)/,
125
+ /^interface\s+(\w+)/,
126
+ /^trait\s+(\w+)/
127
+ ],
128
+ ruby: [
129
+ /^def\s+(\w+)/,
130
+ /^class\s+(\w+)/,
131
+ /^module\s+(\w+)/
132
+ ],
133
+ csharp: [
134
+ /^(?:public|private|protected|internal)?\s*(?:static\s+)?(?:partial\s+)?class\s+(\w+)/,
135
+ /^(?:public|private|protected|internal)?\s*interface\s+(\w+)/,
136
+ /^(?:public|private|protected|internal)?\s*(?:static\s+)?(?:async\s+)?(?:\w+\s+)+(\w+)\s*\(/
137
+ ],
138
+ cpp: [
139
+ /^(?:class|struct)\s+(\w+)/,
140
+ /^(?:\w+\s+)*(\w+)\s*\([^)]*\)\s*(?:const)?\s*(?:override)?\s*{/,
141
+ /^namespace\s+(\w+)/
142
+ ],
143
+ c: [
144
+ /^(?:\w+\s+)*(\w+)\s*\([^)]*\)\s*{/,
145
+ /^struct\s+(\w+)/,
146
+ /^typedef\s+.*\s+(\w+)\s*;/
147
+ ]
148
+ };
149
+ const langPatterns = patterns[language] || patterns.javascript;
150
+ lines.forEach((line, index) => {
151
+ const trimmedLine = line.trim();
152
+ for (const pattern of langPatterns) {
153
+ const match = trimmedLine.match(pattern);
154
+ if (match && match[1]) {
155
+ const name = match[1];
156
+ // Skip common false positives
157
+ if (['if', 'else', 'for', 'while', 'switch', 'catch', 'try', 'return', 'new', 'throw'].includes(name)) {
158
+ continue;
159
+ }
160
+ const type = this.inferTypeFromPattern(pattern, language);
161
+ children.push({
162
+ id: `${filePath}:${name}:${index}`,
163
+ type,
164
+ name,
165
+ filePath,
166
+ startLine: index,
167
+ endLine: index,
168
+ children: [],
169
+ metadata: {
170
+ text: trimmedLine.substring(0, 200),
171
+ hasErrors: false,
172
+ regexParsed: true
173
+ }
174
+ });
175
+ break; // One match per line
176
+ }
177
+ }
178
+ });
179
+ return {
180
+ id: `${filePath}:0:0`,
181
+ type: 'program',
182
+ name: filePath.split(/[/\\]/).pop() || '',
183
+ filePath,
184
+ startLine: 0,
185
+ endLine: lines.length,
186
+ children,
187
+ metadata: {
188
+ text: `File: ${filePath}`,
189
+ hasErrors: false,
190
+ regexParsed: true,
191
+ symbolCount: children.length
192
+ }
193
+ };
194
+ }
195
+ inferTypeFromPattern(pattern, _language) {
196
+ const patternStr = pattern.source;
197
+ if (patternStr.includes('class'))
198
+ return 'class_declaration';
199
+ if (patternStr.includes('interface'))
200
+ return 'interface_declaration';
201
+ if (patternStr.includes('function') || patternStr.includes('def') || patternStr.includes('fn'))
202
+ return 'function_declaration';
203
+ if (patternStr.includes('type') || patternStr.includes('enum'))
204
+ return 'type_alias_declaration';
205
+ if (patternStr.includes('struct'))
206
+ return 'struct_declaration';
207
+ if (patternStr.includes('trait'))
208
+ return 'trait_declaration';
209
+ if (patternStr.includes('const') || patternStr.includes('let'))
210
+ return 'variable_declaration';
211
+ if (patternStr.includes('module') || patternStr.includes('namespace'))
212
+ return 'module_declaration';
213
+ if (patternStr.includes('impl'))
214
+ return 'impl_declaration';
215
+ return 'function_declaration';
216
+ }
217
+ extractVueScript(content) {
218
+ // Find script blocks - there may be multiple (setup + regular)
219
+ // We want the main script block, not script setup (which uses different syntax)
220
+ const scriptRegex = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
221
+ let match;
222
+ let bestMatch = null;
223
+ while ((match = scriptRegex.exec(content)) !== null) {
224
+ const attrs = match[1] || '';
225
+ const scriptContent = match[2] || '';
226
+ // Skip empty scripts
227
+ if (!scriptContent.trim())
228
+ continue;
229
+ // Prefer non-setup script, but take setup if it's the only one
230
+ const isSetup = /\bsetup\b/i.test(attrs);
231
+ if (!bestMatch || !isSetup) {
232
+ bestMatch = { attrs, content: scriptContent };
233
+ // If this is a regular (non-setup) script with content, use it
234
+ if (!isSetup)
235
+ break;
236
+ }
237
+ }
238
+ if (!bestMatch || !bestMatch.content.trim()) {
239
+ return null;
240
+ }
241
+ // Check if TypeScript
242
+ const isTypeScript = /lang\s*=\s*["']ts["']/.test(bestMatch.attrs) ||
243
+ /lang\s*=\s*["']typescript["']/.test(bestMatch.attrs);
244
+ return {
245
+ content: bestMatch.content.trim(),
246
+ isTypeScript
247
+ };
248
+ }
249
+ async parseProject(rootDir) {
250
+ const files = await FileUtils.getAllFiles(rootDir);
251
+ const asts = new Map();
252
+ Logger.progress(`Parsing ${files.length} files...`);
253
+ for (const file of files) {
254
+ const ast = await this.parseFile(file);
255
+ if (ast) {
256
+ asts.set(file, ast);
257
+ }
258
+ }
259
+ Logger.success(`Parsed ${asts.size} files successfully`);
260
+ return asts;
261
+ }
262
+ convertToASTNode(node, filePath) {
263
+ const children = [];
264
+ for (let i = 0; i < node.childCount; i++) {
265
+ const child = node.child(i);
266
+ if (child) {
267
+ children.push(this.convertToASTNode(child, filePath));
268
+ }
269
+ }
270
+ return {
271
+ id: `${filePath}:${node.startPosition.row}:${node.startPosition.column}`,
272
+ type: node.type,
273
+ name: this.extractName(node),
274
+ filePath,
275
+ startLine: node.startPosition.row,
276
+ endLine: node.endPosition.row,
277
+ children,
278
+ metadata: {
279
+ text: node.text.length > 200 ? node.text.substring(0, 200) + '...' : node.text,
280
+ hasErrors: node.hasError
281
+ }
282
+ };
283
+ }
284
+ extractName(node) {
285
+ // Try field name first (works for most languages)
286
+ const nameNode = node.childForFieldName('name');
287
+ if (nameNode) {
288
+ return nameNode.text;
289
+ }
290
+ // Python: decorated_definition has the actual definition as child
291
+ if (node.type === 'decorated_definition') {
292
+ const defNode = node.children.find(c => c.type === 'function_definition' || c.type === 'class_definition');
293
+ if (defNode) {
294
+ const innerName = defNode.childForFieldName('name');
295
+ if (innerName)
296
+ return innerName.text;
297
+ }
298
+ }
299
+ const interestingTypes = [
300
+ // JS/TS
301
+ 'function_declaration',
302
+ 'class_declaration',
303
+ 'interface_declaration',
304
+ 'type_alias_declaration',
305
+ 'variable_declaration',
306
+ // Python
307
+ 'function_definition',
308
+ 'class_definition',
309
+ 'async_function_definition'
310
+ ];
311
+ if (interestingTypes.includes(node.type)) {
312
+ // Try to extract name from text
313
+ const match = node.text.match(/(?:def|class|function|async\s+def)\s+(\w+)/);
314
+ if (match)
315
+ return match[1];
316
+ return node.text.split(/[\s({:]/)[1] || 'anonymous';
317
+ }
318
+ return '';
319
+ }
320
+ extractLocation(node, filePath) {
321
+ return {
322
+ filePath,
323
+ startLine: node.startPosition.row,
324
+ endLine: node.endPosition.row,
325
+ startColumn: node.startPosition.column,
326
+ endColumn: node.endPosition.column
327
+ };
328
+ }
329
+ }
330
+ //# sourceMappingURL=ast-parser.js.map
@@ -0,0 +1,16 @@
1
+ import { DependencyGraph, Symbol, ASTNode } from '../types/index.js';
2
+ export declare class DependencyGraphBuilder {
3
+ buildGraph(symbols: Map<string, Symbol>, asts: Map<string, ASTNode>): DependencyGraph;
4
+ private buildNodes;
5
+ private buildEdges;
6
+ private addEdge;
7
+ private findSymbolsBySource;
8
+ private findSymbolAtLocation;
9
+ private getFileName;
10
+ private estimateComplexity;
11
+ private countEdges;
12
+ findDependents(graph: DependencyGraph, nodeId: string): Set<string>;
13
+ findDependencies(graph: DependencyGraph, nodeId: string): Set<string>;
14
+ getTransitiveDependencies(graph: DependencyGraph, nodeId: string, maxDepth?: number): Set<string>;
15
+ }
16
+ //# sourceMappingURL=dependency-graph.d.ts.map