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.
- package/.agents/backend.md +3 -3
- package/.agents/frontend.md +2 -2
- package/.agents/producto.md +9 -11
- package/.claude/hooks/spec-guard.sh +132 -0
- package/.claude/settings.json.template +15 -0
- package/.clinerules +3 -3
- package/.coderabbit.yaml +2 -4
- package/.commands/compliance.md +89 -0
- package/.commands/inicio.md +15 -15
- package/.commands/nuevo/README.md +2 -2
- package/.commands/planificar.md +1 -1
- package/.continue/rules/04-git-workflow.md +5 -5
- package/.continuerules +3 -4
- package/.cursorrules +1 -1
- package/.github/copilot-instructions.md +1 -1
- package/.specleap/i18n/en.json +177 -0
- package/.specleap/i18n/es.json +177 -0
- package/.specleap/i18n.sh +63 -0
- package/CHANGELOG.md +276 -0
- package/CLAUDE.md +54 -13
- package/README.md +169 -528
- package/SETUP.md +16 -13
- package/openspec/INDEX.md +53 -0
- package/openspec/README.md +104 -0
- package/openspec/SPEC-FORMAT.md +168 -0
- package/openspec/changes/.gitkeep +0 -0
- package/openspec/cli/COMMAND_REFERENCE.md +817 -0
- package/openspec/cli/README.md +189 -0
- package/openspec/cli/apply.sh +229 -0
- package/openspec/cli/archive.sh +240 -0
- package/openspec/cli/code-review.sh +207 -0
- package/openspec/cli/common.sh +171 -0
- package/openspec/cli/enrich.sh +188 -0
- package/openspec/cli/ff.sh +329 -0
- package/openspec/cli/new.sh +260 -0
- package/openspec/cli/openspec +82 -0
- package/openspec/cli/report.sh +244 -0
- package/openspec/cli/status.sh +178 -0
- package/openspec/cli/verify.sh +246 -0
- package/openspec/config.yaml +76 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/00-original-user-story.md +5 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/01-refined-user-story.md +106 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/README.md +333 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/design.md +461 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/proposal.md +124 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/specs/functional/F001-authentication.spec.md +399 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/specs/technical/T001-jwt-implementation.spec.md +606 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/tasks.md +433 -0
- package/openspec/examples/MERMAID_DIAGRAMS.md +481 -0
- package/openspec/examples/README.md +334 -0
- package/openspec/specs/functional/.gitkeep +0 -0
- package/openspec/specs/integration/.gitkeep +0 -0
- package/openspec/specs/security/.gitkeep +0 -0
- package/openspec/specs/technical/.gitkeep +0 -0
- package/openspec/templates/.coderabbit.yaml +259 -0
- package/openspec/templates/design.md +181 -0
- package/openspec/templates/proposal.md +79 -0
- package/openspec/templates/tasks.md +193 -0
- package/package.json +10 -5
- package/rules/git-workflow.md +3 -3
- package/rules/session-protocol.md +3 -3
- package/scripts/README.md +13 -25
- package/scripts/compliance-audit.sh +325 -0
- package/scripts/generate-contract.sh +4 -4
- package/scripts/install-skills.sh +12 -11
- package/scripts/lib/render-contrato.py +1 -1
- package/scripts/quality-baseline.sh +210 -0
- package/scripts/quality-healing.sh +241 -0
- package/setup.sh +3 -3
- package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
- package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
- package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/search.cpython-314.pyc +0 -0
- package/scripts/lib/jira-project-utils.sh +0 -222
- package/scripts/setup-mcp.sh +0 -654
- 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
|