@saulwade/swl-ses 1.9.0 → 2.1.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/CLAUDE.md +196 -196
- package/README.md +579 -579
- package/agentes/_propose-step.md +90 -0
- package/agentes/accesibilidad-wcag-swl.md +3 -3
- package/agentes/auto-evolucion-swl.md +908 -908
- package/agentes/disenador-ui-swl.md +6 -5
- package/agentes/frontend-angular-swl.md +2 -2
- package/agentes/frontend-css-swl.md +2 -2
- package/agentes/frontend-react-swl.md +4 -4
- package/agentes/frontend-swl.md +6 -6
- package/agentes/implementador-swl.md +2 -0
- package/agentes/investigador-ux-swl.md +5 -5
- package/agentes/orquestador-swl.md +9 -7
- package/agentes/perfilador-usuario-swl.md +321 -308
- package/agentes/producto-prd-swl.md +1 -1
- package/agentes/red-team-swl.md +218 -218
- package/agentes/tdd-qa-swl.md +17 -1
- package/bin/swl-ses.js +1 -1
- package/comandos/swl/actualizar.md +1 -1
- package/comandos/swl/aprender.md +2 -2
- package/comandos/swl/aprobar-plan.md +153 -0
- package/comandos/swl/ayuda.md +3 -3
- package/comandos/swl/briefing.md +122 -0
- package/comandos/swl/compactar.md +29 -2
- package/comandos/swl/discutir-fase.md +23 -2
- package/comandos/swl/ejecutar-fase.md +59 -6
- package/comandos/swl/evolucionar.md +1 -1
- package/comandos/swl/inbox.md +1 -1
- package/comandos/swl/instalar.md +1 -1
- package/comandos/swl/nemesis.md +1 -1
- package/comandos/swl/planear-fase.md +19 -1
- package/comandos/swl/plugins.md +1 -1
- package/comandos/swl/release.md +47 -1
- package/comandos/swl/status.md +348 -0
- package/comandos/swl/verificar.md +27 -1
- package/habilidades/ai-runtime-security/SKILL.md +1 -1
- package/habilidades/auto-evolucion-protocolo/SKILL.md +276 -276
- package/habilidades/benchmark-memoria/SKILL.md +1 -1
- package/habilidades/calidad-contract-testing/SKILL.md +165 -0
- package/habilidades/changelog-generator/SKILL.md +9 -2
- package/habilidades/changelog-generator/scripts/parse-commits.js +13 -1
- package/habilidades/diagrama-arquitectura/SKILL.md +1 -1
- package/habilidades/drift-detection/SKILL.md +179 -179
- package/habilidades/ejecutar-fase/SKILL.md +541 -468
- package/habilidades/estructura-proyecto-claude/SKILL.md +17 -14
- package/habilidades/estructura-proyecto-claude/recursos/configuracion-y-extensiones.md +34 -23
- package/habilidades/estructura-proyecto-claude/recursos/frontmatter-y-hooks-referencia.md +70 -53
- package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +57 -77
- package/habilidades/extractor-de-aprendizajes/SKILL.md +9 -5
- package/habilidades/harness-claude-code/SKILL.md +10 -7
- package/{reglas/harness-claude-code.md → habilidades/harness-claude-code/recursos/disciplina-harness-regla.md} +2 -2
- package/habilidades/instalar-sistema/SKILL.md +3 -3
- package/habilidades/meta-skills-estandar/recursos/frameworks-seguridad.md +1 -1
- package/habilidades/perfil-usuario/SKILL.md +200 -200
- package/habilidades/planear-fase/SKILL.md +26 -4
- package/habilidades/proceso-ddia-fundamentos/SKILL.md +1 -1
- package/habilidades/proceso-ddia-streaming/SKILL.md +4 -4
- package/habilidades/proceso-debate-adversarial/SKILL.md +2 -2
- package/habilidades/protocolo-revision-swl/SKILL.md +1 -1
- package/habilidades/seguridad-skills-ia/SKILL.md +1 -1
- package/habilidades/swl-claudemd/SKILL.md +50 -210
- package/habilidades/swl-claudemd/recursos/contrato-aprender.md +83 -0
- package/habilidades/swl-claudemd/recursos/duplicacion-reglas-globales.md +85 -0
- package/habilidades/swl-claudemd/recursos/plantillas-init.md +94 -0
- package/habilidades/swl-dashboard/SKILL.md +9 -9
- package/habilidades/swl-revisar-impacto/SKILL.md +1 -1
- package/habilidades/tdd-workflow/SKILL.md +715 -673
- package/habilidades/validacion-ci-sistema/SKILL.md +20 -4
- package/hooks/calidad-pre-commit.js +344 -3
- package/hooks/check-update.js +39 -1
- package/hooks/ciclo-evolucion-subagente.js +26 -0
- package/hooks/ciclo-evolucion.js +26 -0
- package/hooks/extraccion-aprendizajes.js +13 -0
- package/hooks/lib/autonomia.js +208 -0
- package/hooks/lib/briefing.js +474 -0
- package/hooks/lib/ciclo-evolucion.js +47 -0
- package/hooks/{auto-evolucion.js → lib/etapa-auto-evolucion.js} +701 -700
- package/hooks/{metricas-evolucion.js → lib/etapa-metricas.js} +388 -376
- package/hooks/{actualizar-perfil-usuario.js → lib/etapa-perfil-usuario.js} +376 -364
- package/hooks/lib/evolution-tracker.js +24 -3
- package/hooks/lib/propose-step.js +357 -0
- package/hooks/session-briefing.js +98 -0
- package/hooks/spec-gate.js +211 -0
- package/hooks/tdd-gate.js +241 -0
- package/hooks/telemetria-skill-routing.js +100 -0
- package/hooks/validar-intent-spec.js +30 -10
- package/instintos/autonomia.yaml +27 -0
- package/llms.txt +6 -6
- package/manifiestos/hooks-config.json +44 -17
- package/manifiestos/modulos.json +40 -15
- package/manifiestos/skills-lock.json +64 -57
- package/package.json +93 -93
- package/plugin.json +371 -375
- package/reglas/accesibilidad.md +10 -0
- package/reglas/analizar-directorios-antes-de-escribir.md +228 -0
- package/reglas/api-diseno.md +9 -0
- package/reglas/auditorias-documentales-estructurales.md +7 -0
- package/reglas/cloud-infra.md +8 -0
- package/reglas/consultar-vault-primero.md +195 -0
- package/reglas/debatir-antes-de-aceptar.md +158 -0
- package/reglas/fragmentos-compartidos.md +5 -0
- package/reglas/git-coauthor.md +100 -0
- package/reglas/gobernanza.md +4 -4
- package/reglas/hooks.md +6 -0
- package/reglas/intent-engineering.md +4 -0
- package/reglas/markitdown.md +8 -0
- package/reglas/memoria-consolidada.md +1 -1
- package/reglas/monitor-ci.md +309 -0
- package/reglas/patrones.md +6 -0
- package/reglas/registro-componentes-nuevos.md +39 -2
- package/reglas/seguridad-agentes.md +1 -1
- package/reglas/sesiones-paralelas.md +180 -0
- package/reglas/skills-estandar.md +6 -0
- package/reglas/testing.md +7 -0
- package/reglas/tests-cleanup.md +4 -0
- package/reglas/usar-code-review-graph.md +155 -0
- package/reglas/usar-sistema-swl.md +1 -1
- package/reglas/verificar-citas-normativas.md +548 -0
- package/scripts/instalador.js +52 -6
- package/scripts/lib/ci-reader.js +193 -0
- package/scripts/lib/detectar-host-swl.js +175 -0
- package/scripts/lib/evidencia-release.js +322 -0
- package/scripts/lib/gate-hooks-requires.js +249 -0
- package/scripts/lib/gate-licencias.js +212 -0
- package/scripts/lib/git-metricas.js +257 -0
- package/scripts/lib/gitignore-manifest.js +29 -1
- package/scripts/lib/metricas-dora.js +204 -0
- package/scripts/lib/plan-lock.js +275 -0
- package/scripts/migrar-fase-dominio.js +0 -1
- package/scripts/tui/ejecutores.js +1 -1
- package/scripts/validar-manifest.js +92 -1
- package/scripts/verificar-evolucion.js +54 -4
- package/scripts/verificar-release.js +102 -0
- package/scripts/verificar-trazabilidad.js +298 -0
- package/agentes/ux-disenador-swl.md +0 -503
- package/comandos/swl/dashboard.md +0 -146
- package/comandos/swl/evolucion-estado.md +0 -191
- package/comandos/swl/metricas.md +0 -376
- package/comandos/swl/salud.md +0 -481
- package/reglas/arquitectura.evolved.json +0 -7
- package/reglas/seguridad.evolved.json +0 -7
- package/reglas/verificar-citas-temporales.md +0 -139
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* verificar-trazabilidad.js — Valida la cadena REQ→T→commit→test de una fase (G4).
|
|
6
|
+
*
|
|
7
|
+
* Uso:
|
|
8
|
+
* node scripts/verificar-trazabilidad.js --fase=10 [--solo-plan] [--json]
|
|
9
|
+
*
|
|
10
|
+
* Fuentes:
|
|
11
|
+
* - REQ-NN: `.planning/fases/NN-CONTEXTO.md` § Criterios de aceptación
|
|
12
|
+
* (definiciones `**REQ-NN**:` o `REQ-NN:`; los marcados RETIRADO
|
|
13
|
+
* se excluyen del universo exigible)
|
|
14
|
+
* - T-NN: `.planning/fases/NN-PLAN.md` — campo `**Verifica REQ**: ...`
|
|
15
|
+
* por tarea `### T-NN:`
|
|
16
|
+
* - Commits: `git log` — footer `Refs: REQ-NN[, REQ-MM]`
|
|
17
|
+
* - Tests: archivos de test versionados (git ls-files + clasificador de
|
|
18
|
+
* calidad-pre-commit) con marker `verifica: REQ-NN`
|
|
19
|
+
*
|
|
20
|
+
* Exit codes:
|
|
21
|
+
* 0 — cadena completa, o CONTEXTO legacy sin REQ-IDs (gracia, con nota)
|
|
22
|
+
* 1 — huérfanos detectados (REQ sin tarea / sin commit / sin test)
|
|
23
|
+
* 2 — error de invocación o parseo (fase inexistente, args inválidos)
|
|
24
|
+
*
|
|
25
|
+
* `--solo-plan`: valida únicamente la matriz REQ×T (lo usa /swl:aprobar-plan,
|
|
26
|
+
* donde commits y tests aún no existen).
|
|
27
|
+
*
|
|
28
|
+
* Zero-deps. Espejo estructural de verificar-release.js (gates + exit codes).
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
const fs = require('fs');
|
|
32
|
+
const path = require('path');
|
|
33
|
+
const { execFileSync } = require('child_process');
|
|
34
|
+
|
|
35
|
+
const LIMITE_COMMITS = 300;
|
|
36
|
+
|
|
37
|
+
// ─── Parseo de fuentes ──────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Extrae definiciones REQ-NN del CONTEXTO.
|
|
41
|
+
* Método de verificación por REQ (patrón requirements engineering):
|
|
42
|
+
* - default `test` — exige test con marker `verifica: REQ-NN`
|
|
43
|
+
* - `(verificación: inspección)` en el criterio — satisfecho por prosa/docs/grep;
|
|
44
|
+
* exige tarea y commit, pero NO test automatizado.
|
|
45
|
+
* @returns {{activos: string[], retirados: string[], metodo: Object<string,string>}}
|
|
46
|
+
*/
|
|
47
|
+
function extraerReqs(contextoTexto) {
|
|
48
|
+
const activos = new Set();
|
|
49
|
+
const retirados = new Set();
|
|
50
|
+
const metodo = {};
|
|
51
|
+
// Definición: línea con `**REQ-NN**:` o `REQ-NN:` (con bullet opcional).
|
|
52
|
+
// El criterio puede continuar en líneas siguientes indentadas — se capturan
|
|
53
|
+
// hasta la próxima definición para detectar la anotación de método.
|
|
54
|
+
// REQ-NN (legacy) o REQ-NN-MM (namespaceado por fase, DT-IDS-NAMESPACE).
|
|
55
|
+
// El grupo `(?:-\d{1,3})?` es greedy: en `REQ-12-01` captura el ID completo,
|
|
56
|
+
// nunca el prefijo `REQ-12` truncado.
|
|
57
|
+
const re = /^\s*(?:[-*]\s*)?\*{0,2}(REQ-\d{1,3}(?:-\d{1,3})?)\*{0,2}\s*:([\s\S]*?)(?=^\s*(?:[-*]\s*)?\*{0,2}REQ-\d|^#|\n\n|$(?![\s\S]))/gm;
|
|
58
|
+
let m;
|
|
59
|
+
while ((m = re.exec(contextoTexto)) !== null) {
|
|
60
|
+
const cuerpo = m[2];
|
|
61
|
+
if (/RETIRADO/i.test(cuerpo)) {
|
|
62
|
+
retirados.add(m[1]);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
activos.add(m[1]);
|
|
66
|
+
metodo[m[1]] = /verificaci[oó]n:\s*inspecci[oó]n/i.test(cuerpo) ? 'inspeccion' : 'test';
|
|
67
|
+
}
|
|
68
|
+
return { activos: [...activos], retirados: [...retirados], metodo };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Extrae mapa tarea→REQs del PLAN. @returns {Map<string, string[]>} */
|
|
72
|
+
function extraerMatrizPlan(planTexto) {
|
|
73
|
+
const tareas = new Map();
|
|
74
|
+
let tareaActual = null;
|
|
75
|
+
for (const linea of planTexto.split('\n')) {
|
|
76
|
+
const t = linea.match(/^#{2,4}\s*(T-\d{1,3})\s*:/);
|
|
77
|
+
if (t) {
|
|
78
|
+
tareaActual = t[1];
|
|
79
|
+
if (!tareas.has(tareaActual)) tareas.set(tareaActual, []);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
// Solo la línea-campo cuenta (`- **Verifica REQ**: ...` al inicio, bullet
|
|
83
|
+
// opcional). Una descripción que cite `**Verifica REQ**:` en prosa NO debe
|
|
84
|
+
// contaminar la matriz ni ocultar huérfanos (ancla `^` obligatoria).
|
|
85
|
+
if (tareaActual && /^\s*(?:[-*]\s*)?\*\*Verifica REQ\*\*\s*:/i.test(linea)) {
|
|
86
|
+
const reqs = linea.match(/REQ-\d{1,3}(?:-\d{1,3})?/g) || [];
|
|
87
|
+
tareas.get(tareaActual).push(...reqs);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return tareas;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Commits con footer `Refs: REQ-NN`. @returns {Map<string, string[]>} REQ → hashes cortos */
|
|
94
|
+
function extraerCommitsConRefs(cwd) {
|
|
95
|
+
const porReq = new Map();
|
|
96
|
+
let salida;
|
|
97
|
+
try {
|
|
98
|
+
salida = execFileSync(
|
|
99
|
+
'git',
|
|
100
|
+
['log', `-${LIMITE_COMMITS}`, '--format=%h%x00%B%x01'],
|
|
101
|
+
{ cwd, encoding: 'utf8', timeout: 10000 }
|
|
102
|
+
);
|
|
103
|
+
} catch (_) {
|
|
104
|
+
return porReq; // sin repo git o sin commits — el reporte lo refleja como gap
|
|
105
|
+
}
|
|
106
|
+
for (const bloque of salida.split('\x01')) {
|
|
107
|
+
const [hash, cuerpo] = bloque.split('\x00');
|
|
108
|
+
if (!hash || !cuerpo) continue;
|
|
109
|
+
const refs = cuerpo.match(/^Refs:\s*(.+)$/m);
|
|
110
|
+
if (!refs) continue;
|
|
111
|
+
for (const req of refs[1].match(/REQ-\d{1,3}(?:-\d{1,3})?/g) || []) {
|
|
112
|
+
if (!porReq.has(req)) porReq.set(req, []);
|
|
113
|
+
porReq.get(req).push(hash.trim());
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return porReq;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Tests con marker `verifica: REQ-NN`. @returns {Map<string, string[]>} REQ → archivos */
|
|
120
|
+
function extraerTestsConMarker(cwd) {
|
|
121
|
+
const porReq = new Map();
|
|
122
|
+
let esArchivoTest;
|
|
123
|
+
try {
|
|
124
|
+
({ esArchivoTest } = require(path.join(__dirname, '..', 'hooks', 'calidad-pre-commit.js')));
|
|
125
|
+
} catch (_) {
|
|
126
|
+
esArchivoTest = (r) => /\.(test|spec)\.[jt]sx?$/.test(r) || /test_.*\.py$/.test(r);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let archivos = [];
|
|
130
|
+
try {
|
|
131
|
+
archivos = execFileSync('git', ['ls-files'], { cwd, encoding: 'utf8', timeout: 10000 })
|
|
132
|
+
.split('\n')
|
|
133
|
+
.map((l) => l.trim())
|
|
134
|
+
.filter(Boolean)
|
|
135
|
+
.filter(esArchivoTest);
|
|
136
|
+
} catch (_) {
|
|
137
|
+
return porReq;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
for (const archivo of archivos) {
|
|
141
|
+
let contenido;
|
|
142
|
+
try {
|
|
143
|
+
contenido = fs.readFileSync(path.join(cwd, archivo), 'utf-8');
|
|
144
|
+
} catch (_) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
for (const m of contenido.matchAll(/verifica:\s*(REQ-\d{1,3}(?:-\d{1,3})?)/g)) {
|
|
148
|
+
const req = m[1];
|
|
149
|
+
if (!porReq.has(req)) porReq.set(req, []);
|
|
150
|
+
if (!porReq.get(req).includes(archivo)) porReq.get(req).push(archivo);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return porReq;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ─── Validación ─────────────────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
function validarFase({ cwd, fase, soloPlan }) {
|
|
159
|
+
const nn = String(Number(fase)).padStart(2, '0');
|
|
160
|
+
const contextoPath = path.join(cwd, '.planning', 'fases', `${nn}-CONTEXTO.md`);
|
|
161
|
+
if (!fs.existsSync(contextoPath)) {
|
|
162
|
+
return { error: `No existe ${path.relative(cwd, contextoPath)}`, exit: 2 };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const { activos, retirados, metodo } = extraerReqs(fs.readFileSync(contextoPath, 'utf-8'));
|
|
166
|
+
if (activos.length === 0) {
|
|
167
|
+
return {
|
|
168
|
+
fase: nn,
|
|
169
|
+
legacy: true,
|
|
170
|
+
nota: 'CONTEXTO sin REQ-IDs — trazabilidad no exigible (gracia legacy, fases pre-10)',
|
|
171
|
+
exit: 0,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const planPath = path.join(cwd, '.planning', 'fases', `${nn}-PLAN.md`);
|
|
176
|
+
const matrizTareas = fs.existsSync(planPath)
|
|
177
|
+
? extraerMatrizPlan(fs.readFileSync(planPath, 'utf-8'))
|
|
178
|
+
: new Map();
|
|
179
|
+
|
|
180
|
+
const reqATareas = new Map(activos.map((r) => [r, []]));
|
|
181
|
+
const tareasSinReq = [];
|
|
182
|
+
for (const [tarea, reqs] of matrizTareas) {
|
|
183
|
+
if (reqs.length === 0) tareasSinReq.push(tarea);
|
|
184
|
+
for (const req of reqs) {
|
|
185
|
+
if (reqATareas.has(req)) reqATareas.get(req).push(tarea);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const commits = soloPlan ? new Map() : extraerCommitsConRefs(cwd);
|
|
190
|
+
const tests = soloPlan ? new Map() : extraerTestsConMarker(cwd);
|
|
191
|
+
|
|
192
|
+
const validaciones = {
|
|
193
|
+
req_sin_tarea: activos.filter((r) => (reqATareas.get(r) || []).length === 0),
|
|
194
|
+
req_sin_commit: soloPlan ? [] : activos.filter((r) => !(commits.get(r) || []).length),
|
|
195
|
+
req_sin_test: soloPlan
|
|
196
|
+
? []
|
|
197
|
+
: activos.filter((r) => metodo[r] === 'test' && !(tests.get(r) || []).length),
|
|
198
|
+
tarea_sin_req: tareasSinReq, // informativo, no rompe
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const trazabilidad = {};
|
|
202
|
+
for (const req of activos) {
|
|
203
|
+
trazabilidad[req] = {
|
|
204
|
+
metodo: metodo[req],
|
|
205
|
+
tareas: reqATareas.get(req) || [],
|
|
206
|
+
commits: commits.get(req) || [],
|
|
207
|
+
tests: tests.get(req) || [],
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const huerfanos =
|
|
212
|
+
validaciones.req_sin_tarea.length +
|
|
213
|
+
validaciones.req_sin_commit.length +
|
|
214
|
+
validaciones.req_sin_test.length;
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
fase: nn,
|
|
218
|
+
legacy: false,
|
|
219
|
+
soloPlan: Boolean(soloPlan),
|
|
220
|
+
reqs: activos,
|
|
221
|
+
retirados,
|
|
222
|
+
trazabilidad,
|
|
223
|
+
validaciones,
|
|
224
|
+
exit: huerfanos > 0 ? 1 : 0,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ─── Reporte ────────────────────────────────────────────────────────────────
|
|
229
|
+
|
|
230
|
+
function imprimirReporte(r) {
|
|
231
|
+
if (r.error) {
|
|
232
|
+
console.error(`ERROR: ${r.error}`);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (r.legacy) {
|
|
236
|
+
console.log(`Fase ${r.fase}: ${r.nota}. Exit 0.`);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
console.log(`Trazabilidad REQ→T→commit→test — Fase ${r.fase}${r.soloPlan ? ' (--solo-plan: matriz REQ×T)' : ''}`);
|
|
240
|
+
console.log('');
|
|
241
|
+
for (const req of r.reqs) {
|
|
242
|
+
const t = r.trazabilidad[req];
|
|
243
|
+
const partes = [`tareas: ${t.tareas.join(', ') || '—'}`];
|
|
244
|
+
if (!r.soloPlan) {
|
|
245
|
+
partes.push(`commits: ${t.commits.join(', ') || '—'}`);
|
|
246
|
+
partes.push(t.metodo === 'inspeccion' ? 'verificación: inspección' : `tests: ${t.tests.join(', ') || '—'}`);
|
|
247
|
+
}
|
|
248
|
+
console.log(` ${req} → ${partes.join(' | ')}`);
|
|
249
|
+
}
|
|
250
|
+
if (r.retirados.length) console.log(` Retirados (no exigibles): ${r.retirados.join(', ')}`);
|
|
251
|
+
console.log('');
|
|
252
|
+
|
|
253
|
+
const v = r.validaciones;
|
|
254
|
+
const gaps = [];
|
|
255
|
+
if (v.req_sin_tarea.length) gaps.push(`REQ sin tarea: ${v.req_sin_tarea.join(', ')}`);
|
|
256
|
+
if (v.req_sin_commit.length) gaps.push(`REQ sin commit (footer Refs:): ${v.req_sin_commit.join(', ')}`);
|
|
257
|
+
if (v.req_sin_test.length) gaps.push(`REQ sin test (marker verifica:): ${v.req_sin_test.join(', ')}`);
|
|
258
|
+
|
|
259
|
+
if (gaps.length) {
|
|
260
|
+
console.log('HUÉRFANOS DETECTADOS:');
|
|
261
|
+
for (const g of gaps) console.log(` ✗ ${g}`);
|
|
262
|
+
} else {
|
|
263
|
+
console.log('✓ Cadena de trazabilidad completa: 0 huérfanos.');
|
|
264
|
+
}
|
|
265
|
+
if (v.tarea_sin_req.length) {
|
|
266
|
+
console.log(` (info) Tareas sin REQ declarado: ${v.tarea_sin_req.join(', ')} — válido para infraestructura/refactor`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
271
|
+
|
|
272
|
+
function main() {
|
|
273
|
+
const args = process.argv.slice(2);
|
|
274
|
+
const faseArg = args.find((a) => a.startsWith('--fase='));
|
|
275
|
+
if (!faseArg || !/^\d+$/.test(faseArg.split('=')[1] || '')) {
|
|
276
|
+
console.error('Uso: node scripts/verificar-trazabilidad.js --fase=N [--solo-plan] [--json]');
|
|
277
|
+
process.exit(2);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const resultado = validarFase({
|
|
281
|
+
cwd: process.cwd(),
|
|
282
|
+
fase: faseArg.split('=')[1],
|
|
283
|
+
soloPlan: args.includes('--solo-plan'),
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
if (args.includes('--json')) {
|
|
287
|
+
console.log(JSON.stringify(resultado, null, 2));
|
|
288
|
+
} else {
|
|
289
|
+
imprimirReporte(resultado);
|
|
290
|
+
}
|
|
291
|
+
process.exit(resultado.exit);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (require.main === module) {
|
|
295
|
+
main();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
module.exports = { extraerReqs, extraerMatrizPlan, extraerCommitsConRefs, extraerTestsConMarker, validarFase };
|