@itz4blitz/agentful 0.1.0 → 0.1.4

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.
@@ -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
+ }