swl-ses 3.3.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.
Files changed (177) hide show
  1. package/CLAUDE.md +425 -0
  2. package/_userland/agentes/.gitkeep +0 -0
  3. package/_userland/habilidades/.gitkeep +0 -0
  4. package/agentes/accesibilidad-wcag-swl.md +683 -0
  5. package/agentes/arquitecto-swl.md +210 -0
  6. package/agentes/auto-evolucion-swl.md +408 -0
  7. package/agentes/backend-api-swl.md +442 -0
  8. package/agentes/backend-node-swl.md +439 -0
  9. package/agentes/backend-python-swl.md +469 -0
  10. package/agentes/backend-workers-swl.md +444 -0
  11. package/agentes/cloud-infra-swl.md +466 -0
  12. package/agentes/consolidador-swl.md +487 -0
  13. package/agentes/datos-swl.md +568 -0
  14. package/agentes/depurador-swl.md +301 -0
  15. package/agentes/devops-ci-swl.md +352 -0
  16. package/agentes/disenador-ui-swl.md +546 -0
  17. package/agentes/documentador-swl.md +323 -0
  18. package/agentes/frontend-angular-swl.md +603 -0
  19. package/agentes/frontend-css-swl.md +700 -0
  20. package/agentes/frontend-react-swl.md +672 -0
  21. package/agentes/frontend-swl.md +483 -0
  22. package/agentes/frontend-tailwind-swl.md +808 -0
  23. package/agentes/implementador-swl.md +235 -0
  24. package/agentes/investigador-swl.md +274 -0
  25. package/agentes/investigador-ux-swl.md +482 -0
  26. package/agentes/migrador-swl.md +389 -0
  27. package/agentes/mobile-android-swl.md +473 -0
  28. package/agentes/mobile-cross-swl.md +501 -0
  29. package/agentes/mobile-ios-swl.md +464 -0
  30. package/agentes/notificador-swl.md +886 -0
  31. package/agentes/observabilidad-swl.md +408 -0
  32. package/agentes/orquestador-swl.md +490 -0
  33. package/agentes/planificador-swl.md +222 -0
  34. package/agentes/producto-prd-swl.md +565 -0
  35. package/agentes/release-manager-swl.md +545 -0
  36. package/agentes/rendimiento-swl.md +691 -0
  37. package/agentes/revisor-codigo-swl.md +254 -0
  38. package/agentes/revisor-seguridad-swl.md +316 -0
  39. package/agentes/tdd-qa-swl.md +323 -0
  40. package/agentes/ux-disenador-swl.md +498 -0
  41. package/bin/swl-ses.js +119 -0
  42. package/comandos/swl/actualizar.md +117 -0
  43. package/comandos/swl/aprender.md +348 -0
  44. package/comandos/swl/auditar-deps.md +390 -0
  45. package/comandos/swl/autoresearch.md +346 -0
  46. package/comandos/swl/checkpoint.md +296 -0
  47. package/comandos/swl/compactar.md +283 -0
  48. package/comandos/swl/crear-skill.md +609 -0
  49. package/comandos/swl/discutir-fase.md +230 -0
  50. package/comandos/swl/ejecutar-fase.md +302 -0
  51. package/comandos/swl/evolucionar.md +377 -0
  52. package/comandos/swl/instalar.md +220 -0
  53. package/comandos/swl/mapear-codebase.md +205 -0
  54. package/comandos/swl/nuevo-proyecto.md +154 -0
  55. package/comandos/swl/planear-fase.md +221 -0
  56. package/comandos/swl/release.md +405 -0
  57. package/comandos/swl/salud.md +382 -0
  58. package/comandos/swl/verificar.md +292 -0
  59. package/habilidades/accesibilidad-a11y/SKILL.md +584 -0
  60. package/habilidades/angular-avanzado/SKILL.md +491 -0
  61. package/habilidades/angular-moderno/SKILL.md +326 -0
  62. package/habilidades/api-rest-diseno/SKILL.md +302 -0
  63. package/habilidades/api-rest-diseno/recursos/openapi-template.yaml +506 -0
  64. package/habilidades/aprendizaje-continuo/SKILL.md +369 -0
  65. package/habilidades/async-python/SKILL.md +474 -0
  66. package/habilidades/auth-patrones/SKILL.md +488 -0
  67. package/habilidades/auto-evolucion-protocolo/SKILL.md +376 -0
  68. package/habilidades/autoresearch/SKILL.md +248 -0
  69. package/habilidades/autoresearch/recursos/checklist-template.md +191 -0
  70. package/habilidades/autoresearch/scripts/calcular-score.js +88 -0
  71. package/habilidades/checklist-calidad/SKILL.md +247 -0
  72. package/habilidades/checklist-calidad/recursos/quality-report-template.md +148 -0
  73. package/habilidades/checklist-seguridad/SKILL.md +224 -0
  74. package/habilidades/checkpoints-verificacion/SKILL.md +309 -0
  75. package/habilidades/checkpoints-verificacion/recursos/checkpoint-templates.md +360 -0
  76. package/habilidades/ci-cd-pipelines/SKILL.md +583 -0
  77. package/habilidades/ci-cd-pipelines/recursos/github-actions-template.yaml +403 -0
  78. package/habilidades/cloud-aws/SKILL.md +497 -0
  79. package/habilidades/compactacion-contexto/SKILL.md +201 -0
  80. package/habilidades/contenedores-docker/SKILL.md +453 -0
  81. package/habilidades/contenedores-docker/recursos/dockerfile-template.dockerfile +160 -0
  82. package/habilidades/css-moderno/SKILL.md +463 -0
  83. package/habilidades/datos-etl/SKILL.md +486 -0
  84. package/habilidades/dependencias-auditoria/SKILL.md +293 -0
  85. package/habilidades/deprecacion-migracion/SKILL.md +485 -0
  86. package/habilidades/design-tokens/SKILL.md +519 -0
  87. package/habilidades/discutir-fase/SKILL.md +167 -0
  88. package/habilidades/diseno-responsivo/SKILL.md +326 -0
  89. package/habilidades/django-experto/SKILL.md +395 -0
  90. package/habilidades/doc-sync/SKILL.md +259 -0
  91. package/habilidades/ejecutar-fase/SKILL.md +199 -0
  92. package/habilidades/estructura-proyecto-claude/SKILL.md +459 -0
  93. package/habilidades/estructura-proyecto-claude/recursos/claude-md-template.md +261 -0
  94. package/habilidades/estructura-proyecto-claude/recursos/frontmatter-y-hooks-referencia.md +213 -0
  95. package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +77 -0
  96. package/habilidades/estructura-proyecto-claude/recursos/variantes-por-stack.md +177 -0
  97. package/habilidades/event-driven/SKILL.md +580 -0
  98. package/habilidades/extractor-de-aprendizajes/SKILL.md +234 -0
  99. package/habilidades/fastapi-experto/SKILL.md +368 -0
  100. package/habilidades/frontend-avanzado/SKILL.md +555 -0
  101. package/habilidades/git-worktrees-paralelo/SKILL.md +246 -0
  102. package/habilidades/iam-secretos/SKILL.md +511 -0
  103. package/habilidades/instalar-sistema/SKILL.md +140 -0
  104. package/habilidades/kubernetes-orquestacion/SKILL.md +549 -0
  105. package/habilidades/manejo-errores/SKILL.md +512 -0
  106. package/habilidades/mapear-codebase/SKILL.md +199 -0
  107. package/habilidades/microservicios/SKILL.md +473 -0
  108. package/habilidades/mobile-flutter/SKILL.md +566 -0
  109. package/habilidades/mobile-react-native/SKILL.md +493 -0
  110. package/habilidades/monitoring-alertas/SKILL.md +447 -0
  111. package/habilidades/node-experto/SKILL.md +521 -0
  112. package/habilidades/notificaciones-multicanal/SKILL.md +448 -0
  113. package/habilidades/notificaciones-multicanal/recursos/config-template.json +115 -0
  114. package/habilidades/nuevo-proyecto/SKILL.md +183 -0
  115. package/habilidades/patrones-python/SKILL.md +381 -0
  116. package/habilidades/performance-baseline/SKILL.md +243 -0
  117. package/habilidades/planear-fase/SKILL.md +184 -0
  118. package/habilidades/postgresql-experto/SKILL.md +379 -0
  119. package/habilidades/react-experto/SKILL.md +434 -0
  120. package/habilidades/react-optimizacion/SKILL.md +328 -0
  121. package/habilidades/release-semver/SKILL.md +226 -0
  122. package/habilidades/release-semver/scripts/generar-changelog.sh +238 -0
  123. package/habilidades/sql-optimizacion/SKILL.md +314 -0
  124. package/habilidades/tailwind-experto/SKILL.md +412 -0
  125. package/habilidades/tdd-workflow/SKILL.md +267 -0
  126. package/habilidades/testing-python/SKILL.md +350 -0
  127. package/habilidades/threat-model-lite/SKILL.md +218 -0
  128. package/habilidades/typescript-avanzado/SKILL.md +454 -0
  129. package/habilidades/ux-diseno/SKILL.md +488 -0
  130. package/habilidades/validacion-ci-sistema/SKILL.md +543 -0
  131. package/habilidades/validacion-ci-sistema/scripts/validar-sistema.sh +286 -0
  132. package/habilidades/verificar-trabajo/SKILL.md +208 -0
  133. package/habilidades/wireframes-flujos/SKILL.md +396 -0
  134. package/habilidades/workflow-claude-code/SKILL.md +359 -0
  135. package/hooks/calidad-pre-commit.js +578 -0
  136. package/hooks/escaneo-secretos.js +302 -0
  137. package/hooks/extraccion-aprendizajes.js +550 -0
  138. package/hooks/linea-estado.js +249 -0
  139. package/hooks/monitor-contexto.js +230 -0
  140. package/hooks/proteccion-rutas.js +249 -0
  141. package/manifiestos/hooks-config.json +41 -0
  142. package/manifiestos/modulos.json +318 -0
  143. package/manifiestos/perfiles.json +189 -0
  144. package/package.json +45 -0
  145. package/plantillas/PROJECT.md +122 -0
  146. package/plantillas/REQUIREMENTS.md +132 -0
  147. package/plantillas/ROADMAP.md +143 -0
  148. package/plantillas/STATE.md +109 -0
  149. package/plantillas/research/ARCHITECTURE.md +220 -0
  150. package/plantillas/research/FEATURES.md +175 -0
  151. package/plantillas/research/PITFALLS.md +299 -0
  152. package/plantillas/research/STACK.md +233 -0
  153. package/plantillas/research/SUMMARY.md +165 -0
  154. package/plugin.json +144 -0
  155. package/reglas/accesibilidad.md +269 -0
  156. package/reglas/api-diseno.md +400 -0
  157. package/reglas/arquitectura.md +183 -0
  158. package/reglas/cloud-infra.md +247 -0
  159. package/reglas/docs.md +245 -0
  160. package/reglas/estilo-codigo.md +179 -0
  161. package/reglas/git-workflow.md +186 -0
  162. package/reglas/performance.md +195 -0
  163. package/reglas/pruebas.md +159 -0
  164. package/reglas/seguridad.md +151 -0
  165. package/reglas/skills-estandar.md +473 -0
  166. package/scripts/actualizar.js +51 -0
  167. package/scripts/desinstalar.js +86 -0
  168. package/scripts/doctor.js +222 -0
  169. package/scripts/inicializar.js +89 -0
  170. package/scripts/instalador.js +333 -0
  171. package/scripts/lib/detectar-runtime.js +177 -0
  172. package/scripts/lib/estado.js +112 -0
  173. package/scripts/lib/hooks-settings.js +283 -0
  174. package/scripts/lib/manifiestos.js +138 -0
  175. package/scripts/lib/seguridad.js +160 -0
  176. package/scripts/publicar.js +209 -0
  177. package/scripts/validar.js +120 -0
@@ -0,0 +1,302 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Hook: escaneo-secretos.js
6
+ * Tipo: PreToolUse (aplica a: Write, Edit)
7
+ *
8
+ * Escanea el contenido que Claude Code está a punto de escribir en disco
9
+ * buscando patrones conocidos de secretos y credenciales sensibles:
10
+ *
11
+ * - API keys de Anthropic / OpenAI (sk-ant-*, sk-proj-*, sk-live-*, sk-test-*)
12
+ * - Tokens de AWS (AKIA*, ASIA*)
13
+ * - Tokens de GitHub (ghp_*, gho_*, ghu_*, ghs_*, ghr_*)
14
+ * - Tokens de GitLab (glpat-*)
15
+ * - Claves privadas PEM / SSH
16
+ * - Passwords hardcodeados en asignaciones de variable comunes
17
+ * - Tokens JWT (tres segmentos base64 separados por puntos)
18
+ * - Connection strings con credenciales embebidas (usuario:contraseña@host)
19
+ * - Variables de entorno sensibles hardcodeadas en código
20
+ *
21
+ * Resultado:
22
+ * - Secreto detectado → exit 2, JSON {result:"block", reason:"..."} en stdout
23
+ * - Sin secreto → exit 0, sin output
24
+ * - Error interno → exit 0 (nunca bloquear por fallo del hook)
25
+ *
26
+ * NOTA: Este hook es una primera línea de defensa heurística, no un escáner
27
+ * de seguridad exhaustivo. Complementar con herramientas como trufflehog,
28
+ * gitleaks o detect-secrets en el pipeline CI.
29
+ */
30
+
31
+ // ---------------------------------------------------------------------------
32
+ // Patrones de detección
33
+ // ---------------------------------------------------------------------------
34
+
35
+ /**
36
+ * Cada entrada describe un tipo de secreto con:
37
+ * - nombre: Etiqueta para el mensaje de error.
38
+ * - patron: Expresión regular para detectarlo.
39
+ * - ejemplos: Comentario ilustrativo (no se usa en código).
40
+ */
41
+ const PATRONES_SECRETOS = [
42
+ {
43
+ nombre: 'Anthropic API Key (sk-ant-*)',
44
+ // sk-ant-api03-xxxxxxxx...
45
+ patron: /\bsk-ant-[A-Za-z0-9\-_]{20,}\b/,
46
+ },
47
+ {
48
+ nombre: 'OpenAI API Key (sk-proj-* / sk-live-* / sk-test-* / sk-[a-z0-9]{48})',
49
+ // sk-proj-..., sk-live-..., sk-test-..., sk-<48 chars alfanum>
50
+ patron: /\bsk-(?:proj|live|test)-[A-Za-z0-9\-_]{20,}\b|\bsk-[A-Za-z0-9]{48,}\b/,
51
+ },
52
+ {
53
+ nombre: 'AWS Access Key ID (AKIA* / ASIA*)',
54
+ // AKIAIOSFODNN7EXAMPLE o ASIAXXX...
55
+ patron: /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/,
56
+ },
57
+ {
58
+ nombre: 'GitHub Personal Access Token (ghp_* / gho_* / ghu_* / ghs_* / ghr_*)',
59
+ patron: /\bgh[pousr]_[A-Za-z0-9]{36,}\b/,
60
+ },
61
+ {
62
+ nombre: 'GitLab Personal Access Token (glpat-*)',
63
+ patron: /\bglpat-[A-Za-z0-9\-_]{20,}\b/,
64
+ },
65
+ {
66
+ nombre: 'Clave privada PEM (RSA / EC / OpenSSH)',
67
+ // -----BEGIN RSA PRIVATE KEY----- o -----BEGIN OPENSSH PRIVATE KEY-----
68
+ patron: /-----BEGIN (?:RSA |EC |DSA |OPENSSH |)PRIVATE KEY-----/,
69
+ },
70
+ {
71
+ nombre: 'Password hardcodeado en variable',
72
+ // password = "valor", passwd='valor', pwd="valor" (no vacío, no placeholder)
73
+ // Se ignoran valores que claramente son placeholders: "xxx", "your_", "<", "ENV", os.environ
74
+ patron: /(?:password|passwd|pwd|secret|secret_key|api_secret)\s*[:=]\s*["'](?!(?:your_|<|xxx|ENV|os\.environ|\$\{|%\w+%))[^"'\s]{6,}["']/i,
75
+ },
76
+ {
77
+ nombre: 'Token JWT (tres segmentos base64url)',
78
+ // eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3IifQ.xxxx
79
+ // Solo se marca si el header decodifica como JSON con "alg" (heurística fuerte)
80
+ patron: /\beyJ[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\b/,
81
+ },
82
+ {
83
+ nombre: 'Connection string con credenciales (usuario:contraseña@host)',
84
+ // postgresql://user:pass@host, mysql://user:pass@host, mongodb://user:pass@host
85
+ patron: /(?:postgresql|mysql|mongodb|redis|amqp|mssql|jdbc):\/\/[^:@\s]+:[^@\s]+@[^\s"']+/i,
86
+ },
87
+ {
88
+ nombre: 'Stripe Secret Key (sk_live_* / sk_test_*)',
89
+ patron: /\bsk_(?:live|test)_[A-Za-z0-9]{24,}\b/,
90
+ },
91
+ {
92
+ nombre: 'Slack Bot / App Token (xoxb-* / xoxa-* / xoxe-*)',
93
+ patron: /\bxox[bpae]-[A-Za-z0-9\-]{20,}\b/,
94
+ },
95
+ {
96
+ nombre: 'Twilio Account SID / Auth Token',
97
+ // AC seguido de 32 hex chars
98
+ patron: /\bAC[0-9a-fA-F]{32}\b/,
99
+ },
100
+ {
101
+ nombre: 'Google API Key (AIzaSy*)',
102
+ patron: /\bAIzaSy[A-Za-z0-9\-_]{33}\b/,
103
+ },
104
+ {
105
+ nombre: 'Secreto en variable de entorno hardcodeado',
106
+ // SECRET_KEY="valor_largo_no_placeholder" fuera de archivos .env.example
107
+ patron: /(?:SECRET_KEY|AUTH_TOKEN|ACCESS_TOKEN|PRIVATE_KEY|CLIENT_SECRET)\s*=\s*["'][^"'\s]{10,}["']/,
108
+ },
109
+ ];
110
+
111
+ // ---------------------------------------------------------------------------
112
+ // Lógica de exclusión (falsos positivos comunes)
113
+ // ---------------------------------------------------------------------------
114
+
115
+ /**
116
+ * Patrones que indican que el contenido es un archivo de ejemplo, test o
117
+ * documentación donde los secretos son ficticios.
118
+ * Si el file_path o el contenido coincide con alguno, se omite el bloqueo.
119
+ */
120
+ const PATRONES_EXCLUSION_RUTA = [
121
+ /\.example$/i,
122
+ /\.sample$/i,
123
+ /\.test\.[jt]s$/i,
124
+ /\.spec\.[jt]s$/i,
125
+ /test[s]?[/\\]/i,
126
+ /__tests__[/\\]/i,
127
+ /fixtures?[/\\]/i,
128
+ /mocks?[/\\]/i,
129
+ /README/i,
130
+ /CHANGELOG/i,
131
+ /\.md$/i,
132
+ ];
133
+
134
+ /**
135
+ * Cadenas que indican un valor ficticio o placeholder dentro del contenido.
136
+ * Si la línea que contiene el patrón también contiene alguna de estas cadenas,
137
+ * se considera falso positivo.
138
+ */
139
+ const MARCADORES_PLACEHOLDER = [
140
+ 'YOUR_',
141
+ 'your_',
142
+ '<YOUR',
143
+ 'PLACEHOLDER',
144
+ 'placeholder',
145
+ 'example.com',
146
+ 'example_',
147
+ 'fake_',
148
+ 'dummy_',
149
+ 'test_token',
150
+ 'test_key',
151
+ 'xxxxxxxx',
152
+ '# noqa',
153
+ '// nosec',
154
+ 'pragma: allowlist secret',
155
+ ];
156
+
157
+ // ---------------------------------------------------------------------------
158
+ // Funciones auxiliares
159
+ // ---------------------------------------------------------------------------
160
+
161
+ /**
162
+ * Verifica si la ruta del archivo indica que el contenido es un ejemplo/test.
163
+ * @param {string} filePath
164
+ * @returns {boolean}
165
+ */
166
+ function esArchivoExcluido(filePath) {
167
+ return PATRONES_EXCLUSION_RUTA.some(p => p.test(filePath));
168
+ }
169
+
170
+ /**
171
+ * Verifica si la línea que contiene el match parece ser un placeholder.
172
+ * @param {string} linea
173
+ * @returns {boolean}
174
+ */
175
+ function esPlaceholder(linea) {
176
+ return MARCADORES_PLACEHOLDER.some(m => linea.includes(m));
177
+ }
178
+
179
+ /**
180
+ * Extrae el contenido textual a escanear desde el payload del hook.
181
+ * Para Write: campo "content".
182
+ * Para Edit: campos "new_string" y "content".
183
+ *
184
+ * @param {string} toolName - Nombre de la herramienta ("Write" | "Edit").
185
+ * @param {object} toolInput - Parámetros de la herramienta.
186
+ * @returns {string} Contenido concatenado a escanear.
187
+ */
188
+ function extraerContenido(toolName, toolInput) {
189
+ const partes = [];
190
+
191
+ if (toolName === 'Write') {
192
+ if (typeof toolInput.content === 'string') partes.push(toolInput.content);
193
+ } else if (toolName === 'Edit') {
194
+ // Solo escanear lo que se está insertando, no el texto que se reemplaza
195
+ if (typeof toolInput.new_string === 'string') partes.push(toolInput.new_string);
196
+ // Algunos editores pasan "content" como campo alternativo
197
+ if (typeof toolInput.content === 'string') partes.push(toolInput.content);
198
+ }
199
+
200
+ return partes.join('\n');
201
+ }
202
+
203
+ /**
204
+ * Escanea el contenido contra todos los patrones de secretos.
205
+ * Retorna la primera coincidencia encontrada, o null si el contenido es seguro.
206
+ *
207
+ * @param {string} contenido
208
+ * @param {string} filePath - Ruta del archivo destino (para contexto en mensajes).
209
+ * @returns {{ nombrePatron: string, linea: string, numeroLinea: number }|null}
210
+ */
211
+ function escanear(contenido, filePath) {
212
+ if (!contenido) return null;
213
+
214
+ const lineas = contenido.split('\n');
215
+
216
+ for (let i = 0; i < lineas.length; i++) {
217
+ const linea = lineas[i];
218
+
219
+ // Saltar líneas comentadas con marcadores de exclusión explícitos
220
+ if (esPlaceholder(linea)) continue;
221
+
222
+ for (const { nombre, patron } of PATRONES_SECRETOS) {
223
+ if (patron.test(linea)) {
224
+ return {
225
+ nombrePatron: nombre,
226
+ // Ofuscar el valor real en el mensaje de error para no exponerlo en logs
227
+ linea: linea.replace(patron, (match) => {
228
+ const visible = match.slice(0, 8);
229
+ return `${visible}${'*'.repeat(Math.max(0, match.length - 8))}`;
230
+ }),
231
+ numeroLinea: i + 1,
232
+ filePath,
233
+ };
234
+ }
235
+ }
236
+ }
237
+
238
+ return null;
239
+ }
240
+
241
+ // ---------------------------------------------------------------------------
242
+ // Entrypoint principal
243
+ // ---------------------------------------------------------------------------
244
+
245
+ let inputRaw = '';
246
+
247
+ process.stdin.on('data', chunk => {
248
+ inputRaw += chunk;
249
+ });
250
+
251
+ process.stdin.on('end', () => {
252
+ try {
253
+ const data = JSON.parse(inputRaw);
254
+
255
+ const toolName = String(data.tool_name || data.tool?.name || '');
256
+ const toolInput = data.tool_input || data.tool?.input || {};
257
+ const filePath = String(toolInput.file_path || toolInput.path || '');
258
+
259
+ // Este hook solo aplica a Write y Edit
260
+ if (!['Write', 'Edit'].includes(toolName)) {
261
+ process.exit(0);
262
+ }
263
+
264
+ // Si la ruta indica archivo de ejemplo/test, saltar escaneo
265
+ if (filePath && esArchivoExcluido(filePath)) {
266
+ process.exit(0);
267
+ }
268
+
269
+ // Extraer y escanear el contenido
270
+ const contenido = extraerContenido(toolName, toolInput);
271
+ const coincidencia = escanear(contenido, filePath);
272
+
273
+ if (coincidencia) {
274
+ // BLOQUEAR: emitir JSON de bloqueo en stdout
275
+ const razon = [
276
+ `Secreto detectado antes de escribir en disco.`,
277
+ ``,
278
+ `Tipo: ${coincidencia.nombrePatron}`,
279
+ `Archivo: ${coincidencia.filePath || '(desconocido)'}`,
280
+ `Línea: ${coincidencia.numeroLinea}`,
281
+ `Contexto: ${coincidencia.linea.trim().slice(0, 120)}`,
282
+ ``,
283
+ `Acciones recomendadas:`,
284
+ ` 1. Mover el secreto a una variable de entorno (.env, no commiteado).`,
285
+ ` 2. Usar un gestor de secretos (Vault, AWS Secrets Manager, etc.).`,
286
+ ` 3. Si es un valor ficticio, agregar un marcador como YOUR_TOKEN,`,
287
+ ` o una línea: # pragma: allowlist secret`,
288
+ ].join('\n');
289
+
290
+ process.stdout.write(JSON.stringify({ result: 'block', reason: razon }));
291
+ process.exit(2);
292
+ }
293
+
294
+ // Sin secretos detectados — permitir la escritura
295
+ process.exit(0);
296
+
297
+ } catch (err) {
298
+ // El hook nunca bloquea por errores internos propios
299
+ process.stderr.write(`[escaneo-secretos] Error interno: ${err.message}\n`);
300
+ process.exit(0);
301
+ }
302
+ });