@saulwade/swl-ses 1.5.0 → 1.5.2
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 +19 -2
- package/README.md +561 -561
- package/agentes/arquitecto-swl.md +33 -1
- package/agentes/nemesis-auditor-swl.md +59 -19
- package/bin/swl-mcp-server.js +214 -214
- package/comandos/swl/.evolved.json +22 -22
- package/comandos/swl/contribuir.md +233 -233
- package/comandos/swl/nemesis.md +230 -56
- package/gateway/lib/event-channel.js +191 -191
- package/habilidades/backend-production-resilience/SKILL.md +288 -288
- package/habilidades/benchmark-memoria/SKILL.md +186 -186
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
- package/habilidades/doubt-driven-review/SKILL.md +171 -171
- package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
- package/habilidades/ejecutar-task-iterativo/SKILL.md +278 -278
- package/habilidades/eval-framework/SKILL.md +212 -212
- package/habilidades/feynman-auditor-swl/SKILL.md +123 -123
- package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
- package/habilidades/harness-claude-code/SKILL.md +299 -299
- package/habilidades/infra-github-actions/SKILL.md +166 -166
- package/habilidades/legacy-code-rescue/SKILL.md +267 -267
- package/habilidades/manejo-errores/.evolved.json +8 -8
- package/habilidades/meta-skills-estandar/SKILL.md +225 -1
- package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
- package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -163
- package/habilidades/nemesis-evaluacion-json/SKILL.md +266 -0
- package/habilidades/nemesis-redistribuir/SKILL.md +341 -0
- package/habilidades/node-experto/SKILL.md +105 -4
- package/habilidades/patrones-python/SKILL.md +229 -229
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
- package/habilidades/planear-fase/SKILL.md +319 -319
- package/habilidades/protocolo-revision-swl/SKILL.md +350 -276
- package/habilidades/release-semver/.evolved.json +8 -8
- package/habilidades/state-inconsistency-auditor-swl/SKILL.md +166 -166
- package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
- package/habilidades/tdd-workflow/SKILL.md +150 -4
- package/habilidades/testing-python/SKILL.md +340 -340
- package/habilidades/verificar-trabajo/SKILL.md +8 -3
- package/habilidades/web-fetcher-routing/SKILL.md +75 -75
- package/hooks/check-update.js +31 -3
- package/hooks/claudemd-bloat-detector.js +161 -161
- package/hooks/lib/agent-routing.js +107 -107
- package/hooks/lib/auto-consolidator.js +335 -335
- package/hooks/lib/error-classifier.js +308 -308
- package/hooks/lib/merkle-audit.js +96 -96
- package/hooks/lib/provenance-tracker.js +191 -191
- package/hooks/lib/rate-limit-tracker.js +253 -253
- package/hooks/lib/resource-quota.js +122 -122
- package/hooks/lib/retry-jitter.js +165 -165
- package/hooks/lib/security-net.js +201 -201
- package/hooks/lib/skill-auditor.js +588 -588
- package/hooks/lib/sync-status.js +228 -228
- package/hooks/lib/taint-tracker.js +107 -107
- package/hooks/lib/text-similarity.js +241 -241
- package/hooks/lib/toon-compressor.js +245 -245
- package/hooks/registro-turnos.js +209 -209
- package/hooks/sugerir-regenerar-inventario.js +170 -170
- package/hooks/validar-formato-post-subagente.js +140 -140
- package/hooks/validar-memoria-hook.js +218 -218
- package/instintos/prompt-appendices.yaml +57 -57
- package/manifiestos/agent-output-schemas.json +57 -57
- package/manifiestos/modulos.json +1324 -1321
- package/manifiestos/skills-lock.json +1114 -1114
- package/package.json +2 -2
- package/plantillas/auditor-veto-template.md +105 -105
- package/plantillas/github-workflows/README.md +47 -47
- package/plantillas/github-workflows/release-please.yml +44 -44
- package/plantillas/github-workflows/swl-ci.yml +107 -107
- package/plantillas/github-workflows/swl-security.yml +51 -51
- package/plugin.json +353 -351
- package/reglas/analisis-previo-tareas-grandes.md +172 -172
- package/reglas/arreglar-al-detectar.md +147 -147
- package/reglas/fragmentos-compartidos.md +152 -152
- package/reglas/harness-claude-code.md +213 -213
- package/reglas/registro-componentes-nuevos.md +192 -0
- package/reglas/usar-context7.md +226 -226
- package/schemas/diary-entry.schema.json +80 -80
- package/scripts/actualizar.js +110 -1
- package/scripts/audit-tools/audit-history.js +330 -330
- package/scripts/audit-tools/bundle-tracker.js +290 -290
- package/scripts/audit-tools/canary-monitor.js +352 -352
- package/scripts/audit-tools/code-profiler.js +605 -605
- package/scripts/audit-tools/dep-doctor.js +320 -320
- package/scripts/audit-tools/env-validator.js +206 -206
- package/scripts/audit-tools/lib/fs-walk.js +48 -48
- package/scripts/audit-tools/lib/output.js +23 -23
- package/scripts/audit-tools/migration-checker.js +392 -392
- package/scripts/audit-tools/pentest-scanner.js +1436 -1436
- package/scripts/benchmark-memoria.js +167 -167
- package/scripts/configurar-branch-protection.js +418 -418
- package/scripts/derivar-feature-list.js +489 -489
- package/scripts/detectar-aprendizajes-duplicados.js +151 -151
- package/scripts/doctor.js +58 -4
- package/scripts/field-report.js +199 -199
- package/scripts/generar-checklists-consolidados.js +273 -273
- package/scripts/generar-inventario.js +420 -420
- package/scripts/generar-matriz-lenguajes.js +271 -271
- package/scripts/lib/artefactos-python.js +43 -43
- package/scripts/lib/benchmark-metrics.js +160 -160
- package/scripts/lib/budget-enforcer.js +252 -252
- package/scripts/lib/configurar-ci.js +380 -380
- package/scripts/lib/contadores-inventario.js +217 -217
- package/scripts/lib/detectar-stack-detallado.js +307 -307
- package/scripts/lib/diary-entry.js +234 -234
- package/scripts/lib/eval-metrics-store.js +218 -218
- package/scripts/lib/eval-quality.js +171 -171
- package/scripts/lib/eval-schemas.js +144 -144
- package/scripts/lib/eval-self-correct.js +106 -106
- package/scripts/lib/eval-validator.js +185 -185
- package/scripts/lib/expandir-targets.js +71 -71
- package/scripts/lib/jaccard-similarity.js +98 -98
- package/scripts/lib/longmemeval-runner.js +125 -125
- package/scripts/lib/mcp_config.py +127 -0
- package/scripts/lib/npm-version.js +261 -261
- package/scripts/lib/paquetes-conocidos.js +50 -50
- package/scripts/lib/prompt-builder.js +264 -264
- package/scripts/lib/rrf-fusion.js +175 -175
- package/scripts/lib/scoring-instintos.js +277 -277
- package/scripts/lib/semantic-search.js +252 -252
- package/scripts/lib/toml-merge.js +204 -204
- package/scripts/lib/transformadores/codex.js +375 -375
- package/scripts/lib/transformadores/cursor.js +359 -359
- package/scripts/limpiar-artefactos-python.js +131 -131
- package/scripts/mcp-orchestrator.py +8 -18
- package/scripts/mcp-pool-manager.py +12 -23
- package/scripts/mcp-server/README.md +170 -170
- package/scripts/mcp-server/auth.js +105 -105
- package/scripts/mcp-server/cache.js +106 -106
- package/scripts/mcp-server/telemetry.js +78 -78
- package/scripts/migrar-csv-a-array.js +168 -168
- package/scripts/migrar-fase-dominio.js +201 -201
- package/scripts/publicar.js +511 -511
- package/scripts/run-eval.js +141 -141
- package/scripts/validar-userland-vacio.js +110 -110
|
@@ -1,218 +1,218 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Hook: validar-memoria-hook.js
|
|
6
|
-
* Tipo: PostToolUse (matcher: Write|Edit|MultiEdit)
|
|
7
|
-
*
|
|
8
|
-
* Cierra el gap señalado en `reglas/memoria-consolidada.md`: el script
|
|
9
|
-
* `scripts/validar-memoria.js` solo se ejecuta manualmente vía CLI, así
|
|
10
|
-
* que la contaminación entre canales (perfil-usuario, APRENDIZAJES,
|
|
11
|
-
* instintos) sucedía sin que nadie se enterara hasta que un humano
|
|
12
|
-
* corría el validador.
|
|
13
|
-
*
|
|
14
|
-
* Este hook dispara `validar-memoria.js` en modo JSON cada vez que se
|
|
15
|
-
* escribe un archivo de memoria sensible:
|
|
16
|
-
* - `.planning/APRENDIZAJES.md`
|
|
17
|
-
* - `instintos/perfil-usuario.yaml`
|
|
18
|
-
* - `instintos/proyecto.yaml`
|
|
19
|
-
* - `instintos/global.yaml`
|
|
20
|
-
*
|
|
21
|
-
* Comportamiento:
|
|
22
|
-
* - Sin violaciones → silencioso (exit 0).
|
|
23
|
-
* - Violaciones menores (canal incorrecto, duplicación cross-canal) →
|
|
24
|
-
* emite nudge informativo via systemMessage.
|
|
25
|
-
* - Violaciones críticas (secretos detectados) → emite mensaje fuerte
|
|
26
|
-
* pero NO bloquea (siguiendo el contrato de hooks PostToolUse: ya pasó).
|
|
27
|
-
* El bloqueo de secretos en escritura corresponde a `escaneo-secretos`
|
|
28
|
-
* (PreToolUse), este hook es la red de defensa secundaria.
|
|
29
|
-
*
|
|
30
|
-
* Opt-out: SWL_VALIDAR_MEMORIA=0 desactiva el hook.
|
|
31
|
-
*
|
|
32
|
-
* El hook nunca bloquea el flujo (siempre exit 0). Los errores internos
|
|
33
|
-
* se ignoran silenciosamente — la regla "PostToolUse no rompe nada".
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
const fs = require('fs');
|
|
37
|
-
const path = require('path');
|
|
38
|
-
const { spawnSync } = require('child_process');
|
|
39
|
-
|
|
40
|
-
// ---------------------------------------------------------------------------
|
|
41
|
-
// Configuración
|
|
42
|
-
// ---------------------------------------------------------------------------
|
|
43
|
-
|
|
44
|
-
/** Archivos cuya modificación dispara la validación. Paths relativos al CWD. */
|
|
45
|
-
const ARCHIVOS_MEMORIA = [
|
|
46
|
-
'.planning/APRENDIZAJES.md',
|
|
47
|
-
'instintos/perfil-usuario.yaml',
|
|
48
|
-
'instintos/proyecto.yaml',
|
|
49
|
-
'instintos/global.yaml',
|
|
50
|
-
];
|
|
51
|
-
|
|
52
|
-
/** Path del validador. */
|
|
53
|
-
const VALIDADOR = path.join(process.cwd(), 'scripts', 'validar-memoria.js');
|
|
54
|
-
|
|
55
|
-
/** Timeout para el validador (ms). Si tarda más, se aborta sin bloquear. */
|
|
56
|
-
const TIMEOUT_MS = 5000;
|
|
57
|
-
|
|
58
|
-
// ---------------------------------------------------------------------------
|
|
59
|
-
// Utilidades
|
|
60
|
-
// ---------------------------------------------------------------------------
|
|
61
|
-
|
|
62
|
-
/** Normaliza un path a forma POSIX para comparar contra ARCHIVOS_MEMORIA. */
|
|
63
|
-
function normalizar(p) {
|
|
64
|
-
if (!p) return '';
|
|
65
|
-
return String(p).replace(/\\/g, '/').replace(/^\.\//, '');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** ¿El path tocado coincide con algún archivo de memoria? */
|
|
69
|
-
function esArchivoMemoria(filePath) {
|
|
70
|
-
const norm = normalizar(filePath);
|
|
71
|
-
return ARCHIVOS_MEMORIA.some(target => norm.endsWith(target));
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Extrae el(los) path(s) afectado(s) por la herramienta del payload del hook.
|
|
76
|
-
* @param {object} data - Payload del hook (tool_input).
|
|
77
|
-
* @returns {string[]} Lista de paths afectados.
|
|
78
|
-
*/
|
|
79
|
-
function extraerPaths(data) {
|
|
80
|
-
const ti = data.tool_input || {};
|
|
81
|
-
const paths = [];
|
|
82
|
-
if (ti.file_path) paths.push(ti.file_path);
|
|
83
|
-
if (ti.notebook_path) paths.push(ti.notebook_path);
|
|
84
|
-
// MultiEdit: file_path único + edits[]; ya cubierto arriba.
|
|
85
|
-
return paths;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Ejecuta el validador y devuelve un resumen normalizado.
|
|
90
|
-
* Schema esperado del validador (--json):
|
|
91
|
-
* {
|
|
92
|
-
* totalNoCriticas: N,
|
|
93
|
-
* totalCriticas: N,
|
|
94
|
-
* issues: {
|
|
95
|
-
* usuario_en_aprendizajes: [...],
|
|
96
|
-
* anti_tecnico_en_perfil: [...],
|
|
97
|
-
* duplicados_cross_canal: [...],
|
|
98
|
-
* secretos: [...], // críticas
|
|
99
|
-
* pii: [...], // críticas
|
|
100
|
-
* }
|
|
101
|
-
* }
|
|
102
|
-
*
|
|
103
|
-
* Si el validador falla o no existe, devuelve null (silencio).
|
|
104
|
-
*/
|
|
105
|
-
function ejecutarValidador() {
|
|
106
|
-
if (!fs.existsSync(VALIDADOR)) return null;
|
|
107
|
-
try {
|
|
108
|
-
const r = spawnSync(process.execPath, [VALIDADOR, '--json'], {
|
|
109
|
-
cwd: process.cwd(),
|
|
110
|
-
encoding: 'utf8',
|
|
111
|
-
timeout: TIMEOUT_MS,
|
|
112
|
-
windowsHide: true,
|
|
113
|
-
});
|
|
114
|
-
if (r.status === null) return null;
|
|
115
|
-
if (!r.stdout) return null;
|
|
116
|
-
const parsed = JSON.parse(r.stdout);
|
|
117
|
-
const issues = parsed.issues || {};
|
|
118
|
-
return {
|
|
119
|
-
totalNoCriticas: Number.isInteger(parsed.totalNoCriticas) ? parsed.totalNoCriticas : 0,
|
|
120
|
-
totalCriticas: Number.isInteger(parsed.totalCriticas) ? parsed.totalCriticas : 0,
|
|
121
|
-
criticas: [
|
|
122
|
-
...(issues.secretos || []).map(x => ({ tipo: 'secreto', detalle: x })),
|
|
123
|
-
...(issues.pii || []).map(x => ({ tipo: 'pii', detalle: x })),
|
|
124
|
-
],
|
|
125
|
-
menores: [
|
|
126
|
-
...(issues.usuario_en_aprendizajes || []).map(x => ({ tipo: 'canal: usuario en aprendizajes', detalle: x })),
|
|
127
|
-
...(issues.anti_tecnico_en_perfil || []).map(x => ({ tipo: 'canal: anti-patrón técnico en perfil', detalle: x })),
|
|
128
|
-
...(issues.duplicados_cross_canal || []).map(x => ({ tipo: 'duplicación cross-canal', detalle: x })),
|
|
129
|
-
],
|
|
130
|
-
};
|
|
131
|
-
} catch (_) {
|
|
132
|
-
return null;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/** Trunca y limpia un detalle para mostrarlo en una línea. */
|
|
137
|
-
function describir(item) {
|
|
138
|
-
if (typeof item === 'string') return item.slice(0, 120);
|
|
139
|
-
if (item && typeof item === 'object') {
|
|
140
|
-
const partes = [];
|
|
141
|
-
if (item.titulo) partes.push(item.titulo);
|
|
142
|
-
if (item.canal) partes.push(`canal=${item.canal}`);
|
|
143
|
-
if (item.linea) partes.push(`L${item.linea}`);
|
|
144
|
-
if (item.muestra) partes.push(item.muestra);
|
|
145
|
-
if (partes.length === 0) return JSON.stringify(item).slice(0, 120);
|
|
146
|
-
return partes.join(' | ').slice(0, 120);
|
|
147
|
-
}
|
|
148
|
-
return String(item).slice(0, 120);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Construye el mensaje de nudge según el resultado.
|
|
153
|
-
*/
|
|
154
|
-
function construirMensaje(resultado, archivo) {
|
|
155
|
-
const { criticas, menores } = resultado;
|
|
156
|
-
if (criticas.length > 0) {
|
|
157
|
-
const lineas = [
|
|
158
|
-
'',
|
|
159
|
-
'╔══════════════════════════════════════════════════════╗',
|
|
160
|
-
'║ MEMORIA: VIOLACIÓN CRÍTICA DETECTADA ║',
|
|
161
|
-
'╚══════════════════════════════════════════════════════╝',
|
|
162
|
-
` Archivo modificado: ${archivo}`,
|
|
163
|
-
` Críticas: ${criticas.length}`,
|
|
164
|
-
];
|
|
165
|
-
for (const c of criticas.slice(0, 3)) {
|
|
166
|
-
lineas.push(` • ${c.tipo}: ${describir(c.detalle)}`);
|
|
167
|
-
}
|
|
168
|
-
lineas.push(' Acción recomendada: revertir el cambio o sanitizar antes de commit.');
|
|
169
|
-
lineas.push(' Ver: reglas/memoria-consolidada.md');
|
|
170
|
-
lineas.push('');
|
|
171
|
-
return lineas.join('\n');
|
|
172
|
-
}
|
|
173
|
-
if (menores.length > 0) {
|
|
174
|
-
const lineas = [
|
|
175
|
-
'',
|
|
176
|
-
'⚠ Memoria: posibles violaciones de canal',
|
|
177
|
-
` Archivo: ${archivo} | ${menores.length} hallazgo(s)`,
|
|
178
|
-
];
|
|
179
|
-
for (const v of menores.slice(0, 3)) {
|
|
180
|
-
lineas.push(` • ${v.tipo}: ${describir(v.detalle)}`);
|
|
181
|
-
}
|
|
182
|
-
if (menores.length > 3) {
|
|
183
|
-
lineas.push(` • (y ${menores.length - 3} más — ejecutar: node scripts/validar-memoria.js)`);
|
|
184
|
-
}
|
|
185
|
-
lineas.push(' Ver: reglas/memoria-consolidada.md (canal correcto por tipo de dato)');
|
|
186
|
-
lineas.push('');
|
|
187
|
-
return lineas.join('\n');
|
|
188
|
-
}
|
|
189
|
-
return '';
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// ---------------------------------------------------------------------------
|
|
193
|
-
// Entrypoint
|
|
194
|
-
// ---------------------------------------------------------------------------
|
|
195
|
-
|
|
196
|
-
let inputRaw = '';
|
|
197
|
-
process.stdin.on('data', chunk => { inputRaw += chunk; });
|
|
198
|
-
|
|
199
|
-
process.stdin.on('end', () => {
|
|
200
|
-
try {
|
|
201
|
-
if (process.env.SWL_VALIDAR_MEMORIA === '0') return;
|
|
202
|
-
|
|
203
|
-
const data = JSON.parse(inputRaw);
|
|
204
|
-
const paths = extraerPaths(data);
|
|
205
|
-
const archivoTocado = paths.find(esArchivoMemoria);
|
|
206
|
-
if (!archivoTocado) return;
|
|
207
|
-
|
|
208
|
-
const resultado = ejecutarValidador();
|
|
209
|
-
if (!resultado) return;
|
|
210
|
-
|
|
211
|
-
const mensaje = construirMensaje(resultado, normalizar(archivoTocado));
|
|
212
|
-
if (mensaje) {
|
|
213
|
-
process.stdout.write(JSON.stringify({ systemMessage: mensaje }));
|
|
214
|
-
}
|
|
215
|
-
} catch (_) {
|
|
216
|
-
// PostToolUse no bloquea por errores internos.
|
|
217
|
-
}
|
|
218
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hook: validar-memoria-hook.js
|
|
6
|
+
* Tipo: PostToolUse (matcher: Write|Edit|MultiEdit)
|
|
7
|
+
*
|
|
8
|
+
* Cierra el gap señalado en `reglas/memoria-consolidada.md`: el script
|
|
9
|
+
* `scripts/validar-memoria.js` solo se ejecuta manualmente vía CLI, así
|
|
10
|
+
* que la contaminación entre canales (perfil-usuario, APRENDIZAJES,
|
|
11
|
+
* instintos) sucedía sin que nadie se enterara hasta que un humano
|
|
12
|
+
* corría el validador.
|
|
13
|
+
*
|
|
14
|
+
* Este hook dispara `validar-memoria.js` en modo JSON cada vez que se
|
|
15
|
+
* escribe un archivo de memoria sensible:
|
|
16
|
+
* - `.planning/APRENDIZAJES.md`
|
|
17
|
+
* - `instintos/perfil-usuario.yaml`
|
|
18
|
+
* - `instintos/proyecto.yaml`
|
|
19
|
+
* - `instintos/global.yaml`
|
|
20
|
+
*
|
|
21
|
+
* Comportamiento:
|
|
22
|
+
* - Sin violaciones → silencioso (exit 0).
|
|
23
|
+
* - Violaciones menores (canal incorrecto, duplicación cross-canal) →
|
|
24
|
+
* emite nudge informativo via systemMessage.
|
|
25
|
+
* - Violaciones críticas (secretos detectados) → emite mensaje fuerte
|
|
26
|
+
* pero NO bloquea (siguiendo el contrato de hooks PostToolUse: ya pasó).
|
|
27
|
+
* El bloqueo de secretos en escritura corresponde a `escaneo-secretos`
|
|
28
|
+
* (PreToolUse), este hook es la red de defensa secundaria.
|
|
29
|
+
*
|
|
30
|
+
* Opt-out: SWL_VALIDAR_MEMORIA=0 desactiva el hook.
|
|
31
|
+
*
|
|
32
|
+
* El hook nunca bloquea el flujo (siempre exit 0). Los errores internos
|
|
33
|
+
* se ignoran silenciosamente — la regla "PostToolUse no rompe nada".
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
const fs = require('fs');
|
|
37
|
+
const path = require('path');
|
|
38
|
+
const { spawnSync } = require('child_process');
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Configuración
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
/** Archivos cuya modificación dispara la validación. Paths relativos al CWD. */
|
|
45
|
+
const ARCHIVOS_MEMORIA = [
|
|
46
|
+
'.planning/APRENDIZAJES.md',
|
|
47
|
+
'instintos/perfil-usuario.yaml',
|
|
48
|
+
'instintos/proyecto.yaml',
|
|
49
|
+
'instintos/global.yaml',
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
/** Path del validador. */
|
|
53
|
+
const VALIDADOR = path.join(process.cwd(), 'scripts', 'validar-memoria.js');
|
|
54
|
+
|
|
55
|
+
/** Timeout para el validador (ms). Si tarda más, se aborta sin bloquear. */
|
|
56
|
+
const TIMEOUT_MS = 5000;
|
|
57
|
+
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Utilidades
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
/** Normaliza un path a forma POSIX para comparar contra ARCHIVOS_MEMORIA. */
|
|
63
|
+
function normalizar(p) {
|
|
64
|
+
if (!p) return '';
|
|
65
|
+
return String(p).replace(/\\/g, '/').replace(/^\.\//, '');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** ¿El path tocado coincide con algún archivo de memoria? */
|
|
69
|
+
function esArchivoMemoria(filePath) {
|
|
70
|
+
const norm = normalizar(filePath);
|
|
71
|
+
return ARCHIVOS_MEMORIA.some(target => norm.endsWith(target));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Extrae el(los) path(s) afectado(s) por la herramienta del payload del hook.
|
|
76
|
+
* @param {object} data - Payload del hook (tool_input).
|
|
77
|
+
* @returns {string[]} Lista de paths afectados.
|
|
78
|
+
*/
|
|
79
|
+
function extraerPaths(data) {
|
|
80
|
+
const ti = data.tool_input || {};
|
|
81
|
+
const paths = [];
|
|
82
|
+
if (ti.file_path) paths.push(ti.file_path);
|
|
83
|
+
if (ti.notebook_path) paths.push(ti.notebook_path);
|
|
84
|
+
// MultiEdit: file_path único + edits[]; ya cubierto arriba.
|
|
85
|
+
return paths;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Ejecuta el validador y devuelve un resumen normalizado.
|
|
90
|
+
* Schema esperado del validador (--json):
|
|
91
|
+
* {
|
|
92
|
+
* totalNoCriticas: N,
|
|
93
|
+
* totalCriticas: N,
|
|
94
|
+
* issues: {
|
|
95
|
+
* usuario_en_aprendizajes: [...],
|
|
96
|
+
* anti_tecnico_en_perfil: [...],
|
|
97
|
+
* duplicados_cross_canal: [...],
|
|
98
|
+
* secretos: [...], // críticas
|
|
99
|
+
* pii: [...], // críticas
|
|
100
|
+
* }
|
|
101
|
+
* }
|
|
102
|
+
*
|
|
103
|
+
* Si el validador falla o no existe, devuelve null (silencio).
|
|
104
|
+
*/
|
|
105
|
+
function ejecutarValidador() {
|
|
106
|
+
if (!fs.existsSync(VALIDADOR)) return null;
|
|
107
|
+
try {
|
|
108
|
+
const r = spawnSync(process.execPath, [VALIDADOR, '--json'], {
|
|
109
|
+
cwd: process.cwd(),
|
|
110
|
+
encoding: 'utf8',
|
|
111
|
+
timeout: TIMEOUT_MS,
|
|
112
|
+
windowsHide: true,
|
|
113
|
+
});
|
|
114
|
+
if (r.status === null) return null;
|
|
115
|
+
if (!r.stdout) return null;
|
|
116
|
+
const parsed = JSON.parse(r.stdout);
|
|
117
|
+
const issues = parsed.issues || {};
|
|
118
|
+
return {
|
|
119
|
+
totalNoCriticas: Number.isInteger(parsed.totalNoCriticas) ? parsed.totalNoCriticas : 0,
|
|
120
|
+
totalCriticas: Number.isInteger(parsed.totalCriticas) ? parsed.totalCriticas : 0,
|
|
121
|
+
criticas: [
|
|
122
|
+
...(issues.secretos || []).map(x => ({ tipo: 'secreto', detalle: x })),
|
|
123
|
+
...(issues.pii || []).map(x => ({ tipo: 'pii', detalle: x })),
|
|
124
|
+
],
|
|
125
|
+
menores: [
|
|
126
|
+
...(issues.usuario_en_aprendizajes || []).map(x => ({ tipo: 'canal: usuario en aprendizajes', detalle: x })),
|
|
127
|
+
...(issues.anti_tecnico_en_perfil || []).map(x => ({ tipo: 'canal: anti-patrón técnico en perfil', detalle: x })),
|
|
128
|
+
...(issues.duplicados_cross_canal || []).map(x => ({ tipo: 'duplicación cross-canal', detalle: x })),
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
} catch (_) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Trunca y limpia un detalle para mostrarlo en una línea. */
|
|
137
|
+
function describir(item) {
|
|
138
|
+
if (typeof item === 'string') return item.slice(0, 120);
|
|
139
|
+
if (item && typeof item === 'object') {
|
|
140
|
+
const partes = [];
|
|
141
|
+
if (item.titulo) partes.push(item.titulo);
|
|
142
|
+
if (item.canal) partes.push(`canal=${item.canal}`);
|
|
143
|
+
if (item.linea) partes.push(`L${item.linea}`);
|
|
144
|
+
if (item.muestra) partes.push(item.muestra);
|
|
145
|
+
if (partes.length === 0) return JSON.stringify(item).slice(0, 120);
|
|
146
|
+
return partes.join(' | ').slice(0, 120);
|
|
147
|
+
}
|
|
148
|
+
return String(item).slice(0, 120);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Construye el mensaje de nudge según el resultado.
|
|
153
|
+
*/
|
|
154
|
+
function construirMensaje(resultado, archivo) {
|
|
155
|
+
const { criticas, menores } = resultado;
|
|
156
|
+
if (criticas.length > 0) {
|
|
157
|
+
const lineas = [
|
|
158
|
+
'',
|
|
159
|
+
'╔══════════════════════════════════════════════════════╗',
|
|
160
|
+
'║ MEMORIA: VIOLACIÓN CRÍTICA DETECTADA ║',
|
|
161
|
+
'╚══════════════════════════════════════════════════════╝',
|
|
162
|
+
` Archivo modificado: ${archivo}`,
|
|
163
|
+
` Críticas: ${criticas.length}`,
|
|
164
|
+
];
|
|
165
|
+
for (const c of criticas.slice(0, 3)) {
|
|
166
|
+
lineas.push(` • ${c.tipo}: ${describir(c.detalle)}`);
|
|
167
|
+
}
|
|
168
|
+
lineas.push(' Acción recomendada: revertir el cambio o sanitizar antes de commit.');
|
|
169
|
+
lineas.push(' Ver: reglas/memoria-consolidada.md');
|
|
170
|
+
lineas.push('');
|
|
171
|
+
return lineas.join('\n');
|
|
172
|
+
}
|
|
173
|
+
if (menores.length > 0) {
|
|
174
|
+
const lineas = [
|
|
175
|
+
'',
|
|
176
|
+
'⚠ Memoria: posibles violaciones de canal',
|
|
177
|
+
` Archivo: ${archivo} | ${menores.length} hallazgo(s)`,
|
|
178
|
+
];
|
|
179
|
+
for (const v of menores.slice(0, 3)) {
|
|
180
|
+
lineas.push(` • ${v.tipo}: ${describir(v.detalle)}`);
|
|
181
|
+
}
|
|
182
|
+
if (menores.length > 3) {
|
|
183
|
+
lineas.push(` • (y ${menores.length - 3} más — ejecutar: node scripts/validar-memoria.js)`);
|
|
184
|
+
}
|
|
185
|
+
lineas.push(' Ver: reglas/memoria-consolidada.md (canal correcto por tipo de dato)');
|
|
186
|
+
lineas.push('');
|
|
187
|
+
return lineas.join('\n');
|
|
188
|
+
}
|
|
189
|
+
return '';
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// Entrypoint
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
|
|
196
|
+
let inputRaw = '';
|
|
197
|
+
process.stdin.on('data', chunk => { inputRaw += chunk; });
|
|
198
|
+
|
|
199
|
+
process.stdin.on('end', () => {
|
|
200
|
+
try {
|
|
201
|
+
if (process.env.SWL_VALIDAR_MEMORIA === '0') return;
|
|
202
|
+
|
|
203
|
+
const data = JSON.parse(inputRaw);
|
|
204
|
+
const paths = extraerPaths(data);
|
|
205
|
+
const archivoTocado = paths.find(esArchivoMemoria);
|
|
206
|
+
if (!archivoTocado) return;
|
|
207
|
+
|
|
208
|
+
const resultado = ejecutarValidador();
|
|
209
|
+
if (!resultado) return;
|
|
210
|
+
|
|
211
|
+
const mensaje = construirMensaje(resultado, normalizar(archivoTocado));
|
|
212
|
+
if (mensaje) {
|
|
213
|
+
process.stdout.write(JSON.stringify({ systemMessage: mensaje }));
|
|
214
|
+
}
|
|
215
|
+
} catch (_) {
|
|
216
|
+
// PostToolUse no bloquea por errores internos.
|
|
217
|
+
}
|
|
218
|
+
});
|
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
# instintos/prompt-appendices.yaml
|
|
2
|
-
#
|
|
3
|
-
# Catálogo de apéndices reusables para construcción dinámica de prompts/
|
|
4
|
-
# instrucciones via `scripts/lib/prompt-builder.js`. Comandos `/swl:*` y
|
|
5
|
-
# agentes pueden referenciar estos apéndices por nombre al construir mensajes
|
|
6
|
-
# para subagentes o para output al usuario.
|
|
7
|
-
#
|
|
8
|
-
# Patrón adaptado de claudian (Yishen Tu, MIT) — buildSystemPrompt() con
|
|
9
|
-
# apéndices declarativos.
|
|
10
|
-
#
|
|
11
|
-
# Schema:
|
|
12
|
-
# <nombre_apendice>: |
|
|
13
|
-
# Texto del apéndice. Puede contener placeholders {clave} que se
|
|
14
|
-
# interpolan con el contexto al construir el prompt.
|
|
15
|
-
#
|
|
16
|
-
# Soporta operadores YAML `>` (folded — colapsa newlines a espacios) y
|
|
17
|
-
# `|` (literal — preserva newlines). Sin anidamiento profundo.
|
|
18
|
-
#
|
|
19
|
-
# Uso desde código:
|
|
20
|
-
# const pb = require('./scripts/lib/prompt-builder');
|
|
21
|
-
# const ap = pb.cargarApendices();
|
|
22
|
-
# const msg = pb.construirPrompt({
|
|
23
|
-
# base: 'Implementa la fase {fase}.',
|
|
24
|
-
# contexto: { fase: '03-validacion' },
|
|
25
|
-
# appendices: ['estilo_respuesta_breve', 'restricciones_dominio'],
|
|
26
|
-
# apendicesDisponibles: ap,
|
|
27
|
-
# });
|
|
28
|
-
|
|
29
|
-
estilo_respuesta_breve: >
|
|
30
|
-
Responde directo y conciso. Sin preámbulos de cortesía
|
|
31
|
-
("Por supuesto", "Claro"), sin cierres genéricos
|
|
32
|
-
("Espero que ayude"). Resultado primero, contexto solo
|
|
33
|
-
si es no-obvio. Máximo 100 palabras salvo que la tarea
|
|
34
|
-
requiera más detalle.
|
|
35
|
-
|
|
36
|
-
estilo_respuesta_detallado: >
|
|
37
|
-
Responde con detalle técnico. Incluye contexto, ejemplos
|
|
38
|
-
cuando ayuden a clarificar, y trade-offs si hay decisiones
|
|
39
|
-
no obvias. Estructura con encabezados o tablas si el
|
|
40
|
-
contenido lo justifica.
|
|
41
|
-
|
|
42
|
-
restricciones_dominio: |
|
|
43
|
-
Restricciones de dominio del proyecto SWL-SES:
|
|
44
|
-
- Stack: Node.js zero-dependencies en hooks/lib/ y scripts/lib/
|
|
45
|
-
- Idioma: 100% español de México
|
|
46
|
-
- Filosofía: integración ligera y reutilización, no reescritura
|
|
47
|
-
- Antes de invocar herramientas externas opt-in, verificar disponibilidad
|
|
48
|
-
con `command -v` (Bash) o `execSync('<bin> --version', { stdio: 'ignore' })`
|
|
49
|
-
(Node) y caer al flujo nativo si no están
|
|
50
|
-
|
|
51
|
-
contexto_proyecto: >
|
|
52
|
-
Proyecto: {proyecto}. Fase: {fase}. Working directory: {cwd}.
|
|
53
|
-
|
|
54
|
-
referencia_planning: >
|
|
55
|
-
Antes de modificar archivos, lee `.planning/CONTEXTO.md`,
|
|
56
|
-
`.planning/ESTADO.md` y `CLAUDE.md` del proyecto. No editar
|
|
57
|
-
código que no se ha leído primero.
|
|
1
|
+
# instintos/prompt-appendices.yaml
|
|
2
|
+
#
|
|
3
|
+
# Catálogo de apéndices reusables para construcción dinámica de prompts/
|
|
4
|
+
# instrucciones via `scripts/lib/prompt-builder.js`. Comandos `/swl:*` y
|
|
5
|
+
# agentes pueden referenciar estos apéndices por nombre al construir mensajes
|
|
6
|
+
# para subagentes o para output al usuario.
|
|
7
|
+
#
|
|
8
|
+
# Patrón adaptado de claudian (Yishen Tu, MIT) — buildSystemPrompt() con
|
|
9
|
+
# apéndices declarativos.
|
|
10
|
+
#
|
|
11
|
+
# Schema:
|
|
12
|
+
# <nombre_apendice>: |
|
|
13
|
+
# Texto del apéndice. Puede contener placeholders {clave} que se
|
|
14
|
+
# interpolan con el contexto al construir el prompt.
|
|
15
|
+
#
|
|
16
|
+
# Soporta operadores YAML `>` (folded — colapsa newlines a espacios) y
|
|
17
|
+
# `|` (literal — preserva newlines). Sin anidamiento profundo.
|
|
18
|
+
#
|
|
19
|
+
# Uso desde código:
|
|
20
|
+
# const pb = require('./scripts/lib/prompt-builder');
|
|
21
|
+
# const ap = pb.cargarApendices();
|
|
22
|
+
# const msg = pb.construirPrompt({
|
|
23
|
+
# base: 'Implementa la fase {fase}.',
|
|
24
|
+
# contexto: { fase: '03-validacion' },
|
|
25
|
+
# appendices: ['estilo_respuesta_breve', 'restricciones_dominio'],
|
|
26
|
+
# apendicesDisponibles: ap,
|
|
27
|
+
# });
|
|
28
|
+
|
|
29
|
+
estilo_respuesta_breve: >
|
|
30
|
+
Responde directo y conciso. Sin preámbulos de cortesía
|
|
31
|
+
("Por supuesto", "Claro"), sin cierres genéricos
|
|
32
|
+
("Espero que ayude"). Resultado primero, contexto solo
|
|
33
|
+
si es no-obvio. Máximo 100 palabras salvo que la tarea
|
|
34
|
+
requiera más detalle.
|
|
35
|
+
|
|
36
|
+
estilo_respuesta_detallado: >
|
|
37
|
+
Responde con detalle técnico. Incluye contexto, ejemplos
|
|
38
|
+
cuando ayuden a clarificar, y trade-offs si hay decisiones
|
|
39
|
+
no obvias. Estructura con encabezados o tablas si el
|
|
40
|
+
contenido lo justifica.
|
|
41
|
+
|
|
42
|
+
restricciones_dominio: |
|
|
43
|
+
Restricciones de dominio del proyecto SWL-SES:
|
|
44
|
+
- Stack: Node.js zero-dependencies en hooks/lib/ y scripts/lib/
|
|
45
|
+
- Idioma: 100% español de México
|
|
46
|
+
- Filosofía: integración ligera y reutilización, no reescritura
|
|
47
|
+
- Antes de invocar herramientas externas opt-in, verificar disponibilidad
|
|
48
|
+
con `command -v` (Bash) o `execSync('<bin> --version', { stdio: 'ignore' })`
|
|
49
|
+
(Node) y caer al flujo nativo si no están
|
|
50
|
+
|
|
51
|
+
contexto_proyecto: >
|
|
52
|
+
Proyecto: {proyecto}. Fase: {fase}. Working directory: {cwd}.
|
|
53
|
+
|
|
54
|
+
referencia_planning: >
|
|
55
|
+
Antes de modificar archivos, lee `.planning/CONTEXTO.md`,
|
|
56
|
+
`.planning/ESTADO.md` y `CLAUDE.md` del proyecto. No editar
|
|
57
|
+
código que no se ha leído primero.
|