don-cheli-sdd 1.16.4 → 1.17.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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ Todos los cambios notables en Don Cheli SDD Framework.
4
4
 
5
5
  Formato basado en [Conventional Commits](https://www.conventionalcommits.org/).
6
6
 
7
+ ## [1.17.0](https://github.com/doncheli/don-cheli-sdd/compare/v1.16.5...v1.17.0) (2026-04-02)
8
+
9
+ ### Nuevas Funcionalidades
10
+
11
+ * CI/CD integration, Custom Quality Gates y Telemetría (Fases 1-3) ([ac8facb](https://github.com/doncheli/don-cheli-sdd/commit/ac8facbef51543f6c136e614abf07c9f287eafee))
12
+
13
+ ## [1.16.5](https://github.com/doncheli/don-cheli-sdd/compare/v1.16.4...v1.16.5) (2026-04-02)
14
+
15
+ ### Correcciones
16
+
17
+ * **opencode:** add skills.paths para que OpenCode descubra los skills de Don Cheli ([c73b9fc](https://github.com/doncheli/don-cheli-sdd/commit/c73b9fc55e33be0449975e14a1e530cf97b83c8c))
18
+
7
19
  ## [1.16.4](https://github.com/doncheli/don-cheli-sdd/compare/v1.16.3...v1.16.4) (2026-04-02)
8
20
 
9
21
  ### Correcciones
package/README.es.md CHANGED
@@ -12,7 +12,7 @@
12
12
  </p>
13
13
  <p align="center">
14
14
  <a href="#-instalación"><img src="https://img.shields.io/badge/instalación-2_minutos-brightgreen" alt="Install"></a>
15
- <img src="https://img.shields.io/badge/versión-1.16.4-blue" alt="Version">
15
+ <img src="https://img.shields.io/badge/versión-1.17.0-blue" alt="Version">
16
16
  <img src="https://img.shields.io/badge/licencia-Apache%202.0-green" alt="License">
17
17
  <img src="https://img.shields.io/badge/idiomas-ES%20|%20EN%20|%20PT-red" alt="Languages">
18
18
  <img src="https://img.shields.io/badge/comandos-85+-purple" alt="Commands">
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
  </p>
13
13
  <p align="center">
14
14
  <a href="#-installation"><img src="https://img.shields.io/badge/install-2_minutes-brightgreen" alt="Install"></a>
15
- <img src="https://img.shields.io/badge/version-1.16.4-blue" alt="Version">
15
+ <img src="https://img.shields.io/badge/version-1.17.0-blue" alt="Version">
16
16
  <img src="https://img.shields.io/badge/license-Apache%202.0-green" alt="License">
17
17
  <img src="https://img.shields.io/badge/languages-ES%20|%20EN%20|%20PT-red" alt="Languages">
18
18
  <img src="https://img.shields.io/badge/commands-85+-purple" alt="Commands">
package/README.pt.md CHANGED
@@ -12,7 +12,7 @@
12
12
  </p>
13
13
  <p align="center">
14
14
  <a href="#-instalação"><img src="https://img.shields.io/badge/instalação-2_minutos-brightgreen" alt="Install"></a>
15
- <img src="https://img.shields.io/badge/versão-1.16.4-blue" alt="Version">
15
+ <img src="https://img.shields.io/badge/versão-1.17.0-blue" alt="Version">
16
16
  <img src="https://img.shields.io/badge/licença-Apache%202.0-green" alt="License">
17
17
  <img src="https://img.shields.io/badge/idiomas-ES%20|%20EN%20|%20PT-red" alt="Languages">
18
18
  <img src="https://img.shields.io/badge/comandos-85+-purple" alt="Commands">
package/VERSION CHANGED
@@ -1 +1 @@
1
- 1.16.4
1
+ 1.17.0
@@ -0,0 +1,52 @@
1
+ ---
2
+ description: Generar dashboard HTML local con métricas de eficiencia del proyecto SDD
3
+ ---
4
+
5
+ # /dc:dashboard — Dashboard de Métricas SDD
6
+
7
+ ## Qué hace
8
+
9
+ Genera un archivo `dashboard.html` interactivo con las métricas acumuladas del proyecto.
10
+
11
+ ## Fuente de datos
12
+
13
+ Lee `.dc/metrics.json` que se actualiza automáticamente al cerrar cada sesión.
14
+
15
+ ## Métricas que muestra
16
+
17
+ ### Eficiencia del pipeline
18
+ - **Tiempo por fase:** Promedio de duración de cada fase (Especificar → Revisar)
19
+ - **Quality Gates:** Tasa de aprobación (primera vez vs reintentos)
20
+ - **Stubs detectados:** Cuántos stubs fantasma se encontraron y eliminaron
21
+
22
+ ### TDD
23
+ - **Tasa de acierto:** Tests que pasaron al primer intento vs reintentos
24
+ - **Cobertura por feature:** Evolución de cobertura por sesión
25
+ - **RED→GREEN ratio:** Cuántos ciclos TDD se completaron
26
+
27
+ ### Estimaciones
28
+ - **Precisión:** Estimado vs real por feature (desviación %)
29
+ - **Modelo más preciso:** Cuál de los 4 modelos acertó más
30
+
31
+ ### Seguridad
32
+ - **Hallazgos OWASP:** Por categoría y severidad
33
+ - **Tendencia:** Hallazgos por sesión (debería decrecer)
34
+
35
+ ### Sesiones
36
+ - **Total de sesiones:** Completadas y promedio de duración
37
+ - **Productividad:** Features completadas por sesión
38
+
39
+ ## Output
40
+
41
+ ```
42
+ .dc/dashboard.html — Dashboard HTML standalone (abrir en navegador)
43
+ .dc/metrics.csv — Export CSV para reporting corporativo
44
+ ```
45
+
46
+ ## Ejecución
47
+
48
+ ```bash
49
+ /dc:dashboard # Genera dashboard.html
50
+ /dc:dashboard --abrir # Genera y abre en navegador
51
+ /dc:dashboard --csv # Solo exporta CSV
52
+ ```
@@ -0,0 +1,62 @@
1
+ ---
2
+ description: Crear, listar y ejecutar quality gates custom del proyecto
3
+ ---
4
+
5
+ # /dc:gate — Custom Quality Gates
6
+
7
+ ## Subcomandos
8
+
9
+ ### `/dc:gate crear <nombre>`
10
+ Crear un nuevo quality gate custom en `.dc/gates/`:
11
+
12
+ 1. Preguntar: nombre descriptivo, tipo (grep o script), severidad (block o warn)
13
+ 2. Generar archivo YAML en `.dc/gates/<nombre>.yml`
14
+ 3. Ejecutar el gate para verificar que funciona
15
+
16
+ ### `/dc:gate listar`
17
+ Listar todos los gates custom del proyecto:
18
+
19
+ 1. Leer `.dc/gates/*.yml`
20
+ 2. Mostrar tabla: nombre, tipo, severidad, estado (activo/desactivado)
21
+
22
+ ### `/dc:gate ejecutar [nombre]`
23
+ Ejecutar gates custom:
24
+
25
+ 1. Si se pasa nombre: ejecutar solo ese gate
26
+ 2. Sin nombre: ejecutar todos los gates en `.dc/gates/`
27
+ 3. Mostrar resultado por gate con ✅/⚠️/❌
28
+ 4. Resumen final: X pasaron, Y fallaron, Z advertencias
29
+
30
+ ### `/dc:gate ci`
31
+ Generar workflow de CI/CD para el proyecto actual:
32
+
33
+ 1. Detectar plataforma (GitHub Actions / GitLab CI)
34
+ 2. Copiar template desde `examples/ci/` adaptado al proyecto
35
+ 3. Incluir custom gates si existen
36
+
37
+ ## Formato de gate custom
38
+
39
+ ```yaml
40
+ name: Nombre descriptivo
41
+ type: grep | script
42
+ pattern: "regex" # Solo para type: grep
43
+ files: "src/**/*.ts" # Solo para type: grep
44
+ severity: block | warn
45
+ message: "Mensaje al fallar"
46
+ run: | # Solo para type: script
47
+ comando a ejecutar
48
+ ```
49
+
50
+ ## Severidad
51
+
52
+ - `block` — Falla la puerta, bloquea el merge
53
+ - `warn` — Advertencia, no bloquea (configurable con `fail-on-warn`)
54
+
55
+ ## Ejemplos incluidos
56
+
57
+ Ver `examples/gates/` para 5 gates pre-configurados:
58
+ - `no-console-log.yml` — Prohibir console.log en producción
59
+ - `no-any-typescript.yml` — Evitar tipo `any` en TypeScript
60
+ - `no-hardcoded-secrets.yml` — Detectar secretos hardcodeados
61
+ - `max-file-lines.yml` — Archivos de máximo 300 líneas
62
+ - `require-error-handling.yml` — Manejo de errores en async
@@ -0,0 +1,42 @@
1
+ ---
2
+ description: Ver resumen de métricas de eficiencia SDD en terminal
3
+ ---
4
+
5
+ # /dc:metricas — Métricas en Terminal
6
+
7
+ ## Qué hace
8
+
9
+ Muestra un resumen compacto de las métricas SDD del proyecto directamente en la terminal.
10
+
11
+ ## Fuente de datos
12
+
13
+ Lee `.dc/metrics.json` que se actualiza automáticamente en cada sesión.
14
+
15
+ ## Output en terminal
16
+
17
+ ```
18
+ ═══════════════════════════════════════════
19
+ Don Cheli SDD — Métricas del Proyecto
20
+ ═══════════════════════════════════════════
21
+
22
+ 📊 Sesiones: 12 completadas (prom. 38 min)
23
+ ✅ Quality Gates: 94% aprobación a la primera
24
+ 🧪 TDD Acierto: 87% tests pasan al primer intento
25
+ 📈 Cobertura: 91% (objetivo: 85%)
26
+ 🔍 Stubs: 3 detectados, 3 eliminados
27
+ 🛡️ OWASP: 0 críticos, 2 warnings
28
+ 📐 Estimación: ±15% desviación promedio
29
+
30
+ Modelo más preciso: Planning Poker IA (±8%)
31
+ Feature más rápida: auth-jwt (1.2h vs 2h estimado)
32
+ ═══════════════════════════════════════════
33
+ ```
34
+
35
+ ## Subcomandos
36
+
37
+ ```bash
38
+ /dc:metricas # Resumen completo
39
+ /dc:metricas --tdd # Solo métricas de TDD
40
+ /dc:metricas --owasp # Solo métricas de seguridad
41
+ /dc:metricas --estimados # Solo precisión de estimados
42
+ ```
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: custom-gates
3
+ description: Sistema de quality gates extensible con plugins YAML
4
+ version: 1.0.0
5
+ tags: [quality, ci-cd, gates, plugins]
6
+ grado_libertad: medio
7
+ ---
8
+
9
+ # Custom Quality Gates
10
+
11
+ ## Qué hace
12
+
13
+ Permite a equipos definir sus propias puertas de calidad mediante archivos YAML en `.dc/gates/`.
14
+
15
+ ## Tipos de gate
16
+
17
+ ### grep
18
+ Busca patrones regex en archivos del proyecto:
19
+ ```yaml
20
+ type: grep
21
+ pattern: "regex_aqui"
22
+ files: "src/**/*.ts"
23
+ ```
24
+
25
+ ### script
26
+ Ejecuta un script bash que retorna exit 0 (pass) o exit 1 (fail):
27
+ ```yaml
28
+ type: script
29
+ run: |
30
+ tu_comando_aqui
31
+ ```
32
+
33
+ ## Severidad
34
+
35
+ - `block` — Bloquea el merge/avance
36
+ - `warn` — Advertencia, no bloquea
37
+
38
+ ## Integración
39
+
40
+ - Se ejecutan automáticamente en `/dc:revisar`
41
+ - Se ejecutan en CI/CD via `sdd-check.sh` con `INPUT_GATES=custom`
42
+ - Se listan con `/dc:gate listar`
43
+
44
+ ## Directorio
45
+
46
+ ```
47
+ .dc/gates/
48
+ ├── no-console-log.yml
49
+ ├── no-hardcoded-secrets.yml
50
+ └── tu-gate-custom.yml
51
+ ```
@@ -0,0 +1,72 @@
1
+ ---
2
+ name: metricas-sesion
3
+ description: Telemetría local de eficiencia SDD con tracking de TDD, estimaciones y quality gates
4
+ version: 1.0.0
5
+ tags: [telemetry, metrics, dashboard, reporting]
6
+ grado_libertad: bajo
7
+ ---
8
+
9
+ # Métricas de Sesión
10
+
11
+ ## Qué hace
12
+
13
+ Registra automáticamente métricas de cada sesión SDD en `.dc/metrics.json` para tracking de eficiencia.
14
+
15
+ ## Datos que captura
16
+
17
+ ### Por sesión
18
+ - Timestamp inicio/fin
19
+ - Duración
20
+ - Fase alcanzada (1-6)
21
+ - Quality gates: aprobadas/rechazadas
22
+ - Cobertura alcanzada
23
+ - Stubs detectados
24
+
25
+ ### Por feature
26
+ - Escenarios Gherkin generados
27
+ - Tareas TDD completadas
28
+ - Ciclos RED→GREEN
29
+ - Estimado vs real (si se usó /dc:estimar)
30
+
31
+ ### Acumulado
32
+ - Total sesiones
33
+ - Promedios por fase
34
+ - Tendencias de cobertura
35
+ - Precisión de estimaciones
36
+
37
+ ## Formato de metrics.json
38
+
39
+ ```json
40
+ {
41
+ "version": "1.0.0",
42
+ "project": "mi-proyecto",
43
+ "sessions": [
44
+ {
45
+ "id": "2026-04-01-001",
46
+ "start": "2026-04-01T10:00:00Z",
47
+ "end": "2026-04-01T10:45:00Z",
48
+ "duration_min": 45,
49
+ "phase_reached": 6,
50
+ "gates_passed": 6,
51
+ "gates_total": 6,
52
+ "coverage": 91,
53
+ "stubs_found": 1,
54
+ "stubs_fixed": 1,
55
+ "tdd_cycles": 7,
56
+ "tdd_first_pass": 6
57
+ }
58
+ ],
59
+ "features": [],
60
+ "estimates": []
61
+ }
62
+ ```
63
+
64
+ ## Activación
65
+
66
+ Se activa automáticamente cuando la habilidad está instalada.
67
+ Se registra al ejecutar `/dc:cerrar-sesion`.
68
+
69
+ ## Visualización
70
+
71
+ - `/dc:metricas` — Resumen en terminal
72
+ - `/dc:dashboard` — Dashboard HTML interactivo
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "don-cheli-sdd",
3
- "version": "1.16.4",
3
+ "version": "1.17.0",
4
4
  "description": "Framework SDD para Claude Code — 72+ comandos, 43 habilidades, 15 modelos de razonamiento. TDD como ley de hierro. EN/ES/PT.",
5
5
  "main": "bin/don-cheli.js",
6
6
  "scripts": {
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "project": "",
4
+ "sessions": [],
5
+ "features": [],
6
+ "estimates": []
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "project": "",
4
+ "sessions": [],
5
+ "features": [],
6
+ "estimates": []
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "project": "",
4
+ "sessions": [],
5
+ "features": [],
6
+ "estimates": []
7
+ }
@@ -0,0 +1,6 @@
1
+ name: No console.log en produccion
2
+ type: grep
3
+ pattern: "console\\.log"
4
+ files: "src/**/*.ts"
5
+ severity: block
6
+ message: "console.log detectado. Usa el logger del proyecto."
@@ -0,0 +1,6 @@
1
+ name: No secrets hardcodeados
2
+ type: grep
3
+ pattern: "(password|secret|api_key|token)\\s*=\\s*['\"][^'\"]{8,}"
4
+ files: "src/**/*"
5
+ severity: block
6
+ message: "Posible secreto hardcodeado. Usa variables de entorno."
@@ -1 +1 @@
1
- 1.16.4
1
+ 1.17.0
@@ -4,7 +4,7 @@
4
4
 
5
5
  set -euo pipefail
6
6
 
7
- VERSION="1.16.4"
7
+ VERSION="1.17.0"
8
8
  REPO_URL="https://github.com/doncheli/don-cheli-sdd"
9
9
  CLEANUP_TMPDIR=""
10
10
 
@@ -238,7 +238,7 @@ _gen_continue() {
238
238
  cat > "$dir/.continue/config/don-cheli.json" 2>/dev/null << 'CONTEOF' || true
239
239
  {
240
240
  "name": "don-cheli-sdd",
241
- "version": "1.16.4",
241
+ "version": "1.17.0",
242
242
  "description": "Don Cheli SDD Framework",
243
243
  "rules": [
244
244
  "All production code requires tests (TDD: RED → GREEN → REFACTOR)",
@@ -4,7 +4,7 @@
4
4
 
5
5
  set -euo pipefail
6
6
 
7
- VERSION="1.16.4"
7
+ VERSION="1.17.0"
8
8
  REPO_URL="https://github.com/doncheli/don-cheli-sdd"
9
9
  CLEANUP_TMPDIR=""
10
10
 
@@ -0,0 +1,407 @@
1
+ #!/bin/bash
2
+ # Don Cheli SDD Check — Quality Gates for CI/CD
3
+ # Validates SDD artifacts, TDD compliance, coverage and security
4
+ #
5
+ # Usage:
6
+ # bash scripts/sdd-check.sh # Run all gates
7
+ # INPUT_GATES=spec,tdd bash scripts/sdd-check.sh # Run specific gates
8
+ #
9
+ # Environment variables (set by GitHub Action or manually):
10
+ # INPUT_GATES — Gates to run: all, spec, tdd, coverage, owasp, custom
11
+ # INPUT_MIN_COVERAGE — Minimum coverage percentage (default: 85)
12
+ # INPUT_FAIL_ON_WARN — Treat warnings as failures (default: false)
13
+ # INPUT_DC_DIR — Path to .dc/ (auto-detect if empty)
14
+ # INPUT_CUSTOM_GATES_DIR — Path to custom gates (default: .dc/gates)
15
+
16
+ set -euo pipefail
17
+
18
+ # ═══════════════════════════════════════════════════════════════
19
+ # CONFIG
20
+ # ═══════════════════════════════════════════════════════════════
21
+
22
+ GATES="${INPUT_GATES:-all}"
23
+ MIN_COVERAGE="${INPUT_MIN_COVERAGE:-85}"
24
+ FAIL_ON_WARN="${INPUT_FAIL_ON_WARN:-false}"
25
+ CUSTOM_GATES_DIR="${INPUT_CUSTOM_GATES_DIR:-.dc/gates}"
26
+
27
+ # Auto-detect .dc/ or .especdev/ (retrocompatible)
28
+ if [ -n "${INPUT_DC_DIR:-}" ]; then
29
+ DC_DIR="$INPUT_DC_DIR"
30
+ elif [ -d ".dc" ]; then
31
+ DC_DIR=".dc"
32
+ elif [ -d ".especdev" ]; then
33
+ DC_DIR=".especdev"
34
+ else
35
+ DC_DIR=".dc"
36
+ fi
37
+
38
+ # ═══════════════════════════════════════════════════════════════
39
+ # STATE
40
+ # ═══════════════════════════════════════════════════════════════
41
+
42
+ TOTAL_GATES=0
43
+ PASSED_GATES=0
44
+ FAILED_GATES=0
45
+ WARNINGS=0
46
+ REPORT=""
47
+ COVERAGE_VALUE="N/A"
48
+
49
+ # Colors (only for terminal, not for GitHub output)
50
+ if [ -t 1 ]; then
51
+ GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'
52
+ CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
53
+ else
54
+ GREEN=''; RED=''; YELLOW=''; CYAN=''; BOLD=''; NC=''
55
+ fi
56
+
57
+ # ═══════════════════════════════════════════════════════════════
58
+ # HELPERS
59
+ # ═══════════════════════════════════════════════════════════════
60
+
61
+ pass_gate() {
62
+ local name="$1" detail="${2:-}"
63
+ TOTAL_GATES=$((TOTAL_GATES + 1))
64
+ PASSED_GATES=$((PASSED_GATES + 1))
65
+ echo -e "${GREEN}✅${NC} $name${detail:+ — $detail}"
66
+ REPORT="${REPORT}| ✅ | ${name} | ${detail:-OK} |\n"
67
+ }
68
+
69
+ fail_gate() {
70
+ local name="$1" detail="${2:-}"
71
+ TOTAL_GATES=$((TOTAL_GATES + 1))
72
+ FAILED_GATES=$((FAILED_GATES + 1))
73
+ echo -e "${RED}❌${NC} $name${detail:+ — $detail}"
74
+ REPORT="${REPORT}| ❌ | ${name} | ${detail:-FAILED} |\n"
75
+ }
76
+
77
+ warn_gate() {
78
+ local name="$1" detail="${2:-}"
79
+ TOTAL_GATES=$((TOTAL_GATES + 1))
80
+ WARNINGS=$((WARNINGS + 1))
81
+ if [ "$FAIL_ON_WARN" = "true" ]; then
82
+ FAILED_GATES=$((FAILED_GATES + 1))
83
+ echo -e "${YELLOW}⚠️${NC} $name${detail:+ — $detail} (treated as failure)"
84
+ REPORT="${REPORT}| ⚠️→❌ | ${name} | ${detail:-WARNING} |\n"
85
+ else
86
+ PASSED_GATES=$((PASSED_GATES + 1))
87
+ echo -e "${YELLOW}⚠️${NC} $name${detail:+ — $detail}"
88
+ REPORT="${REPORT}| ⚠️ | ${name} | ${detail:-WARNING} |\n"
89
+ fi
90
+ }
91
+
92
+ should_run() {
93
+ local gate="$1"
94
+ [ "$GATES" = "all" ] && return 0
95
+ echo ",$GATES," | grep -q ",$gate," && return 0
96
+ return 1
97
+ }
98
+
99
+ # ═══════════════════════════════════════════════════════════════
100
+ # GATE 1: SPEC VALIDATION
101
+ # ═══════════════════════════════════════════════════════════════
102
+
103
+ run_spec_gate() {
104
+ echo -e "\n${BOLD}Gate 1: Spec Validation${NC}"
105
+
106
+ if [ ! -d "$DC_DIR" ]; then
107
+ fail_gate "SDD Directory" "$DC_DIR/ not found"
108
+ return
109
+ fi
110
+ pass_gate "SDD Directory" "$DC_DIR/ exists"
111
+
112
+ # Check for config
113
+ if [ -f "$DC_DIR/config.yaml" ]; then
114
+ pass_gate "Config" "config.yaml present"
115
+ else
116
+ warn_gate "Config" "config.yaml not found"
117
+ fi
118
+
119
+ # Check for specs
120
+ SPEC_COUNT=$(find "$DC_DIR/specs" -name "*.feature" 2>/dev/null | wc -l | tr -d ' ')
121
+ if [ "$SPEC_COUNT" -gt 0 ]; then
122
+ pass_gate "Gherkin Specs" "$SPEC_COUNT .feature files found"
123
+ else
124
+ fail_gate "Gherkin Specs" "No .feature files in $DC_DIR/specs/"
125
+ fi
126
+
127
+ # Check for blueprint
128
+ BP_COUNT=$(find "$DC_DIR/blueprints" -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
129
+ if [ "$BP_COUNT" -gt 0 ]; then
130
+ pass_gate "Blueprint" "$BP_COUNT blueprint(s) found"
131
+ else
132
+ warn_gate "Blueprint" "No blueprints in $DC_DIR/blueprints/"
133
+ fi
134
+
135
+ # Check estado/status
136
+ if [ -f "$DC_DIR/estado.md" ] || [ -f "$DC_DIR/status.md" ]; then
137
+ pass_gate "Status File" "Project status tracked"
138
+ else
139
+ warn_gate "Status File" "No estado.md or status.md"
140
+ fi
141
+ }
142
+
143
+ # ═══════════════════════════════════════════════════════════════
144
+ # GATE 2: TDD COMPLIANCE
145
+ # ═══════════════════════════════════════════════════════════════
146
+
147
+ run_tdd_gate() {
148
+ echo -e "\n${BOLD}Gate 2: TDD Compliance${NC}"
149
+
150
+ # Check for test files
151
+ TEST_COUNT=0
152
+ for pattern in "test/" "tests/" "__tests__/" "spec/" "*_test.go" "*.test.ts" "*.test.js" "*.spec.ts" "*.spec.js" "test_*.py" "*_test.py"; do
153
+ COUNT=$(find . -path "./$DC_DIR" -prune -o -path "./node_modules" -prune -o \( -name "$pattern" -o -path "*/$pattern*" \) -print 2>/dev/null | wc -l | tr -d ' ')
154
+ TEST_COUNT=$((TEST_COUNT + COUNT))
155
+ done
156
+
157
+ if [ "$TEST_COUNT" -gt 0 ]; then
158
+ pass_gate "Test Files" "$TEST_COUNT test file(s) found"
159
+ else
160
+ fail_gate "Test Files" "No test files found"
161
+ fi
162
+
163
+ # Check for stub detection (// TODO, // FIXME in src)
164
+ STUB_COUNT=0
165
+ for ext in ts js py go rb java; do
166
+ STUBS=$(grep -rn "// TODO\|// FIXME\|# TODO\|# FIXME\|pass #\|raise NotImplementedError" \
167
+ --include="*.$ext" . 2>/dev/null \
168
+ | grep -v node_modules | grep -v "$DC_DIR" | grep -v test | wc -l | tr -d ' ')
169
+ STUB_COUNT=$((STUB_COUNT + STUBS))
170
+ done
171
+
172
+ if [ "$STUB_COUNT" -eq 0 ]; then
173
+ pass_gate "Stub Detection" "No TODO/FIXME stubs in source code"
174
+ elif [ "$STUB_COUNT" -le 3 ]; then
175
+ warn_gate "Stub Detection" "$STUB_COUNT stub(s) found"
176
+ else
177
+ fail_gate "Stub Detection" "$STUB_COUNT stubs found — possible silent stubs"
178
+ fi
179
+ }
180
+
181
+ # ═══════════════════════════════════════════════════════════════
182
+ # GATE 3: COVERAGE
183
+ # ═══════════════════════════════════════════════════════════════
184
+
185
+ run_coverage_gate() {
186
+ echo -e "\n${BOLD}Gate 3: Coverage${NC}"
187
+
188
+ # Try to find coverage report
189
+ COV_FILE=""
190
+ for f in coverage/lcov.info coverage/cobertura.xml coverage/coverage.json htmlcov/index.html coverage.xml .coverage; do
191
+ if [ -f "$f" ]; then
192
+ COV_FILE="$f"
193
+ break
194
+ fi
195
+ done
196
+
197
+ if [ -z "$COV_FILE" ]; then
198
+ warn_gate "Coverage Report" "No coverage report found. Run tests with coverage first."
199
+ return
200
+ fi
201
+
202
+ # Extract coverage percentage based on format
203
+ COV_PCT=0
204
+ case "$COV_FILE" in
205
+ *lcov.info)
206
+ LINES_HIT=$(grep -c "^DA:" "$COV_FILE" 2>/dev/null || echo 0)
207
+ LINES_FOUND=$(grep "^DA:" "$COV_FILE" 2>/dev/null | grep -c ",0$" || echo 0)
208
+ if [ "$LINES_HIT" -gt 0 ]; then
209
+ COV_PCT=$(( (LINES_HIT - LINES_FOUND) * 100 / LINES_HIT ))
210
+ fi
211
+ ;;
212
+ *cobertura.xml|*coverage.xml)
213
+ COV_PCT=$(grep -o 'line-rate="[0-9.]*"' "$COV_FILE" 2>/dev/null | head -1 | grep -o '[0-9.]*' | head -1 || echo 0)
214
+ COV_PCT=$(echo "$COV_PCT * 100" | bc 2>/dev/null | cut -d. -f1 || echo 0)
215
+ ;;
216
+ *coverage.json)
217
+ COV_PCT=$(python3 -c "import json; d=json.load(open('$COV_FILE')); print(int(d.get('totals',{}).get('lines',{}).get('percent',0)))" 2>/dev/null || echo 0)
218
+ ;;
219
+ *)
220
+ COV_PCT=0
221
+ ;;
222
+ esac
223
+
224
+ COVERAGE_VALUE="${COV_PCT}%"
225
+
226
+ if [ "$COV_PCT" -ge "$MIN_COVERAGE" ]; then
227
+ pass_gate "Coverage" "${COV_PCT}% >= ${MIN_COVERAGE}% minimum"
228
+ else
229
+ fail_gate "Coverage" "${COV_PCT}% < ${MIN_COVERAGE}% minimum"
230
+ fi
231
+ }
232
+
233
+ # ═══════════════════════════════════════════════════════════════
234
+ # GATE 4: OWASP QUICK AUDIT
235
+ # ═══════════════════════════════════════════════════════════════
236
+
237
+ run_owasp_gate() {
238
+ echo -e "\n${BOLD}Gate 4: OWASP Quick Audit${NC}"
239
+
240
+ OWASP_ISSUES=0
241
+
242
+ # A01: Hardcoded secrets
243
+ SECRETS=$(grep -rn "password\s*=\s*['\"].\+['\"]" --include="*.py" --include="*.js" --include="*.ts" --include="*.go" --include="*.java" \
244
+ . 2>/dev/null | grep -v node_modules | grep -v test | grep -v "$DC_DIR" | wc -l | tr -d ' ')
245
+ if [ "$SECRETS" -gt 0 ]; then
246
+ warn_gate "A01: Hardcoded Secrets" "$SECRETS potential hardcoded passwords"
247
+ OWASP_ISSUES=$((OWASP_ISSUES + SECRETS))
248
+ fi
249
+
250
+ # A03: SQL Injection
251
+ SQLI=$(grep -rn "f\".*SELECT\|f\".*INSERT\|f\".*DELETE\|f\".*UPDATE\|\".*SELECT.*\" *%" \
252
+ --include="*.py" --include="*.js" --include="*.ts" . 2>/dev/null \
253
+ | grep -v node_modules | grep -v test | grep -v "$DC_DIR" | wc -l | tr -d ' ')
254
+ if [ "$SQLI" -gt 0 ]; then
255
+ fail_gate "A03: SQL Injection" "$SQLI potential SQL injection patterns"
256
+ OWASP_ISSUES=$((OWASP_ISSUES + SQLI))
257
+ fi
258
+
259
+ # A03: XSS via innerHTML/dangerouslySetInnerHTML
260
+ XSS=$(grep -rn "innerHTML\|dangerouslySetInnerHTML\|v-html" \
261
+ --include="*.js" --include="*.ts" --include="*.tsx" --include="*.jsx" --include="*.vue" . 2>/dev/null \
262
+ | grep -v node_modules | grep -v test | grep -v "$DC_DIR" | wc -l | tr -d ' ')
263
+ if [ "$XSS" -gt 0 ]; then
264
+ warn_gate "A03: XSS" "$XSS innerHTML/dangerouslySetInnerHTML usages"
265
+ OWASP_ISSUES=$((OWASP_ISSUES + XSS))
266
+ fi
267
+
268
+ # A07: .env committed
269
+ if [ -f ".env" ] && ! grep -q "^\.env$" .gitignore 2>/dev/null; then
270
+ fail_gate "A07: .env exposed" ".env file exists and is not in .gitignore"
271
+ OWASP_ISSUES=$((OWASP_ISSUES + 1))
272
+ fi
273
+
274
+ if [ "$OWASP_ISSUES" -eq 0 ]; then
275
+ pass_gate "OWASP Quick Audit" "No critical issues detected"
276
+ fi
277
+ }
278
+
279
+ # ═══════════════════════════════════════════════════════════════
280
+ # GATE 5: CUSTOM GATES
281
+ # ═══════════════════════════════════════════════════════════════
282
+
283
+ run_custom_gates() {
284
+ echo -e "\n${BOLD}Gate 5: Custom Gates${NC}"
285
+
286
+ if [ ! -d "$CUSTOM_GATES_DIR" ]; then
287
+ echo -e " ${CYAN}ℹ${NC} No custom gates directory ($CUSTOM_GATES_DIR)"
288
+ return
289
+ fi
290
+
291
+ GATE_FILES=$(find "$CUSTOM_GATES_DIR" -name "*.yml" -o -name "*.yaml" 2>/dev/null)
292
+ if [ -z "$GATE_FILES" ]; then
293
+ echo -e " ${CYAN}ℹ${NC} No custom gate files found"
294
+ return
295
+ fi
296
+
297
+ for gate_file in $GATE_FILES; do
298
+ # Parse YAML manually (no yq dependency)
299
+ GATE_NAME=$(grep "^name:" "$gate_file" | sed 's/^name:\s*//' | tr -d '"' | tr -d "'")
300
+ GATE_TYPE=$(grep "^type:" "$gate_file" | sed 's/^type:\s*//' | tr -d '"' | tr -d "'")
301
+ GATE_SEVERITY=$(grep "^severity:" "$gate_file" | sed 's/^severity:\s*//' | tr -d '"' | tr -d "'")
302
+ GATE_PATTERN=$(grep "^pattern:" "$gate_file" | sed 's/^pattern:\s*//' | tr -d '"' | tr -d "'")
303
+ GATE_FILES_GLOB=$(grep "^files:" "$gate_file" | sed 's/^files:\s*//' | tr -d '"' | tr -d "'")
304
+ GATE_MESSAGE=$(grep "^message:" "$gate_file" | sed 's/^message:\s*//' | tr -d '"' | tr -d "'")
305
+
306
+ [ -z "$GATE_NAME" ] && GATE_NAME=$(basename "$gate_file" .yml)
307
+ [ -z "$GATE_SEVERITY" ] && GATE_SEVERITY="warn"
308
+
309
+ case "$GATE_TYPE" in
310
+ grep)
311
+ MATCHES=$(grep -rn "$GATE_PATTERN" --include="$(echo "$GATE_FILES_GLOB" | sed 's|.*/||')" . 2>/dev/null \
312
+ | grep -v node_modules | grep -v "$DC_DIR" | wc -l | tr -d ' ')
313
+ if [ "$MATCHES" -gt 0 ]; then
314
+ if [ "$GATE_SEVERITY" = "block" ]; then
315
+ fail_gate "Custom: $GATE_NAME" "${GATE_MESSAGE:-$MATCHES matches found}"
316
+ else
317
+ warn_gate "Custom: $GATE_NAME" "${GATE_MESSAGE:-$MATCHES matches found}"
318
+ fi
319
+ else
320
+ pass_gate "Custom: $GATE_NAME" "No matches"
321
+ fi
322
+ ;;
323
+ script)
324
+ GATE_RUN=$(sed -n '/^run:/,/^[a-z]/p' "$gate_file" | grep -v "^run:" | grep -v "^[a-z]" | sed 's/^\s*//')
325
+ if [ -n "$GATE_RUN" ]; then
326
+ if eval "$GATE_RUN" > /dev/null 2>&1; then
327
+ pass_gate "Custom: $GATE_NAME" "Script passed"
328
+ else
329
+ if [ "$GATE_SEVERITY" = "block" ]; then
330
+ fail_gate "Custom: $GATE_NAME" "${GATE_MESSAGE:-Script failed}"
331
+ else
332
+ warn_gate "Custom: $GATE_NAME" "${GATE_MESSAGE:-Script failed}"
333
+ fi
334
+ fi
335
+ fi
336
+ ;;
337
+ *)
338
+ warn_gate "Custom: $GATE_NAME" "Unknown gate type: $GATE_TYPE"
339
+ ;;
340
+ esac
341
+ done
342
+ }
343
+
344
+ # ═══════════════════════════════════════════════════════════════
345
+ # MAIN
346
+ # ═══════════════════════════════════════════════════════════════
347
+
348
+ echo -e "${BOLD}═══════════════════════════════════════════${NC}"
349
+ echo -e "${BOLD} Don Cheli SDD — Quality Gates Check${NC}"
350
+ echo -e "${BOLD}═══════════════════════════════════════════${NC}"
351
+ echo ""
352
+ echo -e " Directory: ${CYAN}$DC_DIR${NC}"
353
+ echo -e " Gates: ${CYAN}$GATES${NC}"
354
+ echo -e " Coverage: ${CYAN}>= ${MIN_COVERAGE}%${NC}"
355
+
356
+ should_run "spec" && run_spec_gate
357
+ should_run "tdd" && run_tdd_gate
358
+ should_run "coverage" && run_coverage_gate
359
+ should_run "owasp" && run_owasp_gate
360
+ should_run "custom" && run_custom_gates
361
+
362
+ # ═══════════════════════════════════════════════════════════════
363
+ # RESULTS
364
+ # ═══════════════════════════════════════════════════════════════
365
+
366
+ echo ""
367
+ echo -e "${BOLD}═══════════════════════════════════════════${NC}"
368
+
369
+ ALL_PASSED="true"
370
+ if [ "$FAILED_GATES" -gt 0 ]; then
371
+ ALL_PASSED="false"
372
+ echo -e "${RED}${BOLD} ❌ FAILED — $PASSED_GATES/$TOTAL_GATES gates passed ($FAILED_GATES failed, $WARNINGS warnings)${NC}"
373
+ else
374
+ echo -e "${GREEN}${BOLD} ✅ PASSED — $PASSED_GATES/$TOTAL_GATES gates passed ($WARNINGS warnings)${NC}"
375
+ fi
376
+
377
+ echo -e "${BOLD}═══════════════════════════════════════════${NC}"
378
+
379
+ # Build markdown report for PR comment
380
+ FULL_REPORT="## 🛡️ Don Cheli SDD — Quality Gates\n\n"
381
+ if [ "$ALL_PASSED" = "true" ]; then
382
+ FULL_REPORT="${FULL_REPORT}**✅ All gates passed** ($PASSED_GATES/$TOTAL_GATES)\n\n"
383
+ else
384
+ FULL_REPORT="${FULL_REPORT}**❌ $FAILED_GATES gate(s) failed** ($PASSED_GATES/$TOTAL_GATES passed)\n\n"
385
+ fi
386
+ FULL_REPORT="${FULL_REPORT}| Status | Gate | Detail |\n|--------|------|--------|\n${REPORT}\n"
387
+ FULL_REPORT="${FULL_REPORT}---\n*Generated by [Don Cheli SDD](https://github.com/doncheli/don-cheli-sdd)*"
388
+
389
+ # GitHub Actions outputs
390
+ if [ -n "${GITHUB_OUTPUT:-}" ]; then
391
+ {
392
+ echo "passed=$ALL_PASSED"
393
+ echo "coverage=$COVERAGE_VALUE"
394
+ echo "gates_passed=$PASSED_GATES"
395
+ echo "gates_total=$TOTAL_GATES"
396
+ # Multiline output
397
+ echo "report<<ENDOFREPORT"
398
+ printf "%b" "$FULL_REPORT"
399
+ echo ""
400
+ echo "ENDOFREPORT"
401
+ } >> "$GITHUB_OUTPUT"
402
+ fi
403
+
404
+ # Exit code
405
+ if [ "$ALL_PASSED" = "false" ]; then
406
+ exit 1
407
+ fi