@itz4blitz/agentful 0.1.0 → 0.1.5
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/.claude/agents/architect.md +283 -11
- package/.claude/agents/backend.md +282 -218
- package/.claude/agents/frontend.md +242 -319
- package/.claude/agents/orchestrator.md +27 -27
- package/.claude/agents/reviewer.md +1 -1
- package/.claude/agents/tester.md +375 -284
- package/.claude/commands/agentful-decide.md +104 -29
- package/.claude/commands/agentful-start.md +18 -16
- package/.claude/commands/agentful-status.md +28 -22
- package/.claude/commands/agentful-validate.md +42 -20
- package/.claude/commands/agentful.md +329 -0
- package/.claude/product/README.md +1 -1
- package/.claude/product/index.md +1 -1
- package/.claude/settings.json +4 -3
- package/.claude/skills/conversation/SKILL.md +1130 -0
- package/LICENSE +1 -1
- package/README.md +557 -222
- package/bin/cli.js +319 -36
- package/lib/agent-generator.js +685 -0
- package/lib/domain-detector.js +468 -0
- package/lib/domain-structure-generator.js +770 -0
- package/lib/index.js +40 -0
- package/lib/project-analyzer.js +701 -0
- package/lib/tech-stack-detector.js +1091 -0
- package/lib/template-engine.js +153 -0
- package/package.json +14 -5
- package/template/CLAUDE.md +62 -21
- package/template/PRODUCT.md +89 -1
|
@@ -0,0 +1,1091 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tech Stack Detector
|
|
5
|
+
* Comprehensive technology detection for multiple languages and frameworks
|
|
6
|
+
* Supports: JavaScript, TypeScript, Python, Go, Rust, Java, .NET, Ruby, PHP, and more
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Language detection patterns
|
|
14
|
+
*/
|
|
15
|
+
const LANGUAGE_PATTERNS = {
|
|
16
|
+
'JavaScript': {
|
|
17
|
+
extensions: ['.js', '.jsx'],
|
|
18
|
+
dependencies: ['javascript'],
|
|
19
|
+
configFiles: ['package.json']
|
|
20
|
+
},
|
|
21
|
+
'TypeScript': {
|
|
22
|
+
extensions: ['.ts', '.tsx'],
|
|
23
|
+
dependencies: ['typescript'],
|
|
24
|
+
configFiles: ['tsconfig.json', 'jsconfig.json']
|
|
25
|
+
},
|
|
26
|
+
'Python': {
|
|
27
|
+
extensions: ['.py'],
|
|
28
|
+
dependencies: [],
|
|
29
|
+
configFiles: ['requirements.txt', 'pyproject.toml', 'setup.py', 'Pipfile', 'poetry.lock']
|
|
30
|
+
},
|
|
31
|
+
'Go': {
|
|
32
|
+
extensions: ['.go'],
|
|
33
|
+
dependencies: [],
|
|
34
|
+
configFiles: ['go.mod', 'go.sum']
|
|
35
|
+
},
|
|
36
|
+
'Rust': {
|
|
37
|
+
extensions: ['.rs'],
|
|
38
|
+
dependencies: [],
|
|
39
|
+
configFiles: ['Cargo.toml', 'Cargo.lock']
|
|
40
|
+
},
|
|
41
|
+
'Java': {
|
|
42
|
+
extensions: ['.java'],
|
|
43
|
+
dependencies: [],
|
|
44
|
+
configFiles: ['pom.xml', 'build.gradle', 'gradle.properties']
|
|
45
|
+
},
|
|
46
|
+
'C#': {
|
|
47
|
+
extensions: ['.cs'],
|
|
48
|
+
dependencies: [],
|
|
49
|
+
configFiles: ['*.csproj', '*.vbproj', 'project.json']
|
|
50
|
+
},
|
|
51
|
+
'Ruby': {
|
|
52
|
+
extensions: ['.rb'],
|
|
53
|
+
dependencies: [],
|
|
54
|
+
configFiles: ['Gemfile', '*.gemspec']
|
|
55
|
+
},
|
|
56
|
+
'PHP': {
|
|
57
|
+
extensions: ['.php'],
|
|
58
|
+
dependencies: [],
|
|
59
|
+
configFiles: ['composer.json']
|
|
60
|
+
},
|
|
61
|
+
'C++': {
|
|
62
|
+
extensions: ['.cpp', '.cc', '.cxx'],
|
|
63
|
+
dependencies: [],
|
|
64
|
+
configFiles: ['CMakeLists.txt', 'Makefile']
|
|
65
|
+
},
|
|
66
|
+
'Elixir': {
|
|
67
|
+
extensions: ['.ex', '.exs'],
|
|
68
|
+
dependencies: [],
|
|
69
|
+
configFiles: ['mix.exs']
|
|
70
|
+
},
|
|
71
|
+
'Dart': {
|
|
72
|
+
extensions: ['.dart'],
|
|
73
|
+
dependencies: [],
|
|
74
|
+
configFiles: ['pubspec.yaml']
|
|
75
|
+
},
|
|
76
|
+
'Swift': {
|
|
77
|
+
extensions: ['.swift'],
|
|
78
|
+
dependencies: [],
|
|
79
|
+
configFiles: ['Package.swift']
|
|
80
|
+
},
|
|
81
|
+
'Kotlin': {
|
|
82
|
+
extensions: ['.kt', '.kts'],
|
|
83
|
+
dependencies: [],
|
|
84
|
+
configFiles: ['build.gradle.kts']
|
|
85
|
+
},
|
|
86
|
+
'Scala': {
|
|
87
|
+
extensions: ['.scala'],
|
|
88
|
+
dependencies: [],
|
|
89
|
+
configFiles: ['build.sbt']
|
|
90
|
+
},
|
|
91
|
+
'Clojure': {
|
|
92
|
+
extensions: ['.clj', '.cljs'],
|
|
93
|
+
dependencies: [],
|
|
94
|
+
configFiles: ['project.clj', 'deps.edn']
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Framework detection patterns
|
|
100
|
+
*/
|
|
101
|
+
const FRAMEWORK_PATTERNS = {
|
|
102
|
+
// JavaScript/TypeScript frameworks
|
|
103
|
+
'Next.js': {
|
|
104
|
+
dependencies: ['next', 'next.js'],
|
|
105
|
+
configFiles: ['next.config.js', 'next.config.mjs', 'next.config.ts'],
|
|
106
|
+
language: ['TypeScript', 'JavaScript']
|
|
107
|
+
},
|
|
108
|
+
'React': {
|
|
109
|
+
dependencies: ['react', 'react-dom'],
|
|
110
|
+
configFiles: [],
|
|
111
|
+
language: ['TypeScript', 'JavaScript']
|
|
112
|
+
},
|
|
113
|
+
'Vue': {
|
|
114
|
+
dependencies: ['vue', '@vue/core'],
|
|
115
|
+
configFiles: ['vue.config.js', 'vite.config.js', 'nuxt.config.js'],
|
|
116
|
+
language: ['TypeScript', 'JavaScript']
|
|
117
|
+
},
|
|
118
|
+
'Angular': {
|
|
119
|
+
dependencies: ['@angular/core', '@angular/common'],
|
|
120
|
+
configFiles: ['angular.json'],
|
|
121
|
+
language: ['TypeScript']
|
|
122
|
+
},
|
|
123
|
+
'Svelte': {
|
|
124
|
+
dependencies: ['svelte'],
|
|
125
|
+
configFiles: ['svelte.config.js'],
|
|
126
|
+
language: ['TypeScript', 'JavaScript']
|
|
127
|
+
},
|
|
128
|
+
'Express': {
|
|
129
|
+
dependencies: ['express'],
|
|
130
|
+
configFiles: [],
|
|
131
|
+
language: ['TypeScript', 'JavaScript']
|
|
132
|
+
},
|
|
133
|
+
'NestJS': {
|
|
134
|
+
dependencies: ['@nestjs/common', '@nestjs/core'],
|
|
135
|
+
configFiles: [],
|
|
136
|
+
language: ['TypeScript']
|
|
137
|
+
},
|
|
138
|
+
'Koa': {
|
|
139
|
+
dependencies: ['koa'],
|
|
140
|
+
configFiles: [],
|
|
141
|
+
language: ['TypeScript', 'JavaScript']
|
|
142
|
+
},
|
|
143
|
+
'Fastify': {
|
|
144
|
+
dependencies: ['fastify'],
|
|
145
|
+
configFiles: [],
|
|
146
|
+
language: ['TypeScript', 'JavaScript']
|
|
147
|
+
},
|
|
148
|
+
'Remix': {
|
|
149
|
+
dependencies: ['@remix-run/node', '@remix-run/react'],
|
|
150
|
+
configFiles: [],
|
|
151
|
+
language: ['TypeScript', 'JavaScript']
|
|
152
|
+
},
|
|
153
|
+
'Astro': {
|
|
154
|
+
dependencies: ['astro'],
|
|
155
|
+
configFiles: ['astro.config.js'],
|
|
156
|
+
language: ['TypeScript', 'JavaScript']
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
// Python frameworks
|
|
160
|
+
'Django': {
|
|
161
|
+
dependencies: ['django'],
|
|
162
|
+
configFiles: ['settings.py', 'manage.py'],
|
|
163
|
+
language: ['Python']
|
|
164
|
+
},
|
|
165
|
+
'Flask': {
|
|
166
|
+
dependencies: ['flask'],
|
|
167
|
+
configFiles: [],
|
|
168
|
+
language: ['Python']
|
|
169
|
+
},
|
|
170
|
+
'FastAPI': {
|
|
171
|
+
dependencies: ['fastapi', 'starlette'],
|
|
172
|
+
configFiles: [],
|
|
173
|
+
language: ['Python']
|
|
174
|
+
},
|
|
175
|
+
'Tornado': {
|
|
176
|
+
dependencies: ['tornado'],
|
|
177
|
+
configFiles: [],
|
|
178
|
+
language: ['Python']
|
|
179
|
+
},
|
|
180
|
+
'Falcon': {
|
|
181
|
+
dependencies: ['falcon'],
|
|
182
|
+
configFiles: [],
|
|
183
|
+
language: ['Python']
|
|
184
|
+
},
|
|
185
|
+
'Pyramid': {
|
|
186
|
+
dependencies: ['pyramid'],
|
|
187
|
+
configFiles: [],
|
|
188
|
+
language: ['Python']
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
// Go frameworks
|
|
192
|
+
'Gin': {
|
|
193
|
+
dependencies: ['gin-gonic/gin', 'github.com/gin-gonic/gin'],
|
|
194
|
+
configFiles: [],
|
|
195
|
+
language: ['Go']
|
|
196
|
+
},
|
|
197
|
+
'Echo': {
|
|
198
|
+
dependencies: ['labstack/echo', 'github.com/labstack/echo'],
|
|
199
|
+
configFiles: [],
|
|
200
|
+
language: ['Go']
|
|
201
|
+
},
|
|
202
|
+
'Fiber': {
|
|
203
|
+
dependencies: ['gofiber/fiber', 'github.com/gofiber/fiber'],
|
|
204
|
+
configFiles: [],
|
|
205
|
+
language: ['Go']
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
// Rust frameworks
|
|
209
|
+
'Actix Web': {
|
|
210
|
+
dependencies: ['actix-web'],
|
|
211
|
+
configFiles: [],
|
|
212
|
+
language: ['Rust']
|
|
213
|
+
},
|
|
214
|
+
'Rocket': {
|
|
215
|
+
dependencies: ['rocket'],
|
|
216
|
+
configFiles: [],
|
|
217
|
+
language: ['Rust']
|
|
218
|
+
},
|
|
219
|
+
'Warp': {
|
|
220
|
+
dependencies: ['warp'],
|
|
221
|
+
configFiles: [],
|
|
222
|
+
language: ['Rust']
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
// Java frameworks
|
|
226
|
+
'Spring Boot': {
|
|
227
|
+
dependencies: ['spring-boot-starter'],
|
|
228
|
+
configFiles: [],
|
|
229
|
+
language: ['Java']
|
|
230
|
+
},
|
|
231
|
+
'Micronaut': {
|
|
232
|
+
dependencies: ['io.micronaut'],
|
|
233
|
+
configFiles: [],
|
|
234
|
+
language: ['Java']
|
|
235
|
+
},
|
|
236
|
+
'Quarkus': {
|
|
237
|
+
dependencies: ['io.quarkus'],
|
|
238
|
+
configFiles: [],
|
|
239
|
+
language: ['Java']
|
|
240
|
+
},
|
|
241
|
+
'Vert.x': {
|
|
242
|
+
dependencies: ['io.vertx'],
|
|
243
|
+
configFiles: [],
|
|
244
|
+
language: ['Java']
|
|
245
|
+
},
|
|
246
|
+
'Jakarta EE': {
|
|
247
|
+
dependencies: ['jakarta'],
|
|
248
|
+
configFiles: [],
|
|
249
|
+
language: ['Java']
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
// .NET frameworks
|
|
253
|
+
'ASP.NET Core': {
|
|
254
|
+
dependencies: ['Microsoft.AspNetCore'],
|
|
255
|
+
configFiles: [],
|
|
256
|
+
language: ['C#']
|
|
257
|
+
},
|
|
258
|
+
'Entity Framework': {
|
|
259
|
+
dependencies: ['Microsoft.EntityFrameworkCore'],
|
|
260
|
+
configFiles: [],
|
|
261
|
+
language: ['C#']
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
// Ruby frameworks
|
|
265
|
+
'Rails': {
|
|
266
|
+
dependencies: ['rails'],
|
|
267
|
+
configFiles: ['config/application.rb'],
|
|
268
|
+
language: ['Ruby']
|
|
269
|
+
},
|
|
270
|
+
'Sinatra': {
|
|
271
|
+
dependencies: ['sinatra'],
|
|
272
|
+
configFiles: [],
|
|
273
|
+
language: ['Ruby']
|
|
274
|
+
},
|
|
275
|
+
'Grape': {
|
|
276
|
+
dependencies: ['grape'],
|
|
277
|
+
configFiles: [],
|
|
278
|
+
language: ['Ruby']
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
// PHP frameworks
|
|
282
|
+
'Laravel': {
|
|
283
|
+
dependencies: ['laravel/framework'],
|
|
284
|
+
configFiles: [],
|
|
285
|
+
language: ['PHP']
|
|
286
|
+
},
|
|
287
|
+
'Symfony': {
|
|
288
|
+
dependencies: ['symfony'],
|
|
289
|
+
configFiles: [],
|
|
290
|
+
language: ['PHP']
|
|
291
|
+
},
|
|
292
|
+
'Slim': {
|
|
293
|
+
dependencies: ['slim/slim'],
|
|
294
|
+
configFiles: [],
|
|
295
|
+
language: ['PHP']
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
// Elixir frameworks
|
|
299
|
+
'Phoenix': {
|
|
300
|
+
dependencies: ['phoenix'],
|
|
301
|
+
configFiles: ['mix.exs'],
|
|
302
|
+
language: ['Elixir']
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
// Dart/Flutter
|
|
306
|
+
'Flutter': {
|
|
307
|
+
dependencies: ['flutter'],
|
|
308
|
+
configFiles: [],
|
|
309
|
+
language: ['Dart']
|
|
310
|
+
},
|
|
311
|
+
'Angel': {
|
|
312
|
+
dependencies: ['angel'],
|
|
313
|
+
configFiles: [],
|
|
314
|
+
language: ['Dart']
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
// Swift frameworks
|
|
318
|
+
'Vapor': {
|
|
319
|
+
dependencies: ['vapor'],
|
|
320
|
+
configFiles: [],
|
|
321
|
+
language: ['Swift']
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Database detection patterns
|
|
327
|
+
*/
|
|
328
|
+
const DATABASE_PATTERNS = {
|
|
329
|
+
'PostgreSQL': {
|
|
330
|
+
dependencies: ['pg', 'postgres', 'postgresql', 'pg-promise', 'node-postgres'],
|
|
331
|
+
orm: ['prisma', 'typeorm', 'sequelize', 'knex'],
|
|
332
|
+
configFiles: []
|
|
333
|
+
},
|
|
334
|
+
'MySQL': {
|
|
335
|
+
dependencies: ['mysql', 'mysql2', 'mysqldb'],
|
|
336
|
+
orm: ['prisma', 'typeorm', 'sequelize', 'knex'],
|
|
337
|
+
configFiles: []
|
|
338
|
+
},
|
|
339
|
+
'SQLite': {
|
|
340
|
+
dependencies: ['sqlite3', 'better-sqlite3'],
|
|
341
|
+
orm: ['prisma', 'typeorm', 'sequelize'],
|
|
342
|
+
configFiles: []
|
|
343
|
+
},
|
|
344
|
+
'MongoDB': {
|
|
345
|
+
dependencies: ['mongodb', 'mongoose'],
|
|
346
|
+
orm: ['mongoose', 'mongodb'],
|
|
347
|
+
configFiles: []
|
|
348
|
+
},
|
|
349
|
+
'Redis': {
|
|
350
|
+
dependencies: ['redis', 'ioredis'],
|
|
351
|
+
orm: [],
|
|
352
|
+
configFiles: []
|
|
353
|
+
},
|
|
354
|
+
'DynamoDB': {
|
|
355
|
+
dependencies: ['aws-sdk', '@aws-sdk/client-dynamodb'],
|
|
356
|
+
orm: [],
|
|
357
|
+
configFiles: []
|
|
358
|
+
},
|
|
359
|
+
'Cassandra': {
|
|
360
|
+
dependencies: ['cassandra-driver'],
|
|
361
|
+
orm: [],
|
|
362
|
+
configFiles: []
|
|
363
|
+
},
|
|
364
|
+
'Couchbase': {
|
|
365
|
+
dependencies: ['couchbase'],
|
|
366
|
+
orm: [],
|
|
367
|
+
configFiles: []
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Testing framework detection
|
|
373
|
+
*/
|
|
374
|
+
const TESTING_PATTERNS = {
|
|
375
|
+
'Jest': {
|
|
376
|
+
dependencies: ['jest', '@jest/globals'],
|
|
377
|
+
configFiles: ['jest.config.js', 'jest.config.ts', 'jest.config.json']
|
|
378
|
+
},
|
|
379
|
+
'Vitest': {
|
|
380
|
+
dependencies: ['vitest'],
|
|
381
|
+
configFiles: ['vitest.config.js', 'vitest.config.ts']
|
|
382
|
+
},
|
|
383
|
+
'Mocha': {
|
|
384
|
+
dependencies: ['mocha'],
|
|
385
|
+
configFiles: ['.mocharc.js', '.mocharc.json']
|
|
386
|
+
},
|
|
387
|
+
'Jasmine': {
|
|
388
|
+
dependencies: ['jasmine'],
|
|
389
|
+
configFiles: []
|
|
390
|
+
},
|
|
391
|
+
'PyTest': {
|
|
392
|
+
dependencies: ['pytest'],
|
|
393
|
+
configFiles: ['pytest.ini', 'pyproject.toml', 'setup.cfg']
|
|
394
|
+
},
|
|
395
|
+
'unittest': {
|
|
396
|
+
dependencies: [],
|
|
397
|
+
configFiles: [],
|
|
398
|
+
language: ['Python']
|
|
399
|
+
},
|
|
400
|
+
'Go Testing': {
|
|
401
|
+
dependencies: [],
|
|
402
|
+
configFiles: [],
|
|
403
|
+
language: ['Go']
|
|
404
|
+
},
|
|
405
|
+
'JUnit': {
|
|
406
|
+
dependencies: ['junit'],
|
|
407
|
+
configFiles: [],
|
|
408
|
+
language: ['Java']
|
|
409
|
+
},
|
|
410
|
+
'TestNG': {
|
|
411
|
+
dependencies: ['testng'],
|
|
412
|
+
configFiles: [],
|
|
413
|
+
language: ['Java']
|
|
414
|
+
},
|
|
415
|
+
'RSpec': {
|
|
416
|
+
dependencies: ['rspec'],
|
|
417
|
+
configFiles: [],
|
|
418
|
+
language: ['Ruby']
|
|
419
|
+
},
|
|
420
|
+
'PHPUnit': {
|
|
421
|
+
dependencies: ['phpunit'],
|
|
422
|
+
configFiles: ['phpunit.xml'],
|
|
423
|
+
language: ['PHP']
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Styling detection patterns
|
|
429
|
+
*/
|
|
430
|
+
const STYLING_PATTERNS = {
|
|
431
|
+
'Tailwind CSS': {
|
|
432
|
+
dependencies: ['tailwindcss', 'autoprefixer', 'postcss'],
|
|
433
|
+
configFiles: ['tailwind.config.js', 'tailwind.config.ts', 'postcss.config.js']
|
|
434
|
+
},
|
|
435
|
+
'CSS Modules': {
|
|
436
|
+
dependencies: [],
|
|
437
|
+
configFiles: [],
|
|
438
|
+
filePattern: ['.module.css', '.module.scss', '.module.sass']
|
|
439
|
+
},
|
|
440
|
+
'Styled Components': {
|
|
441
|
+
dependencies: ['styled-components'],
|
|
442
|
+
configFiles: []
|
|
443
|
+
},
|
|
444
|
+
'Emotion': {
|
|
445
|
+
dependencies: ['@emotion/react', '@emotion/styled'],
|
|
446
|
+
configFiles: []
|
|
447
|
+
},
|
|
448
|
+
'Sass/SCSS': {
|
|
449
|
+
dependencies: ['sass', 'node-sass', 'dart-sass'],
|
|
450
|
+
configFiles: []
|
|
451
|
+
},
|
|
452
|
+
'Less': {
|
|
453
|
+
dependencies: ['less'],
|
|
454
|
+
configFiles: []
|
|
455
|
+
},
|
|
456
|
+
'Styled JSX': {
|
|
457
|
+
dependencies: ['styled-jsx'],
|
|
458
|
+
configFiles: []
|
|
459
|
+
},
|
|
460
|
+
'Aphrodite': {
|
|
461
|
+
dependencies: ['aphrodite'],
|
|
462
|
+
configFiles: []
|
|
463
|
+
},
|
|
464
|
+
'Radium': {
|
|
465
|
+
dependencies: ['radium'],
|
|
466
|
+
configFiles: []
|
|
467
|
+
},
|
|
468
|
+
'Glamor': {
|
|
469
|
+
dependencies: ['glamor'],
|
|
470
|
+
configFiles: []
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Main tech stack detection function
|
|
476
|
+
* @param {string} projectRoot - Root directory of the project
|
|
477
|
+
* @returns {Promise<Object>} Comprehensive tech stack analysis
|
|
478
|
+
*/
|
|
479
|
+
export async function detectTechStack(projectRoot = process.cwd()) {
|
|
480
|
+
const techStack = {
|
|
481
|
+
language: 'unknown',
|
|
482
|
+
primaryLanguage: 'unknown',
|
|
483
|
+
languages: [],
|
|
484
|
+
frameworks: [],
|
|
485
|
+
databases: [],
|
|
486
|
+
testingFrameworks: [],
|
|
487
|
+
styling: [],
|
|
488
|
+
buildSystem: 'unknown',
|
|
489
|
+
packageManager: 'unknown',
|
|
490
|
+
dependencies: [],
|
|
491
|
+
devDependencies: [],
|
|
492
|
+
confidence: 0
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
try {
|
|
496
|
+
// Detect primary language
|
|
497
|
+
const languageDetection = await detectLanguage(projectRoot);
|
|
498
|
+
techStack.language = languageDetection.primary;
|
|
499
|
+
techStack.primaryLanguage = languageDetection.primary;
|
|
500
|
+
techStack.languages = languageDetection.all;
|
|
501
|
+
|
|
502
|
+
// Detect package manager and build system
|
|
503
|
+
const buildDetection = await detectBuildSystem(projectRoot, languageDetection.primary);
|
|
504
|
+
techStack.packageManager = buildDetection.packageManager;
|
|
505
|
+
techStack.buildSystem = buildDetection.buildSystem;
|
|
506
|
+
|
|
507
|
+
// Load dependencies if available
|
|
508
|
+
const depAnalysis = await analyzeDependencies(projectRoot, languageDetection.primary);
|
|
509
|
+
techStack.dependencies = depAnalysis.dependencies;
|
|
510
|
+
techStack.devDependencies = depAnalysis.devDependencies;
|
|
511
|
+
|
|
512
|
+
// Detect frameworks
|
|
513
|
+
techStack.frameworks = await detectFrameworks(projectRoot, languageDetection.primary, depAnalysis);
|
|
514
|
+
|
|
515
|
+
// Detect databases
|
|
516
|
+
techStack.databases = await detectDatabases(projectRoot, depAnalysis);
|
|
517
|
+
|
|
518
|
+
// Detect testing frameworks
|
|
519
|
+
techStack.testingFrameworks = await detectTestingFrameworks(projectRoot, languageDetection.primary, depAnalysis);
|
|
520
|
+
|
|
521
|
+
// Detect styling (for web projects)
|
|
522
|
+
if (['JavaScript', 'TypeScript'].includes(languageDetection.primary)) {
|
|
523
|
+
techStack.styling = await detectStyling(projectRoot, depAnalysis);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Calculate overall confidence
|
|
527
|
+
techStack.confidence = calculateTechStackConfidence(techStack);
|
|
528
|
+
|
|
529
|
+
} catch (error) {
|
|
530
|
+
techStack.error = error.message;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return techStack;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Detect primary programming language
|
|
538
|
+
*/
|
|
539
|
+
async function detectLanguage(projectRoot) {
|
|
540
|
+
const detection = {
|
|
541
|
+
primary: 'unknown',
|
|
542
|
+
all: [],
|
|
543
|
+
scores: {}
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
try {
|
|
547
|
+
const entries = fs.readdirSync(projectRoot, { withFileTypes: true });
|
|
548
|
+
|
|
549
|
+
// Score languages based on file extensions and config files
|
|
550
|
+
for (const [language, patterns] of Object.entries(LANGUAGE_PATTERNS)) {
|
|
551
|
+
let score = 0;
|
|
552
|
+
|
|
553
|
+
// Check for config files
|
|
554
|
+
for (const configFile of patterns.configFiles) {
|
|
555
|
+
const configPath = path.join(projectRoot, configFile.replace('*', ''));
|
|
556
|
+
if (fs.existsSync(configPath)) {
|
|
557
|
+
score += 10;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// Check for files with matching extensions (sample a few directories)
|
|
562
|
+
for (const entry of entries) {
|
|
563
|
+
if (entry.isDirectory()) {
|
|
564
|
+
const dirPath = path.join(projectRoot, entry.name);
|
|
565
|
+
try {
|
|
566
|
+
const subEntries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
567
|
+
for (const subEntry of subEntries) {
|
|
568
|
+
if (subEntry.isFile()) {
|
|
569
|
+
const ext = path.extname(subEntry.name);
|
|
570
|
+
if (patterns.extensions.includes(ext)) {
|
|
571
|
+
score += 1;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
} catch (error) {
|
|
576
|
+
// Skip directories we can't read
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (score > 0) {
|
|
582
|
+
detection.scores[language] = score;
|
|
583
|
+
detection.all.push(language);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Determine primary language (highest score)
|
|
588
|
+
let maxScore = 0;
|
|
589
|
+
for (const [language, score] of Object.entries(detection.scores)) {
|
|
590
|
+
if (score > maxScore) {
|
|
591
|
+
maxScore = score;
|
|
592
|
+
detection.primary = language;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Sort languages by score
|
|
597
|
+
detection.all.sort((a, b) => detection.scores[b] - detection.scores[a]);
|
|
598
|
+
|
|
599
|
+
} catch (error) {
|
|
600
|
+
detection.error = error.message;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return detection;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Detect build system and package manager
|
|
608
|
+
*/
|
|
609
|
+
async function detectBuildSystem(projectRoot, primaryLanguage) {
|
|
610
|
+
const detection = {
|
|
611
|
+
packageManager: 'unknown',
|
|
612
|
+
buildSystem: 'unknown'
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
try {
|
|
616
|
+
// Detect by language
|
|
617
|
+
switch (primaryLanguage) {
|
|
618
|
+
case 'JavaScript':
|
|
619
|
+
case 'TypeScript':
|
|
620
|
+
// Detect package manager
|
|
621
|
+
if (fs.existsSync(path.join(projectRoot, 'pnpm-lock.yaml'))) {
|
|
622
|
+
detection.packageManager = 'pnpm';
|
|
623
|
+
} else if (fs.existsSync(path.join(projectRoot, 'yarn.lock'))) {
|
|
624
|
+
detection.packageManager = 'yarn';
|
|
625
|
+
} else if (fs.existsSync(path.join(projectRoot, 'package-lock.json'))) {
|
|
626
|
+
detection.packageManager = 'npm';
|
|
627
|
+
} else if (fs.existsSync(path.join(projectRoot, 'bun.lockb'))) {
|
|
628
|
+
detection.packageManager = 'bun';
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// Detect build system
|
|
632
|
+
if (fs.existsSync(path.join(projectRoot, 'tsconfig.json'))) {
|
|
633
|
+
detection.buildSystem = 'tsc';
|
|
634
|
+
}
|
|
635
|
+
if (fs.existsSync(path.join(projectRoot, 'webpack.config.js')) ||
|
|
636
|
+
fs.existsSync(path.join(projectRoot, 'webpack.config.ts'))) {
|
|
637
|
+
detection.buildSystem = 'webpack';
|
|
638
|
+
}
|
|
639
|
+
if (fs.existsSync(path.join(projectRoot, 'vite.config.js')) ||
|
|
640
|
+
fs.existsSync(path.join(projectRoot, 'vite.config.ts'))) {
|
|
641
|
+
detection.buildSystem = 'vite';
|
|
642
|
+
}
|
|
643
|
+
if (fs.existsSync(path.join(projectRoot, 'rollup.config.js')) ||
|
|
644
|
+
fs.existsSync(path.join(projectRoot, 'rollup.config.ts'))) {
|
|
645
|
+
detection.buildSystem = 'rollup';
|
|
646
|
+
}
|
|
647
|
+
if (fs.existsSync(path.join(projectRoot, 'esbuild.config.js'))) {
|
|
648
|
+
detection.buildSystem = 'esbuild';
|
|
649
|
+
}
|
|
650
|
+
if (fs.existsSync(path.join(projectRoot, 'next.config.js'))) {
|
|
651
|
+
detection.buildSystem = 'next.js';
|
|
652
|
+
}
|
|
653
|
+
if (fs.existsSync(path.join(projectRoot, 'nuxt.config.js'))) {
|
|
654
|
+
detection.buildSystem = 'nuxt';
|
|
655
|
+
}
|
|
656
|
+
break;
|
|
657
|
+
|
|
658
|
+
case 'Python':
|
|
659
|
+
if (fs.existsSync(path.join(projectRoot, 'poetry.lock'))) {
|
|
660
|
+
detection.packageManager = 'poetry';
|
|
661
|
+
} else if (fs.existsSync(path.join(projectRoot, 'Pipfile'))) {
|
|
662
|
+
detection.packageManager = 'pipenv';
|
|
663
|
+
} else {
|
|
664
|
+
detection.packageManager = 'pip';
|
|
665
|
+
}
|
|
666
|
+
detection.buildSystem = 'setuptools';
|
|
667
|
+
break;
|
|
668
|
+
|
|
669
|
+
case 'Go':
|
|
670
|
+
detection.packageManager = 'go modules';
|
|
671
|
+
detection.buildSystem = 'go build';
|
|
672
|
+
break;
|
|
673
|
+
|
|
674
|
+
case 'Rust':
|
|
675
|
+
detection.packageManager = 'cargo';
|
|
676
|
+
detection.buildSystem = 'cargo';
|
|
677
|
+
break;
|
|
678
|
+
|
|
679
|
+
case 'Java':
|
|
680
|
+
if (fs.existsSync(path.join(projectRoot, 'pom.xml'))) {
|
|
681
|
+
detection.packageManager = 'maven';
|
|
682
|
+
detection.buildSystem = 'maven';
|
|
683
|
+
} else if (fs.existsSync(path.join(projectRoot, 'build.gradle'))) {
|
|
684
|
+
detection.packageManager = 'gradle';
|
|
685
|
+
detection.buildSystem = 'gradle';
|
|
686
|
+
}
|
|
687
|
+
break;
|
|
688
|
+
|
|
689
|
+
case 'C#':
|
|
690
|
+
detection.packageManager = 'nuget';
|
|
691
|
+
detection.buildSystem = 'dotnet';
|
|
692
|
+
break;
|
|
693
|
+
|
|
694
|
+
case 'Ruby':
|
|
695
|
+
detection.packageManager = 'bundler';
|
|
696
|
+
detection.buildSystem = 'rubygems';
|
|
697
|
+
break;
|
|
698
|
+
|
|
699
|
+
case 'PHP':
|
|
700
|
+
detection.packageManager = 'composer';
|
|
701
|
+
detection.buildSystem = 'composer';
|
|
702
|
+
break;
|
|
703
|
+
|
|
704
|
+
case 'Elixir':
|
|
705
|
+
detection.packageManager = 'hex';
|
|
706
|
+
detection.buildSystem = 'mix';
|
|
707
|
+
break;
|
|
708
|
+
|
|
709
|
+
case 'Dart':
|
|
710
|
+
detection.packageManager = 'pub';
|
|
711
|
+
detection.buildSystem = 'dart';
|
|
712
|
+
break;
|
|
713
|
+
|
|
714
|
+
case 'Swift':
|
|
715
|
+
detection.packageManager = 'swift package manager';
|
|
716
|
+
detection.buildSystem = 'swift';
|
|
717
|
+
break;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
} catch (error) {
|
|
721
|
+
detection.error = error.message;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
return detection;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Analyze project dependencies
|
|
729
|
+
*/
|
|
730
|
+
async function analyzeDependencies(projectRoot, primaryLanguage) {
|
|
731
|
+
const analysis = {
|
|
732
|
+
dependencies: [],
|
|
733
|
+
devDependencies: []
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
try {
|
|
737
|
+
switch (primaryLanguage) {
|
|
738
|
+
case 'JavaScript':
|
|
739
|
+
case 'TypeScript':
|
|
740
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
741
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
742
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
743
|
+
analysis.dependencies = Object.keys(pkg.dependencies || {});
|
|
744
|
+
analysis.devDependencies = Object.keys(pkg.devDependencies || {});
|
|
745
|
+
}
|
|
746
|
+
break;
|
|
747
|
+
|
|
748
|
+
case 'Python':
|
|
749
|
+
const requirementsPath = path.join(projectRoot, 'requirements.txt');
|
|
750
|
+
if (fs.existsSync(requirementsPath)) {
|
|
751
|
+
const requirements = fs.readFileSync(requirementsPath, 'utf-8');
|
|
752
|
+
analysis.dependencies = requirements
|
|
753
|
+
.split('\n')
|
|
754
|
+
.filter(line => line.trim() && !line.startsWith('#'))
|
|
755
|
+
.map(line => line.split('==')[0].split('>=')[0].split('<=')[0].trim())
|
|
756
|
+
.filter(dep => dep);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
const pyprojectPath = path.join(projectRoot, 'pyproject.toml');
|
|
760
|
+
if (fs.existsSync(pyprojectPath)) {
|
|
761
|
+
const pyproject = fs.readFileSync(pyprojectPath, 'utf-8');
|
|
762
|
+
// Simple extraction (would need proper TOML parser for production)
|
|
763
|
+
const deps = pyproject.match(/([a-zA-Z0-9_-]+)\s*=/g) || [];
|
|
764
|
+
analysis.dependencies = [...new Set([...analysis.dependencies, ...deps.map(d => d.replace(/\s*=/, ''))])];
|
|
765
|
+
}
|
|
766
|
+
break;
|
|
767
|
+
|
|
768
|
+
case 'Go':
|
|
769
|
+
const goModPath = path.join(projectRoot, 'go.mod');
|
|
770
|
+
if (fs.existsSync(goModPath)) {
|
|
771
|
+
const goMod = fs.readFileSync(goModPath, 'utf-8');
|
|
772
|
+
const requireMatches = goMod.match(/require\s+([^\s]+)\s+[^\s]+/g) || [];
|
|
773
|
+
analysis.dependencies = requireMatches.map(match => match.split(/\s+/)[1]);
|
|
774
|
+
}
|
|
775
|
+
break;
|
|
776
|
+
|
|
777
|
+
case 'Rust':
|
|
778
|
+
const cargoPath = path.join(projectRoot, 'Cargo.toml');
|
|
779
|
+
if (fs.existsSync(cargoPath)) {
|
|
780
|
+
const cargo = fs.readFileSync(cargoPath, 'utf-8');
|
|
781
|
+
// Simple extraction (would need proper TOML parser for production)
|
|
782
|
+
const deps = cargo.match(/([a-zA-Z0-9_-]+)\s*=/g) || [];
|
|
783
|
+
analysis.dependencies = deps.map(d => d.replace(/\s*=/, ''));
|
|
784
|
+
}
|
|
785
|
+
break;
|
|
786
|
+
|
|
787
|
+
case 'Java':
|
|
788
|
+
const pomPath = path.join(projectRoot, 'pom.xml');
|
|
789
|
+
if (fs.existsSync(pomPath)) {
|
|
790
|
+
const pom = fs.readFileSync(pomPath, 'utf-8');
|
|
791
|
+
const artifactIds = pom.match(/<artifactId>([^<]+)<\/artifactId>/g) || [];
|
|
792
|
+
analysis.dependencies = artifactIds.map(id => id.replace(/<\/?artifactId>/g, ''));
|
|
793
|
+
}
|
|
794
|
+
break;
|
|
795
|
+
|
|
796
|
+
case 'C#':
|
|
797
|
+
const csprojFiles = fs.readdirSync(projectRoot).filter(f => f.endsWith('.csproj'));
|
|
798
|
+
if (csprojFiles.length > 0) {
|
|
799
|
+
const csprojPath = path.join(projectRoot, csprojFiles[0]);
|
|
800
|
+
const csproj = fs.readFileSync(csprojPath, 'utf-8');
|
|
801
|
+
const packageRefs = csproj.match(/<PackageReference.*Include="([^"]+)"/g) || [];
|
|
802
|
+
analysis.dependencies = packageRefs.map(ref => ref.match(/Include="([^"]+)"/)[1]);
|
|
803
|
+
}
|
|
804
|
+
break;
|
|
805
|
+
|
|
806
|
+
case 'Ruby':
|
|
807
|
+
const gemfilePath = path.join(projectRoot, 'Gemfile');
|
|
808
|
+
if (fs.existsSync(gemfilePath)) {
|
|
809
|
+
const gemfile = fs.readFileSync(gemfilePath, 'utf-8');
|
|
810
|
+
const gems = gemfile.match(/gem\s+['"]([^'"]+)['"]/g) || [];
|
|
811
|
+
analysis.dependencies = gems.map(gem => gem.match(/['"]([^'"]+)['"]/)[1]);
|
|
812
|
+
}
|
|
813
|
+
break;
|
|
814
|
+
|
|
815
|
+
case 'PHP':
|
|
816
|
+
const composerPath = path.join(projectRoot, 'composer.json');
|
|
817
|
+
if (fs.existsSync(composerPath)) {
|
|
818
|
+
const composer = JSON.parse(fs.readFileSync(composerPath, 'utf-8'));
|
|
819
|
+
analysis.dependencies = Object.keys(composer.require || {});
|
|
820
|
+
analysis.devDependencies = Object.keys(composer['require-dev'] || {});
|
|
821
|
+
}
|
|
822
|
+
break;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
} catch (error) {
|
|
826
|
+
analysis.error = error.message;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
return analysis;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* Detect frameworks used in the project
|
|
834
|
+
*/
|
|
835
|
+
async function detectFrameworks(projectRoot, primaryLanguage, depAnalysis) {
|
|
836
|
+
const detected = [];
|
|
837
|
+
|
|
838
|
+
try {
|
|
839
|
+
const allDeps = [
|
|
840
|
+
...depAnalysis.dependencies,
|
|
841
|
+
...depAnalysis.devDependencies
|
|
842
|
+
].map(dep => dep.toLowerCase());
|
|
843
|
+
|
|
844
|
+
for (const [framework, pattern] of Object.entries(FRAMEWORK_PATTERNS)) {
|
|
845
|
+
// Skip if language doesn't match
|
|
846
|
+
if (pattern.language.length > 0 && !pattern.language.includes(primaryLanguage)) {
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
let confidence = 0;
|
|
851
|
+
|
|
852
|
+
// Check dependencies
|
|
853
|
+
for (const dep of pattern.dependencies) {
|
|
854
|
+
if (allDeps.some(d => d.includes(dep.toLowerCase()))) {
|
|
855
|
+
confidence += 0.5;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// Check config files
|
|
860
|
+
for (const configFile of pattern.configFiles) {
|
|
861
|
+
if (fs.existsSync(path.join(projectRoot, configFile))) {
|
|
862
|
+
confidence += 0.5;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
if (confidence >= 0.5) {
|
|
867
|
+
detected.push({
|
|
868
|
+
name: framework,
|
|
869
|
+
confidence: Math.min(confidence, 1.0)
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// Sort by confidence
|
|
875
|
+
detected.sort((a, b) => b.confidence - a.confidence);
|
|
876
|
+
|
|
877
|
+
} catch (error) {
|
|
878
|
+
// Return empty array on error
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
return detected.map(f => f.name);
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* Detect databases used in the project
|
|
886
|
+
*/
|
|
887
|
+
async function detectDatabases(projectRoot, depAnalysis) {
|
|
888
|
+
const detected = [];
|
|
889
|
+
|
|
890
|
+
try {
|
|
891
|
+
const allDeps = [
|
|
892
|
+
...depAnalysis.dependencies,
|
|
893
|
+
...depAnalysis.devDependencies
|
|
894
|
+
].map(dep => dep.toLowerCase());
|
|
895
|
+
|
|
896
|
+
for (const [database, pattern] of Object.entries(DATABASE_PATTERNS)) {
|
|
897
|
+
let confidence = 0;
|
|
898
|
+
|
|
899
|
+
// Check direct dependencies
|
|
900
|
+
for (const dep of pattern.dependencies) {
|
|
901
|
+
if (allDeps.some(d => d.includes(dep.toLowerCase()))) {
|
|
902
|
+
confidence += 0.7;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// Check ORM dependencies
|
|
907
|
+
for (const orm of pattern.orm) {
|
|
908
|
+
if (allDeps.some(d => d.includes(orm.toLowerCase()))) {
|
|
909
|
+
confidence += 0.3;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
if (confidence >= 0.5) {
|
|
914
|
+
detected.push({
|
|
915
|
+
name: database,
|
|
916
|
+
confidence: Math.min(confidence, 1.0)
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// Sort by confidence
|
|
922
|
+
detected.sort((a, b) => b.confidence - a.confidence);
|
|
923
|
+
|
|
924
|
+
} catch (error) {
|
|
925
|
+
// Return empty array on error
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
return detected.map(d => d.name);
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* Detect testing frameworks
|
|
933
|
+
*/
|
|
934
|
+
async function detectTestingFrameworks(projectRoot, primaryLanguage, depAnalysis) {
|
|
935
|
+
const detected = [];
|
|
936
|
+
|
|
937
|
+
try {
|
|
938
|
+
const allDeps = [
|
|
939
|
+
...depAnalysis.dependencies,
|
|
940
|
+
...depAnalysis.devDependencies
|
|
941
|
+
].map(dep => dep.toLowerCase());
|
|
942
|
+
|
|
943
|
+
for (const [framework, pattern] of Object.entries(TESTING_PATTERNS)) {
|
|
944
|
+
// Skip if language doesn't match (when specified)
|
|
945
|
+
if (pattern.language.length > 0 && !pattern.language.includes(primaryLanguage)) {
|
|
946
|
+
continue;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
let confidence = 0;
|
|
950
|
+
|
|
951
|
+
// Check dependencies
|
|
952
|
+
for (const dep of pattern.dependencies) {
|
|
953
|
+
if (allDeps.some(d => d.includes(dep.toLowerCase()))) {
|
|
954
|
+
confidence += 0.6;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// Check config files
|
|
959
|
+
for (const configFile of pattern.configFiles) {
|
|
960
|
+
if (fs.existsSync(path.join(projectRoot, configFile))) {
|
|
961
|
+
confidence += 0.4;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
if (confidence >= 0.5) {
|
|
966
|
+
detected.push({
|
|
967
|
+
name: framework,
|
|
968
|
+
confidence: Math.min(confidence, 1.0)
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
// Sort by confidence
|
|
974
|
+
detected.sort((a, b) => b.confidence - a.confidence);
|
|
975
|
+
|
|
976
|
+
} catch (error) {
|
|
977
|
+
// Return empty array on error
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
return detected.map(f => f.name);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Detect styling approaches
|
|
985
|
+
*/
|
|
986
|
+
async function detectStyling(projectRoot, depAnalysis) {
|
|
987
|
+
const detected = [];
|
|
988
|
+
|
|
989
|
+
try {
|
|
990
|
+
const allDeps = [
|
|
991
|
+
...depAnalysis.dependencies,
|
|
992
|
+
...depAnalysis.devDependencies
|
|
993
|
+
].map(dep => dep.toLowerCase());
|
|
994
|
+
|
|
995
|
+
for (const [style, pattern] of Object.entries(STYLING_PATTERNS)) {
|
|
996
|
+
let confidence = 0;
|
|
997
|
+
|
|
998
|
+
// Check dependencies
|
|
999
|
+
for (const dep of pattern.dependencies) {
|
|
1000
|
+
if (allDeps.some(d => d.includes(dep.toLowerCase()))) {
|
|
1001
|
+
confidence += 0.5;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// Check config files
|
|
1006
|
+
for (const configFile of pattern.configFiles) {
|
|
1007
|
+
if (fs.existsSync(path.join(projectRoot, configFile))) {
|
|
1008
|
+
confidence += 0.5;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
// Check file patterns (for CSS modules)
|
|
1013
|
+
if (pattern.filePattern) {
|
|
1014
|
+
for (const entry of fs.readdirSync(projectRoot, { withFileTypes: true })) {
|
|
1015
|
+
if (entry.isDirectory()) {
|
|
1016
|
+
const dirPath = path.join(projectRoot, entry.name);
|
|
1017
|
+
try {
|
|
1018
|
+
const subEntries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
1019
|
+
for (const subEntry of subEntries) {
|
|
1020
|
+
if (subEntry.isFile()) {
|
|
1021
|
+
const fileName = subEntry.name.toLowerCase();
|
|
1022
|
+
if (pattern.filePattern.some(ext => fileName.endsWith(ext))) {
|
|
1023
|
+
confidence += 0.3;
|
|
1024
|
+
break;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
} catch (error) {
|
|
1029
|
+
// Skip directories we can't read
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
if (confidence >= 0.5) {
|
|
1036
|
+
detected.push({
|
|
1037
|
+
name: style,
|
|
1038
|
+
confidence: Math.min(confidence, 1.0)
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// Sort by confidence
|
|
1044
|
+
detected.sort((a, b) => b.confidence - a.confidence);
|
|
1045
|
+
|
|
1046
|
+
} catch (error) {
|
|
1047
|
+
// Return empty array on error
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
return detected.map(s => s.name);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
/**
|
|
1054
|
+
* Calculate overall confidence score for tech stack detection
|
|
1055
|
+
*/
|
|
1056
|
+
function calculateTechStackConfidence(techStack) {
|
|
1057
|
+
let confidence = 0;
|
|
1058
|
+
let factors = 0;
|
|
1059
|
+
|
|
1060
|
+
if (techStack.language && techStack.language !== 'unknown') {
|
|
1061
|
+
confidence += 0.3;
|
|
1062
|
+
factors++;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
if (techStack.frameworks.length > 0) {
|
|
1066
|
+
confidence += 0.25;
|
|
1067
|
+
factors++;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
if (techStack.packageManager && techStack.packageManager !== 'unknown') {
|
|
1071
|
+
confidence += 0.15;
|
|
1072
|
+
factors++;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
if (techStack.buildSystem && techStack.buildSystem !== 'unknown') {
|
|
1076
|
+
confidence += 0.15;
|
|
1077
|
+
factors++;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
if (techStack.databases.length > 0) {
|
|
1081
|
+
confidence += 0.1;
|
|
1082
|
+
factors++;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
if (techStack.testingFrameworks.length > 0) {
|
|
1086
|
+
confidence += 0.05;
|
|
1087
|
+
factors++;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
return Math.min(confidence, 1.0);
|
|
1091
|
+
}
|