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.
- package/.cr-aia.yml +23 -0
- package/.crignore +0 -0
- package/dist/index.js +27 -0
- package/package.json +85 -0
- package/src/analysis/FindingsExtractor.ts +431 -0
- package/src/analysis/ai-detection/analyzers/BaseAnalyzer.ts +267 -0
- package/src/analysis/ai-detection/analyzers/DocumentationAnalyzer.ts +622 -0
- package/src/analysis/ai-detection/analyzers/GitHistoryAnalyzer.ts +430 -0
- package/src/analysis/ai-detection/core/AIDetectionEngine.ts +467 -0
- package/src/analysis/ai-detection/types/DetectionTypes.ts +406 -0
- package/src/analysis/ai-detection/utils/SubmissionConverter.ts +390 -0
- package/src/analysis/context/ReviewContext.ts +378 -0
- package/src/analysis/context/index.ts +7 -0
- package/src/analysis/index.ts +8 -0
- package/src/analysis/tokens/TokenAnalysisFormatter.ts +154 -0
- package/src/analysis/tokens/TokenAnalyzer.ts +747 -0
- package/src/analysis/tokens/index.ts +8 -0
- package/src/clients/base/abstractClient.ts +190 -0
- package/src/clients/base/httpClient.ts +160 -0
- package/src/clients/base/index.ts +12 -0
- package/src/clients/base/modelDetection.ts +107 -0
- package/src/clients/base/responseProcessor.ts +586 -0
- package/src/clients/factory/clientFactory.ts +55 -0
- package/src/clients/factory/index.ts +8 -0
- package/src/clients/implementations/index.ts +8 -0
- package/src/clients/implementations/openRouterClient.ts +411 -0
- package/src/clients/openRouterClient.ts +863 -0
- package/src/clients/openRouterClientWrapper.ts +44 -0
- package/src/clients/utils/directoryStructure.ts +52 -0
- package/src/clients/utils/index.ts +11 -0
- package/src/clients/utils/languageDetection.ts +44 -0
- package/src/clients/utils/promptFormatter.ts +105 -0
- package/src/clients/utils/promptLoader.ts +53 -0
- package/src/clients/utils/tokenCounter.ts +297 -0
- package/src/core/ApiClientSelector.ts +37 -0
- package/src/core/ConfigurationService.ts +591 -0
- package/src/core/ConsolidationService.ts +423 -0
- package/src/core/InteractiveDisplayManager.ts +81 -0
- package/src/core/OutputManager.ts +275 -0
- package/src/core/ReviewGenerator.ts +140 -0
- package/src/core/fileDiscovery.ts +237 -0
- package/src/core/handlers/EstimationHandler.ts +104 -0
- package/src/core/handlers/FileProcessingHandler.ts +204 -0
- package/src/core/handlers/OutputHandler.ts +125 -0
- package/src/core/handlers/ReviewExecutor.ts +104 -0
- package/src/core/reviewOrchestrator.ts +333 -0
- package/src/core/utils/ModelInfoUtils.ts +56 -0
- package/src/formatters/outputFormatter.ts +62 -0
- package/src/formatters/utils/IssueFormatters.ts +83 -0
- package/src/formatters/utils/JsonFormatter.ts +77 -0
- package/src/formatters/utils/MarkdownFormatters.ts +609 -0
- package/src/formatters/utils/MetadataFormatter.ts +269 -0
- package/src/formatters/utils/ModelInfoExtractor.ts +115 -0
- package/src/index.ts +27 -0
- package/src/plugins/PluginInterface.ts +50 -0
- package/src/plugins/PluginManager.ts +126 -0
- package/src/prompts/PromptManager.ts +69 -0
- package/src/prompts/cache/PromptCache.ts +50 -0
- package/src/prompts/promptText/common/variables/css-frameworks.json +33 -0
- package/src/prompts/promptText/common/variables/framework-versions.json +45 -0
- package/src/prompts/promptText/frameworks/react/comprehensive.hbs +19 -0
- package/src/prompts/promptText/languages/css/comprehensive.hbs +18 -0
- package/src/prompts/promptText/languages/generic/comprehensive.hbs +20 -0
- package/src/prompts/promptText/languages/html/comprehensive.hbs +18 -0
- package/src/prompts/promptText/languages/javascript/comprehensive.hbs +18 -0
- package/src/prompts/promptText/languages/python/comprehensive.hbs +18 -0
- package/src/prompts/promptText/languages/typescript/comprehensive.hbs +18 -0
- package/src/runtime/auth/service.ts +58 -0
- package/src/runtime/auth/session.ts +103 -0
- package/src/runtime/auth/types.ts +11 -0
- package/src/runtime/cliEntry.ts +196 -0
- package/src/runtime/errors.ts +13 -0
- package/src/runtime/fileCollector.ts +188 -0
- package/src/runtime/manifest.ts +64 -0
- package/src/runtime/openrouterProxy.ts +45 -0
- package/src/runtime/proxyConfig.ts +94 -0
- package/src/runtime/proxyEnvironment.ts +71 -0
- package/src/runtime/reportMerge.ts +102 -0
- package/src/runtime/reporting/markdownReportBuilder.ts +138 -0
- package/src/runtime/reporting/reportDataCollector.ts +234 -0
- package/src/runtime/reporting/summaryGenerator.ts +86 -0
- package/src/runtime/reviewPipeline.ts +155 -0
- package/src/runtime/runAiCodeReview.ts +153 -0
- package/src/runtime/runtimeConfig.ts +5 -0
- package/src/runtime/ui/Layout.tsx +57 -0
- package/src/runtime/ui/RuntimeApp.tsx +150 -0
- package/src/runtime/ui/inkModules.ts +73 -0
- package/src/runtime/ui/screens/AuthScreen.tsx +128 -0
- package/src/runtime/ui/screens/ModeSelection.tsx +52 -0
- package/src/runtime/ui/screens/ProgressScreen.tsx +55 -0
- package/src/runtime/ui/screens/ResultsScreen.tsx +76 -0
- package/src/strategies/ArchitecturalReviewStrategy.ts +54 -0
- package/src/strategies/CodingTestReviewStrategy.ts +920 -0
- package/src/strategies/ConsolidatedReviewStrategy.ts +59 -0
- package/src/strategies/ExtractPatternsReviewStrategy.ts +64 -0
- package/src/strategies/MultiPassReviewStrategy.ts +785 -0
- package/src/strategies/ReviewStrategy.ts +64 -0
- package/src/strategies/StrategyFactory.ts +79 -0
- package/src/strategies/index.ts +14 -0
- package/src/tokenizers/baseTokenizer.ts +61 -0
- package/src/tokenizers/gptTokenizer.ts +27 -0
- package/src/tokenizers/index.ts +8 -0
- package/src/types/apiResponses.ts +40 -0
- package/src/types/cli.ts +24 -0
- package/src/types/common.ts +39 -0
- package/src/types/configuration.ts +201 -0
- package/src/types/handlebars.d.ts +5 -0
- package/src/types/patch.d.ts +25 -0
- package/src/types/review.ts +294 -0
- package/src/types/reviewContext.d.ts +65 -0
- package/src/types/reviewSchema.ts +181 -0
- package/src/types/structuredReview.ts +167 -0
- package/src/types/tokenAnalysis.ts +56 -0
- package/src/utils/FileReader.ts +93 -0
- package/src/utils/FileWriter.ts +76 -0
- package/src/utils/PathGenerator.ts +97 -0
- package/src/utils/api/apiUtils.ts +14 -0
- package/src/utils/api/index.ts +1 -0
- package/src/utils/apiErrorHandler.ts +287 -0
- package/src/utils/ciDataCollector.ts +252 -0
- package/src/utils/codingTestConfigLoader.ts +466 -0
- package/src/utils/dependencies/aiDependencyAnalyzer.ts +454 -0
- package/src/utils/detection/frameworkDetector.ts +879 -0
- package/src/utils/detection/index.ts +10 -0
- package/src/utils/detection/projectTypeDetector.ts +518 -0
- package/src/utils/diagramGenerator.ts +206 -0
- package/src/utils/errorLogger.ts +60 -0
- package/src/utils/estimationUtils.ts +407 -0
- package/src/utils/fileFilters.ts +373 -0
- package/src/utils/fileSystem.ts +57 -0
- package/src/utils/index.ts +36 -0
- package/src/utils/logger.ts +240 -0
- package/src/utils/pathValidator.ts +98 -0
- package/src/utils/priorityFilter.ts +59 -0
- package/src/utils/projectDocs.ts +189 -0
- package/src/utils/promptPaths.ts +29 -0
- package/src/utils/promptTemplateManager.ts +157 -0
- package/src/utils/review/consolidateReview.ts +553 -0
- package/src/utils/review/fixDisplay.ts +100 -0
- package/src/utils/review/fixImplementation.ts +61 -0
- package/src/utils/review/index.ts +36 -0
- package/src/utils/review/interactiveProcessing.ts +294 -0
- package/src/utils/review/progressTracker.ts +296 -0
- package/src/utils/review/reviewExtraction.ts +382 -0
- package/src/utils/review/types.ts +46 -0
- package/src/utils/reviewActionHandler.ts +18 -0
- package/src/utils/reviewParser.ts +253 -0
- package/src/utils/sanitizer.ts +238 -0
- package/src/utils/smartFileSelector.ts +255 -0
- package/src/utils/templateLoader.ts +514 -0
- package/src/utils/treeGenerator.ts +153 -0
- package/tsconfig.build.json +14 -0
- 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
|
+
}
|