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,133 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # SpecLeap — Crear tareas individuales en Asana
4
+ # Permite crear tareas rápidamente desde línea de comandos
5
+
6
+ set -euo pipefail
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ ASANA_UTILS="$SCRIPT_DIR/lib/asana-utils.sh"
10
+
11
+ source "$ASANA_UTILS"
12
+
13
+ # Colores
14
+ GREEN='\033[0;32m'
15
+ RED='\033[0;31m'
16
+ YELLOW='\033[1;33m'
17
+ CYAN='\033[0;36m'
18
+ BOLD='\033[1m'
19
+ RESET='\033[0m'
20
+
21
+ usage() {
22
+ cat << EOF
23
+ Uso: $(basename "$0") [OPCIONES]
24
+
25
+ Crea tareas individuales en Asana
26
+
27
+ OPCIONES:
28
+ -p, --proyecto GID ID del proyecto (obligatorio)
29
+ -t, --titulo "TEXTO" Título de la tarea (obligatorio)
30
+ -s, --seccion GID ID de la sección (opcional)
31
+ -P, --puntos NUM Story points (default: 3)
32
+ -l, --listar Listar proyectos disponibles
33
+ -h, --help Muestra esta ayuda
34
+
35
+ EJEMPLOS:
36
+ # Listar proyectos:
37
+ $(basename "$0") --listar
38
+
39
+ # Crear tarea simple:
40
+ $(basename "$0") -p 1234567890 -t "Implementar login"
41
+
42
+ # Crear tarea con sección y puntos:
43
+ $(basename "$0") -p 1234567890 -t "Setup CI/CD" -s 9876543210 -P 5
44
+
45
+ EOF
46
+ }
47
+
48
+ # Parsear argumentos
49
+ PROJECT_GID=""
50
+ TASK_NAME=""
51
+ SECTION_GID=""
52
+ POINTS=3
53
+ LIST_MODE=false
54
+
55
+ while [[ $# -gt 0 ]]; do
56
+ case $1 in
57
+ -p|--proyecto)
58
+ PROJECT_GID="$2"
59
+ shift 2
60
+ ;;
61
+ -t|--titulo)
62
+ TASK_NAME="$2"
63
+ shift 2
64
+ ;;
65
+ -s|--seccion)
66
+ SECTION_GID="$2"
67
+ shift 2
68
+ ;;
69
+ -P|--puntos)
70
+ POINTS="$2"
71
+ shift 2
72
+ ;;
73
+ -l|--listar)
74
+ LIST_MODE=true
75
+ shift
76
+ ;;
77
+ -h|--help)
78
+ usage
79
+ exit 0
80
+ ;;
81
+ *)
82
+ echo -e "${RED}❌ Opción desconocida: $1${RESET}"
83
+ usage
84
+ exit 1
85
+ ;;
86
+ esac
87
+ done
88
+
89
+ # Verificar token
90
+ if [[ -z "${ASANA_ACCESS_TOKEN:-}" ]]; then
91
+ echo -e "${RED}❌ Error: ASANA_ACCESS_TOKEN no configurado${RESET}"
92
+ echo ""
93
+ echo "Configúralo en ~/.zshrc o ~/.bashrc:"
94
+ echo " export ASANA_ACCESS_TOKEN=\"tu-token-aqui\""
95
+ exit 1
96
+ fi
97
+
98
+ # Modo listar
99
+ if [[ "$LIST_MODE" == true ]]; then
100
+ echo -e "${CYAN}${BOLD}📋 Proyectos disponibles:${RESET}\n"
101
+ asana_list_projects | while IFS=$'\t' read -r gid name; do
102
+ echo -e " ${BOLD}$gid${RESET} $name"
103
+ done
104
+ exit 0
105
+ fi
106
+
107
+ # Validar argumentos obligatorios
108
+ if [[ -z "$PROJECT_GID" ]]; then
109
+ echo -e "${RED}❌ Error: --proyecto es obligatorio${RESET}"
110
+ usage
111
+ exit 1
112
+ fi
113
+
114
+ if [[ -z "$TASK_NAME" ]]; then
115
+ echo -e "${RED}❌ Error: --titulo es obligatorio${RESET}"
116
+ usage
117
+ exit 1
118
+ fi
119
+
120
+ # Crear tarea
121
+ echo -e "${CYAN}Creando tarea en Asana...${RESET}"
122
+ TASK_GID=$(asana_create_task "$PROJECT_GID" "$TASK_NAME" "$SECTION_GID" "$POINTS")
123
+
124
+ if [[ -n "$TASK_GID" ]]; then
125
+ echo -e "${GREEN}${BOLD}✅ Tarea creada exitosamente${RESET}"
126
+ echo -e " ${BOLD}ID:${RESET} $TASK_GID"
127
+ echo -e " ${BOLD}Título:${RESET} $TASK_NAME"
128
+ echo -e " ${BOLD}Points:${RESET} $POINTS"
129
+ echo -e "\n📊 Ver tarea: https://app.asana.com/0/$PROJECT_GID/$TASK_GID"
130
+ else
131
+ echo -e "${RED}❌ Error al crear tarea${RESET}"
132
+ exit 1
133
+ fi
@@ -0,0 +1,141 @@
1
+ #!/bin/bash
2
+ # detect-project-type.sh
3
+ # Analiza las respuestas del cuestionario base y determina qué preguntas condicionales cargar
4
+
5
+ set -euo pipefail
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ SPECLEAP_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
9
+ RESPONSES_FILE="${1:-}"
10
+
11
+ if [[ -z "$RESPONSES_FILE" || ! -f "$RESPONSES_FILE" ]]; then
12
+ echo "❌ Error: Archivo de respuestas no encontrado"
13
+ echo "Uso: $0 <responses.yaml>"
14
+ exit 1
15
+ fi
16
+
17
+ # Colores
18
+ GREEN='\033[0;32m'
19
+ BLUE='\033[0;34m'
20
+ YELLOW='\033[1;33m'
21
+ NC='\033[0m' # No Color
22
+
23
+ echo -e "${BLUE}🔍 Analizando tipo de proyecto...${NC}"
24
+ echo ""
25
+
26
+ # ===== EXTRAER RESPUESTAS CLAVE =====
27
+ BACKEND=$(grep "^backend_framework:" "$RESPONSES_FILE" | cut -d: -f2 | xargs)
28
+ FRONTEND=$(grep "^frontend_framework:" "$RESPONSES_FILE" | cut -d: -f2 | xargs)
29
+ NEEDS_AUTH=$(grep "^needs_auth:" "$RESPONSES_FILE" | cut -d: -f2 | xargs)
30
+ NEEDS_ADMIN=$(grep "^needs_admin_panel:" "$RESPONSES_FILE" | cut -d: -f2 | xargs)
31
+ FILE_UPLOADS=$(grep "^file_uploads:" "$RESPONSES_FILE" | cut -d: -f2 | xargs)
32
+ HOSTING=$(grep "^hosting:" "$RESPONSES_FILE" | cut -d: -f2 | xargs)
33
+
34
+ # Extraer funcionalidades (multilínea)
35
+ CORE_FEATURES=$(sed -n '/^core_features:/,/^[a-z_]*:/p' "$RESPONSES_FILE" | sed '1d;$d' | sed 's/^[[:space:]]*-//' | xargs)
36
+
37
+ # ===== DETECTAR PATRONES =====
38
+ DETECTED_TYPES=()
39
+
40
+ # E-commerce
41
+ if echo "$CORE_FEATURES" | grep -qiE "(pago|compra|carrito|stripe|paypal|producto|inventario|pedido)"; then
42
+ DETECTED_TYPES+=("ecommerce")
43
+ echo -e "${GREEN}✓${NC} Detectado: E-commerce"
44
+ fi
45
+
46
+ # SaaS
47
+ if echo "$CORE_FEATURES" | grep -qiE "(suscripción|plan|billing|mensualidad|trial|premium)"; then
48
+ DETECTED_TYPES+=("saas")
49
+ echo -e "${GREEN}✓${NC} Detectado: SaaS"
50
+ fi
51
+
52
+ # API
53
+ if [[ "$FRONTEND" == "none" ]] || echo "$CORE_FEATURES" | grep -qiE "(api|endpoint|rest|graphql|webhook)"; then
54
+ DETECTED_TYPES+=("api")
55
+ echo -e "${GREEN}✓${NC} Detectado: API/Backend"
56
+ fi
57
+
58
+ # CMS/Blog
59
+ if echo "$CORE_FEATURES" | grep -qiE "(blog|post|artículo|contenido|cms|publicar|editor)"; then
60
+ DETECTED_TYPES+=("cms")
61
+ echo -e "${GREEN}✓${NC} Detectado: CMS/Blog"
62
+ fi
63
+
64
+ # Tiempo Real
65
+ if echo "$CORE_FEATURES" | grep -qiE "(chat|mensaje|notificación|tiempo real|websocket|live)"; then
66
+ DETECTED_TYPES+=("realtime")
67
+ echo -e "${GREEN}✓${NC} Detectado: Funcionalidad en tiempo real"
68
+ fi
69
+
70
+ # Autenticación compleja
71
+ if [[ "$NEEDS_AUTH" != "none" ]]; then
72
+ DETECTED_TYPES+=("auth")
73
+ echo -e "${GREEN}✓${NC} Detectado: Autenticación de usuarios"
74
+ fi
75
+
76
+ # Admin Panel
77
+ if [[ "$NEEDS_ADMIN" != "none" ]]; then
78
+ DETECTED_TYPES+=("admin")
79
+ echo -e "${GREEN}✓${NC} Detectado: Panel de administración"
80
+ fi
81
+
82
+ # Storage
83
+ if [[ "$FILE_UPLOADS" == "cloud" ]]; then
84
+ DETECTED_TYPES+=("storage")
85
+ echo -e "${GREEN}✓${NC} Detectado: Almacenamiento en la nube"
86
+ fi
87
+
88
+ # AWS
89
+ if [[ "$HOSTING" == "aws" ]]; then
90
+ DETECTED_TYPES+=("aws")
91
+ echo -e "${GREEN}✓${NC} Detectado: Despliegue en AWS"
92
+ fi
93
+
94
+ # OAuth
95
+ if [[ "$NEEDS_AUTH" == "oauth" ]] || [[ "$NEEDS_AUTH" == "both" ]]; then
96
+ DETECTED_TYPES+=("oauth")
97
+ echo -e "${GREEN}✓${NC} Detectado: OAuth"
98
+ fi
99
+
100
+ # Stack-specific
101
+ case "$BACKEND" in
102
+ laravel)
103
+ DETECTED_TYPES+=("php")
104
+ echo -e "${GREEN}✓${NC} Detectado: Stack PHP/Laravel"
105
+ ;;
106
+ nodejs)
107
+ DETECTED_TYPES+=("nodejs")
108
+ echo -e "${GREEN}✓${NC} Detectado: Stack Node.js"
109
+ ;;
110
+ python)
111
+ DETECTED_TYPES+=("python")
112
+ echo -e "${GREEN}✓${NC} Detectado: Stack Python"
113
+ ;;
114
+ esac
115
+
116
+ case "$FRONTEND" in
117
+ react)
118
+ DETECTED_TYPES+=("react")
119
+ echo -e "${GREEN}✓${NC} Detectado: Frontend React"
120
+ ;;
121
+ vue)
122
+ DETECTED_TYPES+=("vue")
123
+ echo -e "${GREEN}✓${NC} Detectado: Frontend Vue.js"
124
+ ;;
125
+ esac
126
+
127
+ # ===== GUARDAR RESULTADO =====
128
+ OUTPUT_FILE="${RESPONSES_FILE%.yaml}.types.txt"
129
+ printf "%s\n" "${DETECTED_TYPES[@]}" > "$OUTPUT_FILE"
130
+
131
+ echo ""
132
+ echo -e "${BLUE}📋 Tipos detectados guardados en:${NC} $OUTPUT_FILE"
133
+ echo ""
134
+ echo -e "${YELLOW}Preguntas condicionales que se cargarán:${NC}"
135
+ for type in "${DETECTED_TYPES[@]}"; do
136
+ echo " - questions-${type}.yaml"
137
+ done
138
+ echo ""
139
+
140
+ # ===== RETORNAR TIPOS (para captura en scripts) =====
141
+ echo "${DETECTED_TYPES[*]}"
@@ -0,0 +1,290 @@
1
+ #!/bin/bash
2
+
3
+ # estimate-effort.sh
4
+ # Estima el esfuerzo requerido para resolver issues de deuda técnica
5
+ # Uso: bash estimate-effort.sh /tmp/specleap-analysis.json
6
+
7
+ set -e
8
+
9
+ ANALYSIS_FILE="$1"
10
+
11
+ if [ -z "$ANALYSIS_FILE" ]; then
12
+ echo "Error: Debes proporcionar el archivo de análisis JSON"
13
+ echo "Uso: bash estimate-effort.sh /tmp/specleap-analysis.json"
14
+ exit 1
15
+ fi
16
+
17
+ if [ ! -f "$ANALYSIS_FILE" ]; then
18
+ echo "Error: Archivo '$ANALYSIS_FILE' no encontrado"
19
+ exit 1
20
+ fi
21
+
22
+ echo "📊 Estimando esfuerzo de deuda técnica..."
23
+ echo ""
24
+
25
+ # Extraer métricas clave del JSON
26
+ php_files=$(grep '"php_files"' "$ANALYSIS_FILE" | grep -o '[0-9]*')
27
+ js_files=$(grep '"js_files"' "$ANALYSIS_FILE" | grep -o '[0-9]*')
28
+ total_lines=$(grep '"total_lines"' "$ANALYSIS_FILE" | grep -o '[0-9]*')
29
+ controllers=$(grep '"controllers"' "$ANALYSIS_FILE" | grep -o '[0-9]*')
30
+ models=$(grep '"models"' "$ANALYSIS_FILE" | grep -o '[0-9]*')
31
+ components=$(grep '"components"' "$ANALYSIS_FILE" | grep -o '[0-9]*')
32
+
33
+ php_coverage=$(grep '"php":' "$ANALYSIS_FILE" | grep -o '[0-9]*%' | head -1 | sed 's/%//')
34
+ js_coverage=$(grep '"js":' "$ANALYSIS_FILE" | grep -o '[0-9]*%' | head -1 | sed 's/%//')
35
+
36
+ phpstan_errors=$(grep -A 2 '"phpstan":' "$ANALYSIS_FILE" | grep '"errors"' | grep -o '[0-9]*')
37
+ eslint_errors=$(grep -A 2 '"eslint":' "$ANALYSIS_FILE" | grep '"errors"' | grep -o '[0-9]*')
38
+
39
+ # Valores por defecto si no se encontraron
40
+ php_files=${php_files:-0}
41
+ js_files=${js_files:-0}
42
+ total_lines=${total_lines:-0}
43
+ controllers=${controllers:-0}
44
+ models=${models:-0}
45
+ components=${components:-0}
46
+ php_coverage=${php_coverage:-0}
47
+ js_coverage=${js_coverage:-0}
48
+ phpstan_errors=${phpstan_errors:-0}
49
+ eslint_errors=${eslint_errors:-0}
50
+
51
+ # ==============================================================================
52
+ # ESTIMACIÓN POR TIPO DE ISSUE
53
+ # ==============================================================================
54
+
55
+ # TD-001: Tests (cobertura insuficiente)
56
+ estimate_tests() {
57
+ local effort=0
58
+
59
+ # PHP tests
60
+ if [ "$php_coverage" -lt 90 ] && [ "$php_files" -gt 0 ]; then
61
+ local missing_coverage=$((90 - php_coverage))
62
+ local files_to_test=$((php_files * missing_coverage / 100))
63
+
64
+ # Estimación: 30 min por archivo simple, 1h por controller, 2h por servicio
65
+ local controller_effort=$((controllers * 60)) # 1h por controller
66
+ local model_effort=$((models * 30)) # 30 min por model
67
+ local other_effort=$(((files_to_test - controllers - models) * 30)) # 30 min otros
68
+
69
+ effort=$((controller_effort + model_effort + other_effort))
70
+ fi
71
+
72
+ # JS tests
73
+ if [ "$js_coverage" -lt 90 ] && [ "$js_files" -gt 0 ]; then
74
+ local missing_coverage=$((90 - js_coverage))
75
+ local files_to_test=$((js_files * missing_coverage / 100))
76
+
77
+ # Estimación: 45 min por componente, 30 min por utility
78
+ local component_effort=$((components * 45)) # 45 min por componente
79
+ local other_effort=$(((files_to_test - components) * 30)) # 30 min otros
80
+
81
+ effort=$((effort + component_effort + other_effort))
82
+ fi
83
+
84
+ # Convertir minutos a horas (redondear hacia arriba)
85
+ effort=$(((effort + 59) / 60))
86
+
87
+ # Mínimo 8 horas, máximo 80 horas
88
+ if [ "$effort" -lt 8 ]; then
89
+ effort=8
90
+ elif [ "$effort" -gt 80 ]; then
91
+ effort=80
92
+ fi
93
+
94
+ echo "$effort"
95
+ }
96
+
97
+ # TD-002: PHPStan errors
98
+ estimate_phpstan() {
99
+ local effort=0
100
+
101
+ if [ "$phpstan_errors" -gt 0 ]; then
102
+ # Estimación: 10 min por error simple, 30 min por error complejo
103
+ # Asumimos 70% simples, 30% complejos
104
+ local simple_errors=$((phpstan_errors * 70 / 100))
105
+ local complex_errors=$((phpstan_errors * 30 / 100))
106
+
107
+ effort=$(((simple_errors * 10 + complex_errors * 30 + 59) / 60))
108
+
109
+ # Mínimo 2 horas, máximo 40 horas
110
+ if [ "$effort" -lt 2 ]; then
111
+ effort=2
112
+ elif [ "$effort" -gt 40 ]; then
113
+ effort=40
114
+ fi
115
+ fi
116
+
117
+ echo "$effort"
118
+ }
119
+
120
+ # TD-003: N+1 queries
121
+ estimate_n_plus_one() {
122
+ local effort=0
123
+
124
+ # Estimación basada en número de controllers
125
+ # Asumimos que ~30% de controllers tienen N+1
126
+ local affected_controllers=$((controllers * 30 / 100))
127
+
128
+ if [ "$affected_controllers" -gt 0 ]; then
129
+ # Estimación: 1-2 horas por controller
130
+ effort=$((affected_controllers * 90 / 60)) # 1.5h promedio
131
+
132
+ # Mínimo 3 horas, máximo 20 horas
133
+ if [ "$effort" -lt 3 ]; then
134
+ effort=3
135
+ elif [ "$effort" -gt 20 ]; then
136
+ effort=20
137
+ fi
138
+ fi
139
+
140
+ echo "$effort"
141
+ }
142
+
143
+ # TD-004: Código duplicado
144
+ estimate_duplication() {
145
+ local effort=0
146
+
147
+ # Estimación basada en tamaño del proyecto
148
+ if [ "$total_lines" -gt 50000 ]; then
149
+ effort=12
150
+ elif [ "$total_lines" -gt 20000 ]; then
151
+ effort=8
152
+ elif [ "$total_lines" -gt 10000 ]; then
153
+ effort=4
154
+ else
155
+ effort=2
156
+ fi
157
+
158
+ echo "$effort"
159
+ }
160
+
161
+ # TD-005: Rate limiting
162
+ estimate_rate_limiting() {
163
+ # Esfuerzo fijo: configuración middleware + testing
164
+ echo "3"
165
+ }
166
+
167
+ # TD-006: Dependencias obsoletas
168
+ estimate_outdated_deps() {
169
+ local composer_outdated=$(grep -A 2 '"outdated_dependencies":' "$ANALYSIS_FILE" | grep '"composer"' | grep -o '[0-9]*')
170
+ local npm_outdated=$(grep -A 2 '"outdated_dependencies":' "$ANALYSIS_FILE" | grep '"npm"' | grep -o '[0-9]*')
171
+
172
+ composer_outdated=${composer_outdated:-0}
173
+ npm_outdated=${npm_outdated:-0}
174
+
175
+ local total_outdated=$((composer_outdated + npm_outdated))
176
+ local effort=0
177
+
178
+ if [ "$total_outdated" -gt 0 ]; then
179
+ # Estimación: 20 min por paquete (testing + resolución conflictos)
180
+ effort=$(((total_outdated * 20 + 59) / 60))
181
+
182
+ # Mínimo 2 horas, máximo 20 horas
183
+ if [ "$effort" -lt 2 ]; then
184
+ effort=2
185
+ elif [ "$effort" -gt 20 ]; then
186
+ effort=20
187
+ fi
188
+ fi
189
+
190
+ echo "$effort"
191
+ }
192
+
193
+ # TD-007: Complejidad ciclomática
194
+ estimate_complexity() {
195
+ # Estimación basada en número de controllers
196
+ # Asumimos que ~20% tienen complejidad alta
197
+ local affected_controllers=$((controllers * 20 / 100))
198
+
199
+ local effort=0
200
+ if [ "$affected_controllers" -gt 0 ]; then
201
+ # Estimación: 2 horas por controller (refactor + tests)
202
+ effort=$((affected_controllers * 2))
203
+
204
+ # Mínimo 2 horas, máximo 15 horas
205
+ if [ "$effort" -lt 2 ]; then
206
+ effort=2
207
+ elif [ "$effort" -gt 15 ]; then
208
+ effort=15
209
+ fi
210
+ fi
211
+
212
+ echo "$effort"
213
+ }
214
+
215
+ # TD-008: Documentación API
216
+ estimate_api_docs() {
217
+ # Buscar routes/api.php para contar endpoints
218
+ # (Este script asume que el análisis ya corrió en el directorio del proyecto)
219
+
220
+ local effort=0
221
+
222
+ # Estimación fija basada en tamaño del proyecto
223
+ if [ "$total_lines" -gt 30000 ]; then
224
+ effort=12 # Proyecto grande, muchos endpoints
225
+ elif [ "$total_lines" -gt 10000 ]; then
226
+ effort=8 # Proyecto mediano
227
+ else
228
+ effort=4 # Proyecto pequeño
229
+ fi
230
+
231
+ echo "$effort"
232
+ }
233
+
234
+ # ==============================================================================
235
+ # CÁLCULO TOTAL
236
+ # ==============================================================================
237
+
238
+ effort_tests=$(estimate_tests)
239
+ effort_phpstan=$(estimate_phpstan)
240
+ effort_n_plus_one=$(estimate_n_plus_one)
241
+ effort_duplication=$(estimate_duplication)
242
+ effort_rate_limiting=$(estimate_rate_limiting)
243
+ effort_outdated=$(estimate_outdated_deps)
244
+ effort_complexity=$(estimate_complexity)
245
+ effort_api_docs=$(estimate_api_docs)
246
+
247
+ total_effort=$((effort_tests + effort_phpstan + effort_n_plus_one + effort_duplication + effort_rate_limiting + effort_outdated + effort_complexity + effort_api_docs))
248
+
249
+ # ==============================================================================
250
+ # OUTPUT
251
+ # ==============================================================================
252
+
253
+ echo "📊 Estimación de esfuerzo por issue:"
254
+ echo ""
255
+ echo " TD-001 (Tests): $effort_tests horas"
256
+ echo " TD-002 (PHPStan): $effort_phpstan horas"
257
+ echo " TD-003 (N+1 queries): $effort_n_plus_one horas"
258
+ echo " TD-004 (Duplicación): $effort_duplication horas"
259
+ echo " TD-005 (Rate limiting): $effort_rate_limiting horas"
260
+ echo " TD-006 (Deps obsoletas): $effort_outdated horas"
261
+ echo " TD-007 (Complejidad): $effort_complexity horas"
262
+ echo " TD-008 (API docs): $effort_api_docs horas"
263
+ echo ""
264
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
265
+ echo " TOTAL ESTIMADO: $total_effort horas"
266
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
267
+ echo ""
268
+
269
+ # Convertir a semanas (40 horas/semana)
270
+ weeks=$(echo "scale=1; $total_effort / 40" | bc)
271
+ echo " Equivalente a: ~$weeks semanas de trabajo"
272
+ echo ""
273
+
274
+ # Guardar en archivo temporal para que la IA lo lea
275
+ cat > /tmp/specleap-effort-estimate.txt <<EOF
276
+ Total effort: $total_effort hours
277
+ Equivalent: ~$weeks weeks
278
+
279
+ Breakdown:
280
+ - TD-001 Tests: $effort_tests hours
281
+ - TD-002 PHPStan: $effort_phpstan hours
282
+ - TD-003 N+1: $effort_n_plus_one hours
283
+ - TD-004 Duplication: $effort_duplication hours
284
+ - TD-005 Rate limiting: $effort_rate_limiting hours
285
+ - TD-006 Outdated deps: $effort_outdated hours
286
+ - TD-007 Complexity: $effort_complexity hours
287
+ - TD-008 API docs: $effort_api_docs hours
288
+ EOF
289
+
290
+ echo "✅ Estimación guardada en /tmp/specleap-effort-estimate.txt"