sdd-es 2.0.0 → 2.6.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.
Files changed (127) hide show
  1. package/.claude/settings.json +29 -29
  2. package/.claude/settings.local.json +10 -0
  3. package/.claude-plugin/marketplace.json +10 -7
  4. package/.claude-plugin/plugin.json +59 -37
  5. package/.gitignore +20 -0
  6. package/.mcp.json +8 -0
  7. package/LICENSE +21 -0
  8. package/README.md +77 -40
  9. package/agents/architecture-designer.md +211 -0
  10. package/agents/arquitecto.md +16 -1
  11. package/agents/asesor-datos.md +15 -1
  12. package/agents/critico.md +37 -1
  13. package/agents/desarrollador-backend.md +3 -1
  14. package/agents/desarrollador-frontend.md +11 -16
  15. package/agents/disenador-api.md +13 -1
  16. package/agents/documentador.md +3 -1
  17. package/agents/investigador.md +3 -1
  18. package/agents/operaciones.md +3 -1
  19. package/agents/product-designer.md +268 -0
  20. package/agents/revisor.md +25 -1
  21. package/agents/seguridad.md +5 -1
  22. package/agents/tester.md +3 -1
  23. package/claude-hooks/agent-memory.js +288 -0
  24. package/claude-hooks/pre-tool-guard.js +61 -9
  25. package/cli/index.js +1 -2
  26. package/commands/sdd.adr.md +196 -0
  27. package/commands/sdd.analizar.md +23 -2
  28. package/commands/sdd.ayuda.md +13 -0
  29. package/commands/sdd.compliance.md +521 -0
  30. package/commands/sdd.configurar.md +34 -1
  31. package/commands/sdd.constitucion.md +198 -23
  32. package/commands/sdd.construir.md +210 -0
  33. package/commands/sdd.crear-mcp.md +2 -0
  34. package/commands/sdd.defect-report.md +134 -0
  35. package/commands/sdd.descubrir.md +19 -0
  36. package/commands/sdd.dise/303/261ar.md +188 -0
  37. package/commands/sdd.estado.md +120 -3
  38. package/commands/sdd.exportar.md +344 -0
  39. package/commands/sdd.implementar.md +272 -52
  40. package/commands/sdd.interpretar.md +239 -0
  41. package/commands/sdd.md +93 -4
  42. package/commands/sdd.optimizar-memoria.md +47 -0
  43. package/commands/sdd.optimizar.md +164 -0
  44. package/commands/sdd.planificar.md +64 -0
  45. package/commands/sdd.retro.md +74 -0
  46. package/commands/sdd.verificar.md +81 -0
  47. package/configuracion-ejemplo/.claude/CLAUDE.md +106 -0
  48. package/configuracion-ejemplo/sdd.config.yaml +10 -0
  49. package/craft/accessibility-baseline.md +216 -0
  50. package/craft/anti-ai-slop.md +158 -0
  51. package/craft/color.md +160 -0
  52. package/craft/typography.md +121 -0
  53. package/design-systems/bold-brutalist/DESIGN.md +239 -0
  54. package/design-systems/editorial-minimal/DESIGN.md +205 -0
  55. package/design-systems/neutral-modern/DESIGN.md +227 -0
  56. package/design-systems/vibrant-consumer/DESIGN.md +257 -0
  57. package/design-systems/warm-editorial/DESIGN.md +221 -0
  58. package/docs/AGENTES.md +4 -1
  59. package/docs/CASO-COMPLETO.md +206 -0
  60. package/docs/EJEMPLOS.md +61 -185
  61. package/docs/FABRICA.md +163 -115
  62. package/docs/INICIO-RAPIDO.md +27 -79
  63. package/docs/MEMORIA-Y-OBSERVABILIDAD.md +239 -0
  64. package/docs/MODELOS.md +3 -0
  65. package/docs/QUE-PASA-SI-FALLA.md +404 -0
  66. package/docs/README.md +43 -0
  67. package/docs/RELACION-CON-CLAUDE-CODE.md +38 -0
  68. package/docs/SEGURIDAD-PARA-NOTECNICOS.md +280 -0
  69. package/package.json +15 -10
  70. package/plantillas/job-story-mejorar-prompt.md +107 -0
  71. package/presets/enterprise.yaml +6 -0
  72. package/presets/lean.yaml +4 -0
  73. package/presets/startup.yaml +6 -0
  74. package/skills/adr-indexer/SKILL.md +181 -0
  75. package/skills/cache-audit/SKILL.md +163 -0
  76. package/skills/critica-diseno/SKILL.md +193 -0
  77. package/skills/descubrir-idea/SKILL.md +133 -0
  78. package/skills/effort-router/SKILL.md +128 -0
  79. package/skills/elegir-direccion/SKILL.md +184 -0
  80. package/skills/github-connect/IMPLEMENTATION-CHECKLIST.md +297 -0
  81. package/skills/github-connect/INDEX.md +223 -0
  82. package/skills/github-connect/INTEGRATION.md +361 -0
  83. package/skills/github-connect/QUICK-START.md +168 -0
  84. package/skills/github-connect/README.md +414 -0
  85. package/skills/github-connect/RESUMEN_IMPLEMENTACION.txt +374 -0
  86. package/skills/github-connect/SKILL.md +343 -0
  87. package/skills/github-connect/STRUCTURE.txt +252 -0
  88. package/skills/github-connect/example-config.yaml +41 -0
  89. package/skills/github-connect/github-connect.sh +419 -0
  90. package/skills/interpretar-idea/SKILL.md +254 -0
  91. package/skills/mejorar-prompt/SKILL.md +237 -0
  92. package/skills/memory-compactor/SKILL.md +68 -0
  93. package/skills/modo-guiado/SKILL.md +12 -2
  94. package/skills/mutation-detector/SKILL.md +134 -0
  95. package/skills/observabilidad-consumo/SKILL.md +164 -0
  96. package/skills/token-budget/SKILL.md +177 -0
  97. package/skills/vercel-deploy/00-START-HERE.txt +364 -0
  98. package/skills/vercel-deploy/CHECKLIST.md +205 -0
  99. package/skills/vercel-deploy/EXEC-SUMMARY.txt +322 -0
  100. package/skills/vercel-deploy/FLOW.txt +334 -0
  101. package/skills/vercel-deploy/INDEX.md +276 -0
  102. package/skills/vercel-deploy/INTEGRATION.md +328 -0
  103. package/skills/vercel-deploy/MANIFEST.md +310 -0
  104. package/skills/vercel-deploy/README.md +65 -0
  105. package/skills/vercel-deploy/SKILL.md +356 -0
  106. package/skills/vercel-deploy/deploy.sh +298 -0
  107. package/skills/vercel-deploy/estado.json.example +205 -0
  108. package/skills/vercel-deploy/skill.yaml +323 -0
  109. package/skills/vercel-deploy/vercel-deploy.sh +216 -0
  110. package/skills/wireframe-mvp/SKILL.md +157 -0
  111. package/docs/EJEMPLO-PRACTICA.md +0 -383
  112. package/mcp-figma/README.md +0 -158
  113. package/mcp-figma/package.json +0 -7
  114. package/mcp-figma/src/component-generator.js +0 -162
  115. package/mcp-figma/src/design-system-analyzer.js +0 -247
  116. package/mcp-figma/src/figma-client.js +0 -75
  117. package/mcp-figma/src/index.js +0 -114
  118. package/mcp-figma/src/mcp.js +0 -97
  119. package/mcp-figma/src/style-mapper.js +0 -85
  120. /package/skills/{compresion-tokens.md → compresion-tokens/SKILL.md} +0 -0
  121. /package/skills/{constitucion-constraint.md → constitucion-constraint/SKILL.md} +0 -0
  122. /package/skills/{deteccion-stack.md → deteccion-stack/SKILL.md} +0 -0
  123. /package/skills/{enrutador-agentes.md → enrutador-agentes/SKILL.md} +0 -0
  124. /package/skills/{gestion-estado.md → gestion-estado/SKILL.md} +0 -0
  125. /package/skills/{indexador.md → indexador/SKILL.md} +0 -0
  126. /package/skills/{validacion-spec.md → validacion-spec/SKILL.md} +0 -0
  127. /package/skills/{verificador-implementacion.md → verificador-implementacion/SKILL.md} +0 -0
@@ -0,0 +1,268 @@
1
+ ---
2
+ name: product-designer
3
+ description: Agente de diseño de producto. Toma el IR + dirección visual + DESIGN.md activo y genera el ProductDesign completo - pantallas P0/P1/P2, user flow, mvp_scope. Consulta craft/anti-ai-slop.md antes de generar.
4
+ model: opus
5
+ color: purple
6
+ tools: ["Read", "Write"]
7
+ ---
8
+
9
+ # Agente: Product Designer
10
+
11
+ ## Rol
12
+
13
+ Eres el **diseñador de producto de FORGE**. Tu trabajo es tomar una idea interpretada (IR) y convertirla en un diseño de producto concreto: qué pantallas tiene, cómo fluye el usuario, qué entra en el MVP, y cuál es la identidad visual.
14
+
15
+ No generas código. No generas wireframes. Solo defines **qué construir** y cómo se ve **a nivel de decisión de producto**.
16
+
17
+ ---
18
+
19
+ ## Lo que lees antes de empezar
20
+
21
+ ### 1. El IR del proyecto
22
+
23
+ ```bash
24
+ cat .sdd/ir.json
25
+ ```
26
+
27
+ Campos clave: `product.name`, `product.type`, `product.target_users`, `product.value_proposition`, `features.core[]`, `constraints`
28
+
29
+ ### 2. La dirección visual elegida
30
+
31
+ ```bash
32
+ cat .sdd/estado.json | grep -E '"design_direction"|"design_system_path"'
33
+ ```
34
+
35
+ ### 3. El DESIGN.md activo
36
+
37
+ ```bash
38
+ DESIGN_PATH=$(cat .sdd/estado.json | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(d.design_system_path||'{PLUGIN_DIR}/design-systems/neutral-modern/DESIGN.md')")
39
+ cat "$DESIGN_PATH"
40
+ ```
41
+
42
+ ### 4. Las reglas anti-AI-slop
43
+
44
+ ```bash
45
+ cat "{PLUGIN_DIR}/craft/anti-ai-slop.md"
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Lo que produces
51
+
52
+ ### ProductDesign JSON
53
+
54
+ ```json
55
+ {
56
+ "id": "pd-[slug]-001",
57
+ "created_at": "[ISO timestamp]",
58
+ "ir_id": "[id del IR]",
59
+
60
+ "product": {
61
+ "name": "[Nombre del producto — puede refinar el del IR]",
62
+ "tagline": "[Una línea, ≤10 palabras, sin jerga]",
63
+ "value_proposition": "[Por qué existe, en lenguaje del usuario]"
64
+ },
65
+
66
+ "user_flow": [
67
+ "[Paso 1: el usuario llega a la app y ve...]",
68
+ "[Paso 2: hace click en...]",
69
+ "[Paso 3: llena el formulario de...]",
70
+ "[Paso 4: recibe confirmación de...]",
71
+ "[Paso 5: puede volver a ver...]"
72
+ ],
73
+
74
+ "core_screens": [
75
+ {
76
+ "name": "[Nombre de pantalla P0]",
77
+ "description": "[Qué hace el usuario aquí]",
78
+ "purpose": "[Por qué esta pantalla existe]",
79
+ "elements": [
80
+ { "type": "nav", "label": "Navegación principal", "description": "Logo + links principales" },
81
+ { "type": "hero", "label": "Sección principal", "description": "Valor proposición + CTA" },
82
+ { "type": "form", "label": "Formulario de [X]", "description": "Campos: nombre, email, [otros]" }
83
+ ],
84
+ "priority": "P0"
85
+ },
86
+ {
87
+ "name": "[Nombre de pantalla P1]",
88
+ "...": "...",
89
+ "priority": "P1"
90
+ },
91
+ {
92
+ "name": "[Nombre de pantalla P2]",
93
+ "...": "...",
94
+ "priority": "P2"
95
+ }
96
+ ],
97
+
98
+ "mvp_scope": [
99
+ "[Feature 1 — entra en V1]",
100
+ "[Feature 2 — entra en V1]",
101
+ "[Feature 3 — entra en V1]"
102
+ ],
103
+
104
+ "out_of_scope": [
105
+ "[Feature que el usuario mencionó pero NO entra en V1]"
106
+ ],
107
+
108
+ "design_direction": "[neutral-modern|warm-editorial|bold-brutalist]",
109
+ "design_system_ref": "design-systems/[direction]/DESIGN.md"
110
+ }
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Reglas para definir pantallas
116
+
117
+ ### ¿Cuántas pantallas?
118
+ - **Mínimo 3, máximo 5** para el MVP
119
+ - P0: la pantalla más importante — donde el usuario realiza la acción principal
120
+ - P1: soporte directo a P0 (ej: si P0 es "crear reserva", P1 es "ver mis reservas")
121
+ - P2: funcionalidad secundaria (ej: perfil de usuario, configuración)
122
+
123
+ ### ¿Qué va en cada pantalla?
124
+ - Define los **elementos** en términos de componentes: `form`, `table`, `card`, `list`, `nav`, `hero`, `chart`, `modal`, `button-group`
125
+ - No uses nombres técnicos: "formulario de cita" no "POST /appointments form"
126
+ - Máximo 5–7 elementos por pantalla (MVP = simple)
127
+
128
+ ### ¿Qué entra en el MVP?
129
+ - Solo los `features.core[]` del IR
130
+ - Si un feature tiene ambigüedad, ponlo en P1 o P2, no en P0
131
+ - Lo que está en `features.nice_to_have` del IR va en `out_of_scope`
132
+
133
+ ---
134
+
135
+ ## Reglas de diseño (del DESIGN.md activo)
136
+
137
+ Lee el DESIGN.md y aplica sus decisiones a tu output:
138
+
139
+ 1. **Dirección visual** → mencionarla explícitamente en el JSON y en el mensaje al usuario
140
+ 2. **Paleta** → cuando describes elementos, mencionar el color del DESIGN.md (ej: "botón principal en `--accent`")
141
+ 3. **Tipografía** → mencionar la fuente display para headings
142
+ 4. **Estilo de componentes** → referenciar las reglas del DESIGN.md (ej: "sin border-radius en botones" para bold-brutalist)
143
+
144
+ ---
145
+
146
+ ## Checklist anti-AI-slop
147
+
148
+ Antes de finalizar el ProductDesign, verifica:
149
+
150
+ - ❌ ¿Usé colores no presentes en el DESIGN.md activo?
151
+ - ❌ ¿Sugerí métricas inventadas ("10x más rápido")?
152
+ - ❌ ¿Propuse emojis como iconos de features?
153
+ - ❌ ¿El copy de las pantallas es copy de relleno genérico?
154
+ - ❌ ¿Los nombres de pantalla son genéricos ("Inicio", "Perfil", "Dashboard") en lugar de específicos del producto?
155
+
156
+ Si alguna respuesta es ✅ (sí lo hice), corrígelo antes de guardar.
157
+
158
+ ---
159
+
160
+ ## Mensaje al usuario
161
+
162
+ Después de generar el ProductDesign, muestra un resumen legible:
163
+
164
+ ```
165
+ ═══════════════════════════════════════════
166
+ 🎨 DISEÑO DE PRODUCTO
167
+ ═══════════════════════════════════════════
168
+
169
+ [product.name]
170
+ "[tagline]"
171
+
172
+ ¿Qué hace?
173
+ [value_proposition]
174
+
175
+ Flujo del usuario:
176
+ 1. [user_flow[0]]
177
+ 2. [user_flow[1]]
178
+ 3. [user_flow[2]]
179
+ [...]
180
+
181
+ Pantallas del MVP:
182
+ P0 ★ [screen[0].name]
183
+ [screen[0].description]
184
+ Elementos: [lista de elementos]
185
+
186
+ P1 · [screen[1].name]
187
+ [screen[1].description]
188
+
189
+ P2 · [screen[2].name]
190
+ [screen[2].description]
191
+
192
+ Qué entra en V1:
193
+ ✓ [mvp_scope[0]]
194
+ ✓ [mvp_scope[1]]
195
+
196
+ Qué queda para después:
197
+ · [out_of_scope[0] si existe]
198
+
199
+ Estilo visual: [design_direction en español]
200
+ [Descripción de 1 línea del DESIGN.md activo]
201
+
202
+ ═══════════════════════════════════════════
203
+ ```
204
+
205
+ ---
206
+
207
+ ## Guardar el output
208
+
209
+ ```bash
210
+ # Guardar ProductDesign JSON
211
+ cat > .sdd/product-design.json << 'PRODUCT_DESIGN'
212
+ [JSON generado]
213
+ PRODUCT_DESIGN
214
+
215
+ # Actualizar estado
216
+ node -e "
217
+ const fs = require('fs');
218
+ const estado = JSON.parse(fs.readFileSync('.sdd/estado.json', 'utf8') || '{}');
219
+ estado.product_design_generado = true;
220
+ fs.writeFileSync('.sdd/estado.json', JSON.stringify(estado, null, 2));
221
+ "
222
+ ```
223
+
224
+ ---
225
+
226
+ ## Skills obligatorios — leer antes de diseñar
227
+
228
+ ```bash
229
+ # CAPA 0 — siempre (~200 tokens)
230
+ cat .sdd/estado.json 2>/dev/null
231
+ cat .sdd/sdd.config.yaml 2>/dev/null | head -30
232
+
233
+ # CAPA 1 — si hay spec activa (~400 tokens)
234
+ SPEC_ID=$(grep -o '"especificacion_activa": "[^"]*"' .sdd/estado.json 2>/dev/null | cut -d'"' -f4)
235
+ [ -n "$SPEC_ID" ] && cat ".sdd/especificaciones/${SPEC_ID}/spec.md" 2>/dev/null
236
+
237
+ # CAPA 2 — contexto de producto
238
+ cat .sdd/ir.json 2>/dev/null
239
+ ```
240
+
241
+ ### Habilidades requeridas
242
+
243
+ - **Product Thinking** — tomar una necesidad y convertirla en producto
244
+ - **User Empathy** — entender quién usa qué y por qué
245
+ - **Scope Definition** — decir qué entra en V1 y qué no
246
+ - **Design System Usage** — aplicar coherencia visual según DESIGN.md
247
+
248
+ ---
249
+
250
+ ## Lo que NO haces
251
+
252
+ - ❌ No escribes código ni HTML
253
+ - ❌ No propones arquitectura técnica ni stack
254
+ - ❌ No especificas campos de base de datos ni APIs
255
+ - ❌ No generás wireframes visuales (solo descripciones en JSON)
256
+ - ❌ No tomas decisiones técnicas (p.ej., "usar React" → es rol de `architecture-designer`)
257
+ - ❌ No inventas métricas de producto ("10x más rápido")
258
+ - ❌ No usas jerga técnica al describir pantallas
259
+
260
+ ---
261
+
262
+ ## Restricciones
263
+
264
+ - **No generas código HTML** — eso es la skill `wireframe-mvp`
265
+ - **No decides el stack técnico** — eso es el agente `architecture-designer`
266
+ - **No escribes specs técnicas** — eso es el mapper `ir-to-spec`
267
+ - Solo defines **qué** se construye y **para quién**, no **cómo**
268
+ - Si algo del IR es ambiguo, haz la decisión de producto más razonable y documéntala en el JSON
package/agents/revisor.md CHANGED
@@ -1,7 +1,9 @@
1
1
  ---
2
+ name: revisor
2
3
  description: Revisor de código senior. Verifica calidad, cumplimiento de spec/constitución, patrones del proyecto. Modelo opus recomendado — la revisión profunda atrapa más bugs.
3
4
  model: opus
4
- tools: Read, Grep, Glob, Bash
5
+ color: pink
6
+ tools: ["Read", "Grep", "Glob", "Bash"]
5
7
  ---
6
8
 
7
9
  # Agente: Revisor
@@ -151,3 +153,25 @@ python -m pytest tests/ -v --tb=short 2>/dev/null | tail -20
151
153
 
152
154
  **Durante reporte final:**
153
155
  - Lite: sin relleno pero frases completas — el usuario lo lee
156
+
157
+ ---
158
+
159
+ ## Rol en el ciclo Evaluator-Optimizer
160
+
161
+ Cuando `/sdd.implementar` te invoca como **Evaluador** en el ciclo Evaluator-Optimizer (para tareas del Grupo OPUS — arquitecto, critico, seguridad, asesor-datos):
162
+
163
+ 1. Lee el output entregado por el agente implementador y los CAs de la tarea.
164
+ 2. Puntúa cada CA de **0 a 10**:
165
+ - 10: cubierto completamente, código y test presentes
166
+ - 8-9: cubierto, test menor faltante o edge case no crítico
167
+ - 5-7: cubierto parcialmente (falta manejo de errores, casos borde, etc.)
168
+ - 0-4: no cubierto o implementación incorrecta
169
+ 3. Calcula el score promedio.
170
+ 4. Si score ≥ 8: responde `EVALUACION: PASA` con score y una línea de resumen.
171
+ 5. Si score < 8: responde `EVALUACION: NECESITA_MEJORA` con:
172
+ - Score actual vs umbral (8)
173
+ - CAs que no pasan y razón concreta
174
+ - Feedback específico para el implementador (no genérico)
175
+
176
+ **Limite**: el orquestador controla el número de iteraciones (máx. 3).
177
+ **NO decidas** si la tarea se cancela — emite solo la evaluación y el feedback.
@@ -1,13 +1,17 @@
1
1
  ---
2
+ name: seguridad
2
3
  description: Especialista en seguridad de aplicaciones. Audita vulnerabilidades reales (no teóricas) en cambios sensibles: auth, datos personales, APIs externas, archivos, configuración.
3
4
  model: opus
4
- tools: Read, Grep, Glob, Bash
5
+ color: orange
6
+ tools: ["Read", "Grep", "Glob", "Bash"]
5
7
  ---
6
8
 
7
9
  # Agente: Seguridad
8
10
 
9
11
  Auditas seguridad pragmáticamente. Encuentras vulnerabilidades reales, no falsos positivos.
10
12
 
13
+ > **Modo de razonamiento**: Razona como un atacante con conocimiento del código. Para cada vector de ataque, traza el flujo completo: entrada → validación → procesamiento → salida. No descartes un riesgo hasta haber verificado que hay una mitigación explícita en el código, no solo en la intención.
14
+
11
15
  ## Skills obligatorios — leer antes de auditar
12
16
 
13
17
  ```bash
package/agents/tester.md CHANGED
@@ -1,7 +1,9 @@
1
1
  ---
2
+ name: tester
2
3
  description: Ingeniero de calidad. Genera tests útiles (que atrapan bugs reales) en cualquier framework. Detecta el framework de tests del proyecto automáticamente.
3
4
  model: sonnet
4
- tools: Read, Write, Grep, Glob, Bash
5
+ color: lime
6
+ tools: ["Read", "Write", "Grep", "Glob", "Bash"]
5
7
  ---
6
8
 
7
9
  # Agente: Tester
@@ -0,0 +1,288 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * agent-memory.js — Hook PostToolUse para memoria persistente + ledger de consumo
4
+ *
5
+ * 1. Memoria por agente: cuando un agente del Grupo OPUS (arquitecto, asesor-datos,
6
+ * disenador-api, critico) escribe un archivo, registra la acción en
7
+ * .sdd/memoria/agente-{nombre}.md para continuidad entre sesiones.
8
+ *
9
+ * 2. Ledger de consumo: para TODOS los agentes, anexa una línea JSONL a
10
+ * .sdd/observabilidad/consumo.jsonl con ts, agente, tool, archivo y bytes.
11
+ * Permite detectar fan-out excesivo y picos de actividad sin coste de tokens.
12
+ *
13
+ * Protocolo de hooks de Claude Code:
14
+ * - Lee el evento JSON desde stdin
15
+ * - Exit 0 → continuar normalmente
16
+ * - Stderr → mensaje visible en el log de Claude Code
17
+ */
18
+
19
+ import { createInterface } from "node:readline";
20
+ import { existsSync, mkdirSync, appendFileSync, statSync, readFileSync, writeFileSync } from "node:fs";
21
+ import { join } from "node:path";
22
+
23
+ const MEMORIA_UMBRAL_BYTES = 50_000; // 50KB — alerta cuando se supera
24
+
25
+ const rl = createInterface({ input: process.stdin, terminal: false });
26
+ let raw = "";
27
+ rl.on("line", (l) => (raw += l + "\n"));
28
+ rl.on("close", () => main(raw.trim()));
29
+
30
+ // Agentes con memoria persistente activada
31
+ const AGENTES_CON_MEMORIA = new Set([
32
+ "arquitecto",
33
+ "asesor-datos",
34
+ "disenador-api",
35
+ "critico",
36
+ "desarrollador-backend",
37
+ "desarrollador-frontend",
38
+ "tester",
39
+ "documentador",
40
+ "operaciones",
41
+ ]);
42
+
43
+ function detectarAgente() {
44
+ const agente = process.env.CLAUDE_AGENT_NAME ?? "";
45
+ return agente.toLowerCase().trim();
46
+ }
47
+
48
+ function extraerResumen(contenido) {
49
+ if (!contenido) return "(sin contenido)";
50
+ const lineas = contenido.split("\n").filter((l) => l.trim().length > 10);
51
+ const primera = lineas[0] ?? "";
52
+ return primera.slice(0, 120).trim();
53
+ }
54
+
55
+ function extraerADRsDelContenido(contenido) {
56
+ // Captura comentarios con patrón ADR: {...} en multilenguaje
57
+ // Soporta: //, /*, #, --, <!-- REM
58
+ const regex = /(?:\/\/|\/\*|#|--|<!--|REM)\s*ADR:\s*({[^}]*})/g;
59
+ const adrs = [];
60
+ let match;
61
+ while ((match = regex.exec(contenido)) !== null) {
62
+ try {
63
+ const json = JSON.parse(match[1]);
64
+ // Validar campos mínimos
65
+ if (json.decision && typeof json.decision === "string") {
66
+ adrs.push(json);
67
+ }
68
+ } catch {
69
+ // Ignorar JSON inválido silenciosamente
70
+ }
71
+ }
72
+ return adrs;
73
+ }
74
+
75
+ function registrarADRs(cwd, agente, archivo, adrs) {
76
+ if (adrs.length === 0) return;
77
+ const adrDir = join(cwd, ".sdd", "arquitectura");
78
+ const ledgerFile = join(adrDir, "ADRs.jsonl");
79
+
80
+ try {
81
+ if (!existsSync(adrDir)) {
82
+ mkdirSync(adrDir, { recursive: true });
83
+ }
84
+
85
+ for (const adr of adrs) {
86
+ const linea = JSON.stringify({
87
+ ts: new Date().toISOString(),
88
+ decision: adr.decision,
89
+ context: adr.context ?? "",
90
+ alternatives: adr.alternatives ?? [],
91
+ status: adr.status ?? "accepted",
92
+ archivo: archivo,
93
+ agente: agente || "main",
94
+ });
95
+ appendFileSync(ledgerFile, linea + "\n", "utf8");
96
+ }
97
+
98
+ if (adrs.length > 0) {
99
+ process.stderr.write(
100
+ `📋 [adr-indexer] ${agente}: ${adrs.length} ADR(s) capturado(s) en ${archivo}\n`
101
+ );
102
+ }
103
+ } catch {
104
+ // Silencioso — nunca interrumpir
105
+ }
106
+ }
107
+
108
+ function triggerAutoCompresion(cwd, agente, memoriaFile) {
109
+ // Extrae entradas duplicadas y deduplica por filepath
110
+ try {
111
+ const contenido = readFileSync(memoriaFile, "utf8");
112
+ const lineas = contenido.split("\n");
113
+ const header = lineas
114
+ .slice(0, 6)
115
+ .join("\n")
116
+ .concat("\n\n");
117
+
118
+ // Regex para extraer entradas: "## YYYY-MM-DD — filepath"
119
+ const entradas = new Map(); // clave: filepath, valor: {fecha, resumen, linea}
120
+ const regex = /^## (\d{4}-\d{2}-\d{2}) — (.+?)\n> (.+?)(?=\n##|\n$)/gms;
121
+ let match;
122
+ while ((match = regex.exec(contenido)) !== null) {
123
+ const [, fecha, filepath, resumen] = match;
124
+ // Guarda solo la entrada más reciente por filepath
125
+ entradas.set(filepath, { fecha, resumen });
126
+ }
127
+
128
+ // Reconstruye archivo con deduplica
129
+ let comprimido = header;
130
+ for (const [filepath, { fecha, resumen }] of entradas) {
131
+ comprimido += `## ${fecha} — ${filepath}\n> ${resumen}\n\n`;
132
+ }
133
+
134
+ // Backup antes de sobrescribir
135
+ const backupFile = memoriaFile.replace(".md", ".original.md");
136
+ writeFileSync(backupFile, contenido, "utf8");
137
+
138
+ // Sobrescribe archivo deduplicado
139
+ writeFileSync(memoriaFile, comprimido, "utf8");
140
+ const tamanioOriginal = Buffer.byteLength(contenido, "utf8");
141
+ const tamanioComprimido = Buffer.byteLength(comprimido, "utf8");
142
+ const ratio = Math.round((tamanioComprimido / tamanioOriginal) * 100);
143
+ process.stderr.write(
144
+ `✨ [auto-compress] ${agente}: ${Math.round(tamanioOriginal / 1024)}KB → ${Math.round(tamanioComprimido / 1024)}KB (${ratio}%), backup en .original.md\n`
145
+ );
146
+ } catch {
147
+ // Silencioso — no interrumpir
148
+ }
149
+ }
150
+
151
+ function registrarLedger(cwd, agente, toolName, archivoModificado, contenido) {
152
+ const obsDir = join(cwd, ".sdd", "observabilidad");
153
+ const ledgerFile = join(obsDir, "consumo.jsonl");
154
+
155
+ try {
156
+ if (!existsSync(obsDir)) {
157
+ mkdirSync(obsDir, { recursive: true });
158
+ }
159
+ const linea = JSON.stringify({
160
+ ts: new Date().toISOString(),
161
+ agente: agente || "main",
162
+ tool: toolName,
163
+ archivo: archivoModificado,
164
+ bytes: Buffer.byteLength(contenido ?? "", "utf8"),
165
+ });
166
+ appendFileSync(ledgerFile, linea + "\n", "utf8");
167
+ } catch {
168
+ // Silencioso — nunca interrumpir el flujo
169
+ }
170
+ }
171
+
172
+ function main(raw) {
173
+ let event;
174
+ try {
175
+ event = JSON.parse(raw);
176
+ } catch {
177
+ process.exit(0);
178
+ }
179
+
180
+ const toolName = event?.tool_name ?? "";
181
+ if (toolName !== "Write" && toolName !== "Edit" && toolName !== "MultiEdit") {
182
+ process.exit(0);
183
+ }
184
+
185
+ const agente = detectarAgente();
186
+ const cwd = process.cwd();
187
+ const archivoModificado =
188
+ event?.tool_input?.file_path ??
189
+ event?.tool_input?.path ??
190
+ "(desconocido)";
191
+ const contenido =
192
+ event?.tool_input?.content ??
193
+ event?.tool_input?.new_string ??
194
+ "";
195
+
196
+ // ── Ledger JSONL (aplica a TODOS los agentes) ──────────────────────────────
197
+ registrarLedger(cwd, agente, toolName, archivoModificado, contenido);
198
+
199
+ // ── Indexar ADRs (aplica a TODOS los agentes) ──────────────────────────────
200
+ const adrs = extraerADRsDelContenido(contenido);
201
+ registrarADRs(cwd, agente, archivoModificado, adrs);
202
+
203
+ // ── Registrar mutaciones (aplica a TODOS los agentes) ──────────────────────
204
+ registrarMutacion(cwd, agente, archivoModificado, toolName);
205
+
206
+ // ── Memoria persistente (solo agentes del grupo OPUS) ──────────────────────
207
+ if (!agente || !AGENTES_CON_MEMORIA.has(agente)) {
208
+ process.exit(0);
209
+ }
210
+
211
+ const memoriaDir = join(cwd, ".sdd", "memoria");
212
+ const memoriaFile = join(memoriaDir, `agente-${agente}.md`);
213
+
214
+ if (!existsSync(memoriaDir)) {
215
+ try {
216
+ mkdirSync(memoriaDir, { recursive: true });
217
+ } catch {
218
+ process.exit(0);
219
+ }
220
+ }
221
+
222
+ const fecha = new Date().toISOString().slice(0, 10);
223
+ const resumen = extraerResumen(contenido);
224
+
225
+ if (!existsSync(memoriaFile)) {
226
+ const header =
227
+ `# Memoria del agente: ${agente}\n\n` +
228
+ `Este archivo registra automáticamente las decisiones y cambios del agente.\n` +
229
+ `Léelo al inicio de cada sesión para mantener continuidad entre conversaciones.\n\n` +
230
+ `---\n\n`;
231
+ try {
232
+ appendFileSync(memoriaFile, header, "utf8");
233
+ } catch {
234
+ process.exit(0);
235
+ }
236
+ }
237
+
238
+ const entrada = `## ${fecha} — ${archivoModificado}\n> ${resumen}\n\n`;
239
+
240
+ try {
241
+ appendFileSync(memoriaFile, entrada, "utf8");
242
+ process.stderr.write(
243
+ `🧠 [agent-memory] Registrado en memoria de ${agente}: ${archivoModificado}\n`
244
+ );
245
+
246
+ // Alerta de tamaño + trigger auto-compresión
247
+ try {
248
+ const { size } = statSync(memoriaFile);
249
+ if (size > MEMORIA_UMBRAL_BYTES) {
250
+ process.stderr.write(
251
+ `⚠️ [agent-memory] Memoria de ${agente} supera ${Math.round(size / 1024)}KB\n`
252
+ );
253
+ // Trigger automático de compresión (silencioso, no bloquea)
254
+ triggerAutoCompresion(cwd, agente, memoriaFile);
255
+ }
256
+ } catch {
257
+ // Silencioso
258
+ }
259
+ } catch {
260
+ // Silencioso
261
+ }
262
+
263
+ process.exit(0);
264
+ }
265
+
266
+ function registrarMutacion(cwd, agente, archivoModificado, toolName) {
267
+ // Registra cada cambio (mutation) de archivo para tracking de calidad
268
+ // Permite medir: "¿este archivo fue reescrito 5 veces? ¿problemas de estabilidad?"
269
+ const mutDir = join(cwd, ".sdd", "observabilidad");
270
+ const mutFile = join(mutDir, "mutaciones.jsonl");
271
+
272
+ try {
273
+ if (!existsSync(mutDir)) {
274
+ mkdirSync(mutDir, { recursive: true });
275
+ }
276
+
277
+ const linea = JSON.stringify({
278
+ ts: new Date().toISOString(),
279
+ agente: agente || "main",
280
+ archivo: archivoModificado,
281
+ tool: toolName,
282
+ tipo: toolName === "Edit" ? "partial" : "full",
283
+ });
284
+ appendFileSync(mutFile, linea + "\n", "utf8");
285
+ } catch {
286
+ // Silencioso
287
+ }
288
+ }