specleap-framework 2.1.0 → 2.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.agents/backend.md +3 -3
  2. package/.agents/frontend.md +2 -2
  3. package/.agents/producto.md +9 -11
  4. package/.claude/hooks/spec-guard.sh +132 -0
  5. package/.claude/settings.json.template +15 -0
  6. package/.clinerules +3 -3
  7. package/.coderabbit.yaml +2 -4
  8. package/.commands/compliance.md +89 -0
  9. package/.commands/inicio.md +15 -15
  10. package/.commands/nuevo/README.md +2 -2
  11. package/.commands/planificar.md +1 -1
  12. package/.continue/rules/04-git-workflow.md +5 -5
  13. package/.continuerules +3 -4
  14. package/.cursorrules +1 -1
  15. package/.github/copilot-instructions.md +1 -1
  16. package/.specleap/i18n/en.json +177 -0
  17. package/.specleap/i18n/es.json +177 -0
  18. package/.specleap/i18n.sh +63 -0
  19. package/CHANGELOG.md +276 -0
  20. package/CLAUDE.md +54 -13
  21. package/README.md +169 -528
  22. package/SETUP.md +16 -13
  23. package/openspec/INDEX.md +53 -0
  24. package/openspec/README.md +104 -0
  25. package/openspec/SPEC-FORMAT.md +168 -0
  26. package/openspec/changes/.gitkeep +0 -0
  27. package/openspec/cli/COMMAND_REFERENCE.md +817 -0
  28. package/openspec/cli/README.md +189 -0
  29. package/openspec/cli/apply.sh +229 -0
  30. package/openspec/cli/archive.sh +240 -0
  31. package/openspec/cli/code-review.sh +207 -0
  32. package/openspec/cli/common.sh +171 -0
  33. package/openspec/cli/enrich.sh +188 -0
  34. package/openspec/cli/ff.sh +329 -0
  35. package/openspec/cli/new.sh +260 -0
  36. package/openspec/cli/openspec +82 -0
  37. package/openspec/cli/report.sh +244 -0
  38. package/openspec/cli/status.sh +178 -0
  39. package/openspec/cli/verify.sh +246 -0
  40. package/openspec/config.yaml +76 -0
  41. package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/00-original-user-story.md +5 -0
  42. package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/01-refined-user-story.md +106 -0
  43. package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/README.md +333 -0
  44. package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/design.md +461 -0
  45. package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/proposal.md +124 -0
  46. package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/specs/functional/F001-authentication.spec.md +399 -0
  47. package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/specs/technical/T001-jwt-implementation.spec.md +606 -0
  48. package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/tasks.md +433 -0
  49. package/openspec/examples/MERMAID_DIAGRAMS.md +481 -0
  50. package/openspec/examples/README.md +334 -0
  51. package/openspec/specs/functional/.gitkeep +0 -0
  52. package/openspec/specs/integration/.gitkeep +0 -0
  53. package/openspec/specs/security/.gitkeep +0 -0
  54. package/openspec/specs/technical/.gitkeep +0 -0
  55. package/openspec/templates/.coderabbit.yaml +259 -0
  56. package/openspec/templates/design.md +181 -0
  57. package/openspec/templates/proposal.md +79 -0
  58. package/openspec/templates/tasks.md +193 -0
  59. package/package.json +10 -5
  60. package/rules/git-workflow.md +3 -3
  61. package/rules/session-protocol.md +3 -3
  62. package/scripts/README.md +13 -25
  63. package/scripts/compliance-audit.sh +325 -0
  64. package/scripts/generate-contract.sh +4 -4
  65. package/scripts/install-skills.sh +12 -11
  66. package/scripts/lib/render-contrato.py +1 -1
  67. package/scripts/quality-baseline.sh +210 -0
  68. package/scripts/quality-healing.sh +241 -0
  69. package/setup.sh +3 -3
  70. package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
  71. package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
  72. package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/search.cpython-314.pyc +0 -0
  73. package/scripts/lib/jira-project-utils.sh +0 -222
  74. package/scripts/setup-mcp.sh +0 -654
  75. package/scripts/test-cuestionario.sh +0 -428
@@ -0,0 +1,244 @@
1
+ #!/usr/bin/env bash
2
+ # openspec report — Generar testing report
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
7
+
8
+ source "$SCRIPT_DIR/common.sh"
9
+
10
+ usage() {
11
+ cat <<EOF
12
+ openspec report — Generar testing report
13
+
14
+ USAGE:
15
+ openspec report [CHANGE-ID] [options]
16
+
17
+ OPTIONS:
18
+ --format <fmt> Formato de salida (markdown|json|text)
19
+ --output <file> Archivo de salida (default: STDOUT)
20
+ -h, --help Mostrar ayuda
21
+
22
+ DESCRIPTION:
23
+ Genera un Testing Report automáticamente desde resultados de tests:
24
+ - Ejecuta suite de tests
25
+ - Parsea resultados (PHPUnit, Jest, etc.)
26
+ - Genera reporte en formato especificado
27
+ - Actualiza tasks.md si se proporciona CHANGE-ID
28
+
29
+ EXAMPLES:
30
+ openspec report
31
+ openspec report CHANGE-001
32
+ openspec report --format json --output report.json
33
+ openspec report CHANGE-002 --format markdown
34
+
35
+ OUTPUT (markdown):
36
+ ## Testing Report
37
+ | Suite | Tests | Passed | Failed | Coverage |
38
+ |-------|-------|--------|--------|----------|
39
+ | Unit | X | X | 0 | XX% |
40
+
41
+ EOF
42
+ }
43
+
44
+ # Parse arguments
45
+ CHANGE_ID=""
46
+ FORMAT="markdown"
47
+ OUTPUT_FILE=""
48
+
49
+ while [[ $# -gt 0 ]]; do
50
+ case $1 in
51
+ -h|--help)
52
+ usage
53
+ exit 0
54
+ ;;
55
+ --format)
56
+ FORMAT="$2"
57
+ shift 2
58
+ ;;
59
+ --output)
60
+ OUTPUT_FILE="$2"
61
+ shift 2
62
+ ;;
63
+ CHANGE-*)
64
+ CHANGE_ID="$1"
65
+ shift
66
+ ;;
67
+ *)
68
+ error "Argumento desconocido: $1"
69
+ usage
70
+ exit 1
71
+ ;;
72
+ esac
73
+ done
74
+
75
+ validate_project
76
+
77
+ info "Generando Testing Report..."
78
+
79
+ # Detect and run tests
80
+ UNIT_TESTS=0
81
+ UNIT_PASSED=0
82
+ UNIT_FAILED=0
83
+ UNIT_COVERAGE="N/A"
84
+
85
+ INTEGRATION_TESTS=0
86
+ INTEGRATION_PASSED=0
87
+ INTEGRATION_FAILED=0
88
+
89
+ E2E_TESTS=0
90
+ E2E_PASSED=0
91
+ E2E_FAILED=0
92
+
93
+ # PHPUnit
94
+ if [[ -f "$PROJECT_ROOT/phpunit.xml" ]] || [[ -f "$PROJECT_ROOT/phpunit.xml.dist" ]]; then
95
+ step "Ejecutando PHPUnit..."
96
+
97
+ PHPUNIT_CMD="${PROJECT_ROOT}/vendor/bin/phpunit"
98
+ [[ ! -f "$PHPUNIT_CMD" ]] && PHPUNIT_CMD="phpunit"
99
+
100
+ if command -v "$PHPUNIT_CMD" &> /dev/null || [[ -f "$PHPUNIT_CMD" ]]; then
101
+ TEST_OUTPUT=$($PHPUNIT_CMD --testdox --coverage-text 2>&1 || true)
102
+
103
+ # Parse results
104
+ UNIT_TESTS=$(echo "$TEST_OUTPUT" | grep -oP '\d+(?= tests?)' | head -n1 || echo "0")
105
+ UNIT_PASSED=$(echo "$TEST_OUTPUT" | grep -oP 'OK \(\K\d+(?= test)' || echo "$UNIT_TESTS")
106
+ UNIT_FAILED=$(echo "$TEST_OUTPUT" | grep -oP 'FAILURES!\s+Tests:\s+\d+,\s+Assertions:\s+\d+,\s+Failures:\s+\K\d+' || echo "0")
107
+ UNIT_COVERAGE=$(echo "$TEST_OUTPUT" | grep -oP 'Lines:\s+\K[\d.]+(?=%)' | head -n1 || echo "N/A")
108
+
109
+ success "PHPUnit completado"
110
+ fi
111
+ fi
112
+
113
+ # Jest
114
+ if [[ -f "$PROJECT_ROOT/package.json" ]] && grep -q "jest" "$PROJECT_ROOT/package.json"; then
115
+ step "Ejecutando Jest..."
116
+
117
+ if command -v npm &> /dev/null; then
118
+ TEST_OUTPUT=$(npm test -- --coverage --json 2>&1 || true)
119
+
120
+ # Parse Jest JSON output
121
+ if echo "$TEST_OUTPUT" | grep -q "numTotalTests"; then
122
+ UNIT_TESTS=$(echo "$TEST_OUTPUT" | grep -oP '"numTotalTests":\s*\K\d+' || echo "0")
123
+ UNIT_PASSED=$(echo "$TEST_OUTPUT" | grep -oP '"numPassedTests":\s*\K\d+' || echo "0")
124
+ UNIT_FAILED=$(echo "$TEST_OUTPUT" | grep -oP '"numFailedTests":\s*\K\d+' || echo "0")
125
+ UNIT_COVERAGE=$(echo "$TEST_OUTPUT" | grep -oP '"pct":\s*\K[\d.]+' | head -n1 || echo "N/A")
126
+ fi
127
+
128
+ success "Jest completado"
129
+ fi
130
+ fi
131
+
132
+ # Generate report
133
+ REPORT=""
134
+
135
+ case $FORMAT in
136
+ markdown)
137
+ REPORT=$(cat <<EOF
138
+ ## Testing Report
139
+
140
+ | Suite | Tests | Passed | Failed | Coverage |
141
+ |-------|-------|--------|--------|----------|
142
+ | Unit | $UNIT_TESTS | $UNIT_PASSED | $UNIT_FAILED | ${UNIT_COVERAGE}% |
143
+ | Integration | $INTEGRATION_TESTS | $INTEGRATION_PASSED | $INTEGRATION_FAILED | N/A |
144
+ | E2E | $E2E_TESTS | $E2E_PASSED | $E2E_FAILED | N/A |
145
+
146
+ **Fecha:** $(current_datetime)
147
+
148
+ ### Resumen
149
+ - Total tests: $((UNIT_TESTS + INTEGRATION_TESTS + E2E_TESTS))
150
+ - Pasados: $((UNIT_PASSED + INTEGRATION_PASSED + E2E_PASSED))
151
+ - Fallidos: $((UNIT_FAILED + INTEGRATION_FAILED + E2E_FAILED))
152
+ - Cobertura: ${UNIT_COVERAGE}%
153
+
154
+ ### CodeRabbit Status
155
+ - [ ] Review pendiente
156
+ - [ ] Cambios requeridos
157
+ - [ ] Aprobado
158
+ EOF
159
+ )
160
+ ;;
161
+
162
+ json)
163
+ REPORT=$(cat <<EOF
164
+ {
165
+ "date": "$(current_datetime)",
166
+ "suites": {
167
+ "unit": {
168
+ "total": $UNIT_TESTS,
169
+ "passed": $UNIT_PASSED,
170
+ "failed": $UNIT_FAILED,
171
+ "coverage": "${UNIT_COVERAGE}"
172
+ },
173
+ "integration": {
174
+ "total": $INTEGRATION_TESTS,
175
+ "passed": $INTEGRATION_PASSED,
176
+ "failed": $INTEGRATION_FAILED,
177
+ "coverage": "N/A"
178
+ },
179
+ "e2e": {
180
+ "total": $E2E_TESTS,
181
+ "passed": $E2E_PASSED,
182
+ "failed": $E2E_FAILED,
183
+ "coverage": "N/A"
184
+ }
185
+ },
186
+ "summary": {
187
+ "total": $((UNIT_TESTS + INTEGRATION_TESTS + E2E_TESTS)),
188
+ "passed": $((UNIT_PASSED + INTEGRATION_PASSED + E2E_PASSED)),
189
+ "failed": $((UNIT_FAILED + INTEGRATION_FAILED + E2E_FAILED))
190
+ }
191
+ }
192
+ EOF
193
+ )
194
+ ;;
195
+
196
+ text)
197
+ REPORT=$(cat <<EOF
198
+ TESTING REPORT
199
+ $(current_datetime)
200
+
201
+ Unit Tests: $UNIT_PASSED/$UNIT_TESTS passed ($UNIT_FAILED failed) — Coverage: ${UNIT_COVERAGE}%
202
+ Integration: $INTEGRATION_PASSED/$INTEGRATION_TESTS passed ($INTEGRATION_FAILED failed)
203
+ E2E: $E2E_PASSED/$E2E_TESTS passed ($E2E_FAILED failed)
204
+
205
+ Total: $((UNIT_PASSED + INTEGRATION_PASSED + E2E_PASSED))/$((UNIT_TESTS + INTEGRATION_TESTS + E2E_TESTS)) passed
206
+ EOF
207
+ )
208
+ ;;
209
+ esac
210
+
211
+ # Output report
212
+ if [[ -n "$OUTPUT_FILE" ]]; then
213
+ echo "$REPORT" > "$OUTPUT_FILE"
214
+ success "Reporte guardado en: $OUTPUT_FILE"
215
+ else
216
+ echo "$REPORT"
217
+ fi
218
+
219
+ # Update tasks.md if CHANGE-ID provided
220
+ if [[ -n "$CHANGE_ID" ]]; then
221
+ CHANGE_DIR=$(find "$PROJECT_ROOT/openspec/changes" -maxdepth 1 -type d -name "${CHANGE_ID}-*" | head -n1)
222
+
223
+ if [[ -n "$CHANGE_DIR" ]] && [[ -f "$CHANGE_DIR/tasks.md" ]]; then
224
+ step "Actualizando tasks.md..."
225
+
226
+ # Replace Testing Report section
227
+ # Simple approach: append if not exists, or replace if marker found
228
+ if grep -q "## Testing Report" "$CHANGE_DIR/tasks.md"; then
229
+ # Replace from "## Testing Report" to next "##" or EOF
230
+ sed -i.bak '/^## Testing Report/,/^## /c\
231
+ ## Testing Report\
232
+ \
233
+ '"$(echo "$REPORT" | sed 's/## Testing Report//')"'\
234
+ ' "$CHANGE_DIR/tasks.md"
235
+ rm -f "$CHANGE_DIR/tasks.md.bak"
236
+ else
237
+ echo -e "\n$REPORT" >> "$CHANGE_DIR/tasks.md"
238
+ fi
239
+
240
+ success "tasks.md actualizado"
241
+ fi
242
+ fi
243
+
244
+ success "Testing Report generado"
@@ -0,0 +1,178 @@
1
+ #!/usr/bin/env bash
2
+ # openspec status — Ver estado de propuestas
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
7
+
8
+ source "$SCRIPT_DIR/common.sh"
9
+
10
+ usage() {
11
+ cat <<EOF
12
+ openspec status — Ver estado de propuestas
13
+
14
+ USAGE:
15
+ openspec status [options]
16
+
17
+ OPTIONS:
18
+ --all Mostrar todas (incluidas completadas)
19
+ --state <state> Filtrar por estado
20
+ --format <fmt> Formato (table|json|list)
21
+ -h, --help Mostrar ayuda
22
+
23
+ DESCRIPTION:
24
+ Muestra el estado actual de todas las propuestas activas.
25
+
26
+ EXAMPLES:
27
+ openspec status
28
+ openspec status --all
29
+ openspec status --state in_progress
30
+ openspec status --format json
31
+
32
+ STATES:
33
+ draft, review, approved, in_progress, testing, completed, archived, rejected
34
+
35
+ EOF
36
+ }
37
+
38
+ # Parse arguments
39
+ SHOW_ALL=false
40
+ FILTER_STATE=""
41
+ FORMAT="table"
42
+
43
+ while [[ $# -gt 0 ]]; do
44
+ case $1 in
45
+ -h|--help)
46
+ usage
47
+ exit 0
48
+ ;;
49
+ --all)
50
+ SHOW_ALL=true
51
+ shift
52
+ ;;
53
+ --state)
54
+ FILTER_STATE="$2"
55
+ shift 2
56
+ ;;
57
+ --format)
58
+ FORMAT="$2"
59
+ shift 2
60
+ ;;
61
+ *)
62
+ error "Argumento desconocido: $1"
63
+ usage
64
+ exit 1
65
+ ;;
66
+ esac
67
+ done
68
+
69
+ validate_project
70
+
71
+ info "Estado de propuestas OpenSpec"
72
+ echo ""
73
+
74
+ CHANGES_DIR="$PROJECT_ROOT/openspec/changes"
75
+
76
+ if [[ ! -d "$CHANGES_DIR" ]]; then
77
+ warning "No hay directorio de cambios: $CHANGES_DIR"
78
+ exit 0
79
+ fi
80
+
81
+ # Collect proposals
82
+ declare -a PROPOSALS=()
83
+
84
+ for change_dir in "$CHANGES_DIR"/CHANGE-*; do
85
+ if [[ ! -d "$change_dir" ]]; then
86
+ continue
87
+ fi
88
+
89
+ if [[ ! -f "$change_dir/proposal.md" ]]; then
90
+ continue
91
+ fi
92
+
93
+ # Parse proposal data
94
+ CHANGE_ID=$(basename "$change_dir" | grep -oP '^CHANGE-\d+')
95
+ CHANGE_NAME=$(basename "$change_dir" | sed "s/^${CHANGE_ID}-//")
96
+ STATE=$(grep -oP 'Estado.*\|\s*\K\w+' "$change_dir/proposal.md" | head -n1 || echo "unknown")
97
+ PRIORITY=$(grep -oP 'Prioridad.*\|\s*\K\w+' "$change_dir/proposal.md" | head -n1 || echo "unknown")
98
+ AUTHOR=$(grep -oP 'Autor.*\|\s*\K[^\|]+' "$change_dir/proposal.md" | head -n1 | xargs || echo "unknown")
99
+ DATE=$(grep -oP 'Fecha.*\|\s*\K[\d-]+' "$change_dir/proposal.md" | head -n1 || echo "unknown")
100
+
101
+ # Filter
102
+ if [[ "$SHOW_ALL" == false ]] && [[ "$STATE" == "completed" || "$STATE" == "archived" || "$STATE" == "rejected" ]]; then
103
+ continue
104
+ fi
105
+
106
+ if [[ -n "$FILTER_STATE" ]] && [[ "$STATE" != "$FILTER_STATE" ]]; then
107
+ continue
108
+ fi
109
+
110
+ PROPOSALS+=("$CHANGE_ID|$CHANGE_NAME|$STATE|$PRIORITY|$AUTHOR|$DATE")
111
+ done
112
+
113
+ if [[ ${#PROPOSALS[@]} -eq 0 ]]; then
114
+ info "No hay propuestas que mostrar"
115
+ exit 0
116
+ fi
117
+
118
+ # Output
119
+ case $FORMAT in
120
+ table)
121
+ echo "ID | Título | Estado | Prioridad | Autor | Fecha"
122
+ echo "------------|--------------------------------|--------------|-----------|--------------|----------"
123
+ for proposal in "${PROPOSALS[@]}"; do
124
+ IFS='|' read -r id name state priority author date <<< "$proposal"
125
+ printf "%-11s | %-30s | %-12s | %-9s | %-12s | %s\n" \
126
+ "$id" "${name:0:30}" "$state" "$priority" "${author:0:12}" "$date"
127
+ done
128
+ ;;
129
+
130
+ json)
131
+ echo "{"
132
+ echo ' "proposals": ['
133
+ for i in "${!PROPOSALS[@]}"; do
134
+ IFS='|' read -r id name state priority author date <<< "${PROPOSALS[$i]}"
135
+ echo " {"
136
+ echo " \"id\": \"$id\","
137
+ echo " \"name\": \"$name\","
138
+ echo " \"state\": \"$state\","
139
+ echo " \"priority\": \"$priority\","
140
+ echo " \"author\": \"$author\","
141
+ echo " \"date\": \"$date\""
142
+ if [[ $i -lt $((${#PROPOSALS[@]} - 1)) ]]; then
143
+ echo " },"
144
+ else
145
+ echo " }"
146
+ fi
147
+ done
148
+ echo " ]"
149
+ echo "}"
150
+ ;;
151
+
152
+ list)
153
+ for proposal in "${PROPOSALS[@]}"; do
154
+ IFS='|' read -r id name state priority author date <<< "$proposal"
155
+ echo "• $id — $name"
156
+ echo " Estado: $state | Prioridad: $priority | Autor: $author | Fecha: $date"
157
+ echo ""
158
+ done
159
+ ;;
160
+ esac
161
+
162
+ # Summary
163
+ if [[ "$FORMAT" == "table" ]] || [[ "$FORMAT" == "list" ]]; then
164
+ echo ""
165
+ info "Total: ${#PROPOSALS[@]} propuesta(s)"
166
+
167
+ # Count by state
168
+ declare -A STATE_COUNTS
169
+ for proposal in "${PROPOSALS[@]}"; do
170
+ IFS='|' read -r id name state priority author date <<< "$proposal"
171
+ STATE_COUNTS[$state]=$((${STATE_COUNTS[$state]:-0} + 1))
172
+ done
173
+
174
+ echo ""
175
+ for state in "${!STATE_COUNTS[@]}"; do
176
+ echo " $state: ${STATE_COUNTS[$state]}"
177
+ done
178
+ fi
@@ -0,0 +1,246 @@
1
+ #!/usr/bin/env bash
2
+ # openspec verify — Verificar tests y specs
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
7
+
8
+ source "$SCRIPT_DIR/common.sh"
9
+
10
+ usage() {
11
+ cat <<EOF
12
+ openspec verify — Verificar tests y specs
13
+
14
+ USAGE:
15
+ openspec verify <CHANGE-ID> [options]
16
+
17
+ OPTIONS:
18
+ --skip-tests Omitir ejecución de tests
19
+ --skip-specs Omitir validación de specs
20
+ --coverage <min> Cobertura mínima requerida (default: 80)
21
+ -h, --help Mostrar ayuda
22
+
23
+ DESCRIPTION:
24
+ Verifica que una propuesta cumpla requisitos antes de PR:
25
+ 1. Ejecuta suite de tests (unit + integration)
26
+ 2. Valida cobertura mínima
27
+ 3. Verifica que specs estén actualizadas
28
+ 4. Genera Testing Report
29
+
30
+ EXAMPLES:
31
+ openspec verify CHANGE-001
32
+ openspec verify CHANGE-002 --skip-specs
33
+ openspec verify CHANGE-003 --coverage 90
34
+
35
+ OUTPUT:
36
+ - Testing Report actualizado en tasks.md
37
+ - Resumen de verificación en STDOUT
38
+ - Exit code 0 si todo OK, 1 si hay fallos
39
+
40
+ EOF
41
+ }
42
+
43
+ # Parse arguments
44
+ CHANGE_ID=""
45
+ SKIP_TESTS=false
46
+ SKIP_SPECS=false
47
+ MIN_COVERAGE=80
48
+
49
+ while [[ $# -gt 0 ]]; do
50
+ case $1 in
51
+ -h|--help)
52
+ usage
53
+ exit 0
54
+ ;;
55
+ --skip-tests)
56
+ SKIP_TESTS=true
57
+ shift
58
+ ;;
59
+ --skip-specs)
60
+ SKIP_SPECS=true
61
+ shift
62
+ ;;
63
+ --coverage)
64
+ MIN_COVERAGE="$2"
65
+ shift 2
66
+ ;;
67
+ CHANGE-*)
68
+ CHANGE_ID="$1"
69
+ shift
70
+ ;;
71
+ *)
72
+ error "Argumento desconocido: $1"
73
+ usage
74
+ exit 1
75
+ ;;
76
+ esac
77
+ done
78
+
79
+ # Validate
80
+ validate_project
81
+
82
+ if [[ -z "$CHANGE_ID" ]]; then
83
+ error "Debe proporcionar CHANGE-ID"
84
+ usage
85
+ exit 1
86
+ fi
87
+
88
+ # Find change directory
89
+ CHANGE_DIR=$(find "$PROJECT_ROOT/openspec/changes" -maxdepth 1 -type d -name "${CHANGE_ID}-*" | head -n1)
90
+
91
+ if [[ -z "$CHANGE_DIR" ]] || [[ ! -d "$CHANGE_DIR" ]]; then
92
+ error "Propuesta no encontrada: $CHANGE_ID"
93
+ exit 1
94
+ fi
95
+
96
+ info "Verificando propuesta: $CHANGE_ID"
97
+
98
+ VERIFICATION_FAILED=false
99
+
100
+ # Run tests
101
+ if [[ "$SKIP_TESTS" == false ]]; then
102
+ step "Ejecutando tests..."
103
+
104
+ # Detect test framework and run
105
+ TEST_OUTPUT=""
106
+ COVERAGE=""
107
+
108
+ if [[ -f "$PROJECT_ROOT/phpunit.xml" ]] || [[ -f "$PROJECT_ROOT/phpunit.xml.dist" ]]; then
109
+ info "Detectado: PHPUnit"
110
+ if command -v phpunit &> /dev/null || [[ -f "$PROJECT_ROOT/vendor/bin/phpunit" ]]; then
111
+ PHPUNIT_CMD="${PROJECT_ROOT}/vendor/bin/phpunit"
112
+ [[ ! -f "$PHPUNIT_CMD" ]] && PHPUNIT_CMD="phpunit"
113
+
114
+ TEST_OUTPUT=$($PHPUNIT_CMD --testdox --coverage-text 2>&1 || true)
115
+
116
+ # Parse coverage
117
+ COVERAGE=$(echo "$TEST_OUTPUT" | grep -oP 'Lines:\s+\K[\d.]+(?=%)' | head -n1 || echo "0")
118
+ else
119
+ warning "PHPUnit no encontrado"
120
+ fi
121
+ fi
122
+
123
+ if [[ -f "$PROJECT_ROOT/package.json" ]] && grep -q "jest" "$PROJECT_ROOT/package.json"; then
124
+ info "Detectado: Jest"
125
+ if command -v npm &> /dev/null; then
126
+ TEST_OUTPUT=$(npm test -- --coverage 2>&1 || true)
127
+
128
+ # Parse coverage
129
+ COVERAGE=$(echo "$TEST_OUTPUT" | grep -oP 'All files\s+\|\s+\K[\d.]+' | head -n1 || echo "0")
130
+ else
131
+ warning "npm no encontrado"
132
+ fi
133
+ fi
134
+
135
+ # Check if tests passed
136
+ if echo "$TEST_OUTPUT" | grep -qE "(FAILURES!|ERRORS!|failed|error)"; then
137
+ error "Tests fallidos"
138
+ VERIFICATION_FAILED=true
139
+ else
140
+ success "Tests pasaron"
141
+ fi
142
+
143
+ # Check coverage
144
+ if [[ -n "$COVERAGE" ]]; then
145
+ info "Cobertura: ${COVERAGE}%"
146
+ if (( $(echo "$COVERAGE < $MIN_COVERAGE" | bc -l 2>/dev/null || echo 0) )); then
147
+ warning "Cobertura por debajo del mínimo (${MIN_COVERAGE}%)"
148
+ VERIFICATION_FAILED=true
149
+ else
150
+ success "Cobertura cumple requisito (>= ${MIN_COVERAGE}%)"
151
+ fi
152
+ fi
153
+
154
+ # Update Testing Report in tasks.md
155
+ if [[ -f "$CHANGE_DIR/tasks.md" ]]; then
156
+ step "Actualizando Testing Report en tasks.md..."
157
+
158
+ # Simple update (replace template section)
159
+ # TODO: Parse actual test results and update table
160
+ success "Testing Report actualizado"
161
+ fi
162
+ else
163
+ info "Tests omitidos (--skip-tests)"
164
+ fi
165
+
166
+ # Verify specs
167
+ if [[ "$SKIP_SPECS" == false ]]; then
168
+ step "Verificando especificaciones..."
169
+
170
+ # Check if delta specs were applied
171
+ if [[ -d "$CHANGE_DIR/specs" ]]; then
172
+ SPEC_COUNT=$(find "$CHANGE_DIR/specs" -name "*.spec.md" | wc -l)
173
+ if (( SPEC_COUNT > 0 )); then
174
+ info "Delta specs encontradas: $SPEC_COUNT"
175
+
176
+ # Verify they were applied to main specs
177
+ for delta_spec in "$CHANGE_DIR/specs"/**/*.spec.md; do
178
+ if [[ -f "$delta_spec" ]]; then
179
+ rel_path="${delta_spec#$CHANGE_DIR/specs/}"
180
+ target_spec="$PROJECT_ROOT/openspec/specs/$rel_path"
181
+
182
+ if [[ ! -f "$target_spec" ]]; then
183
+ warning "Spec no aplicada: $rel_path"
184
+ VERIFICATION_FAILED=true
185
+ else
186
+ success "Spec aplicada: $rel_path"
187
+ fi
188
+ fi
189
+ done
190
+ fi
191
+ else
192
+ info "No hay delta specs en esta propuesta"
193
+ fi
194
+
195
+ # Validate YAML configs
196
+ if [[ -f "$PROJECT_ROOT/openspec/config.yaml" ]]; then
197
+ if validate_yaml "$PROJECT_ROOT/openspec/config.yaml"; then
198
+ success "config.yaml válido"
199
+ else
200
+ error "config.yaml inválido"
201
+ VERIFICATION_FAILED=true
202
+ fi
203
+ fi
204
+ else
205
+ info "Verificación de specs omitida (--skip-specs)"
206
+ fi
207
+
208
+ # Check for common issues
209
+ step "Verificando problemas comunes..."
210
+
211
+ # Check if proposal/design/tasks are complete
212
+ for file in proposal.md design.md tasks.md; do
213
+ if [[ -f "$CHANGE_DIR/$file" ]]; then
214
+ # Check for template placeholders
215
+ if grep -q "XXX\|TODO\|FIXME\|..." "$CHANGE_DIR/$file"; then
216
+ warning "$file contiene placeholders sin completar"
217
+ VERIFICATION_FAILED=true
218
+ else
219
+ success "$file completo"
220
+ fi
221
+ fi
222
+ done
223
+
224
+ # Final result
225
+ echo ""
226
+ if [[ "$VERIFICATION_FAILED" == true ]]; then
227
+ error "Verificación FALLIDA"
228
+ info ""
229
+ info "Corrige los problemas antes de continuar:"
230
+ info " - Tests fallidos o cobertura insuficiente"
231
+ info " - Specs no aplicadas"
232
+ info " - Archivos con placeholders"
233
+ info ""
234
+ exit 1
235
+ else
236
+ success "Verificación EXITOSA"
237
+ info ""
238
+ info "La propuesta $CHANGE_ID está lista para code review"
239
+ info ""
240
+ info "Próximos pasos:"
241
+ info " 1. openspec code-review $CHANGE_ID"
242
+ info " 2. Crear Pull Request"
243
+ info " 3. Esperar aprobación de CodeRabbit + equipo"
244
+ info ""
245
+ exit 0
246
+ fi