codereview-aia 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 (153) hide show
  1. package/.cr-aia.yml +23 -0
  2. package/.crignore +0 -0
  3. package/dist/index.js +27 -0
  4. package/package.json +85 -0
  5. package/src/analysis/FindingsExtractor.ts +431 -0
  6. package/src/analysis/ai-detection/analyzers/BaseAnalyzer.ts +267 -0
  7. package/src/analysis/ai-detection/analyzers/DocumentationAnalyzer.ts +622 -0
  8. package/src/analysis/ai-detection/analyzers/GitHistoryAnalyzer.ts +430 -0
  9. package/src/analysis/ai-detection/core/AIDetectionEngine.ts +467 -0
  10. package/src/analysis/ai-detection/types/DetectionTypes.ts +406 -0
  11. package/src/analysis/ai-detection/utils/SubmissionConverter.ts +390 -0
  12. package/src/analysis/context/ReviewContext.ts +378 -0
  13. package/src/analysis/context/index.ts +7 -0
  14. package/src/analysis/index.ts +8 -0
  15. package/src/analysis/tokens/TokenAnalysisFormatter.ts +154 -0
  16. package/src/analysis/tokens/TokenAnalyzer.ts +747 -0
  17. package/src/analysis/tokens/index.ts +8 -0
  18. package/src/clients/base/abstractClient.ts +190 -0
  19. package/src/clients/base/httpClient.ts +160 -0
  20. package/src/clients/base/index.ts +12 -0
  21. package/src/clients/base/modelDetection.ts +107 -0
  22. package/src/clients/base/responseProcessor.ts +586 -0
  23. package/src/clients/factory/clientFactory.ts +55 -0
  24. package/src/clients/factory/index.ts +8 -0
  25. package/src/clients/implementations/index.ts +8 -0
  26. package/src/clients/implementations/openRouterClient.ts +411 -0
  27. package/src/clients/openRouterClient.ts +863 -0
  28. package/src/clients/openRouterClientWrapper.ts +44 -0
  29. package/src/clients/utils/directoryStructure.ts +52 -0
  30. package/src/clients/utils/index.ts +11 -0
  31. package/src/clients/utils/languageDetection.ts +44 -0
  32. package/src/clients/utils/promptFormatter.ts +105 -0
  33. package/src/clients/utils/promptLoader.ts +53 -0
  34. package/src/clients/utils/tokenCounter.ts +297 -0
  35. package/src/core/ApiClientSelector.ts +37 -0
  36. package/src/core/ConfigurationService.ts +591 -0
  37. package/src/core/ConsolidationService.ts +423 -0
  38. package/src/core/InteractiveDisplayManager.ts +81 -0
  39. package/src/core/OutputManager.ts +275 -0
  40. package/src/core/ReviewGenerator.ts +140 -0
  41. package/src/core/fileDiscovery.ts +237 -0
  42. package/src/core/handlers/EstimationHandler.ts +104 -0
  43. package/src/core/handlers/FileProcessingHandler.ts +204 -0
  44. package/src/core/handlers/OutputHandler.ts +125 -0
  45. package/src/core/handlers/ReviewExecutor.ts +104 -0
  46. package/src/core/reviewOrchestrator.ts +333 -0
  47. package/src/core/utils/ModelInfoUtils.ts +56 -0
  48. package/src/formatters/outputFormatter.ts +62 -0
  49. package/src/formatters/utils/IssueFormatters.ts +83 -0
  50. package/src/formatters/utils/JsonFormatter.ts +77 -0
  51. package/src/formatters/utils/MarkdownFormatters.ts +609 -0
  52. package/src/formatters/utils/MetadataFormatter.ts +269 -0
  53. package/src/formatters/utils/ModelInfoExtractor.ts +115 -0
  54. package/src/index.ts +27 -0
  55. package/src/plugins/PluginInterface.ts +50 -0
  56. package/src/plugins/PluginManager.ts +126 -0
  57. package/src/prompts/PromptManager.ts +69 -0
  58. package/src/prompts/cache/PromptCache.ts +50 -0
  59. package/src/prompts/promptText/common/variables/css-frameworks.json +33 -0
  60. package/src/prompts/promptText/common/variables/framework-versions.json +45 -0
  61. package/src/prompts/promptText/frameworks/react/comprehensive.hbs +19 -0
  62. package/src/prompts/promptText/languages/css/comprehensive.hbs +18 -0
  63. package/src/prompts/promptText/languages/generic/comprehensive.hbs +20 -0
  64. package/src/prompts/promptText/languages/html/comprehensive.hbs +18 -0
  65. package/src/prompts/promptText/languages/javascript/comprehensive.hbs +18 -0
  66. package/src/prompts/promptText/languages/python/comprehensive.hbs +18 -0
  67. package/src/prompts/promptText/languages/typescript/comprehensive.hbs +18 -0
  68. package/src/runtime/auth/service.ts +58 -0
  69. package/src/runtime/auth/session.ts +103 -0
  70. package/src/runtime/auth/types.ts +11 -0
  71. package/src/runtime/cliEntry.ts +196 -0
  72. package/src/runtime/errors.ts +13 -0
  73. package/src/runtime/fileCollector.ts +188 -0
  74. package/src/runtime/manifest.ts +64 -0
  75. package/src/runtime/openrouterProxy.ts +45 -0
  76. package/src/runtime/proxyConfig.ts +94 -0
  77. package/src/runtime/proxyEnvironment.ts +71 -0
  78. package/src/runtime/reportMerge.ts +102 -0
  79. package/src/runtime/reporting/markdownReportBuilder.ts +138 -0
  80. package/src/runtime/reporting/reportDataCollector.ts +234 -0
  81. package/src/runtime/reporting/summaryGenerator.ts +86 -0
  82. package/src/runtime/reviewPipeline.ts +155 -0
  83. package/src/runtime/runAiCodeReview.ts +153 -0
  84. package/src/runtime/runtimeConfig.ts +5 -0
  85. package/src/runtime/ui/Layout.tsx +57 -0
  86. package/src/runtime/ui/RuntimeApp.tsx +150 -0
  87. package/src/runtime/ui/inkModules.ts +73 -0
  88. package/src/runtime/ui/screens/AuthScreen.tsx +128 -0
  89. package/src/runtime/ui/screens/ModeSelection.tsx +52 -0
  90. package/src/runtime/ui/screens/ProgressScreen.tsx +55 -0
  91. package/src/runtime/ui/screens/ResultsScreen.tsx +76 -0
  92. package/src/strategies/ArchitecturalReviewStrategy.ts +54 -0
  93. package/src/strategies/CodingTestReviewStrategy.ts +920 -0
  94. package/src/strategies/ConsolidatedReviewStrategy.ts +59 -0
  95. package/src/strategies/ExtractPatternsReviewStrategy.ts +64 -0
  96. package/src/strategies/MultiPassReviewStrategy.ts +785 -0
  97. package/src/strategies/ReviewStrategy.ts +64 -0
  98. package/src/strategies/StrategyFactory.ts +79 -0
  99. package/src/strategies/index.ts +14 -0
  100. package/src/tokenizers/baseTokenizer.ts +61 -0
  101. package/src/tokenizers/gptTokenizer.ts +27 -0
  102. package/src/tokenizers/index.ts +8 -0
  103. package/src/types/apiResponses.ts +40 -0
  104. package/src/types/cli.ts +24 -0
  105. package/src/types/common.ts +39 -0
  106. package/src/types/configuration.ts +201 -0
  107. package/src/types/handlebars.d.ts +5 -0
  108. package/src/types/patch.d.ts +25 -0
  109. package/src/types/review.ts +294 -0
  110. package/src/types/reviewContext.d.ts +65 -0
  111. package/src/types/reviewSchema.ts +181 -0
  112. package/src/types/structuredReview.ts +167 -0
  113. package/src/types/tokenAnalysis.ts +56 -0
  114. package/src/utils/FileReader.ts +93 -0
  115. package/src/utils/FileWriter.ts +76 -0
  116. package/src/utils/PathGenerator.ts +97 -0
  117. package/src/utils/api/apiUtils.ts +14 -0
  118. package/src/utils/api/index.ts +1 -0
  119. package/src/utils/apiErrorHandler.ts +287 -0
  120. package/src/utils/ciDataCollector.ts +252 -0
  121. package/src/utils/codingTestConfigLoader.ts +466 -0
  122. package/src/utils/dependencies/aiDependencyAnalyzer.ts +454 -0
  123. package/src/utils/detection/frameworkDetector.ts +879 -0
  124. package/src/utils/detection/index.ts +10 -0
  125. package/src/utils/detection/projectTypeDetector.ts +518 -0
  126. package/src/utils/diagramGenerator.ts +206 -0
  127. package/src/utils/errorLogger.ts +60 -0
  128. package/src/utils/estimationUtils.ts +407 -0
  129. package/src/utils/fileFilters.ts +373 -0
  130. package/src/utils/fileSystem.ts +57 -0
  131. package/src/utils/index.ts +36 -0
  132. package/src/utils/logger.ts +240 -0
  133. package/src/utils/pathValidator.ts +98 -0
  134. package/src/utils/priorityFilter.ts +59 -0
  135. package/src/utils/projectDocs.ts +189 -0
  136. package/src/utils/promptPaths.ts +29 -0
  137. package/src/utils/promptTemplateManager.ts +157 -0
  138. package/src/utils/review/consolidateReview.ts +553 -0
  139. package/src/utils/review/fixDisplay.ts +100 -0
  140. package/src/utils/review/fixImplementation.ts +61 -0
  141. package/src/utils/review/index.ts +36 -0
  142. package/src/utils/review/interactiveProcessing.ts +294 -0
  143. package/src/utils/review/progressTracker.ts +296 -0
  144. package/src/utils/review/reviewExtraction.ts +382 -0
  145. package/src/utils/review/types.ts +46 -0
  146. package/src/utils/reviewActionHandler.ts +18 -0
  147. package/src/utils/reviewParser.ts +253 -0
  148. package/src/utils/sanitizer.ts +238 -0
  149. package/src/utils/smartFileSelector.ts +255 -0
  150. package/src/utils/templateLoader.ts +514 -0
  151. package/src/utils/treeGenerator.ts +153 -0
  152. package/tsconfig.build.json +14 -0
  153. package/tsconfig.json +59 -0
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @fileoverview Detection utilities index.
3
+ *
4
+ * This module exports all detection-related utilities including project type
5
+ * and framework detection.
6
+ */
7
+
8
+ export * from './frameworkDetector';
9
+ export { detectPrimaryLanguage } from './frameworkDetector';
10
+ export * from './projectTypeDetector';
@@ -0,0 +1,518 @@
1
+ /**
2
+ * @fileoverview Project type detection utilities.
3
+ *
4
+ * This module provides functions to automatically detect project types
5
+ * and programming languages from project files and structure. It's used
6
+ * to set default language options without requiring manual specification.
7
+ */
8
+
9
+ import { existsSync } from 'node:fs';
10
+ import fs from 'node:fs/promises';
11
+ import path from 'node:path';
12
+ import { DEFAULT_LANGUAGE, type ProgrammingLanguage } from '../../types/common';
13
+ import logger from '../logger';
14
+
15
+ /**
16
+ * Project type detection result
17
+ */
18
+ export interface ProjectDetectionResult {
19
+ /** The primary programming language of the project */
20
+ language: ProgrammingLanguage;
21
+ /** Confidence level of the detection (high, medium, low) */
22
+ confidence: 'high' | 'medium' | 'low';
23
+ /** Additional detected languages */
24
+ additionalLanguages?: ProgrammingLanguage[];
25
+ /** Project type (framework, library, application, etc.) */
26
+ projectType?: string;
27
+ }
28
+
29
+ /**
30
+ * Project type signature defining files that are checked
31
+ * to identify a project's language and type
32
+ */
33
+ interface ProjectTypeSignature {
34
+ /** Programming language of the project */
35
+ language: ProgrammingLanguage;
36
+ /** Required files that must exist for this project type */
37
+ requiredFiles: string[];
38
+ /** Optional files that help confirm the project type but aren't required */
39
+ optionalFiles?: string[];
40
+ /** Additional check function for complex conditions */
41
+ additionalCheck?: (projectPath: string) => Promise<boolean>;
42
+ /** Project type name (framework, library, application, etc.) */
43
+ projectType?: string;
44
+ /** Detection confidence based on how specific the signature is */
45
+ confidence: 'high' | 'medium' | 'low';
46
+ }
47
+
48
+ /**
49
+ * Project type signatures for different languages and frameworks
50
+ */
51
+ const PROJECT_SIGNATURES: ProjectTypeSignature[] = [
52
+ // Ruby signatures
53
+ {
54
+ language: 'ruby',
55
+ requiredFiles: ['Gemfile'],
56
+ optionalFiles: ['config/routes.rb', 'app/controllers'],
57
+ projectType: 'Ruby on Rails',
58
+ confidence: 'high',
59
+ },
60
+ {
61
+ language: 'ruby',
62
+ requiredFiles: ['config/routes.rb'],
63
+ projectType: 'Ruby on Rails',
64
+ confidence: 'high',
65
+ },
66
+ {
67
+ language: 'ruby',
68
+ requiredFiles: ['config/application.rb'],
69
+ projectType: 'Ruby on Rails',
70
+ confidence: 'high',
71
+ },
72
+ {
73
+ language: 'ruby',
74
+ requiredFiles: ['Rakefile', 'Gemfile'],
75
+ confidence: 'high',
76
+ },
77
+ {
78
+ language: 'ruby',
79
+ requiredFiles: ['config.ru'],
80
+ projectType: 'Rack',
81
+ confidence: 'medium',
82
+ },
83
+ {
84
+ language: 'ruby',
85
+ requiredFiles: ['bin/rails'],
86
+ projectType: 'Ruby on Rails',
87
+ confidence: 'high',
88
+ },
89
+ {
90
+ language: 'ruby',
91
+ requiredFiles: ['.ruby-version'],
92
+ confidence: 'medium',
93
+ },
94
+ {
95
+ language: 'ruby',
96
+ requiredFiles: [],
97
+ additionalCheck: async (projectPath: string): Promise<boolean> => {
98
+ // Check for .rb files
99
+ try {
100
+ const files = await fs.readdir(projectPath);
101
+ return files.some((file) => file.endsWith('.rb'));
102
+ } catch {
103
+ return false;
104
+ }
105
+ },
106
+ confidence: 'low',
107
+ },
108
+
109
+ // Python signatures
110
+ {
111
+ language: 'python',
112
+ requiredFiles: ['requirements.txt'],
113
+ optionalFiles: ['setup.py', 'pyproject.toml'],
114
+ confidence: 'high',
115
+ },
116
+ {
117
+ language: 'python',
118
+ requiredFiles: ['setup.py'],
119
+ confidence: 'high',
120
+ },
121
+ {
122
+ language: 'python',
123
+ requiredFiles: ['pyproject.toml'],
124
+ confidence: 'high',
125
+ },
126
+ {
127
+ language: 'python',
128
+ requiredFiles: ['Pipfile'],
129
+ confidence: 'high',
130
+ },
131
+ {
132
+ language: 'python',
133
+ requiredFiles: ['manage.py'],
134
+ projectType: 'Django',
135
+ confidence: 'high',
136
+ },
137
+ {
138
+ language: 'python',
139
+ requiredFiles: ['app.py'],
140
+ optionalFiles: ['wsgi.py', 'templates/'],
141
+ projectType: 'Flask',
142
+ confidence: 'medium',
143
+ },
144
+ {
145
+ language: 'python',
146
+ requiredFiles: ['__init__.py'],
147
+ confidence: 'medium',
148
+ },
149
+ {
150
+ language: 'python',
151
+ requiredFiles: [],
152
+ additionalCheck: async (projectPath: string): Promise<boolean> => {
153
+ // Check for .py files
154
+ try {
155
+ const files = await fs.readdir(projectPath);
156
+ return files.some((file) => file.endsWith('.py'));
157
+ } catch {
158
+ return false;
159
+ }
160
+ },
161
+ confidence: 'low',
162
+ },
163
+
164
+ // Go signatures
165
+ {
166
+ language: 'go',
167
+ requiredFiles: ['go.mod'],
168
+ optionalFiles: ['go.sum', 'main.go'],
169
+ confidence: 'high',
170
+ },
171
+ {
172
+ language: 'go',
173
+ requiredFiles: ['main.go'],
174
+ confidence: 'medium',
175
+ },
176
+ {
177
+ language: 'go',
178
+ requiredFiles: ['cmd/'],
179
+ optionalFiles: ['internal/', 'pkg/'],
180
+ projectType: 'Go CLI Application',
181
+ confidence: 'medium',
182
+ },
183
+ {
184
+ language: 'go',
185
+ requiredFiles: ['Dockerfile'],
186
+ additionalCheck: async (projectPath: string): Promise<boolean> => {
187
+ // Check if Dockerfile mentions Go
188
+ try {
189
+ const dockerfilePath = path.join(projectPath, 'Dockerfile');
190
+ const dockerfileContent = await fs.readFile(dockerfilePath, 'utf-8');
191
+ return dockerfileContent.includes('golang') || dockerfileContent.includes('go:');
192
+ } catch {
193
+ return false;
194
+ }
195
+ },
196
+ projectType: 'Go Docker Application',
197
+ confidence: 'medium',
198
+ },
199
+ {
200
+ language: 'go',
201
+ requiredFiles: [],
202
+ additionalCheck: async (projectPath: string): Promise<boolean> => {
203
+ // Check for .go files
204
+ try {
205
+ const files = await fs.readdir(projectPath);
206
+ return files.some((file) => file.endsWith('.go'));
207
+ } catch {
208
+ return false;
209
+ }
210
+ },
211
+ confidence: 'low',
212
+ },
213
+
214
+ // PHP signatures
215
+ {
216
+ language: 'php',
217
+ requiredFiles: ['composer.json'],
218
+ confidence: 'high',
219
+ },
220
+ {
221
+ language: 'php',
222
+ requiredFiles: ['artisan'],
223
+ optionalFiles: ['app/Http/Controllers/'],
224
+ projectType: 'Laravel',
225
+ confidence: 'high',
226
+ },
227
+ {
228
+ language: 'php',
229
+ requiredFiles: ['vendor/autoload.php'],
230
+ confidence: 'medium',
231
+ },
232
+ {
233
+ language: 'php',
234
+ requiredFiles: ['wp-config.php'],
235
+ projectType: 'WordPress',
236
+ confidence: 'high',
237
+ },
238
+ {
239
+ language: 'php',
240
+ requiredFiles: [],
241
+ additionalCheck: async (projectPath: string): Promise<boolean> => {
242
+ // Check for .php files
243
+ try {
244
+ const files = await fs.readdir(projectPath);
245
+ return files.some((file) => file.endsWith('.php'));
246
+ } catch {
247
+ return false;
248
+ }
249
+ },
250
+ confidence: 'low',
251
+ },
252
+
253
+ // TypeScript signatures
254
+ {
255
+ language: 'typescript',
256
+ requiredFiles: ['tsconfig.json'],
257
+ confidence: 'high',
258
+ },
259
+ {
260
+ language: 'typescript',
261
+ requiredFiles: ['package.json'],
262
+ additionalCheck: async (projectPath: string): Promise<boolean> => {
263
+ // Check for TypeScript in dependencies or devDependencies
264
+ try {
265
+ const packageJsonPath = path.join(projectPath, 'package.json');
266
+ const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
267
+ return packageJson.dependencies?.typescript || packageJson.devDependencies?.typescript;
268
+ } catch {
269
+ return false;
270
+ }
271
+ },
272
+ confidence: 'high',
273
+ },
274
+ {
275
+ language: 'typescript',
276
+ requiredFiles: [],
277
+ additionalCheck: async (projectPath: string): Promise<boolean> => {
278
+ // Check for .ts files
279
+ try {
280
+ const files = await fs.readdir(projectPath);
281
+ return files.some((file) => file.endsWith('.ts') || file.endsWith('.tsx'));
282
+ } catch {
283
+ return false;
284
+ }
285
+ },
286
+ confidence: 'medium',
287
+ },
288
+
289
+ // JavaScript signatures
290
+ {
291
+ language: 'javascript',
292
+ requiredFiles: ['package.json'],
293
+ optionalFiles: ['webpack.config.js', 'babel.config.js', '.babelrc'],
294
+ additionalCheck: async (projectPath: string): Promise<boolean> => {
295
+ // TypeScript check to ensure this isn't a TypeScript project
296
+ try {
297
+ const files = await fs.readdir(projectPath);
298
+ const hasTypeScriptFiles = files.some(
299
+ (file) => file.endsWith('.ts') || file.endsWith('.tsx') || file === 'tsconfig.json',
300
+ );
301
+ return !hasTypeScriptFiles;
302
+ } catch {
303
+ return true; // If we can't check, assume it's JavaScript
304
+ }
305
+ },
306
+ confidence: 'high',
307
+ },
308
+ {
309
+ language: 'javascript',
310
+ requiredFiles: [],
311
+ additionalCheck: async (projectPath: string): Promise<boolean> => {
312
+ // Check for .js files
313
+ try {
314
+ const files = await fs.readdir(projectPath);
315
+ return files.some((file) => file.endsWith('.js') || file.endsWith('.jsx'));
316
+ } catch {
317
+ return false;
318
+ }
319
+ },
320
+ confidence: 'low',
321
+ },
322
+ ];
323
+
324
+ /**
325
+ * Check if all specified files exist in the project directory
326
+ * @param projectPath Project directory path
327
+ * @param files Array of files to check
328
+ * @returns True if all specified files exist
329
+ */
330
+ async function checkFilesExist(projectPath: string, files: string[]): Promise<boolean> {
331
+ if (files.length === 0) return true;
332
+
333
+ for (const file of files) {
334
+ const filePath = path.join(projectPath, file);
335
+
336
+ try {
337
+ if (!existsSync(filePath)) {
338
+ return false;
339
+ }
340
+ } catch {
341
+ return false;
342
+ }
343
+ }
344
+
345
+ return true;
346
+ }
347
+
348
+ /**
349
+ * Count files with specific extensions in a directory
350
+ * @param projectPath Project directory path
351
+ * @param extensions Array of file extensions to count (e.g., ['.py', '.js'])
352
+ * @returns Number of files with the specified extensions
353
+ */
354
+ async function countFilesByExtension(projectPath: string, extensions: string[]): Promise<number> {
355
+ try {
356
+ let count = 0;
357
+ const files = await fs.readdir(projectPath);
358
+
359
+ for (const file of files) {
360
+ const filePath = path.join(projectPath, file);
361
+ try {
362
+ const stats = await fs.stat(filePath);
363
+
364
+ if (stats.isFile() && extensions.some((ext) => file.endsWith(ext))) {
365
+ count++;
366
+ } else if (stats.isDirectory() && file !== 'node_modules' && file !== '.git') {
367
+ // Recursively count files in subdirectories, excluding node_modules and .git
368
+ count += await countFilesByExtension(filePath, extensions);
369
+ }
370
+ } catch {}
371
+ }
372
+
373
+ return count;
374
+ } catch {
375
+ return 0;
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Get file extension counts for major languages
381
+ * @param projectPath Project directory path
382
+ * @returns Object with counts of files by language
383
+ */
384
+ async function getLanguageFileStats(
385
+ projectPath: string,
386
+ ): Promise<Record<ProgrammingLanguage, number>> {
387
+ const extensionMap: Record<ProgrammingLanguage, string[]> = {
388
+ typescript: ['.ts', '.tsx'],
389
+ javascript: ['.js', '.jsx'],
390
+ python: ['.py'],
391
+ php: ['.php'],
392
+ java: ['.java'],
393
+ go: ['.go'],
394
+ rust: ['.rs'],
395
+ c: ['.c', '.h'],
396
+ cpp: ['.cpp', '.hpp'],
397
+ csharp: ['.cs'],
398
+ ruby: ['.rb'],
399
+ swift: ['.swift'],
400
+ kotlin: ['.kt'],
401
+ };
402
+
403
+ const result: Record<ProgrammingLanguage, number> = {
404
+ typescript: 0,
405
+ javascript: 0,
406
+ python: 0,
407
+ php: 0,
408
+ java: 0,
409
+ go: 0,
410
+ rust: 0,
411
+ c: 0,
412
+ cpp: 0,
413
+ csharp: 0,
414
+ ruby: 0,
415
+ swift: 0,
416
+ kotlin: 0,
417
+ };
418
+
419
+ for (const [language, extensions] of Object.entries(extensionMap)) {
420
+ result[language as ProgrammingLanguage] = await countFilesByExtension(projectPath, extensions);
421
+ }
422
+
423
+ return result;
424
+ }
425
+
426
+ /**
427
+ * Auto-detect project type and primary programming language
428
+ * @param projectPath Project directory path
429
+ * @returns Detection result with language and confidence
430
+ */
431
+ export async function detectProjectType(projectPath: string): Promise<ProjectDetectionResult> {
432
+ try {
433
+ // Check project signatures in order (most specific first)
434
+ for (const signature of PROJECT_SIGNATURES) {
435
+ const requiredFilesExist = await checkFilesExist(projectPath, signature.requiredFiles);
436
+
437
+ if (!requiredFilesExist) continue;
438
+
439
+ // Check optional files if specified
440
+ // No longer using this score in calculations, but keeping the logic for future enhancements
441
+ if (signature.optionalFiles && signature.optionalFiles.length > 0) {
442
+ for (const file of signature.optionalFiles) {
443
+ if (existsSync(path.join(projectPath, file))) {
444
+ // Files exist but score is not currently used
445
+ }
446
+ }
447
+ }
448
+
449
+ // Run additional check if specified
450
+ if (signature.additionalCheck) {
451
+ const additionalCheckPassed = await signature.additionalCheck(projectPath);
452
+ if (!additionalCheckPassed) continue;
453
+ }
454
+
455
+ // Calculate additional languages
456
+ const languageStats = await getLanguageFileStats(projectPath);
457
+
458
+ // Filter languages with significant presence (more than 3 files)
459
+ const additionalLanguages = Object.entries(languageStats)
460
+ .filter(
461
+ ([lang, count]) => count > 3 && lang !== signature.language && lang !== 'typescript',
462
+ )
463
+ .sort((a, b) => b[1] - a[1]) // Sort by file count (descending)
464
+ .map(([lang]) => lang as ProgrammingLanguage);
465
+
466
+ return {
467
+ language: signature.language,
468
+ confidence: signature.confidence,
469
+ projectType: signature.projectType,
470
+ additionalLanguages: additionalLanguages.length > 0 ? additionalLanguages : undefined,
471
+ };
472
+ }
473
+
474
+ // Fallback to statistical detection if no signature matched
475
+ const languageStats = await getLanguageFileStats(projectPath);
476
+
477
+ // Get language with most files
478
+ const entries = Object.entries(languageStats);
479
+ if (entries.length === 0 || entries.every(([_, count]) => count === 0)) {
480
+ // No files with known extensions found
481
+ return {
482
+ language: DEFAULT_LANGUAGE,
483
+ confidence: 'low',
484
+ };
485
+ }
486
+
487
+ const sortedLanguages = entries.sort((a, b) => b[1] - a[1]);
488
+ const primaryLanguage = sortedLanguages[0][0] as ProgrammingLanguage;
489
+ const primaryCount = sortedLanguages[0][1];
490
+
491
+ // If very few files, confidence is low
492
+ if (primaryCount < 3) {
493
+ return {
494
+ language: primaryLanguage,
495
+ confidence: 'low',
496
+ };
497
+ }
498
+
499
+ // Filter additional languages (more than 3 files, not the primary language)
500
+ const additionalLanguages = sortedLanguages
501
+ .filter(([lang, count]) => count > 3 && lang !== primaryLanguage)
502
+ .map(([lang]) => lang as ProgrammingLanguage);
503
+
504
+ return {
505
+ language: primaryLanguage,
506
+ confidence: 'medium',
507
+ additionalLanguages: additionalLanguages.length > 0 ? additionalLanguages : undefined,
508
+ };
509
+ } catch (error) {
510
+ logger.error(
511
+ `Error detecting project type: ${error instanceof Error ? error.message : String(error)}`,
512
+ );
513
+ return {
514
+ language: DEFAULT_LANGUAGE,
515
+ confidence: 'low',
516
+ };
517
+ }
518
+ }