claude-flow-novice 1.3.1 → 1.3.2
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/package.json +2 -1
- package/src/language/README.md +503 -0
- package/src/language/claude-md-generator.js +618 -0
- package/src/language/cli.js +422 -0
- package/src/language/example.js +347 -0
- package/src/language/integration-system.js +619 -0
- package/src/language/language-detector.js +581 -0
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import pkg from 'glob';
|
|
4
|
+
const { glob } = pkg;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Language Detection System
|
|
8
|
+
*
|
|
9
|
+
* Scans project files to detect programming languages, frameworks, and dependencies
|
|
10
|
+
* Provides confidence scoring and detailed analysis for CLAUDE.md generation
|
|
11
|
+
*/
|
|
12
|
+
export class LanguageDetector {
|
|
13
|
+
constructor(projectPath = process.cwd()) {
|
|
14
|
+
this.projectPath = projectPath;
|
|
15
|
+
this.detectionResults = {
|
|
16
|
+
languages: {},
|
|
17
|
+
frameworks: {},
|
|
18
|
+
dependencies: {},
|
|
19
|
+
projectType: null,
|
|
20
|
+
confidence: 0,
|
|
21
|
+
metadata: {},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Language patterns and scoring weights
|
|
25
|
+
this.languagePatterns = {
|
|
26
|
+
javascript: {
|
|
27
|
+
extensions: ['.js', '.mjs', '.cjs'],
|
|
28
|
+
files: ['package.json', '.eslintrc*', 'babel.config.*'],
|
|
29
|
+
patterns: [/require\(/, /import\s+.*from/, /module\.exports/, /export\s+(default\s+)?/],
|
|
30
|
+
weight: 1.0,
|
|
31
|
+
},
|
|
32
|
+
typescript: {
|
|
33
|
+
extensions: ['.ts', '.tsx'],
|
|
34
|
+
files: ['tsconfig.json', 'tslint.json', '.tsconfig.json'],
|
|
35
|
+
patterns: [/interface\s+\w+/, /type\s+\w+\s*=/, /:\s*\w+(\[\])?/, /import.*from.*\.ts/],
|
|
36
|
+
weight: 1.2,
|
|
37
|
+
},
|
|
38
|
+
python: {
|
|
39
|
+
extensions: ['.py', '.pyx', '.pyw'],
|
|
40
|
+
files: ['requirements.txt', 'setup.py', 'pyproject.toml', 'Pipfile', '__init__.py'],
|
|
41
|
+
patterns: [/def\s+\w+\s*\(/, /import\s+\w+/, /from\s+\w+\s+import/, /class\s+\w+/],
|
|
42
|
+
weight: 1.0,
|
|
43
|
+
},
|
|
44
|
+
java: {
|
|
45
|
+
extensions: ['.java'],
|
|
46
|
+
files: ['pom.xml', 'build.gradle', '.gradle'],
|
|
47
|
+
patterns: [/public\s+class/, /package\s+[\w.]+/, /import\s+[\w.]+/],
|
|
48
|
+
weight: 1.0,
|
|
49
|
+
},
|
|
50
|
+
go: {
|
|
51
|
+
extensions: ['.go'],
|
|
52
|
+
files: ['go.mod', 'go.sum'],
|
|
53
|
+
patterns: [/package\s+main/, /func\s+\w+/, /import\s*\(/, /var\s+\w+\s+\w+/],
|
|
54
|
+
weight: 1.0,
|
|
55
|
+
},
|
|
56
|
+
rust: {
|
|
57
|
+
extensions: ['.rs'],
|
|
58
|
+
files: ['Cargo.toml', 'Cargo.lock'],
|
|
59
|
+
patterns: [/fn\s+\w+/, /struct\s+\w+/, /impl\s+\w+/, /use\s+[\w:]+/],
|
|
60
|
+
weight: 1.0,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Framework detection patterns
|
|
65
|
+
this.frameworkPatterns = {
|
|
66
|
+
react: {
|
|
67
|
+
dependencies: ['react', '@types/react', 'react-dom'],
|
|
68
|
+
patterns: [/import.*React/, /from\s+['"]react['"]/, /jsx?/, /\.tsx?$/],
|
|
69
|
+
files: ['.babelrc', 'next.config.js'],
|
|
70
|
+
weight: 1.5,
|
|
71
|
+
},
|
|
72
|
+
nextjs: {
|
|
73
|
+
dependencies: ['next'],
|
|
74
|
+
patterns: [/from\s+['"]next\//, /export.*getServerSideProps/, /export.*getStaticProps/],
|
|
75
|
+
files: ['next.config.js', 'pages/', 'app/'],
|
|
76
|
+
weight: 1.8,
|
|
77
|
+
},
|
|
78
|
+
vue: {
|
|
79
|
+
dependencies: ['vue', '@vue/cli'],
|
|
80
|
+
patterns: [/import.*Vue/, /<template>/, /<script>/, /\.vue$/],
|
|
81
|
+
files: ['vue.config.js'],
|
|
82
|
+
weight: 1.5,
|
|
83
|
+
},
|
|
84
|
+
angular: {
|
|
85
|
+
dependencies: ['@angular/core', '@angular/cli'],
|
|
86
|
+
patterns: [/import.*@angular/, /@Component/, /@Injectable/, /\.component\.ts$/],
|
|
87
|
+
files: ['angular.json', '.angular-cli.json'],
|
|
88
|
+
weight: 1.5,
|
|
89
|
+
},
|
|
90
|
+
express: {
|
|
91
|
+
dependencies: ['express'],
|
|
92
|
+
patterns: [/require\(['"]express['"]/, /app\.get/, /app\.post/, /app\.listen/],
|
|
93
|
+
files: [],
|
|
94
|
+
weight: 1.3,
|
|
95
|
+
},
|
|
96
|
+
fastify: {
|
|
97
|
+
dependencies: ['fastify'],
|
|
98
|
+
patterns: [/require\(['"]fastify['"]/, /fastify\.register/, /fastify\.listen/],
|
|
99
|
+
files: [],
|
|
100
|
+
weight: 1.3,
|
|
101
|
+
},
|
|
102
|
+
django: {
|
|
103
|
+
dependencies: ['django'],
|
|
104
|
+
patterns: [/from\s+django/, /django\./, /INSTALLED_APPS/, /urls\.py$/],
|
|
105
|
+
files: ['manage.py', 'settings.py', 'wsgi.py'],
|
|
106
|
+
weight: 1.5,
|
|
107
|
+
},
|
|
108
|
+
flask: {
|
|
109
|
+
dependencies: ['flask'],
|
|
110
|
+
patterns: [/from\s+flask/, /Flask\(__name__\)/, /app\.route/, /@app\.route/],
|
|
111
|
+
files: ['app.py', 'wsgi.py'],
|
|
112
|
+
weight: 1.3,
|
|
113
|
+
},
|
|
114
|
+
fastapi: {
|
|
115
|
+
dependencies: ['fastapi'],
|
|
116
|
+
patterns: [/from\s+fastapi/, /FastAPI\(\)/, /@app\.get/, /@app\.post/],
|
|
117
|
+
files: ['main.py'],
|
|
118
|
+
weight: 1.4,
|
|
119
|
+
},
|
|
120
|
+
spring: {
|
|
121
|
+
dependencies: ['spring-boot-starter'],
|
|
122
|
+
patterns: [/@SpringBootApplication/, /@RestController/, /@Service/],
|
|
123
|
+
files: ['application.properties', 'application.yml'],
|
|
124
|
+
weight: 1.5,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Main detection method - analyzes the entire project
|
|
131
|
+
*/
|
|
132
|
+
async detectProject() {
|
|
133
|
+
console.log(`🔍 Scanning project at: ${this.projectPath}`);
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
// Parallel detection for better performance
|
|
137
|
+
await Promise.all([
|
|
138
|
+
this.scanPackageFiles(),
|
|
139
|
+
this.scanSourceFiles(),
|
|
140
|
+
this.analyzeProjectStructure(),
|
|
141
|
+
this.detectBuildTools(),
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
// Calculate confidence scores
|
|
145
|
+
this.calculateConfidenceScores();
|
|
146
|
+
|
|
147
|
+
// Determine primary project type
|
|
148
|
+
this.determinePrimaryProjectType();
|
|
149
|
+
|
|
150
|
+
console.log(
|
|
151
|
+
`✅ Detection complete. Found ${Object.keys(this.detectionResults.languages).length} languages`,
|
|
152
|
+
);
|
|
153
|
+
return this.detectionResults;
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.error(`❌ Detection failed: ${error.message}`);
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Scan package management files (package.json, requirements.txt, etc.)
|
|
162
|
+
*/
|
|
163
|
+
async scanPackageFiles() {
|
|
164
|
+
const packageFiles = [
|
|
165
|
+
'package.json',
|
|
166
|
+
'requirements.txt',
|
|
167
|
+
'pyproject.toml',
|
|
168
|
+
'Pipfile',
|
|
169
|
+
'pom.xml',
|
|
170
|
+
'build.gradle',
|
|
171
|
+
'Cargo.toml',
|
|
172
|
+
'go.mod',
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
for (const file of packageFiles) {
|
|
176
|
+
const filePath = path.join(this.projectPath, file);
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
180
|
+
await this.analyzePackageFile(file, content);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
// File doesn't exist, continue
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Analyze specific package files for dependencies and metadata
|
|
190
|
+
*/
|
|
191
|
+
async analyzePackageFile(filename, content) {
|
|
192
|
+
switch (filename) {
|
|
193
|
+
case 'package.json':
|
|
194
|
+
await this.analyzePackageJson(content);
|
|
195
|
+
break;
|
|
196
|
+
case 'requirements.txt':
|
|
197
|
+
await this.analyzeRequirementsTxt(content);
|
|
198
|
+
break;
|
|
199
|
+
case 'pyproject.toml':
|
|
200
|
+
await this.analyzePyprojectToml(content);
|
|
201
|
+
break;
|
|
202
|
+
case 'pom.xml':
|
|
203
|
+
await this.analyzePomXml(content);
|
|
204
|
+
break;
|
|
205
|
+
case 'Cargo.toml':
|
|
206
|
+
await this.analyzeCargoToml(content);
|
|
207
|
+
break;
|
|
208
|
+
case 'go.mod':
|
|
209
|
+
await this.analyzeGoMod(content);
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async analyzePackageJson(content) {
|
|
215
|
+
try {
|
|
216
|
+
const pkg = JSON.parse(content);
|
|
217
|
+
const allDeps = {
|
|
218
|
+
...pkg.dependencies,
|
|
219
|
+
...pkg.devDependencies,
|
|
220
|
+
...pkg.peerDependencies,
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// Detect JavaScript/TypeScript
|
|
224
|
+
this.incrementLanguageScore('javascript', 0.8);
|
|
225
|
+
|
|
226
|
+
if (allDeps.typescript || allDeps['@types/node']) {
|
|
227
|
+
this.incrementLanguageScore('typescript', 1.2);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Detect frameworks
|
|
231
|
+
for (const [framework, config] of Object.entries(this.frameworkPatterns)) {
|
|
232
|
+
const hasFramework = config.dependencies.some((dep) =>
|
|
233
|
+
Object.keys(allDeps).some(
|
|
234
|
+
(installedDep) => installedDep.includes(dep) || dep.includes(installedDep),
|
|
235
|
+
),
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
if (hasFramework) {
|
|
239
|
+
this.incrementFrameworkScore(framework, config.weight);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Store metadata
|
|
244
|
+
this.detectionResults.metadata.packageManager = 'npm';
|
|
245
|
+
this.detectionResults.metadata.projectName = pkg.name;
|
|
246
|
+
this.detectionResults.metadata.scripts = pkg.scripts || {};
|
|
247
|
+
this.detectionResults.dependencies = { ...this.detectionResults.dependencies, ...allDeps };
|
|
248
|
+
} catch (error) {
|
|
249
|
+
console.warn(`Failed to parse package.json: ${error.message}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async analyzeRequirementsTxt(content) {
|
|
254
|
+
const lines = content.split('\n').filter((line) => line.trim() && !line.startsWith('#'));
|
|
255
|
+
|
|
256
|
+
this.incrementLanguageScore('python', 0.9);
|
|
257
|
+
this.detectionResults.metadata.packageManager = 'pip';
|
|
258
|
+
|
|
259
|
+
// Extract package names and detect frameworks
|
|
260
|
+
for (const line of lines) {
|
|
261
|
+
const packageName = line
|
|
262
|
+
.split(/[>=<~!]/)[0]
|
|
263
|
+
.toLowerCase()
|
|
264
|
+
.trim();
|
|
265
|
+
this.detectionResults.dependencies[packageName] = line;
|
|
266
|
+
|
|
267
|
+
// Check for Python frameworks
|
|
268
|
+
for (const [framework, config] of Object.entries(this.frameworkPatterns)) {
|
|
269
|
+
if (config.dependencies.some((dep) => packageName.includes(dep.toLowerCase()))) {
|
|
270
|
+
this.incrementFrameworkScore(framework, config.weight);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
async analyzePyprojectToml(content) {
|
|
277
|
+
this.incrementLanguageScore('python', 0.8);
|
|
278
|
+
this.detectionResults.metadata.packageManager = 'poetry';
|
|
279
|
+
|
|
280
|
+
// Basic TOML parsing for dependencies
|
|
281
|
+
const depMatches = content.match(/\[tool\.poetry\.dependencies\]([\s\S]*?)(?=\[|$)/);
|
|
282
|
+
if (depMatches) {
|
|
283
|
+
const depSection = depMatches[1];
|
|
284
|
+
const deps = depSection.match(/^(\w+)\s*=/gm);
|
|
285
|
+
if (deps) {
|
|
286
|
+
deps.forEach((dep) => {
|
|
287
|
+
const name = dep.split('=')[0].trim();
|
|
288
|
+
this.detectionResults.dependencies[name] = dep;
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async analyzePomXml(content) {
|
|
295
|
+
this.incrementLanguageScore('java', 1.0);
|
|
296
|
+
this.detectionResults.metadata.packageManager = 'maven';
|
|
297
|
+
|
|
298
|
+
// Extract Spring Boot detection
|
|
299
|
+
if (content.includes('spring-boot')) {
|
|
300
|
+
this.incrementFrameworkScore('spring', 1.5);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async analyzeCargoToml(content) {
|
|
305
|
+
this.incrementLanguageScore('rust', 1.0);
|
|
306
|
+
this.detectionResults.metadata.packageManager = 'cargo';
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async analyzeGoMod(content) {
|
|
310
|
+
this.incrementLanguageScore('go', 1.0);
|
|
311
|
+
this.detectionResults.metadata.packageManager = 'go';
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Scan source files for language patterns
|
|
316
|
+
*/
|
|
317
|
+
async scanSourceFiles() {
|
|
318
|
+
const sourceFiles = await glob('**/*.{js,ts,tsx,jsx,py,java,go,rs,vue}', {
|
|
319
|
+
cwd: this.projectPath,
|
|
320
|
+
ignore: ['node_modules/**', 'dist/**', 'build/**', '.git/**', 'vendor/**', 'target/**'],
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
console.log(`📁 Found ${sourceFiles.length} source files to analyze`);
|
|
324
|
+
|
|
325
|
+
// Process files in batches for performance
|
|
326
|
+
const batchSize = 50;
|
|
327
|
+
for (let i = 0; i < sourceFiles.length; i += batchSize) {
|
|
328
|
+
const batch = sourceFiles.slice(i, i + batchSize);
|
|
329
|
+
await Promise.all(batch.map((file) => this.analyzeSourceFile(file)));
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Analyze individual source file
|
|
335
|
+
*/
|
|
336
|
+
async analyzeSourceFile(relativePath) {
|
|
337
|
+
const filePath = path.join(this.projectPath, relativePath);
|
|
338
|
+
const ext = path.extname(relativePath);
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
342
|
+
|
|
343
|
+
// Detect language by extension
|
|
344
|
+
for (const [lang, config] of Object.entries(this.languagePatterns)) {
|
|
345
|
+
if (config.extensions.includes(ext)) {
|
|
346
|
+
this.incrementLanguageScore(lang, 0.5);
|
|
347
|
+
|
|
348
|
+
// Check for language-specific patterns
|
|
349
|
+
config.patterns.forEach((pattern) => {
|
|
350
|
+
if (pattern.test(content)) {
|
|
351
|
+
this.incrementLanguageScore(lang, 0.3);
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Check for framework patterns
|
|
358
|
+
for (const [framework, config] of Object.entries(this.frameworkPatterns)) {
|
|
359
|
+
config.patterns.forEach((pattern) => {
|
|
360
|
+
if (pattern.test(content) || pattern.test(relativePath)) {
|
|
361
|
+
this.incrementFrameworkScore(framework, 0.4);
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
} catch (error) {
|
|
366
|
+
console.warn(`Could not analyze ${relativePath}: ${error.message}`);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Analyze project structure for additional clues
|
|
372
|
+
*/
|
|
373
|
+
async analyzeProjectStructure() {
|
|
374
|
+
const directories = [
|
|
375
|
+
'src',
|
|
376
|
+
'lib',
|
|
377
|
+
'app',
|
|
378
|
+
'pages',
|
|
379
|
+
'components',
|
|
380
|
+
'views',
|
|
381
|
+
'models',
|
|
382
|
+
'controllers',
|
|
383
|
+
'routes',
|
|
384
|
+
];
|
|
385
|
+
|
|
386
|
+
for (const dir of directories) {
|
|
387
|
+
const dirPath = path.join(this.projectPath, dir);
|
|
388
|
+
try {
|
|
389
|
+
const stat = await fs.stat(dirPath);
|
|
390
|
+
if (stat.isDirectory()) {
|
|
391
|
+
this.detectionResults.metadata.directories =
|
|
392
|
+
this.detectionResults.metadata.directories || [];
|
|
393
|
+
this.detectionResults.metadata.directories.push(dir);
|
|
394
|
+
|
|
395
|
+
// Specific directory patterns
|
|
396
|
+
if (dir === 'pages' || dir === 'app') {
|
|
397
|
+
this.incrementFrameworkScore('nextjs', 0.5);
|
|
398
|
+
}
|
|
399
|
+
if (dir === 'components') {
|
|
400
|
+
this.incrementFrameworkScore('react', 0.3);
|
|
401
|
+
this.incrementFrameworkScore('vue', 0.3);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
} catch (error) {
|
|
405
|
+
// Directory doesn't exist
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Detect build tools and configuration files
|
|
413
|
+
*/
|
|
414
|
+
async detectBuildTools() {
|
|
415
|
+
const buildFiles = {
|
|
416
|
+
'webpack.config.js': { tool: 'webpack', score: 0.5 },
|
|
417
|
+
'vite.config.js': { tool: 'vite', score: 0.5 },
|
|
418
|
+
'rollup.config.js': { tool: 'rollup', score: 0.5 },
|
|
419
|
+
'gulpfile.js': { tool: 'gulp', score: 0.5 },
|
|
420
|
+
Makefile: { tool: 'make', score: 0.5 },
|
|
421
|
+
'docker-compose.yml': { tool: 'docker', score: 0.7 },
|
|
422
|
+
Dockerfile: { tool: 'docker', score: 0.7 },
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
for (const [file, config] of Object.entries(buildFiles)) {
|
|
426
|
+
try {
|
|
427
|
+
await fs.access(path.join(this.projectPath, file));
|
|
428
|
+
this.detectionResults.metadata.buildTools = this.detectionResults.metadata.buildTools || {};
|
|
429
|
+
this.detectionResults.metadata.buildTools[config.tool] = config.score;
|
|
430
|
+
} catch (error) {
|
|
431
|
+
// File doesn't exist
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Helper methods for scoring
|
|
439
|
+
*/
|
|
440
|
+
incrementLanguageScore(language, score) {
|
|
441
|
+
this.detectionResults.languages[language] =
|
|
442
|
+
(this.detectionResults.languages[language] || 0) + score;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
incrementFrameworkScore(framework, score) {
|
|
446
|
+
this.detectionResults.frameworks[framework] =
|
|
447
|
+
(this.detectionResults.frameworks[framework] || 0) + score;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Calculate confidence scores for all detected technologies
|
|
452
|
+
*/
|
|
453
|
+
calculateConfidenceScores() {
|
|
454
|
+
// Normalize language scores
|
|
455
|
+
const maxLangScore = Math.max(...Object.values(this.detectionResults.languages));
|
|
456
|
+
if (maxLangScore > 0) {
|
|
457
|
+
for (const lang in this.detectionResults.languages) {
|
|
458
|
+
this.detectionResults.languages[lang] = Math.min(
|
|
459
|
+
this.detectionResults.languages[lang] / maxLangScore,
|
|
460
|
+
1.0,
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Normalize framework scores
|
|
466
|
+
const maxFrameworkScore = Math.max(...Object.values(this.detectionResults.frameworks), 1);
|
|
467
|
+
for (const framework in this.detectionResults.frameworks) {
|
|
468
|
+
this.detectionResults.frameworks[framework] = Math.min(
|
|
469
|
+
this.detectionResults.frameworks[framework] / maxFrameworkScore,
|
|
470
|
+
1.0,
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Calculate overall confidence
|
|
475
|
+
const totalLanguages = Object.keys(this.detectionResults.languages).length;
|
|
476
|
+
const totalFrameworks = Object.keys(this.detectionResults.frameworks).length;
|
|
477
|
+
this.detectionResults.confidence = Math.min(
|
|
478
|
+
(totalLanguages * 0.6 + totalFrameworks * 0.4) / 2,
|
|
479
|
+
1.0,
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Determine the primary project type based on scores
|
|
485
|
+
*/
|
|
486
|
+
determinePrimaryProjectType() {
|
|
487
|
+
const languages = this.detectionResults.languages;
|
|
488
|
+
const frameworks = this.detectionResults.frameworks;
|
|
489
|
+
|
|
490
|
+
const primaryLang = Object.keys(languages).reduce(
|
|
491
|
+
(a, b) => (languages[a] > languages[b] ? a : b),
|
|
492
|
+
Object.keys(languages)[0],
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
const primaryFramework = Object.keys(frameworks).reduce(
|
|
496
|
+
(a, b) => (frameworks[a] > frameworks[b] ? a : b),
|
|
497
|
+
Object.keys(frameworks)[0],
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
// Determine project type
|
|
501
|
+
if (frameworks.react && languages.typescript) {
|
|
502
|
+
this.detectionResults.projectType = 'react-typescript';
|
|
503
|
+
} else if (frameworks.react) {
|
|
504
|
+
this.detectionResults.projectType = 'react';
|
|
505
|
+
} else if (frameworks.nextjs) {
|
|
506
|
+
this.detectionResults.projectType = 'nextjs';
|
|
507
|
+
} else if (frameworks.vue) {
|
|
508
|
+
this.detectionResults.projectType = 'vue';
|
|
509
|
+
} else if (frameworks.express) {
|
|
510
|
+
this.detectionResults.projectType = 'express-api';
|
|
511
|
+
} else if (frameworks.django) {
|
|
512
|
+
this.detectionResults.projectType = 'django';
|
|
513
|
+
} else if (frameworks.flask) {
|
|
514
|
+
this.detectionResults.projectType = 'flask';
|
|
515
|
+
} else if (frameworks.fastapi) {
|
|
516
|
+
this.detectionResults.projectType = 'fastapi';
|
|
517
|
+
} else if (primaryLang) {
|
|
518
|
+
this.detectionResults.projectType = primaryLang;
|
|
519
|
+
} else {
|
|
520
|
+
this.detectionResults.projectType = 'unknown';
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
this.detectionResults.metadata.primaryLanguage = primaryLang;
|
|
524
|
+
this.detectionResults.metadata.primaryFramework = primaryFramework;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Get recommendations for tooling and best practices
|
|
529
|
+
*/
|
|
530
|
+
getRecommendations() {
|
|
531
|
+
const recommendations = {
|
|
532
|
+
linting: [],
|
|
533
|
+
testing: [],
|
|
534
|
+
building: [],
|
|
535
|
+
deployment: [],
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
const { languages, frameworks } = this.detectionResults;
|
|
539
|
+
|
|
540
|
+
// Linting recommendations
|
|
541
|
+
if (languages.javascript || languages.typescript) {
|
|
542
|
+
recommendations.linting.push('ESLint', 'Prettier');
|
|
543
|
+
}
|
|
544
|
+
if (languages.python) {
|
|
545
|
+
recommendations.linting.push('Black', 'Flake8', 'mypy');
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Testing recommendations
|
|
549
|
+
if (frameworks.react || frameworks.nextjs) {
|
|
550
|
+
recommendations.testing.push('Jest', 'React Testing Library');
|
|
551
|
+
} else if (languages.javascript || languages.typescript) {
|
|
552
|
+
recommendations.testing.push('Jest', 'Vitest');
|
|
553
|
+
}
|
|
554
|
+
if (languages.python) {
|
|
555
|
+
recommendations.testing.push('pytest', 'unittest');
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Build recommendations
|
|
559
|
+
if (frameworks.react && !frameworks.nextjs) {
|
|
560
|
+
recommendations.building.push('Vite', 'Create React App');
|
|
561
|
+
}
|
|
562
|
+
if (languages.typescript) {
|
|
563
|
+
recommendations.building.push('tsc', 'esbuild');
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return recommendations;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Export results for use by other systems
|
|
571
|
+
*/
|
|
572
|
+
exportResults() {
|
|
573
|
+
return {
|
|
574
|
+
...this.detectionResults,
|
|
575
|
+
timestamp: new Date().toISOString(),
|
|
576
|
+
recommendations: this.getRecommendations(),
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
export default LanguageDetector;
|