specleap-framework 2.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 (47) hide show
  1. package/.agents/backend.md +419 -0
  2. package/.agents/frontend.md +577 -0
  3. package/.agents/producto.md +516 -0
  4. package/.commands/adoptar.md +323 -0
  5. package/.commands/ayuda.md +142 -0
  6. package/.commands/crear-tickets.md +55 -0
  7. package/.commands/documentar.md +285 -0
  8. package/.commands/explicar.md +234 -0
  9. package/.commands/implementar.md +383 -0
  10. package/.commands/inicio.md +824 -0
  11. package/.commands/nuevo/README.md +292 -0
  12. package/.commands/nuevo/questions-base.yaml +320 -0
  13. package/.commands/nuevo/responses-example.yaml +53 -0
  14. package/.commands/planificar.md +253 -0
  15. package/.commands/refinar.md +306 -0
  16. package/LICENSE +21 -0
  17. package/README.md +603 -0
  18. package/SETUP.md +351 -0
  19. package/install.sh +152 -0
  20. package/package.json +60 -0
  21. package/proyectos/_template/.gitkeep +1 -0
  22. package/proyectos/_template/ANEXOS.md +21 -0
  23. package/proyectos/_template/CONTRATO.md +26 -0
  24. package/proyectos/_template/context/.gitkeep +1 -0
  25. package/rules/development-rules.md +113 -0
  26. package/rules/environment-protection.md +97 -0
  27. package/rules/git-workflow.md +142 -0
  28. package/rules/session-protocol.md +121 -0
  29. package/scripts/README.md +129 -0
  30. package/scripts/analyze-project.sh +826 -0
  31. package/scripts/create-asana-tasks.sh +133 -0
  32. package/scripts/detect-project-type.sh +141 -0
  33. package/scripts/estimate-effort.sh +290 -0
  34. package/scripts/generate-asana-structure.sh +262 -0
  35. package/scripts/generate-contract.sh +360 -0
  36. package/scripts/generate-contrato.sh +555 -0
  37. package/scripts/install-git-hooks.sh +141 -0
  38. package/scripts/install-skills.sh +130 -0
  39. package/scripts/lib/asana-utils.sh +191 -0
  40. package/scripts/lib/jira-project-utils.sh +222 -0
  41. package/scripts/lib/questions.json +831 -0
  42. package/scripts/lib/render-contrato.py +195 -0
  43. package/scripts/lib/validate.sh +325 -0
  44. package/scripts/parse-contrato.sh +190 -0
  45. package/scripts/setup-mcp.sh +654 -0
  46. package/scripts/test-cuestionario.sh +428 -0
  47. package/setup.sh +458 -0
@@ -0,0 +1,826 @@
1
+ #!/bin/bash
2
+
3
+ # Cargar sistema i18n
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ source "$SCRIPT_DIR/../.specleap/i18n.sh"
6
+
7
+ # analyze-project.sh
8
+ # Analiza un proyecto legacy para adopciΓ³n en SpecLeap
9
+ # Uso: bash analyze-project.sh /ruta/al/proyecto
10
+
11
+ set -e
12
+
13
+ PROJECT_PATH="$1"
14
+ OUTPUT_FILE="/tmp/specleap-analysis.json"
15
+
16
+ if [ -z "$PROJECT_PATH" ]; then
17
+ echo "$(t "errors.no_path")"
18
+ echo "$(t "errors.usage"): bash analyze-project.sh /ruta/al/proyecto"
19
+ exit 1
20
+ fi
21
+
22
+ if [ ! -d "$PROJECT_PATH" ]; then
23
+ echo "$(t 'errors.invalid_path'): $PROJECT_PATH"
24
+ exit 1
25
+ fi
26
+
27
+ echo "$(t "analyze.scanning"): $PROJECT_PATH"
28
+ echo ""
29
+
30
+ cd "$PROJECT_PATH" || exit 1
31
+
32
+ # ==============================================================================
33
+ # VARIABLES GLOBALES
34
+ # ==============================================================================
35
+
36
+ PROJECT_NAME=$(basename "$PROJECT_PATH")
37
+ DETECTION_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
38
+
39
+ # ==============================================================================
40
+ # FUNCIΓ“N: Detectar Stack Backend
41
+ # ==============================================================================
42
+
43
+ detect_backend() {
44
+ local framework="Unknown"
45
+ local version="Unknown"
46
+ local language="Unknown"
47
+ local lang_version="Unknown"
48
+
49
+ echo "πŸ“¦ Detectando stack backend..."
50
+
51
+ # Laravel
52
+ if [ -f "composer.json" ]; then
53
+ if grep -q "laravel/framework" composer.json; then
54
+ framework="Laravel"
55
+ version=$(grep '"laravel/framework"' composer.json | sed 's/.*"[^0-9]*\([0-9.]*\)".*/\1/')
56
+ language="PHP"
57
+
58
+ if command -v php &> /dev/null; then
59
+ lang_version=$(php -v | head -n 1 | awk '{print $2}')
60
+ fi
61
+ fi
62
+ fi
63
+
64
+ # Symfony
65
+ if [ -f "composer.json" ]; then
66
+ if grep -q "symfony/framework-bundle" composer.json; then
67
+ framework="Symfony"
68
+ version=$(grep '"symfony/framework-bundle"' composer.json | sed 's/.*"[^0-9]*\([0-9.]*\)".*/\1/')
69
+ language="PHP"
70
+
71
+ if command -v php &> /dev/null; then
72
+ lang_version=$(php -v | head -n 1 | awk '{print $2}')
73
+ fi
74
+ fi
75
+ fi
76
+
77
+ # Node.js/Express
78
+ if [ -f "package.json" ] && [ ! -f "composer.json" ]; then
79
+ if grep -q '"express"' package.json; then
80
+ framework="Express"
81
+ version=$(grep '"express"' package.json | sed 's/.*"[^0-9]*\([0-9.]*\)".*/\1/')
82
+ language="JavaScript"
83
+
84
+ if command -v node &> /dev/null; then
85
+ lang_version=$(node -v | sed 's/v//')
86
+ fi
87
+ fi
88
+ fi
89
+
90
+ # Django
91
+ if [ -f "requirements.txt" ]; then
92
+ if grep -q "Django" requirements.txt; then
93
+ framework="Django"
94
+ version=$(grep "Django" requirements.txt | sed 's/.*==\([0-9.]*\).*/\1/')
95
+ language="Python"
96
+
97
+ if command -v python3 &> /dev/null; then
98
+ lang_version=$(python3 --version | awk '{print $2}')
99
+ fi
100
+ fi
101
+ fi
102
+
103
+ echo " Framework: $framework $version"
104
+ echo " Language: $language $lang_version"
105
+ echo ""
106
+
107
+ # Guardar en variables globales
108
+ BACKEND_FRAMEWORK="$framework"
109
+ BACKEND_VERSION="$version"
110
+ BACKEND_LANGUAGE="$language"
111
+ BACKEND_LANG_VERSION="$lang_version"
112
+ }
113
+
114
+ # ==============================================================================
115
+ # FUNCIΓ“N: Detectar Stack Frontend
116
+ # ==============================================================================
117
+
118
+ detect_frontend() {
119
+ local framework="Unknown"
120
+ local version="Unknown"
121
+ local build_tool="Unknown"
122
+ local ui_library="Unknown"
123
+
124
+ echo "🎨 Detectando stack frontend..."
125
+
126
+ if [ -f "package.json" ]; then
127
+ # React
128
+ if grep -q '"react"' package.json; then
129
+ framework="React"
130
+ version=$(grep '"react"' package.json | sed 's/.*"[^0-9]*\([0-9.]*\)".*/\1/')
131
+ fi
132
+
133
+ # Vue
134
+ if grep -q '"vue"' package.json; then
135
+ framework="Vue"
136
+ version=$(grep '"vue"' package.json | sed 's/.*"[^0-9]*\([0-9.]*\)".*/\1/')
137
+ fi
138
+
139
+ # Angular
140
+ if grep -q '"@angular/core"' package.json; then
141
+ framework="Angular"
142
+ version=$(grep '"@angular/core"' package.json | sed 's/.*"[^0-9]*\([0-9.]*\)".*/\1/')
143
+ fi
144
+
145
+ # Svelte
146
+ if grep -q '"svelte"' package.json; then
147
+ framework="Svelte"
148
+ version=$(grep '"svelte"' package.json | sed 's/.*"[^0-9]*\([0-9.]*\)".*/\1/')
149
+ fi
150
+
151
+ # Build tools
152
+ if grep -q '"vite"' package.json; then
153
+ build_tool="Vite"
154
+ elif grep -q '"webpack"' package.json; then
155
+ build_tool="Webpack"
156
+ elif grep -q '"parcel"' package.json; then
157
+ build_tool="Parcel"
158
+ fi
159
+
160
+ # UI Libraries
161
+ if grep -q '"tailwindcss"' package.json; then
162
+ ui_library="Tailwind CSS"
163
+ elif grep -q '"@mui/material"' package.json; then
164
+ ui_library="Material-UI"
165
+ elif grep -q '"bootstrap"' package.json; then
166
+ ui_library="Bootstrap"
167
+ fi
168
+ fi
169
+
170
+ echo " Framework: $framework $version"
171
+ echo " Build Tool: $build_tool"
172
+ echo " UI Library: $ui_library"
173
+ echo ""
174
+
175
+ FRONTEND_FRAMEWORK="$framework"
176
+ FRONTEND_VERSION="$version"
177
+ FRONTEND_BUILD_TOOL="$build_tool"
178
+ FRONTEND_UI_LIBRARY="$ui_library"
179
+ }
180
+
181
+ # ==============================================================================
182
+ # FUNCIΓ“N: Detectar Base de Datos
183
+ # ==============================================================================
184
+
185
+ detect_database() {
186
+ local db_type="Unknown"
187
+ local db_version="Unknown"
188
+ local tables_count=0
189
+
190
+ echo "πŸ—„οΈ Detectando base de datos..."
191
+
192
+ # Laravel migrations
193
+ if [ -d "database/migrations" ]; then
194
+ tables_count=$(grep -r "Schema::create" database/migrations/ 2>/dev/null | wc -l | tr -d ' ')
195
+
196
+ # Detectar tipo por driver en .env.example o config
197
+ if [ -f ".env.example" ]; then
198
+ if grep -q "DB_CONNECTION=mysql" .env.example; then
199
+ db_type="MySQL"
200
+ elif grep -q "DB_CONNECTION=pgsql" .env.example; then
201
+ db_type="PostgreSQL"
202
+ elif grep -q "DB_CONNECTION=sqlite" .env.example; then
203
+ db_type="SQLite"
204
+ fi
205
+ fi
206
+ fi
207
+
208
+ # Django migrations
209
+ if [ -d "migrations" ] || find . -type d -name "migrations" -not -path "./node_modules/*" | grep -q .; then
210
+ tables_count=$(find . -type f -name "*.py" -path "*/migrations/*" -not -path "./node_modules/*" | wc -l | tr -d ' ')
211
+ db_type="PostgreSQL" # Asumido para Django
212
+ fi
213
+
214
+ echo " Type: $db_type"
215
+ echo " Tables: $tables_count"
216
+ echo ""
217
+
218
+ DATABASE_TYPE="$db_type"
219
+ DATABASE_VERSION="$db_version"
220
+ DATABASE_TABLES_COUNT="$tables_count"
221
+ }
222
+
223
+ # ==============================================================================
224
+ # FUNCIΓ“N: Detectar Servicios Externos
225
+ # ==============================================================================
226
+
227
+ detect_services() {
228
+ echo "🌐 Detectando servicios externos..."
229
+
230
+ local services=()
231
+
232
+ # Buscar en archivos de configuraciΓ³n y cΓ³digo
233
+ if grep -rq "stripe" . --include="*.php" --include="*.js" --include="*.env*" 2>/dev/null; then
234
+ services+=("Stripe")
235
+ fi
236
+
237
+ if grep -rq "sendgrid" . --include="*.php" --include="*.js" --include="*.env*" 2>/dev/null; then
238
+ services+=("SendGrid")
239
+ fi
240
+
241
+ if grep -rq "mailgun" . --include="*.php" --include="*.js" --include="*.env*" 2>/dev/null; then
242
+ services+=("Mailgun")
243
+ fi
244
+
245
+ if grep -rq "aws" . --include="*.php" --include="*.js" --include="*.env*" 2>/dev/null; then
246
+ services+=("AWS S3")
247
+ fi
248
+
249
+ if grep -rq "firebase" . --include="*.php" --include="*.js" --include="*.json" 2>/dev/null; then
250
+ services+=("Firebase")
251
+ fi
252
+
253
+ if grep -rq "pusher" . --include="*.php" --include="*.js" --include="*.env*" 2>/dev/null; then
254
+ services+=("Pusher")
255
+ fi
256
+
257
+ if [ ${#services[@]} -gt 0 ]; then
258
+ SERVICES=$(printf '"%s",' "${services[@]}" | sed 's/,$//')
259
+ echo " Services: ${services[*]}"
260
+ else
261
+ SERVICES=""
262
+ echo " Services: None detected"
263
+ fi
264
+
265
+ echo ""
266
+ }
267
+
268
+ # ==============================================================================
269
+ # FUNCIΓ“N: Analizar Estructura del Proyecto
270
+ # ==============================================================================
271
+
272
+ analyze_structure() {
273
+ echo "πŸ“‚ Analizando estructura..."
274
+
275
+ local total_files=0
276
+ local php_files=0
277
+ local js_files=0
278
+ local blade_files=0
279
+ local controllers=0
280
+ local models=0
281
+ local migrations=0
282
+ local components=0
283
+
284
+ total_files=$(find . -type f -not -path "./node_modules/*" -not -path "./vendor/*" -not -path "./.git/*" | wc -l | tr -d ' ')
285
+
286
+ php_files=$(find . -type f -name "*.php" -not -path "./vendor/*" | wc -l | tr -d ' ')
287
+ js_files=$(find . -type f \( -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" \) -not -path "./node_modules/*" | wc -l | tr -d ' ')
288
+ blade_files=$(find . -type f -name "*.blade.php" 2>/dev/null | wc -l | tr -d ' ')
289
+
290
+ # Laravel especΓ­fico
291
+ if [ -d "app/Http/Controllers" ]; then
292
+ controllers=$(find app/Http/Controllers -type f -name "*Controller.php" 2>/dev/null | wc -l | tr -d ' ')
293
+ fi
294
+
295
+ if [ -d "app/Models" ]; then
296
+ models=$(find app/Models -type f -name "*.php" 2>/dev/null | wc -l | tr -d ' ')
297
+ fi
298
+
299
+ if [ -d "database/migrations" ]; then
300
+ migrations=$(find database/migrations -type f -name "*.php" 2>/dev/null | wc -l | tr -d ' ')
301
+ fi
302
+
303
+ # React/Vue components
304
+ if [ -d "resources/js/components" ]; then
305
+ components=$(find resources/js/components -type f \( -name "*.jsx" -o -name "*.tsx" -o -name "*.vue" \) 2>/dev/null | wc -l | tr -d ' ')
306
+ elif [ -d "src/components" ]; then
307
+ components=$(find src/components -type f \( -name "*.jsx" -o -name "*.tsx" -o -name "*.vue" \) 2>/dev/null | wc -l | tr -d ' ')
308
+ fi
309
+
310
+ # Contar lΓ­neas de cΓ³digo (si cloc estΓ‘ instalado)
311
+ local total_lines=0
312
+ if command -v cloc &> /dev/null; then
313
+ total_lines=$(cloc . --json --quiet 2>/dev/null | grep -o '"code":[0-9]*' | head -1 | grep -o '[0-9]*')
314
+ else
315
+ # EstimaciΓ³n simple
316
+ total_lines=$(find . -type f \( -name "*.php" -o -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" \) -not -path "./node_modules/*" -not -path "./vendor/*" -exec wc -l {} + 2>/dev/null | tail -1 | awk '{print $1}')
317
+ fi
318
+
319
+ echo " Total files: $total_files"
320
+ echo " PHP files: $php_files"
321
+ echo " JS files: $js_files"
322
+ echo " Total lines: $total_lines"
323
+ echo " Controllers: $controllers"
324
+ echo " Models: $models"
325
+ echo " Migrations: $migrations"
326
+ echo " Components: $components"
327
+ echo ""
328
+
329
+ STRUCTURE_TOTAL_FILES="$total_files"
330
+ STRUCTURE_PHP_FILES="$php_files"
331
+ STRUCTURE_JS_FILES="$js_files"
332
+ STRUCTURE_BLADE_FILES="$blade_files"
333
+ STRUCTURE_TOTAL_LINES="$total_lines"
334
+ STRUCTURE_CONTROLLERS="$controllers"
335
+ STRUCTURE_MODELS="$models"
336
+ STRUCTURE_MIGRATIONS="$migrations"
337
+ STRUCTURE_COMPONENTS="$components"
338
+ }
339
+
340
+ # ==============================================================================
341
+ # FUNCIΓ“N: AnΓ‘lisis EstΓ‘tico de Calidad (PHPStan)
342
+ # ==============================================================================
343
+
344
+ analyze_quality_phpstan() {
345
+ echo "πŸ” AnΓ‘lisis estΓ‘tico PHP (PHPStan)..."
346
+
347
+ local phpstan_errors=0
348
+ local phpstan_warnings=0
349
+
350
+ if [ "$BACKEND_LANGUAGE" = "PHP" ]; then
351
+ # Verificar si PHPStan estΓ‘ instalado
352
+ if [ -f "vendor/bin/phpstan" ] || command -v phpstan &> /dev/null; then
353
+ echo " Ejecutando PHPStan nivel 8..."
354
+
355
+ # Ejecutar PHPStan
356
+ if vendor/bin/phpstan analyse --level=8 --error-format=json --no-progress > /tmp/phpstan-report.json 2>/dev/null; then
357
+ phpstan_errors=0
358
+ else
359
+ # Contar errores del JSON
360
+ phpstan_errors=$(grep -o '"message"' /tmp/phpstan-report.json 2>/dev/null | wc -l | tr -d ' ')
361
+ fi
362
+
363
+ echo " Errors: $phpstan_errors"
364
+ else
365
+ echo " ⚠️ PHPStan no instalado (recomendado instalarlo)"
366
+ phpstan_errors=-1
367
+ fi
368
+ else
369
+ echo " Skipped (no es proyecto PHP)"
370
+ fi
371
+
372
+ echo ""
373
+
374
+ QUALITY_PHPSTAN_ERRORS="$phpstan_errors"
375
+ QUALITY_PHPSTAN_WARNINGS="$phpstan_warnings"
376
+ }
377
+
378
+ # ==============================================================================
379
+ # FUNCIΓ“N: AnΓ‘lisis EstΓ‘tico de Calidad (ESLint)
380
+ # ==============================================================================
381
+
382
+ analyze_quality_eslint() {
383
+ echo "πŸ” AnΓ‘lisis estΓ‘tico JavaScript (ESLint)..."
384
+
385
+ local eslint_errors=0
386
+ local eslint_warnings=0
387
+
388
+ if [ "$FRONTEND_FRAMEWORK" != "Unknown" ]; then
389
+ # Verificar si ESLint estΓ‘ instalado
390
+ if [ -f "node_modules/.bin/eslint" ] || command -v eslint &> /dev/null; then
391
+ echo " Ejecutando ESLint..."
392
+
393
+ # Ejecutar ESLint
394
+ if npx eslint . --format=json > /tmp/eslint-report.json 2>/dev/null; then
395
+ eslint_errors=0
396
+ eslint_warnings=0
397
+ else
398
+ # Contar errores y warnings
399
+ eslint_errors=$(grep -o '"severity":2' /tmp/eslint-report.json 2>/dev/null | wc -l | tr -d ' ')
400
+ eslint_warnings=$(grep -o '"severity":1' /tmp/eslint-report.json 2>/dev/null | wc -l | tr -d ' ')
401
+ fi
402
+
403
+ echo " Errors: $eslint_errors"
404
+ echo " Warnings: $eslint_warnings"
405
+ else
406
+ echo " ⚠️ ESLint no instalado (recomendado instalarlo)"
407
+ eslint_errors=-1
408
+ eslint_warnings=-1
409
+ fi
410
+ else
411
+ echo " Skipped (no es proyecto frontend)"
412
+ fi
413
+
414
+ echo ""
415
+
416
+ QUALITY_ESLINT_ERRORS="$eslint_errors"
417
+ QUALITY_ESLINT_WARNINGS="$eslint_warnings"
418
+ }
419
+
420
+ # ==============================================================================
421
+ # FUNCIΓ“N: Analizar Cobertura de Tests
422
+ # ==============================================================================
423
+
424
+ analyze_test_coverage() {
425
+ echo "πŸ§ͺ Analizando cobertura de tests..."
426
+
427
+ local php_coverage="0%"
428
+ local js_coverage="0%"
429
+
430
+ # PHP (PHPUnit)
431
+ if [ -f "phpunit.xml" ] && [ -f "vendor/bin/phpunit" ]; then
432
+ echo " Ejecutando PHPUnit con cobertura..."
433
+
434
+ # Ejecutar tests con cobertura (si Xdebug estΓ‘ disponible)
435
+ if php -m | grep -q xdebug; then
436
+ coverage_output=$(vendor/bin/phpunit --coverage-text --colors=never 2>/dev/null | grep "Lines:" | awk '{print $2}')
437
+ if [ -n "$coverage_output" ]; then
438
+ php_coverage="$coverage_output"
439
+ fi
440
+ else
441
+ echo " ⚠️ Xdebug no disponible, no se puede medir cobertura"
442
+ fi
443
+
444
+ echo " PHP Coverage: $php_coverage"
445
+ else
446
+ echo " ⚠️ PHPUnit no configurado"
447
+ fi
448
+
449
+ # JavaScript (Jest)
450
+ if [ -f "jest.config.js" ] || [ -f "jest.config.json" ]; then
451
+ echo " Ejecutando Jest con cobertura..."
452
+
453
+ coverage_output=$(npm test -- --coverage --silent 2>/dev/null | grep "All files" | awk '{print $10}')
454
+ if [ -n "$coverage_output" ]; then
455
+ js_coverage="$coverage_output"
456
+ fi
457
+
458
+ echo " JS Coverage: $js_coverage"
459
+ else
460
+ echo " ⚠️ Jest no configurado"
461
+ fi
462
+
463
+ echo ""
464
+
465
+ TEST_COVERAGE_PHP="$php_coverage"
466
+ TEST_COVERAGE_JS="$js_coverage"
467
+ }
468
+
469
+ # ==============================================================================
470
+ # FUNCIΓ“N: Detectar Dependencias Obsoletas
471
+ # ==============================================================================
472
+
473
+ detect_outdated_dependencies() {
474
+ echo "πŸ“¦ Detectando dependencias obsoletas..."
475
+
476
+ local composer_outdated=0
477
+ local npm_outdated=0
478
+
479
+ # Composer
480
+ if [ -f "composer.json" ] && command -v composer &> /dev/null; then
481
+ echo " Verificando composer..."
482
+ composer_outdated=$(composer outdated --direct 2>/dev/null | grep -c "^" || echo "0")
483
+ echo " Composer outdated: $composer_outdated packages"
484
+ fi
485
+
486
+ # npm
487
+ if [ -f "package.json" ] && command -v npm &> /dev/null; then
488
+ echo " Verificando npm..."
489
+ npm_outdated=$(npm outdated 2>/dev/null | grep -c "^" || echo "0")
490
+ echo " npm outdated: $npm_outdated packages"
491
+ fi
492
+
493
+ echo ""
494
+
495
+ OUTDATED_COMPOSER="$composer_outdated"
496
+ OUTDATED_NPM="$npm_outdated"
497
+ }
498
+
499
+ # ==============================================================================
500
+ # FUNCIΓ“N: Detectar Deuda TΓ©cnica
501
+ # ==============================================================================
502
+
503
+ detect_technical_debt() {
504
+ echo "⚠️ Detectando deuda técnica..."
505
+
506
+ # Array para almacenar issues
507
+ TECH_DEBT_ISSUES=()
508
+
509
+ # TD-001: Tests insuficientes
510
+ php_cov_num=$(echo "$TEST_COVERAGE_PHP" | sed 's/%//')
511
+ js_cov_num=$(echo "$TEST_COVERAGE_JS" | sed 's/%//')
512
+
513
+ if [ "$php_cov_num" != "0" ] && [ "$php_cov_num" -lt 90 ] 2>/dev/null; then
514
+ echo " πŸ”΄ TD-001: Cobertura de tests insuficiente (PHP: $TEST_COVERAGE_PHP, JS: $TEST_COVERAGE_JS)"
515
+ TECH_DEBT_ISSUES+=("TD-001")
516
+ fi
517
+
518
+ # TD-002: Errores PHPStan
519
+ if [ "$QUALITY_PHPSTAN_ERRORS" -gt 0 ] 2>/dev/null; then
520
+ echo " 🟑 TD-002: Errores PHPStan detectados ($QUALITY_PHPSTAN_ERRORS errores)"
521
+ TECH_DEBT_ISSUES+=("TD-002")
522
+ fi
523
+
524
+ # TD-003: N+1 queries (buscar patrones)
525
+ if grep -rq "foreach.*->get()" app/Http/Controllers/ 2>/dev/null; then
526
+ echo " πŸ”΄ TD-003: Posibles N+1 queries detectadas"
527
+ TECH_DEBT_ISSUES+=("TD-003")
528
+ fi
529
+
530
+ # TD-004: CΓ³digo duplicado
531
+ # (RequerirΓ­a phpcpd o similar - placeholder)
532
+ echo " 🟑 TD-004: Verificar código duplicado (requiere anÑlisis manual)"
533
+ TECH_DEBT_ISSUES+=("TD-004")
534
+
535
+ # TD-005: Rate limiting
536
+ if [ -f "routes/api.php" ]; then
537
+ if ! grep -q "throttle" routes/api.php 2>/dev/null; then
538
+ echo " πŸ”΄ TD-005: API sin rate limiting"
539
+ TECH_DEBT_ISSUES+=("TD-005")
540
+ fi
541
+ fi
542
+
543
+ # TD-006: Dependencias obsoletas
544
+ if [ "$OUTDATED_COMPOSER" -gt 0 ] || [ "$OUTDATED_NPM" -gt 0 ]; then
545
+ echo " 🟑 TD-006: Dependencias obsoletas ($OUTDATED_COMPOSER composer, $OUTDATED_NPM npm)"
546
+ TECH_DEBT_ISSUES+=("TD-006")
547
+ fi
548
+
549
+ # TD-007: Complejidad ciclomΓ‘tica
550
+ # (RequerirΓ­a phpmetrics o similar - placeholder)
551
+ echo " 🟒 TD-007: Verificar complejidad ciclomÑtica (requiere anÑlisis manual)"
552
+ TECH_DEBT_ISSUES+=("TD-007")
553
+
554
+ # TD-008: DocumentaciΓ³n API
555
+ if [ -f "routes/api.php" ] && [ ! -f "storage/api-docs/api-docs.json" ]; then
556
+ echo " 🟑 TD-008: API sin documentación OpenAPI"
557
+ TECH_DEBT_ISSUES+=("TD-008")
558
+ fi
559
+
560
+ echo " Total issues: ${#TECH_DEBT_ISSUES[@]}"
561
+ echo ""
562
+ }
563
+
564
+ # ==============================================================================
565
+ # FUNCIΓ“N: Detectar Monorepo
566
+ # ==============================================================================
567
+
568
+ detect_monorepo() {
569
+ echo "πŸ“¦ Detectando monorepo..."
570
+
571
+ local is_monorepo=false
572
+ local packages=()
573
+
574
+ if [ -d "packages" ]; then
575
+ package_count=$(find packages -maxdepth 1 -type d | tail -n +2 | wc -l | tr -d ' ')
576
+
577
+ if [ "$package_count" -gt 0 ]; then
578
+ is_monorepo=true
579
+ echo " βœ… Monorepo detectado ($package_count packages)"
580
+
581
+ # Listar packages
582
+ while IFS= read -r pkg; do
583
+ pkg_name=$(basename "$pkg")
584
+ packages+=("$pkg_name")
585
+ done < <(find packages -maxdepth 1 -type d | tail -n +2)
586
+ fi
587
+ fi
588
+
589
+ if [ "$is_monorepo" = false ]; then
590
+ echo " No es monorepo"
591
+ fi
592
+
593
+ echo ""
594
+
595
+ MONOREPO="$is_monorepo"
596
+ MONOREPO_PACKAGES=$(printf '"%s",' "${packages[@]}" | sed 's/,$//')
597
+ }
598
+
599
+ # ==============================================================================
600
+ # FUNCIΓ“N: Generar JSON de Salida
601
+ # ==============================================================================
602
+
603
+ generate_json_output() {
604
+ echo "πŸ“„ Generando reporte JSON..."
605
+
606
+ # Escapar comillas en strings
607
+ PROJECT_PATH_ESC=$(echo "$PROJECT_PATH" | sed 's/"/\\"/g')
608
+
609
+ cat > "$OUTPUT_FILE" <<EOF
610
+ {
611
+ "project_name": "$PROJECT_NAME",
612
+ "project_path": "$PROJECT_PATH_ESC",
613
+ "detection_date": "$DETECTION_DATE",
614
+
615
+ "stack": {
616
+ "backend": {
617
+ "framework": "$BACKEND_FRAMEWORK",
618
+ "version": "$BACKEND_VERSION",
619
+ "language": "$BACKEND_LANGUAGE",
620
+ "language_version": "$BACKEND_LANG_VERSION"
621
+ },
622
+ "frontend": {
623
+ "framework": "$FRONTEND_FRAMEWORK",
624
+ "version": "$FRONTEND_VERSION",
625
+ "build_tool": "$FRONTEND_BUILD_TOOL",
626
+ "ui_library": "$FRONTEND_UI_LIBRARY"
627
+ },
628
+ "database": {
629
+ "type": "$DATABASE_TYPE",
630
+ "version": "$DATABASE_VERSION",
631
+ "tables_count": $DATABASE_TABLES_COUNT
632
+ },
633
+ "services": [$SERVICES]
634
+ },
635
+
636
+ "structure": {
637
+ "total_files": $STRUCTURE_TOTAL_FILES,
638
+ "php_files": $STRUCTURE_PHP_FILES,
639
+ "js_files": $STRUCTURE_JS_FILES,
640
+ "blade_files": $STRUCTURE_BLADE_FILES,
641
+ "total_lines": $STRUCTURE_TOTAL_LINES,
642
+ "controllers": $STRUCTURE_CONTROLLERS,
643
+ "models": $STRUCTURE_MODELS,
644
+ "migrations": $STRUCTURE_MIGRATIONS,
645
+ "components": $STRUCTURE_COMPONENTS
646
+ },
647
+
648
+ "quality": {
649
+ "phpstan": {
650
+ "errors": $QUALITY_PHPSTAN_ERRORS,
651
+ "warnings": $QUALITY_PHPSTAN_WARNINGS
652
+ },
653
+ "eslint": {
654
+ "errors": $QUALITY_ESLINT_ERRORS,
655
+ "warnings": $QUALITY_ESLINT_WARNINGS
656
+ },
657
+ "test_coverage": {
658
+ "php": "$TEST_COVERAGE_PHP",
659
+ "js": "$TEST_COVERAGE_JS"
660
+ },
661
+ "outdated_dependencies": {
662
+ "composer": $OUTDATED_COMPOSER,
663
+ "npm": $OUTDATED_NPM
664
+ }
665
+ },
666
+
667
+ "technical_debt": [
668
+ $(generate_tech_debt_json)
669
+ ],
670
+
671
+ "monorepo": $MONOREPO,
672
+ "packages": [$MONOREPO_PACKAGES]
673
+ }
674
+ EOF
675
+
676
+ echo " βœ… Reporte guardado en: $OUTPUT_FILE"
677
+ echo ""
678
+ }
679
+
680
+ # ==============================================================================
681
+ # FUNCIΓ“N: Generar JSON de Deuda TΓ©cnica
682
+ # ==============================================================================
683
+
684
+ generate_tech_debt_json() {
685
+ local json_items=()
686
+
687
+ # TD-001: Tests
688
+ if [[ " ${TECH_DEBT_ISSUES[@]} " =~ " TD-001 " ]]; then
689
+ json_items+=(' {
690
+ "id": "TD-001",
691
+ "type": "tests",
692
+ "severity": "high",
693
+ "title": "Insufficient test coverage",
694
+ "description": "PHP coverage: '"$TEST_COVERAGE_PHP"', JS coverage: '"$TEST_COVERAGE_JS"'. Target: 90%+",
695
+ "affected_files": ["all"],
696
+ "estimated_effort_hours": 40
697
+ }')
698
+ fi
699
+
700
+ # TD-002: PHPStan
701
+ if [[ " ${TECH_DEBT_ISSUES[@]} " =~ " TD-002 " ]]; then
702
+ json_items+=(' {
703
+ "id": "TD-002",
704
+ "type": "code_quality",
705
+ "severity": "medium",
706
+ "title": "PHPStan errors and warnings",
707
+ "description": "'"$QUALITY_PHPSTAN_ERRORS"' errors at level 8",
708
+ "affected_files": [],
709
+ "estimated_effort_hours": 8
710
+ }')
711
+ fi
712
+
713
+ # TD-003: N+1
714
+ if [[ " ${TECH_DEBT_ISSUES[@]} " =~ " TD-003 " ]]; then
715
+ json_items+=(' {
716
+ "id": "TD-003",
717
+ "type": "performance",
718
+ "severity": "high",
719
+ "title": "N+1 query problems",
720
+ "description": "Detected potential N+1 queries in controllers",
721
+ "affected_files": [],
722
+ "estimated_effort_hours": 6
723
+ }')
724
+ fi
725
+
726
+ # TD-004: Code duplication
727
+ if [[ " ${TECH_DEBT_ISSUES[@]} " =~ " TD-004 " ]]; then
728
+ json_items+=(' {
729
+ "id": "TD-004",
730
+ "type": "code_duplication",
731
+ "severity": "medium",
732
+ "title": "Duplicated code",
733
+ "description": "Requires manual analysis with phpcpd",
734
+ "affected_files": [],
735
+ "estimated_effort_hours": 4
736
+ }')
737
+ fi
738
+
739
+ # TD-005: Rate limiting
740
+ if [[ " ${TECH_DEBT_ISSUES[@]} " =~ " TD-005 " ]]; then
741
+ json_items+=(' {
742
+ "id": "TD-005",
743
+ "type": "security",
744
+ "severity": "high",
745
+ "title": "Missing rate limiting",
746
+ "description": "API endpoints lack rate limiting configuration",
747
+ "affected_files": ["routes/api.php"],
748
+ "estimated_effort_hours": 3
749
+ }')
750
+ fi
751
+
752
+ # TD-006: Outdated deps
753
+ if [[ " ${TECH_DEBT_ISSUES[@]} " =~ " TD-006 " ]]; then
754
+ json_items+=(' {
755
+ "id": "TD-006",
756
+ "type": "dependencies",
757
+ "severity": "medium",
758
+ "title": "Outdated dependencies",
759
+ "description": "'"$OUTDATED_COMPOSER"' composer packages and '"$OUTDATED_NPM"' npm packages outdated",
760
+ "affected_files": ["composer.json", "package.json"],
761
+ "estimated_effort_hours": 5
762
+ }')
763
+ fi
764
+
765
+ # TD-007: Complexity
766
+ if [[ " ${TECH_DEBT_ISSUES[@]} " =~ " TD-007 " ]]; then
767
+ json_items+=(' {
768
+ "id": "TD-007",
769
+ "type": "code_complexity",
770
+ "severity": "low",
771
+ "title": "High cyclomatic complexity",
772
+ "description": "Requires analysis with phpmetrics",
773
+ "affected_files": [],
774
+ "estimated_effort_hours": 3
775
+ }')
776
+ fi
777
+
778
+ # TD-008: API docs
779
+ if [[ " ${TECH_DEBT_ISSUES[@]} " =~ " TD-008 " ]]; then
780
+ json_items+=(' {
781
+ "id": "TD-008",
782
+ "type": "documentation",
783
+ "severity": "medium",
784
+ "title": "Missing API documentation",
785
+ "description": "No OpenAPI/Swagger specs for API endpoints",
786
+ "affected_files": ["routes/api.php"],
787
+ "estimated_effort_hours": 8
788
+ }')
789
+ fi
790
+
791
+ # Join con comas
792
+ printf '%s' "$(IFS=,; echo "${json_items[*]}")"
793
+ }
794
+
795
+ # ==============================================================================
796
+ # EJECUCIΓ“N PRINCIPAL
797
+ # ==============================================================================
798
+
799
+ main() {
800
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
801
+ echo " SpecLeap β€” AnΓ‘lisis de Proyecto Legacy"
802
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
803
+ echo ""
804
+
805
+ detect_backend
806
+ detect_frontend
807
+ detect_database
808
+ detect_services
809
+ analyze_structure
810
+ analyze_quality_phpstan
811
+ analyze_quality_eslint
812
+ analyze_test_coverage
813
+ detect_outdated_dependencies
814
+ detect_technical_debt
815
+ detect_monorepo
816
+ generate_json_output
817
+
818
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
819
+ echo " βœ… AnΓ‘lisis completado"
820
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
821
+ echo ""
822
+ echo "πŸ“„ Reporte JSON: $OUTPUT_FILE"
823
+ echo ""
824
+ }
825
+
826
+ main