@saulwade/swl-ses 1.3.8 → 1.4.1

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 (148) hide show
  1. package/CLAUDE.md +15 -6
  2. package/README.md +15 -14
  3. package/agentes/nemesis-auditor-swl.md +161 -0
  4. package/bin/swl-mcp-server.js +187 -187
  5. package/bin/swl-webhook-server.js +198 -0
  6. package/comandos/swl/.evolved.json +22 -22
  7. package/comandos/swl/adoptar-proyecto.md +21 -1
  8. package/comandos/swl/claudemd.md +14 -1
  9. package/comandos/swl/contribuir.md +233 -233
  10. package/comandos/swl/exportar-vault.md +108 -0
  11. package/comandos/swl/nemesis.md +122 -0
  12. package/comandos/swl/nuevo-proyecto.md +24 -2
  13. package/comandos/swl/salud.md +34 -0
  14. package/comandos/swl/verificar.md +45 -0
  15. package/gateway/adapters/base.js +109 -0
  16. package/gateway/adapters/discord.js +167 -0
  17. package/gateway/adapters/email.js +221 -0
  18. package/gateway/adapters/slack.js +192 -0
  19. package/gateway/adapters/telegram.js +183 -0
  20. package/gateway/adapters/webhook.js +113 -0
  21. package/gateway/adapters/whatsapp.js +214 -0
  22. package/gateway/agent-executor.js +322 -0
  23. package/gateway/command-relay.js +271 -0
  24. package/gateway/cron/jobs.js +263 -0
  25. package/gateway/cron/scheduler.js +322 -0
  26. package/gateway/cron/store.js +335 -0
  27. package/gateway/index.js +320 -0
  28. package/gateway/lib/event-channel.js +191 -0
  29. package/gateway/session.js +131 -0
  30. package/gateway/webhook-server.js +324 -0
  31. package/habilidades/backend-production-resilience/SKILL.md +288 -288
  32. package/habilidades/benchmark-memoria/SKILL.md +186 -186
  33. package/habilidades/build-errors-nextjs/SKILL.md +55 -1
  34. package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
  35. package/habilidades/doubt-driven-review/SKILL.md +171 -171
  36. package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
  37. package/habilidades/eval-framework/SKILL.md +212 -212
  38. package/habilidades/extractor-de-aprendizajes/SKILL.md +20 -10
  39. package/habilidades/feynman-auditor-swl/SKILL.md +123 -0
  40. package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -0
  41. package/habilidades/harness-claude-code/SKILL.md +299 -299
  42. package/habilidades/infra-github-actions/SKILL.md +166 -166
  43. package/habilidades/legacy-code-rescue/SKILL.md +267 -267
  44. package/habilidades/manejo-errores/.evolved.json +8 -8
  45. package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
  46. package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -163
  47. package/habilidades/nextjs-testing/SKILL.md +89 -5
  48. package/habilidades/node-experto/SKILL.md +37 -1
  49. package/habilidades/patrones-python/SKILL.md +229 -229
  50. package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
  51. package/habilidades/planear-fase/SKILL.md +319 -319
  52. package/habilidades/react-experto/SKILL.md +45 -4
  53. package/habilidades/release-semver/.evolved.json +8 -8
  54. package/habilidades/state-inconsistency-auditor-swl/SKILL.md +166 -0
  55. package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -0
  56. package/habilidades/tdd-workflow/SKILL.md +36 -4
  57. package/habilidades/testing-python/SKILL.md +340 -340
  58. package/habilidades/web-fetcher-routing/SKILL.md +75 -0
  59. package/hooks/claudemd-bloat-detector.js +161 -161
  60. package/hooks/inyeccion-contexto.js +8 -3
  61. package/hooks/lib/agent-routing.js +107 -107
  62. package/hooks/lib/auto-consolidator.js +335 -335
  63. package/hooks/lib/error-classifier.js +308 -308
  64. package/hooks/lib/merkle-audit.js +96 -96
  65. package/hooks/lib/provenance-tracker.js +191 -191
  66. package/hooks/lib/rate-limit-ip.js +177 -0
  67. package/hooks/lib/rate-limit-tracker.js +253 -253
  68. package/hooks/lib/resource-quota.js +122 -122
  69. package/hooks/lib/retry-jitter.js +165 -165
  70. package/hooks/lib/security-net.js +201 -0
  71. package/hooks/lib/skill-auditor.js +588 -588
  72. package/hooks/lib/sync-status.js +228 -228
  73. package/hooks/lib/taint-tracker.js +107 -107
  74. package/hooks/lib/text-similarity.js +241 -241
  75. package/hooks/lib/toon-compressor.js +245 -245
  76. package/hooks/lib/webhook-dedup.js +184 -0
  77. package/hooks/lib/webhook-verify.js +123 -0
  78. package/hooks/proteccion-rutas.js +120 -15
  79. package/hooks/registro-turnos.js +209 -209
  80. package/hooks/sugerir-regenerar-inventario.js +170 -170
  81. package/hooks/validar-formato-post-subagente.js +140 -140
  82. package/hooks/validar-memoria-hook.js +218 -218
  83. package/instintos/prompt-appendices.yaml +57 -57
  84. package/manifiestos/agent-output-schemas.json +57 -57
  85. package/manifiestos/modulos.json +31 -0
  86. package/manifiestos/skills-lock.json +1114 -1093
  87. package/package.json +6 -4
  88. package/plantillas/auditor-veto-template.md +105 -105
  89. package/plantillas/github-workflows/README.md +47 -47
  90. package/plantillas/github-workflows/release-please.yml +44 -44
  91. package/plantillas/github-workflows/swl-ci.yml +107 -107
  92. package/plantillas/github-workflows/swl-security.yml +51 -51
  93. package/plugin.json +2 -2
  94. package/reglas/analisis-previo-tareas-grandes.md +172 -172
  95. package/reglas/arreglar-al-detectar.md +147 -147
  96. package/reglas/fragmentos-compartidos.md +152 -152
  97. package/reglas/harness-claude-code.md +213 -213
  98. package/reglas/usar-context7.md +226 -226
  99. package/reglas/usar-sistema-swl.md +251 -0
  100. package/schemas/diary-entry.schema.json +80 -80
  101. package/scripts/audit-tools/audit-history.js +330 -0
  102. package/scripts/audit-tools/bundle-tracker.js +290 -0
  103. package/scripts/audit-tools/canary-monitor.js +352 -0
  104. package/scripts/audit-tools/code-profiler.js +605 -0
  105. package/scripts/audit-tools/dep-doctor.js +320 -0
  106. package/scripts/audit-tools/env-validator.js +206 -0
  107. package/scripts/audit-tools/lib/fs-walk.js +48 -0
  108. package/scripts/audit-tools/lib/output.js +23 -0
  109. package/scripts/audit-tools/migration-checker.js +392 -0
  110. package/scripts/audit-tools/pentest-scanner.js +1436 -0
  111. package/scripts/benchmark-memoria.js +167 -167
  112. package/scripts/comandos/skills.js +251 -2
  113. package/scripts/configurar-branch-protection.js +418 -418
  114. package/scripts/detectar-aprendizajes-duplicados.js +151 -151
  115. package/scripts/field-report.js +199 -199
  116. package/scripts/generar-checklists-consolidados.js +273 -273
  117. package/scripts/generar-inventario.js +420 -420
  118. package/scripts/generar-matriz-lenguajes.js +271 -271
  119. package/scripts/lib/artefactos-python.js +43 -43
  120. package/scripts/lib/benchmark-metrics.js +160 -160
  121. package/scripts/lib/budget-enforcer.js +252 -252
  122. package/scripts/lib/configurar-ci.js +380 -380
  123. package/scripts/lib/contadores-inventario.js +217 -217
  124. package/scripts/lib/detectar-stack-detallado.js +307 -307
  125. package/scripts/lib/diary-entry.js +234 -234
  126. package/scripts/lib/eval-metrics-store.js +218 -218
  127. package/scripts/lib/eval-quality.js +171 -171
  128. package/scripts/lib/eval-schemas.js +144 -144
  129. package/scripts/lib/eval-self-correct.js +106 -106
  130. package/scripts/lib/eval-validator.js +185 -185
  131. package/scripts/lib/jaccard-similarity.js +98 -98
  132. package/scripts/lib/longmemeval-runner.js +125 -125
  133. package/scripts/lib/npm-version.js +261 -261
  134. package/scripts/lib/paquetes-conocidos.js +50 -50
  135. package/scripts/lib/prompt-builder.js +264 -264
  136. package/scripts/lib/rrf-fusion.js +175 -175
  137. package/scripts/lib/scoring-instintos.js +277 -277
  138. package/scripts/lib/semantic-search.js +252 -252
  139. package/scripts/limpiar-artefactos-python.js +131 -131
  140. package/scripts/mcp-server/README.md +128 -128
  141. package/scripts/mcp-server/handlers.js +206 -206
  142. package/scripts/migrar-csv-a-array.js +168 -168
  143. package/scripts/migrar-fase-dominio.js +201 -201
  144. package/scripts/publicar.js +511 -511
  145. package/scripts/run-eval.js +141 -141
  146. package/scripts/validar-manifest.js +195 -195
  147. package/scripts/validar-userland-vacio.js +110 -110
  148. package/scripts/verificar-release.js +110 -0
@@ -1,187 +1,187 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * swl-mcp-server — Servidor MCP **EXPERIMENTAL** para exponer la memoria
6
- * de swl-ses a clientes MCP externos (Cursor, Gemini CLI, OpenCode, etc.).
7
- *
8
- * **NO PRODUCCIÓN — STUB EXPERIMENTAL**.
9
- * Ver `scripts/mcp-server/README.md` para limitaciones detalladas.
10
- *
11
- * Modo de transporte: stdio (JSON-RPC sobre stdin/stdout).
12
- * No HTTP, no auth, no rate limiting.
13
- *
14
- * Uso (cliente MCP):
15
- * - Configurar el cliente para ejecutar `node /path/to/swl-ses/bin/swl-mcp-server.js`
16
- * con stdio.
17
- * - Los handlers leen el cwd del proceso para localizar `.planning/`,
18
- * `instintos/`, `APRENDIZAJES.md`. Por defecto usa `process.cwd()`.
19
- * - Override con env var `SWL_MCP_BASE_DIR` si el cliente arranca el server
20
- * desde otro directorio.
21
- *
22
- * Protocolo MCP soportado (subset):
23
- * - initialize / initialized
24
- * - tools/list
25
- * - tools/call
26
- *
27
- * NO soporta:
28
- * - resources/list, prompts/list
29
- * - logging, sampling
30
- * - cancellation, progress
31
- * - HTTP transport
32
- *
33
- * Trigger documentado para implementación completa: "uso ≥2 runtimes
34
- * diferentes (Cursor + Claude Code o similar) consistentemente por
35
- * ≥1 mes". Hoy: 0 instalaciones reportadas.
36
- */
37
-
38
- const path = require('path');
39
-
40
- const { HANDLERS } = require('../scripts/mcp-server/handlers');
41
-
42
- const SERVER_NAME = 'swl-mcp-server';
43
- const SERVER_VERSION = '0.1.0-experimental';
44
- const PROTOCOL_VERSION = '2024-11-05';
45
-
46
- const baseDir = process.env.SWL_MCP_BASE_DIR || process.cwd();
47
-
48
- // ── logging ───────────────────────────────────────────────────────────────────
49
-
50
- // Stderr para evitar contaminar stdout (que es JSON-RPC).
51
- function log(level, msg, data) {
52
- const linea = JSON.stringify({
53
- timestamp: new Date().toISOString(),
54
- level,
55
- msg,
56
- ...(data ? { data } : {}),
57
- });
58
- process.stderr.write(linea + '\n');
59
- }
60
-
61
- // ── JSON-RPC helpers ──────────────────────────────────────────────────────────
62
-
63
- function respuesta(id, result) {
64
- return JSON.stringify({ jsonrpc: '2.0', id, result });
65
- }
66
-
67
- function errorResp(id, code, message) {
68
- return JSON.stringify({ jsonrpc: '2.0', id, error: { code, message } });
69
- }
70
-
71
- // ── routing ───────────────────────────────────────────────────────────────────
72
-
73
- function manejarInitialize(request) {
74
- return respuesta(request.id, {
75
- protocolVersion: PROTOCOL_VERSION,
76
- capabilities: {
77
- tools: { listChanged: false },
78
- },
79
- serverInfo: {
80
- name: SERVER_NAME,
81
- version: SERVER_VERSION,
82
- },
83
- });
84
- }
85
-
86
- function manejarToolsList(request) {
87
- const tools = Object.entries(HANDLERS).map(([name, def]) => ({
88
- name,
89
- description: def.description,
90
- inputSchema: def.inputSchema,
91
- }));
92
- return respuesta(request.id, { tools });
93
- }
94
-
95
- function manejarToolsCall(request) {
96
- const { name, arguments: args } = request.params || {};
97
- const def = HANDLERS[name];
98
- if (!def) {
99
- return errorResp(request.id, -32601, `Tool no encontrado: ${name}`);
100
- }
101
- try {
102
- const result = def.handler(baseDir, args || {});
103
- return respuesta(request.id, {
104
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
105
- });
106
- } catch (err) {
107
- log('error', `Excepción en handler ${name}`, { error: err.message });
108
- return errorResp(request.id, -32603, `Error interno: ${err.message}`);
109
- }
110
- }
111
-
112
- function rutear(request) {
113
- switch (request.method) {
114
- case 'initialize':
115
- return manejarInitialize(request);
116
- case 'initialized':
117
- case 'notifications/initialized':
118
- return null; // notification — sin respuesta
119
- case 'tools/list':
120
- return manejarToolsList(request);
121
- case 'tools/call':
122
- return manejarToolsCall(request);
123
- case 'ping':
124
- return respuesta(request.id, {});
125
- default:
126
- return errorResp(request.id, -32601, `Método no soportado: ${request.method}`);
127
- }
128
- }
129
-
130
- // ── loop principal ────────────────────────────────────────────────────────────
131
-
132
- function arrancar() {
133
- log('warn', '⚠ swl-mcp-server stub experimental — NO usar en producción');
134
- log('info', `Server iniciando`, { name: SERVER_NAME, version: SERVER_VERSION, baseDir });
135
-
136
- let buffer = '';
137
-
138
- process.stdin.setEncoding('utf8');
139
- process.stdin.on('data', (chunk) => {
140
- buffer += chunk;
141
-
142
- // Cada mensaje JSON-RPC termina con \n
143
- let nlIndex;
144
- while ((nlIndex = buffer.indexOf('\n')) >= 0) {
145
- const linea = buffer.slice(0, nlIndex).trim();
146
- buffer = buffer.slice(nlIndex + 1);
147
-
148
- if (!linea) continue;
149
-
150
- let request;
151
- try {
152
- request = JSON.parse(linea);
153
- } catch (err) {
154
- log('error', 'JSON inválido recibido', { error: err.message, linea: linea.slice(0, 100) });
155
- process.stdout.write(errorResp(null, -32700, 'Parse error') + '\n');
156
- continue;
157
- }
158
-
159
- const respuestaStr = rutear(request);
160
- if (respuestaStr) {
161
- process.stdout.write(respuestaStr + '\n');
162
- }
163
- }
164
- });
165
-
166
- process.stdin.on('end', () => {
167
- log('info', 'stdin cerrado, server termina');
168
- process.exit(0);
169
- });
170
-
171
- // Manejo de errores no capturados — nunca crashear silenciosamente
172
- process.on('uncaughtException', (err) => {
173
- log('error', 'uncaughtException', { error: err.message, stack: err.stack });
174
- });
175
- }
176
-
177
- if (require.main === module) {
178
- arrancar();
179
- }
180
-
181
- module.exports = {
182
- rutear,
183
- arrancar,
184
- SERVER_NAME,
185
- SERVER_VERSION,
186
- PROTOCOL_VERSION,
187
- };
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * swl-mcp-server — Servidor MCP **EXPERIMENTAL** para exponer la memoria
6
+ * de swl-ses a clientes MCP externos (Cursor, Gemini CLI, OpenCode, etc.).
7
+ *
8
+ * **NO PRODUCCIÓN — STUB EXPERIMENTAL**.
9
+ * Ver `scripts/mcp-server/README.md` para limitaciones detalladas.
10
+ *
11
+ * Modo de transporte: stdio (JSON-RPC sobre stdin/stdout).
12
+ * No HTTP, no auth, no rate limiting.
13
+ *
14
+ * Uso (cliente MCP):
15
+ * - Configurar el cliente para ejecutar `node /path/to/swl-ses/bin/swl-mcp-server.js`
16
+ * con stdio.
17
+ * - Los handlers leen el cwd del proceso para localizar `.planning/`,
18
+ * `instintos/`, `APRENDIZAJES.md`. Por defecto usa `process.cwd()`.
19
+ * - Override con env var `SWL_MCP_BASE_DIR` si el cliente arranca el server
20
+ * desde otro directorio.
21
+ *
22
+ * Protocolo MCP soportado (subset):
23
+ * - initialize / initialized
24
+ * - tools/list
25
+ * - tools/call
26
+ *
27
+ * NO soporta:
28
+ * - resources/list, prompts/list
29
+ * - logging, sampling
30
+ * - cancellation, progress
31
+ * - HTTP transport
32
+ *
33
+ * Trigger documentado para implementación completa: "uso ≥2 runtimes
34
+ * diferentes (Cursor + Claude Code o similar) consistentemente por
35
+ * ≥1 mes". Hoy: 0 instalaciones reportadas.
36
+ */
37
+
38
+ const path = require('path');
39
+
40
+ const { HANDLERS } = require('../scripts/mcp-server/handlers');
41
+
42
+ const SERVER_NAME = 'swl-mcp-server';
43
+ const SERVER_VERSION = '0.1.0-experimental';
44
+ const PROTOCOL_VERSION = '2024-11-05';
45
+
46
+ const baseDir = process.env.SWL_MCP_BASE_DIR || process.cwd();
47
+
48
+ // ── logging ───────────────────────────────────────────────────────────────────
49
+
50
+ // Stderr para evitar contaminar stdout (que es JSON-RPC).
51
+ function log(level, msg, data) {
52
+ const linea = JSON.stringify({
53
+ timestamp: new Date().toISOString(),
54
+ level,
55
+ msg,
56
+ ...(data ? { data } : {}),
57
+ });
58
+ process.stderr.write(linea + '\n');
59
+ }
60
+
61
+ // ── JSON-RPC helpers ──────────────────────────────────────────────────────────
62
+
63
+ function respuesta(id, result) {
64
+ return JSON.stringify({ jsonrpc: '2.0', id, result });
65
+ }
66
+
67
+ function errorResp(id, code, message) {
68
+ return JSON.stringify({ jsonrpc: '2.0', id, error: { code, message } });
69
+ }
70
+
71
+ // ── routing ───────────────────────────────────────────────────────────────────
72
+
73
+ function manejarInitialize(request) {
74
+ return respuesta(request.id, {
75
+ protocolVersion: PROTOCOL_VERSION,
76
+ capabilities: {
77
+ tools: { listChanged: false },
78
+ },
79
+ serverInfo: {
80
+ name: SERVER_NAME,
81
+ version: SERVER_VERSION,
82
+ },
83
+ });
84
+ }
85
+
86
+ function manejarToolsList(request) {
87
+ const tools = Object.entries(HANDLERS).map(([name, def]) => ({
88
+ name,
89
+ description: def.description,
90
+ inputSchema: def.inputSchema,
91
+ }));
92
+ return respuesta(request.id, { tools });
93
+ }
94
+
95
+ function manejarToolsCall(request) {
96
+ const { name, arguments: args } = request.params || {};
97
+ const def = HANDLERS[name];
98
+ if (!def) {
99
+ return errorResp(request.id, -32601, `Tool no encontrado: ${name}`);
100
+ }
101
+ try {
102
+ const result = def.handler(baseDir, args || {});
103
+ return respuesta(request.id, {
104
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
105
+ });
106
+ } catch (err) {
107
+ log('error', `Excepción en handler ${name}`, { error: err.message });
108
+ return errorResp(request.id, -32603, `Error interno: ${err.message}`);
109
+ }
110
+ }
111
+
112
+ function rutear(request) {
113
+ switch (request.method) {
114
+ case 'initialize':
115
+ return manejarInitialize(request);
116
+ case 'initialized':
117
+ case 'notifications/initialized':
118
+ return null; // notification — sin respuesta
119
+ case 'tools/list':
120
+ return manejarToolsList(request);
121
+ case 'tools/call':
122
+ return manejarToolsCall(request);
123
+ case 'ping':
124
+ return respuesta(request.id, {});
125
+ default:
126
+ return errorResp(request.id, -32601, `Método no soportado: ${request.method}`);
127
+ }
128
+ }
129
+
130
+ // ── loop principal ────────────────────────────────────────────────────────────
131
+
132
+ function arrancar() {
133
+ log('warn', '⚠ swl-mcp-server stub experimental — NO usar en producción');
134
+ log('info', `Server iniciando`, { name: SERVER_NAME, version: SERVER_VERSION, baseDir });
135
+
136
+ let buffer = '';
137
+
138
+ process.stdin.setEncoding('utf8');
139
+ process.stdin.on('data', (chunk) => {
140
+ buffer += chunk;
141
+
142
+ // Cada mensaje JSON-RPC termina con \n
143
+ let nlIndex;
144
+ while ((nlIndex = buffer.indexOf('\n')) >= 0) {
145
+ const linea = buffer.slice(0, nlIndex).trim();
146
+ buffer = buffer.slice(nlIndex + 1);
147
+
148
+ if (!linea) continue;
149
+
150
+ let request;
151
+ try {
152
+ request = JSON.parse(linea);
153
+ } catch (err) {
154
+ log('error', 'JSON inválido recibido', { error: err.message, linea: linea.slice(0, 100) });
155
+ process.stdout.write(errorResp(null, -32700, 'Parse error') + '\n');
156
+ continue;
157
+ }
158
+
159
+ const respuestaStr = rutear(request);
160
+ if (respuestaStr) {
161
+ process.stdout.write(respuestaStr + '\n');
162
+ }
163
+ }
164
+ });
165
+
166
+ process.stdin.on('end', () => {
167
+ log('info', 'stdin cerrado, server termina');
168
+ process.exit(0);
169
+ });
170
+
171
+ // Manejo de errores no capturados — nunca crashear silenciosamente
172
+ process.on('uncaughtException', (err) => {
173
+ log('error', 'uncaughtException', { error: err.message, stack: err.stack });
174
+ });
175
+ }
176
+
177
+ if (require.main === module) {
178
+ arrancar();
179
+ }
180
+
181
+ module.exports = {
182
+ rutear,
183
+ arrancar,
184
+ SERVER_NAME,
185
+ SERVER_VERSION,
186
+ PROTOCOL_VERSION,
187
+ };
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * swl-webhook-server — Bootstrap CLI del receptor de webhooks entrantes.
6
+ *
7
+ * Lee variables de entorno opt-in, construye las dependencias inyectadas,
8
+ * arranca el servidor HTTP y maneja shutdown limpio (SIGTERM/SIGINT).
9
+ *
10
+ * Activación: requiere SWL_WEBHOOK_LISTEN_PORT. Sin esa variable, el
11
+ * proceso imprime un mensaje informativo y sale con código 0 (no es
12
+ * error — sólo opt-in no activado).
13
+ *
14
+ * Variables de entorno (ver `docs/variables-entorno.md` § Webhook entrante
15
+ * para descripción completa):
16
+ *
17
+ * SWL_WEBHOOK_LISTEN_PORT unset → no arranca
18
+ * SWL_WEBHOOK_LISTEN_HOST 127.0.0.1
19
+ * SWL_WEBHOOK_GITHUB_SECRET unset → /webhooks/github deshabilitado
20
+ * SWL_WEBHOOK_BEARER_SECRET unset → /webhooks/generic deshabilitado
21
+ * SWL_WEBHOOK_RATE_LIMIT_RPM 60
22
+ * SWL_WEBHOOK_ALLOW_IPS unset (CSV)
23
+ * SWL_WEBHOOK_MAX_PAYLOAD_BYTES 1048576 (1 MB)
24
+ * SWL_WEBHOOK_INBOX_DIR .planning/inbox
25
+ * SWL_WEBHOOK_LEDGER_PATH .planning/webhook-events.jsonl
26
+ *
27
+ * Uso típico con supervisor (no incluido en SWL — el usuario elige):
28
+ *
29
+ * SWL_WEBHOOK_LISTEN_PORT=8787 SWL_WEBHOOK_GITHUB_SECRET=xxx \
30
+ * pm2 start bin/swl-webhook-server.js --name swl-webhook
31
+ *
32
+ * # systemd: ver MANUAL_USO.md § Webhook server.
33
+ *
34
+ * @module bin/swl-webhook-server
35
+ */
36
+
37
+ const path = require('path');
38
+ const { crearServidor } = require('../gateway/webhook-server');
39
+ const { RateLimiterIP } = require('../hooks/lib/rate-limit-ip');
40
+ const { WebhookDedup } = require('../hooks/lib/webhook-dedup');
41
+
42
+ const PUERTO_MIN = 1;
43
+ const PUERTO_MAX = 65535;
44
+ const RPM_DEFAULT = 60;
45
+ const MAX_PAYLOAD_DEFAULT = 1024 * 1024;
46
+ const HOST_DEFAULT = '127.0.0.1';
47
+ const INBOX_DIR_DEFAULT = path.join('.planning', 'inbox');
48
+ const LEDGER_PATH_DEFAULT = path.join('.planning', 'webhook-events.jsonl');
49
+
50
+ /**
51
+ * Construye el objeto de configuración + deps desde variables de entorno.
52
+ *
53
+ * Separado del main() para testear sin tocar process.env real.
54
+ *
55
+ * @param {object} env Objeto con las variables (típicamente process.env).
56
+ * @returns {{configurado: boolean, errores: string[], config: object|null, deps: object|null}}
57
+ */
58
+ function construirDepsDesdeEnv(env) {
59
+ const errores = [];
60
+
61
+ // Opt-in: sin LISTEN_PORT, no se arranca nada
62
+ if (!env.SWL_WEBHOOK_LISTEN_PORT) {
63
+ return { configurado: false, errores: [], config: null, deps: null };
64
+ }
65
+
66
+ const port = Number.parseInt(env.SWL_WEBHOOK_LISTEN_PORT, 10);
67
+ if (!Number.isInteger(port) || port < PUERTO_MIN || port > PUERTO_MAX) {
68
+ errores.push(`SWL_WEBHOOK_LISTEN_PORT inválido: "${env.SWL_WEBHOOK_LISTEN_PORT}" (debe ser entero ${PUERTO_MIN}-${PUERTO_MAX})`);
69
+ }
70
+
71
+ const host = env.SWL_WEBHOOK_LISTEN_HOST || HOST_DEFAULT;
72
+
73
+ const githubSecret = env.SWL_WEBHOOK_GITHUB_SECRET || null;
74
+ const bearerSecret = env.SWL_WEBHOOK_BEARER_SECRET || null;
75
+
76
+ if (!githubSecret && !bearerSecret) {
77
+ errores.push('Ningún endpoint configurado: definir SWL_WEBHOOK_GITHUB_SECRET o SWL_WEBHOOK_BEARER_SECRET (o ambos).');
78
+ }
79
+
80
+ const rpm = parseEntero(env.SWL_WEBHOOK_RATE_LIMIT_RPM, RPM_DEFAULT);
81
+ if (rpm <= 0) {
82
+ errores.push(`SWL_WEBHOOK_RATE_LIMIT_RPM inválido: "${env.SWL_WEBHOOK_RATE_LIMIT_RPM}" (debe ser entero > 0)`);
83
+ }
84
+
85
+ const maxPayloadBytes = parseEntero(env.SWL_WEBHOOK_MAX_PAYLOAD_BYTES, MAX_PAYLOAD_DEFAULT);
86
+ if (maxPayloadBytes <= 0) {
87
+ errores.push(`SWL_WEBHOOK_MAX_PAYLOAD_BYTES inválido: "${env.SWL_WEBHOOK_MAX_PAYLOAD_BYTES}" (debe ser entero > 0)`);
88
+ }
89
+
90
+ const allowIps = parseCsv(env.SWL_WEBHOOK_ALLOW_IPS);
91
+
92
+ const inboxDir = env.SWL_WEBHOOK_INBOX_DIR || INBOX_DIR_DEFAULT;
93
+ const ledgerPath = env.SWL_WEBHOOK_LEDGER_PATH || LEDGER_PATH_DEFAULT;
94
+
95
+ if (errores.length > 0) {
96
+ return { configurado: true, errores, config: null, deps: null };
97
+ }
98
+
99
+ const config = { port, host, githubSecret, bearerSecret, rpm, maxPayloadBytes, allowIps, inboxDir, ledgerPath };
100
+ const deps = {
101
+ inboxDir,
102
+ dedup: new WebhookDedup({ rutaLedger: ledgerPath }),
103
+ rateLimiter: new RateLimiterIP({ rpm }),
104
+ githubSecret,
105
+ bearerSecret,
106
+ maxPayloadBytes,
107
+ allowIps,
108
+ };
109
+
110
+ return { configurado: true, errores: [], config, deps };
111
+ }
112
+
113
+ function parseEntero(raw, defecto) {
114
+ if (raw === undefined || raw === null || raw === '') return defecto;
115
+ const n = Number.parseInt(raw, 10);
116
+ return Number.isInteger(n) ? n : -1; // -1 = inválido (la validación lo captura)
117
+ }
118
+
119
+ function parseCsv(raw) {
120
+ if (!raw || typeof raw !== 'string') return null;
121
+ const items = raw.split(',').map(s => s.trim()).filter(Boolean);
122
+ return items.length > 0 ? items : null;
123
+ }
124
+
125
+ /**
126
+ * Punto de entrada del CLI. Llamado sólo cuando este archivo se ejecuta
127
+ * directamente (no en require/import).
128
+ */
129
+ function main() {
130
+ const { configurado, errores, config, deps } = construirDepsDesdeEnv(process.env);
131
+
132
+ if (!configurado) {
133
+ console.error('SWL_WEBHOOK_LISTEN_PORT no está definido. El webhook server no se arranca.');
134
+ console.error('Para activar, definir las variables opt-in. Ver docs/variables-entorno.md § Webhook entrante.');
135
+ process.exit(0);
136
+ }
137
+
138
+ if (errores.length > 0) {
139
+ for (const err of errores) console.error(`[error] ${err}`);
140
+ process.exit(1);
141
+ }
142
+
143
+ const server = crearServidor(deps);
144
+
145
+ server.on('error', err => {
146
+ console.error(JSON.stringify({ nivel: 'error', msg: 'server-error', err: err.message }));
147
+ process.exit(1);
148
+ });
149
+
150
+ server.listen(config.port, config.host, () => {
151
+ const addr = server.address();
152
+ const endpoints = [];
153
+ if (config.githubSecret) endpoints.push('/webhooks/github');
154
+ if (config.bearerSecret) endpoints.push('/webhooks/generic');
155
+ endpoints.push('/healthz');
156
+
157
+ console.log(JSON.stringify({
158
+ nivel: 'info',
159
+ msg: 'webhook-server-started',
160
+ bind: `${addr.address}:${addr.port}`,
161
+ endpoints,
162
+ inboxDir: config.inboxDir,
163
+ ledgerPath: config.ledgerPath,
164
+ rpm: config.rpm,
165
+ maxPayloadBytes: config.maxPayloadBytes,
166
+ allowIpsCount: config.allowIps ? config.allowIps.length : 0,
167
+ ts: new Date().toISOString(),
168
+ }));
169
+ });
170
+
171
+ const shutdown = (sig) => {
172
+ console.log(JSON.stringify({ nivel: 'info', msg: 'shutdown', sig, ts: new Date().toISOString() }));
173
+ server.close(() => process.exit(0));
174
+ // Forzar salida si el server tarda demasiado en cerrar conexiones
175
+ setTimeout(() => process.exit(1), 10000).unref();
176
+ };
177
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
178
+ process.on('SIGINT', () => shutdown('SIGINT'));
179
+ }
180
+
181
+ // Sólo arrancar si se invoca directamente (no en require)
182
+ if (require.main === module) {
183
+ main();
184
+ }
185
+
186
+ module.exports = {
187
+ construirDepsDesdeEnv,
188
+ // Para tests
189
+ _defaults: {
190
+ PUERTO_MIN,
191
+ PUERTO_MAX,
192
+ RPM_DEFAULT,
193
+ MAX_PAYLOAD_DEFAULT,
194
+ HOST_DEFAULT,
195
+ INBOX_DIR_DEFAULT,
196
+ LEDGER_PATH_DEFAULT,
197
+ },
198
+ };
@@ -1,23 +1,23 @@
1
- {
2
- "release.md": {
3
- "evolved": true,
4
- "evolvedFrom": "5.4.0",
5
- "evolvedAt": "2026-04-11",
6
- "evolvedBy": "aprender",
7
- "evolvedNote": "mejora de metodología: checklist obligatoria de archivos de versión en paso 6"
8
- },
9
- "aprender.md": {
10
- "evolved": true,
11
- "evolvedFrom": "5.12.3",
12
- "evolvedAt": "2026-04-25",
13
- "evolvedBy": "aprender",
14
- "evolvedNote": "Paso 2 — filtro crítico obligatorio sobre reportes de sub-agentes Explore para evitar sobre-ingeniería al analizar papers académicos"
15
- },
16
- "verificar.md": {
17
- "evolved": true,
18
- "evolvedFrom": "5.12.3",
19
- "evolvedAt": "2026-04-26",
20
- "evolvedBy": "evolucionar",
21
- "evolvedNote": "flag --until-converge para iterar verificar→corregir→re-verificar hasta 0 hallazgos CRÍTICO+ALTO+MAYOR (max-iter=5, --no-prompt CI, detección adversarial ≥5 hallazgos nuevos)"
22
- }
1
+ {
2
+ "release.md": {
3
+ "evolved": true,
4
+ "evolvedFrom": "5.4.0",
5
+ "evolvedAt": "2026-04-11",
6
+ "evolvedBy": "aprender",
7
+ "evolvedNote": "mejora de metodología: checklist obligatoria de archivos de versión en paso 6"
8
+ },
9
+ "aprender.md": {
10
+ "evolved": true,
11
+ "evolvedFrom": "5.12.3",
12
+ "evolvedAt": "2026-04-25",
13
+ "evolvedBy": "aprender",
14
+ "evolvedNote": "Paso 2 — filtro crítico obligatorio sobre reportes de sub-agentes Explore para evitar sobre-ingeniería al analizar papers académicos"
15
+ },
16
+ "verificar.md": {
17
+ "evolved": true,
18
+ "evolvedFrom": "5.12.3",
19
+ "evolvedAt": "2026-04-26",
20
+ "evolvedBy": "evolucionar",
21
+ "evolvedNote": "flag --until-converge para iterar verificar→corregir→re-verificar hasta 0 hallazgos CRÍTICO+ALTO+MAYOR (max-iter=5, --no-prompt CI, detección adversarial ≥5 hallazgos nuevos)"
22
+ }
23
23
  }
@@ -168,7 +168,26 @@ Si `/swl:mapear-codebase` ya lo generó (opción B), solo verificar. Si no:
168
168
  - Dependencias con CVEs conocidos (si `pip-audit` o `npm audit` están disponibles)
169
169
  - Archivos con más de 500 líneas
170
170
 
171
- ## Paso 8 — Reporte final
171
+ ## Paso 8 — Verificar/actualizar CLAUDE.md del proyecto
172
+
173
+ Verificar el estado de `CLAUDE.md` en la raíz del proyecto:
174
+
175
+ - **Si NO existe**: generarlo con la estructura mínima de `/swl:claudemd init-project`.
176
+ **OBLIGATORIO** incluir como primera sección bajo el título:
177
+ ```markdown
178
+ ## Reglas obligatorias
179
+
180
+ @reglas/usar-sistema-swl.md
181
+ ```
182
+ - **Si existe pero NO incluye `@reglas/usar-sistema-swl.md`**: agregar la
183
+ referencia en una sección "Reglas obligatorias" cerca del inicio del
184
+ archivo, preservando el contenido existente.
185
+ - **Si existe e incluye la referencia**: no tocar.
186
+
187
+ Esta referencia carga la matriz operacional del sistema SWL al inicio de cada
188
+ sesión y es el contrato base de uso del sistema para el proyecto adoptado.
189
+
190
+ ## Paso 9 — Reporte final
172
191
 
173
192
  ```
174
193
  === Proyecto adoptado exitosamente ===
@@ -183,6 +202,7 @@ Archivos generados:
183
202
  ✓ .planning/research/FUNCIONALIDADES.md — inventario de funcionalidades
184
203
  ✓ .planning/research/RESUMEN.md — resumen ejecutivo consolidado
185
204
  ✓ .planning/research/TRAMPAS.md — deuda técnica y anti-patrones
205
+ ✓ CLAUDE.md — creado o actualizado con @reglas/usar-sistema-swl.md
186
206
 
187
207
  Hallazgos clave:
188
208
  1. [hallazgo más importante]