claude-cli-advanced-starter-pack 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/OVERVIEW.md +597 -0
  3. package/README.md +439 -0
  4. package/bin/gtask.js +282 -0
  5. package/bin/postinstall.js +53 -0
  6. package/package.json +69 -0
  7. package/src/agents/phase-dev-templates.js +1011 -0
  8. package/src/agents/templates.js +668 -0
  9. package/src/analysis/checklist-parser.js +414 -0
  10. package/src/analysis/codebase.js +481 -0
  11. package/src/cli/menu.js +958 -0
  12. package/src/commands/claude-audit.js +1482 -0
  13. package/src/commands/claude-settings.js +2243 -0
  14. package/src/commands/create-agent.js +681 -0
  15. package/src/commands/create-command.js +337 -0
  16. package/src/commands/create-hook.js +262 -0
  17. package/src/commands/create-phase-dev/codebase-analyzer.js +813 -0
  18. package/src/commands/create-phase-dev/documentation-generator.js +352 -0
  19. package/src/commands/create-phase-dev/post-completion.js +404 -0
  20. package/src/commands/create-phase-dev/scale-calculator.js +344 -0
  21. package/src/commands/create-phase-dev/wizard.js +492 -0
  22. package/src/commands/create-phase-dev.js +481 -0
  23. package/src/commands/create-skill.js +313 -0
  24. package/src/commands/create.js +446 -0
  25. package/src/commands/decompose.js +392 -0
  26. package/src/commands/detect-tech-stack.js +768 -0
  27. package/src/commands/explore-mcp/claude-md-updater.js +252 -0
  28. package/src/commands/explore-mcp/mcp-installer.js +346 -0
  29. package/src/commands/explore-mcp/mcp-registry.js +438 -0
  30. package/src/commands/explore-mcp.js +638 -0
  31. package/src/commands/gtask-init.js +641 -0
  32. package/src/commands/help.js +128 -0
  33. package/src/commands/init.js +1890 -0
  34. package/src/commands/install.js +250 -0
  35. package/src/commands/list.js +116 -0
  36. package/src/commands/roadmap.js +750 -0
  37. package/src/commands/setup-wizard.js +482 -0
  38. package/src/commands/setup.js +351 -0
  39. package/src/commands/sync.js +534 -0
  40. package/src/commands/test-run.js +456 -0
  41. package/src/commands/test-setup.js +456 -0
  42. package/src/commands/validate.js +67 -0
  43. package/src/config/tech-stack.defaults.json +182 -0
  44. package/src/config/tech-stack.schema.json +502 -0
  45. package/src/github/client.js +359 -0
  46. package/src/index.js +84 -0
  47. package/src/templates/claude-command.js +244 -0
  48. package/src/templates/issue-body.js +284 -0
  49. package/src/testing/config.js +411 -0
  50. package/src/utils/template-engine.js +398 -0
  51. package/src/utils/validate-templates.js +223 -0
  52. package/src/utils.js +396 -0
  53. package/templates/commands/ccasp-setup.template.md +113 -0
  54. package/templates/commands/context-audit.template.md +97 -0
  55. package/templates/commands/create-task-list.template.md +382 -0
  56. package/templates/commands/deploy-full.template.md +261 -0
  57. package/templates/commands/github-task-start.template.md +99 -0
  58. package/templates/commands/github-update.template.md +69 -0
  59. package/templates/commands/happy-start.template.md +117 -0
  60. package/templates/commands/phase-track.template.md +142 -0
  61. package/templates/commands/tunnel-start.template.md +127 -0
  62. package/templates/commands/tunnel-stop.template.md +106 -0
  63. package/templates/hooks/context-guardian.template.js +173 -0
  64. package/templates/hooks/deployment-orchestrator.template.js +219 -0
  65. package/templates/hooks/github-progress-hook.template.js +197 -0
  66. package/templates/hooks/happy-checkpoint-manager.template.js +222 -0
  67. package/templates/hooks/phase-dev-enforcer.template.js +183 -0
@@ -0,0 +1,813 @@
1
+ /**
2
+ * Codebase Analyzer
3
+ *
4
+ * Auto-detects tech stack by analyzing the user's project structure.
5
+ * Makes create-phase-dev work for ANY codebase, not just specific stacks.
6
+ */
7
+
8
+ import { existsSync, readFileSync, readdirSync } from 'fs';
9
+ import { join } from 'path';
10
+ import chalk from 'chalk';
11
+ import ora from 'ora';
12
+
13
+ /**
14
+ * Analyze codebase and detect tech stack
15
+ *
16
+ * @param {string} cwd - Working directory to analyze
17
+ * @returns {Object} Detected tech stack configuration
18
+ */
19
+ export async function analyzeCodebase(cwd = process.cwd()) {
20
+ const spinner = ora('Analyzing codebase...').start();
21
+
22
+ const result = {
23
+ detected: true,
24
+ confidence: 'high',
25
+ frontend: detectFrontend(cwd),
26
+ backend: detectBackend(cwd),
27
+ database: detectDatabase(cwd),
28
+ testing: detectTesting(cwd),
29
+ deployment: detectDeployment(cwd),
30
+ services: detectServices(cwd),
31
+ projectStructure: detectProjectStructure(cwd),
32
+ packageManager: detectPackageManager(cwd),
33
+ monorepo: detectMonorepo(cwd),
34
+ };
35
+
36
+ // Calculate overall confidence
37
+ const detections = [
38
+ result.frontend.detected,
39
+ result.backend.detected,
40
+ result.database.detected,
41
+ ];
42
+ const detectedCount = detections.filter(Boolean).length;
43
+
44
+ if (detectedCount === 0) {
45
+ result.confidence = 'low';
46
+ result.detected = false;
47
+ } else if (detectedCount < 2) {
48
+ result.confidence = 'medium';
49
+ }
50
+
51
+ spinner.succeed('Codebase analysis complete');
52
+
53
+ return result;
54
+ }
55
+
56
+ /**
57
+ * Detect frontend framework
58
+ */
59
+ function detectFrontend(cwd) {
60
+ const result = {
61
+ detected: false,
62
+ framework: null,
63
+ language: null,
64
+ bundler: null,
65
+ styling: null,
66
+ version: null,
67
+ };
68
+
69
+ // Check package.json
70
+ const packageJsonPath = join(cwd, 'package.json');
71
+ if (existsSync(packageJsonPath)) {
72
+ try {
73
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
74
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
75
+
76
+ // Detect framework
77
+ if (deps.react) {
78
+ result.detected = true;
79
+ result.framework = 'react';
80
+ result.version = deps.react.replace(/[\^~]/, '');
81
+ } else if (deps.vue) {
82
+ result.detected = true;
83
+ result.framework = 'vue';
84
+ result.version = deps.vue.replace(/[\^~]/, '');
85
+ } else if (deps['@angular/core']) {
86
+ result.detected = true;
87
+ result.framework = 'angular';
88
+ result.version = deps['@angular/core'].replace(/[\^~]/, '');
89
+ } else if (deps.svelte) {
90
+ result.detected = true;
91
+ result.framework = 'svelte';
92
+ result.version = deps.svelte.replace(/[\^~]/, '');
93
+ } else if (deps.next) {
94
+ result.detected = true;
95
+ result.framework = 'nextjs';
96
+ result.version = deps.next.replace(/[\^~]/, '');
97
+ } else if (deps.nuxt) {
98
+ result.detected = true;
99
+ result.framework = 'nuxt';
100
+ result.version = deps.nuxt.replace(/[\^~]/, '');
101
+ }
102
+
103
+ // Detect language
104
+ if (deps.typescript || existsSync(join(cwd, 'tsconfig.json'))) {
105
+ result.language = 'typescript';
106
+ } else {
107
+ result.language = 'javascript';
108
+ }
109
+
110
+ // Detect bundler
111
+ if (deps.vite) {
112
+ result.bundler = 'vite';
113
+ } else if (deps.webpack) {
114
+ result.bundler = 'webpack';
115
+ } else if (deps.parcel) {
116
+ result.bundler = 'parcel';
117
+ } else if (deps.esbuild) {
118
+ result.bundler = 'esbuild';
119
+ }
120
+
121
+ // Detect styling
122
+ if (deps.tailwindcss) {
123
+ result.styling = 'tailwind';
124
+ } else if (deps['styled-components']) {
125
+ result.styling = 'styled-components';
126
+ } else if (deps['@emotion/react']) {
127
+ result.styling = 'emotion';
128
+ } else if (deps.sass || deps['node-sass']) {
129
+ result.styling = 'sass';
130
+ }
131
+ } catch (e) {
132
+ // Ignore parse errors
133
+ }
134
+ }
135
+
136
+ return result;
137
+ }
138
+
139
+ /**
140
+ * Detect backend framework
141
+ */
142
+ function detectBackend(cwd) {
143
+ const result = {
144
+ detected: false,
145
+ framework: null,
146
+ language: null,
147
+ version: null,
148
+ };
149
+
150
+ // Check for Python backends
151
+ const requirementsPath = join(cwd, 'requirements.txt');
152
+ const pyprojectPath = join(cwd, 'pyproject.toml');
153
+
154
+ if (existsSync(requirementsPath)) {
155
+ const content = readFileSync(requirementsPath, 'utf8').toLowerCase();
156
+ result.language = 'python';
157
+
158
+ if (content.includes('fastapi')) {
159
+ result.detected = true;
160
+ result.framework = 'fastapi';
161
+ } else if (content.includes('django')) {
162
+ result.detected = true;
163
+ result.framework = 'django';
164
+ } else if (content.includes('flask')) {
165
+ result.detected = true;
166
+ result.framework = 'flask';
167
+ }
168
+ }
169
+
170
+ if (existsSync(pyprojectPath)) {
171
+ const content = readFileSync(pyprojectPath, 'utf8').toLowerCase();
172
+ result.language = 'python';
173
+
174
+ if (content.includes('fastapi')) {
175
+ result.detected = true;
176
+ result.framework = 'fastapi';
177
+ } else if (content.includes('django')) {
178
+ result.detected = true;
179
+ result.framework = 'django';
180
+ }
181
+ }
182
+
183
+ // Check for Node.js backends
184
+ const packageJsonPath = join(cwd, 'package.json');
185
+ if (existsSync(packageJsonPath) && !result.detected) {
186
+ try {
187
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
188
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
189
+
190
+ if (deps.express) {
191
+ result.detected = true;
192
+ result.framework = 'express';
193
+ result.language = 'node';
194
+ } else if (deps.fastify) {
195
+ result.detected = true;
196
+ result.framework = 'fastify';
197
+ result.language = 'node';
198
+ } else if (deps['@nestjs/core']) {
199
+ result.detected = true;
200
+ result.framework = 'nestjs';
201
+ result.language = 'node';
202
+ } else if (deps.koa) {
203
+ result.detected = true;
204
+ result.framework = 'koa';
205
+ result.language = 'node';
206
+ }
207
+ } catch (e) {
208
+ // Ignore parse errors
209
+ }
210
+ }
211
+
212
+ // Check for Go backends
213
+ const goModPath = join(cwd, 'go.mod');
214
+ if (existsSync(goModPath)) {
215
+ const content = readFileSync(goModPath, 'utf8').toLowerCase();
216
+ result.language = 'go';
217
+
218
+ if (content.includes('gin-gonic')) {
219
+ result.detected = true;
220
+ result.framework = 'gin';
221
+ } else if (content.includes('echo')) {
222
+ result.detected = true;
223
+ result.framework = 'echo';
224
+ } else if (content.includes('fiber')) {
225
+ result.detected = true;
226
+ result.framework = 'fiber';
227
+ } else {
228
+ result.detected = true;
229
+ result.framework = 'go-std';
230
+ }
231
+ }
232
+
233
+ // Check for Rust backends
234
+ const cargoPath = join(cwd, 'Cargo.toml');
235
+ if (existsSync(cargoPath)) {
236
+ const content = readFileSync(cargoPath, 'utf8').toLowerCase();
237
+ result.language = 'rust';
238
+
239
+ if (content.includes('actix')) {
240
+ result.detected = true;
241
+ result.framework = 'actix';
242
+ } else if (content.includes('axum')) {
243
+ result.detected = true;
244
+ result.framework = 'axum';
245
+ } else if (content.includes('rocket')) {
246
+ result.detected = true;
247
+ result.framework = 'rocket';
248
+ }
249
+ }
250
+
251
+ // Check for Ruby backends
252
+ const gemfilePath = join(cwd, 'Gemfile');
253
+ if (existsSync(gemfilePath)) {
254
+ const content = readFileSync(gemfilePath, 'utf8').toLowerCase();
255
+ result.language = 'ruby';
256
+
257
+ if (content.includes('rails')) {
258
+ result.detected = true;
259
+ result.framework = 'rails';
260
+ } else if (content.includes('sinatra')) {
261
+ result.detected = true;
262
+ result.framework = 'sinatra';
263
+ }
264
+ }
265
+
266
+ return result;
267
+ }
268
+
269
+ /**
270
+ * Detect database
271
+ */
272
+ function detectDatabase(cwd) {
273
+ const result = {
274
+ detected: false,
275
+ type: null,
276
+ orm: null,
277
+ };
278
+
279
+ // Check package.json for Node ORMs
280
+ const packageJsonPath = join(cwd, 'package.json');
281
+ if (existsSync(packageJsonPath)) {
282
+ try {
283
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
284
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
285
+
286
+ if (deps.prisma || deps['@prisma/client']) {
287
+ result.detected = true;
288
+ result.orm = 'prisma';
289
+ // Prisma schema determines DB type
290
+ } else if (deps.sequelize) {
291
+ result.detected = true;
292
+ result.orm = 'sequelize';
293
+ } else if (deps.typeorm) {
294
+ result.detected = true;
295
+ result.orm = 'typeorm';
296
+ } else if (deps.mongoose) {
297
+ result.detected = true;
298
+ result.type = 'mongodb';
299
+ result.orm = 'mongoose';
300
+ } else if (deps.pg) {
301
+ result.detected = true;
302
+ result.type = 'postgresql';
303
+ } else if (deps.mysql || deps.mysql2) {
304
+ result.detected = true;
305
+ result.type = 'mysql';
306
+ } else if (deps['better-sqlite3'] || deps.sqlite3) {
307
+ result.detected = true;
308
+ result.type = 'sqlite';
309
+ }
310
+
311
+ // Check for Supabase (BaaS with PostgreSQL)
312
+ if (deps['@supabase/supabase-js'] || deps['@supabase/ssr']) {
313
+ result.detected = true;
314
+ result.type = 'postgresql';
315
+ result.baas = 'supabase';
316
+ }
317
+ } catch (e) {
318
+ // Ignore
319
+ }
320
+ }
321
+
322
+ // Check Python requirements
323
+ const requirementsPath = join(cwd, 'requirements.txt');
324
+ if (existsSync(requirementsPath)) {
325
+ const content = readFileSync(requirementsPath, 'utf8').toLowerCase();
326
+
327
+ if (content.includes('sqlalchemy')) {
328
+ result.detected = true;
329
+ result.orm = 'sqlalchemy';
330
+ } else if (content.includes('django')) {
331
+ result.detected = true;
332
+ result.orm = 'django-orm';
333
+ } else if (content.includes('tortoise')) {
334
+ result.detected = true;
335
+ result.orm = 'tortoise';
336
+ }
337
+
338
+ if (content.includes('psycopg') || content.includes('asyncpg')) {
339
+ result.type = 'postgresql';
340
+ } else if (content.includes('pymysql') || content.includes('mysqlclient')) {
341
+ result.type = 'mysql';
342
+ } else if (content.includes('pymongo')) {
343
+ result.type = 'mongodb';
344
+ }
345
+ }
346
+
347
+ // Check for docker-compose database services
348
+ const dockerComposePath = join(cwd, 'docker-compose.yml');
349
+ const dockerComposeAltPath = join(cwd, 'docker-compose.yaml');
350
+ const composePath = existsSync(dockerComposePath)
351
+ ? dockerComposePath
352
+ : existsSync(dockerComposeAltPath)
353
+ ? dockerComposeAltPath
354
+ : null;
355
+
356
+ if (composePath) {
357
+ const content = readFileSync(composePath, 'utf8').toLowerCase();
358
+ if (content.includes('postgres')) {
359
+ result.detected = true;
360
+ result.type = result.type || 'postgresql';
361
+ } else if (content.includes('mysql') || content.includes('mariadb')) {
362
+ result.detected = true;
363
+ result.type = result.type || 'mysql';
364
+ } else if (content.includes('mongo')) {
365
+ result.detected = true;
366
+ result.type = result.type || 'mongodb';
367
+ } else if (content.includes('redis')) {
368
+ result.cache = 'redis';
369
+ }
370
+ }
371
+
372
+ return result;
373
+ }
374
+
375
+ /**
376
+ * Detect testing framework
377
+ */
378
+ function detectTesting(cwd) {
379
+ const result = {
380
+ detected: false,
381
+ framework: null,
382
+ e2e: null,
383
+ };
384
+
385
+ const packageJsonPath = join(cwd, 'package.json');
386
+ if (existsSync(packageJsonPath)) {
387
+ try {
388
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
389
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
390
+
391
+ // Unit testing
392
+ if (deps.vitest) {
393
+ result.detected = true;
394
+ result.framework = 'vitest';
395
+ } else if (deps.jest) {
396
+ result.detected = true;
397
+ result.framework = 'jest';
398
+ } else if (deps.mocha) {
399
+ result.detected = true;
400
+ result.framework = 'mocha';
401
+ } else if (deps.ava) {
402
+ result.detected = true;
403
+ result.framework = 'ava';
404
+ }
405
+
406
+ // E2E testing
407
+ if (deps['@playwright/test'] || deps.playwright) {
408
+ result.e2e = 'playwright';
409
+ } else if (deps.cypress) {
410
+ result.e2e = 'cypress';
411
+ } else if (deps.puppeteer) {
412
+ result.e2e = 'puppeteer';
413
+ }
414
+ } catch (e) {
415
+ // Ignore
416
+ }
417
+ }
418
+
419
+ // Check for pytest (Python)
420
+ const requirementsPath = join(cwd, 'requirements.txt');
421
+ if (existsSync(requirementsPath)) {
422
+ const content = readFileSync(requirementsPath, 'utf8').toLowerCase();
423
+ if (content.includes('pytest')) {
424
+ result.detected = true;
425
+ result.framework = 'pytest';
426
+ }
427
+ }
428
+
429
+ // Check for config files
430
+ if (existsSync(join(cwd, 'vitest.config.ts')) || existsSync(join(cwd, 'vitest.config.js'))) {
431
+ result.detected = true;
432
+ result.framework = 'vitest';
433
+ }
434
+ if (existsSync(join(cwd, 'jest.config.js')) || existsSync(join(cwd, 'jest.config.ts'))) {
435
+ result.detected = true;
436
+ result.framework = 'jest';
437
+ }
438
+ if (existsSync(join(cwd, 'playwright.config.ts')) || existsSync(join(cwd, 'playwright.config.js'))) {
439
+ result.e2e = 'playwright';
440
+ }
441
+ if (existsSync(join(cwd, 'cypress.config.ts')) || existsSync(join(cwd, 'cypress.config.js'))) {
442
+ result.e2e = 'cypress';
443
+ }
444
+
445
+ return result;
446
+ }
447
+
448
+ /**
449
+ * Detect deployment platform
450
+ */
451
+ function detectDeployment(cwd) {
452
+ const result = {
453
+ detected: false,
454
+ platform: null,
455
+ containerized: false,
456
+ };
457
+
458
+ // Check for platform-specific files
459
+ if (existsSync(join(cwd, 'vercel.json')) || existsSync(join(cwd, '.vercel'))) {
460
+ result.detected = true;
461
+ result.platform = 'vercel';
462
+ }
463
+ if (existsSync(join(cwd, 'netlify.toml'))) {
464
+ result.detected = true;
465
+ result.platform = 'netlify';
466
+ }
467
+ if (existsSync(join(cwd, 'railway.json')) || existsSync(join(cwd, 'railway.toml'))) {
468
+ result.detected = true;
469
+ result.platform = 'railway';
470
+ }
471
+ if (existsSync(join(cwd, 'fly.toml'))) {
472
+ result.detected = true;
473
+ result.platform = 'fly';
474
+ }
475
+ if (existsSync(join(cwd, 'render.yaml'))) {
476
+ result.detected = true;
477
+ result.platform = 'render';
478
+ }
479
+ if (existsSync(join(cwd, 'heroku.yml')) || existsSync(join(cwd, 'Procfile'))) {
480
+ result.detected = true;
481
+ result.platform = 'heroku';
482
+ }
483
+ if (existsSync(join(cwd, 'wrangler.toml'))) {
484
+ result.detected = true;
485
+ result.platform = 'cloudflare';
486
+ }
487
+
488
+ // Check for Docker
489
+ if (existsSync(join(cwd, 'Dockerfile')) || existsSync(join(cwd, 'docker-compose.yml'))) {
490
+ result.containerized = true;
491
+ }
492
+
493
+ // Check for Kubernetes
494
+ if (existsSync(join(cwd, 'k8s')) || existsSync(join(cwd, 'kubernetes'))) {
495
+ result.detected = true;
496
+ result.platform = 'kubernetes';
497
+ result.containerized = true;
498
+ }
499
+
500
+ // Check for AWS
501
+ if (existsSync(join(cwd, 'serverless.yml')) || existsSync(join(cwd, 'serverless.yaml'))) {
502
+ result.detected = true;
503
+ result.platform = 'aws-serverless';
504
+ }
505
+ if (existsSync(join(cwd, 'cdk.json'))) {
506
+ result.detected = true;
507
+ result.platform = 'aws-cdk';
508
+ }
509
+
510
+ return result;
511
+ }
512
+
513
+ /**
514
+ * Detect external services and integrations
515
+ */
516
+ function detectServices(cwd) {
517
+ const result = {
518
+ detected: false,
519
+ supabase: false,
520
+ n8n: false,
521
+ stripe: false,
522
+ auth0: false,
523
+ clerk: false,
524
+ resend: false,
525
+ twilio: false,
526
+ };
527
+
528
+ // Check package.json for service SDKs
529
+ const packageJsonPath = join(cwd, 'package.json');
530
+ if (existsSync(packageJsonPath)) {
531
+ try {
532
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
533
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
534
+
535
+ // Supabase
536
+ if (deps['@supabase/supabase-js'] || deps['@supabase/ssr'] || deps['@supabase/auth-helpers-nextjs']) {
537
+ result.detected = true;
538
+ result.supabase = true;
539
+ }
540
+
541
+ // n8n (self-hosted workflow automation)
542
+ if (deps['n8n'] || deps['n8n-workflow'] || deps['n8n-core']) {
543
+ result.detected = true;
544
+ result.n8n = true;
545
+ }
546
+
547
+ // Payment - Stripe
548
+ if (deps.stripe || deps['@stripe/stripe-js']) {
549
+ result.detected = true;
550
+ result.stripe = true;
551
+ }
552
+
553
+ // Auth providers
554
+ if (deps['@auth0/auth0-react'] || deps['@auth0/nextjs-auth0']) {
555
+ result.detected = true;
556
+ result.auth0 = true;
557
+ }
558
+ if (deps['@clerk/nextjs'] || deps['@clerk/clerk-react']) {
559
+ result.detected = true;
560
+ result.clerk = true;
561
+ }
562
+
563
+ // Email - Resend
564
+ if (deps.resend || deps['@react-email/components']) {
565
+ result.detected = true;
566
+ result.resend = true;
567
+ }
568
+
569
+ // Twilio
570
+ if (deps.twilio) {
571
+ result.detected = true;
572
+ result.twilio = true;
573
+ }
574
+ } catch (e) {
575
+ // Ignore parse errors
576
+ }
577
+ }
578
+
579
+ // Check Python requirements for n8n webhooks or Supabase
580
+ const requirementsPath = join(cwd, 'requirements.txt');
581
+ if (existsSync(requirementsPath)) {
582
+ const content = readFileSync(requirementsPath, 'utf8').toLowerCase();
583
+
584
+ if (content.includes('supabase')) {
585
+ result.detected = true;
586
+ result.supabase = true;
587
+ }
588
+ }
589
+
590
+ // Check for n8n config files (self-hosted deployments)
591
+ if (
592
+ existsSync(join(cwd, 'n8n.config.js')) ||
593
+ existsSync(join(cwd, '.n8n')) ||
594
+ existsSync(join(cwd, 'n8n-custom'))
595
+ ) {
596
+ result.detected = true;
597
+ result.n8n = true;
598
+ }
599
+
600
+ // Check docker-compose for n8n service
601
+ const dockerComposePath = join(cwd, 'docker-compose.yml');
602
+ const dockerComposeAltPath = join(cwd, 'docker-compose.yaml');
603
+ const composePath = existsSync(dockerComposePath)
604
+ ? dockerComposePath
605
+ : existsSync(dockerComposeAltPath)
606
+ ? dockerComposeAltPath
607
+ : null;
608
+
609
+ if (composePath) {
610
+ const content = readFileSync(composePath, 'utf8').toLowerCase();
611
+ if (content.includes('n8n') || content.includes('n8nio')) {
612
+ result.detected = true;
613
+ result.n8n = true;
614
+ }
615
+ }
616
+
617
+ // Check env files for service references (without exposing values)
618
+ const envFiles = ['.env', '.env.local', '.env.example', '.env.sample'];
619
+ for (const envFile of envFiles) {
620
+ const envPath = join(cwd, envFile);
621
+ if (existsSync(envPath)) {
622
+ const content = readFileSync(envPath, 'utf8').toUpperCase();
623
+
624
+ if (content.includes('SUPABASE')) {
625
+ result.detected = true;
626
+ result.supabase = true;
627
+ }
628
+ if (content.includes('N8N')) {
629
+ result.detected = true;
630
+ result.n8n = true;
631
+ }
632
+ }
633
+ }
634
+
635
+ return result;
636
+ }
637
+
638
+ /**
639
+ * Detect project structure
640
+ */
641
+ function detectProjectStructure(cwd) {
642
+ const result = {
643
+ hasClaudeDir: existsSync(join(cwd, '.claude')),
644
+ hasSrcDir: existsSync(join(cwd, 'src')),
645
+ hasAppsDir: existsSync(join(cwd, 'apps')),
646
+ hasPackagesDir: existsSync(join(cwd, 'packages')),
647
+ hasDocsDir: existsSync(join(cwd, 'docs')),
648
+ hasTestsDir: existsSync(join(cwd, 'tests')) || existsSync(join(cwd, '__tests__')),
649
+ };
650
+
651
+ return result;
652
+ }
653
+
654
+ /**
655
+ * Detect package manager
656
+ */
657
+ function detectPackageManager(cwd) {
658
+ if (existsSync(join(cwd, 'pnpm-lock.yaml'))) {
659
+ return 'pnpm';
660
+ }
661
+ if (existsSync(join(cwd, 'yarn.lock'))) {
662
+ return 'yarn';
663
+ }
664
+ if (existsSync(join(cwd, 'bun.lockb'))) {
665
+ return 'bun';
666
+ }
667
+ if (existsSync(join(cwd, 'package-lock.json'))) {
668
+ return 'npm';
669
+ }
670
+ return null;
671
+ }
672
+
673
+ /**
674
+ * Detect if it's a monorepo
675
+ */
676
+ function detectMonorepo(cwd) {
677
+ const result = {
678
+ isMonorepo: false,
679
+ tool: null,
680
+ workspaces: [],
681
+ };
682
+
683
+ // Check for workspace configs
684
+ const packageJsonPath = join(cwd, 'package.json');
685
+ if (existsSync(packageJsonPath)) {
686
+ try {
687
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
688
+ if (pkg.workspaces) {
689
+ result.isMonorepo = true;
690
+ result.workspaces = Array.isArray(pkg.workspaces)
691
+ ? pkg.workspaces
692
+ : pkg.workspaces.packages || [];
693
+ }
694
+ } catch (e) {
695
+ // Ignore
696
+ }
697
+ }
698
+
699
+ // Check for monorepo tools
700
+ if (existsSync(join(cwd, 'pnpm-workspace.yaml'))) {
701
+ result.isMonorepo = true;
702
+ result.tool = 'pnpm';
703
+ }
704
+ if (existsSync(join(cwd, 'lerna.json'))) {
705
+ result.isMonorepo = true;
706
+ result.tool = 'lerna';
707
+ }
708
+ if (existsSync(join(cwd, 'nx.json'))) {
709
+ result.isMonorepo = true;
710
+ result.tool = 'nx';
711
+ }
712
+ if (existsSync(join(cwd, 'turbo.json'))) {
713
+ result.isMonorepo = true;
714
+ result.tool = 'turborepo';
715
+ }
716
+
717
+ return result;
718
+ }
719
+
720
+ /**
721
+ * Generate human-readable summary of detected stack
722
+ */
723
+ export function generateStackSummary(analysis) {
724
+ const parts = [];
725
+
726
+ // Frontend
727
+ if (analysis.frontend.detected) {
728
+ let fe = analysis.frontend.framework;
729
+ if (analysis.frontend.version) {
730
+ fe += ` ${analysis.frontend.version}`;
731
+ }
732
+ if (analysis.frontend.language === 'typescript') {
733
+ fe += ' + TypeScript';
734
+ }
735
+ if (analysis.frontend.bundler) {
736
+ fe += ` + ${analysis.frontend.bundler}`;
737
+ }
738
+ if (analysis.frontend.styling) {
739
+ fe += ` + ${analysis.frontend.styling}`;
740
+ }
741
+ parts.push(`Frontend: ${fe}`);
742
+ }
743
+
744
+ // Backend
745
+ if (analysis.backend.detected) {
746
+ let be = analysis.backend.framework;
747
+ if (analysis.backend.language) {
748
+ be = `${analysis.backend.framework} (${analysis.backend.language})`;
749
+ }
750
+ parts.push(`Backend: ${be}`);
751
+ }
752
+
753
+ // Database
754
+ if (analysis.database.detected) {
755
+ let db = analysis.database.type || 'Unknown';
756
+ if (analysis.database.orm) {
757
+ db += ` + ${analysis.database.orm}`;
758
+ }
759
+ parts.push(`Database: ${db}`);
760
+ }
761
+
762
+ // Testing
763
+ if (analysis.testing.detected) {
764
+ let test = analysis.testing.framework;
765
+ if (analysis.testing.e2e) {
766
+ test += ` + ${analysis.testing.e2e} (E2E)`;
767
+ }
768
+ parts.push(`Testing: ${test}`);
769
+ }
770
+
771
+ // Deployment
772
+ if (analysis.deployment.detected) {
773
+ let deploy = analysis.deployment.platform;
774
+ if (analysis.deployment.containerized) {
775
+ deploy += ' (containerized)';
776
+ }
777
+ parts.push(`Deployment: ${deploy}`);
778
+ }
779
+
780
+ // Services
781
+ if (analysis.services?.detected) {
782
+ const services = [];
783
+ if (analysis.services.supabase) services.push('Supabase');
784
+ if (analysis.services.n8n) services.push('n8n');
785
+ if (analysis.services.stripe) services.push('Stripe');
786
+ if (analysis.services.auth0) services.push('Auth0');
787
+ if (analysis.services.clerk) services.push('Clerk');
788
+ if (analysis.services.resend) services.push('Resend');
789
+ if (analysis.services.twilio) services.push('Twilio');
790
+
791
+ if (services.length > 0) {
792
+ parts.push(`Services: ${services.join(', ')}`);
793
+ }
794
+ }
795
+
796
+ return parts.length > 0 ? parts.join('\n') : 'Unable to detect stack';
797
+ }
798
+
799
+ /**
800
+ * Display analysis results
801
+ */
802
+ export function displayAnalysisResults(analysis) {
803
+ console.log('');
804
+ console.log(chalk.cyan.bold('📊 Detected Tech Stack:'));
805
+ console.log('');
806
+
807
+ const summary = generateStackSummary(analysis);
808
+ console.log(chalk.white(summary));
809
+
810
+ console.log('');
811
+ console.log(chalk.dim(`Confidence: ${analysis.confidence}`));
812
+ console.log('');
813
+ }