sdd-es 2.0.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 (101) hide show
  1. package/.claude/settings.json +51 -0
  2. package/.claude-plugin/marketplace.json +31 -0
  3. package/.claude-plugin/plugin.json +97 -0
  4. package/README.md +332 -0
  5. package/agents/arquitecto.md +148 -0
  6. package/agents/asesor-datos.md +163 -0
  7. package/agents/critico.md +142 -0
  8. package/agents/desarrollador-backend.md +242 -0
  9. package/agents/desarrollador-frontend.md +120 -0
  10. package/agents/disenador-api.md +108 -0
  11. package/agents/documentador.md +177 -0
  12. package/agents/investigador.md +174 -0
  13. package/agents/operaciones.md +105 -0
  14. package/agents/revisor.md +153 -0
  15. package/agents/seguridad.md +216 -0
  16. package/agents/tester.md +286 -0
  17. package/claude-hooks/post-write-conventions.js +412 -0
  18. package/claude-hooks/pre-tool-guard.js +159 -0
  19. package/cli/index.js +401 -0
  20. package/commands/sdd.aclarar.md +200 -0
  21. package/commands/sdd.analizar.md +241 -0
  22. package/commands/sdd.ayuda.md +227 -0
  23. package/commands/sdd.canary.md +60 -0
  24. package/commands/sdd.checklist.md +174 -0
  25. package/commands/sdd.comprimir.md +166 -0
  26. package/commands/sdd.configurar.md +195 -0
  27. package/commands/sdd.constitucion.md +343 -0
  28. package/commands/sdd.crear-app.md +168 -0
  29. package/commands/sdd.crear-mcp.md +174 -0
  30. package/commands/sdd.descubrir.md +269 -0
  31. package/commands/sdd.desplegar.md +155 -0
  32. package/commands/sdd.especificar.md +302 -0
  33. package/commands/sdd.estado.md +124 -0
  34. package/commands/sdd.glosario.md +108 -0
  35. package/commands/sdd.implementar.md +377 -0
  36. package/commands/sdd.importar.md +91 -0
  37. package/commands/sdd.mapear.md +120 -0
  38. package/commands/sdd.md +119 -0
  39. package/commands/sdd.planificar.md +372 -0
  40. package/commands/sdd.qa.md +108 -0
  41. package/commands/sdd.release.md +253 -0
  42. package/commands/sdd.retro.md +82 -0
  43. package/commands/sdd.snapshot.md +122 -0
  44. package/commands/sdd.tareas.md +300 -0
  45. package/commands/sdd.verificar.md +239 -0
  46. package/configuracion-ejemplo/hooks-ejemplo/antes_cada_tarea.sh +18 -0
  47. package/configuracion-ejemplo/hooks-ejemplo/antes_implementar.sh +45 -0
  48. package/configuracion-ejemplo/hooks-ejemplo/despues_especificar.sh +14 -0
  49. package/configuracion-ejemplo/hooks-ejemplo/despues_implementar.sh +36 -0
  50. package/configuracion-ejemplo/hooks-ejemplo/despues_planificar.sh +19 -0
  51. package/configuracion-ejemplo/hooks-ejemplo/guardia-seguridad.sh +367 -0
  52. package/configuracion-ejemplo/sdd.config.yaml +310 -0
  53. package/docs/AGENTES.md +74 -0
  54. package/docs/COMPRESION.md +155 -0
  55. package/docs/EJEMPLO-PRACTICA.md +383 -0
  56. package/docs/EJEMPLOS.md +212 -0
  57. package/docs/FABRICA.md +185 -0
  58. package/docs/FILOSOFIA.md +61 -0
  59. package/docs/FLUJO.md +149 -0
  60. package/docs/INICIO-RAPIDO.md +116 -0
  61. package/docs/MAPAS.md +113 -0
  62. package/docs/MODELOS.md +103 -0
  63. package/docs/PERSONALIZACION.md +152 -0
  64. package/instalar.ps1 +39 -0
  65. package/instalar.sh +22 -0
  66. package/mcp-figma/README.md +158 -0
  67. package/mcp-figma/package.json +7 -0
  68. package/mcp-figma/src/component-generator.js +162 -0
  69. package/mcp-figma/src/design-system-analyzer.js +247 -0
  70. package/mcp-figma/src/figma-client.js +75 -0
  71. package/mcp-figma/src/index.js +114 -0
  72. package/mcp-figma/src/mcp.js +97 -0
  73. package/mcp-figma/src/style-mapper.js +85 -0
  74. package/package.json +50 -0
  75. package/plantillas/analisis.md +57 -0
  76. package/plantillas/checklist-especificacion.md +66 -0
  77. package/plantillas/constitucion.md +104 -0
  78. package/plantillas/decision-arquitectura.md +39 -0
  79. package/plantillas/dependencias-mapa.md +89 -0
  80. package/plantillas/especificacion.md +108 -0
  81. package/plantillas/estructura-mapa.md +40 -0
  82. package/plantillas/glosario.md +22 -0
  83. package/plantillas/index-especificaciones.md +15 -0
  84. package/plantillas/mcp-server.md +147 -0
  85. package/plantillas/plan.md +152 -0
  86. package/plantillas/simbolos-mapa.md +57 -0
  87. package/plantillas/snapshot.md +54 -0
  88. package/plantillas/tareas.md +72 -0
  89. package/presets/enterprise.yaml +69 -0
  90. package/presets/lean.yaml +63 -0
  91. package/presets/startup.yaml +67 -0
  92. package/skills/compresion-tokens.md +264 -0
  93. package/skills/constitucion-constraint.md +78 -0
  94. package/skills/deteccion-stack.md +175 -0
  95. package/skills/enrutador-agentes.md +69 -0
  96. package/skills/gestion-estado.md +114 -0
  97. package/skills/indexador.md +199 -0
  98. package/skills/modo-guiado/SKILL.md +78 -0
  99. package/skills/orquestacion-ptc/SKILL.md +96 -0
  100. package/skills/validacion-spec.md +52 -0
  101. package/skills/verificador-implementacion.md +71 -0
package/cli/index.js ADDED
@@ -0,0 +1,401 @@
1
+ #!/usr/bin/env node
2
+ // @ts-check
3
+ /**
4
+ * SDD-ES — CLI de instalación multiplataforma (Windows / macOS / Linux).
5
+ *
6
+ * Reemplaza a instalar.sh (bash-only) con Node puro, cero dependencias.
7
+ * Comparte la misma lógica de copia idempotente.
8
+ *
9
+ * Uso:
10
+ * npx sdd-es init instala en el proyecto actual (.claude/ + .sdd/)
11
+ * npx sdd-es init --global instala en $HOME/.claude (todos los proyectos)
12
+ * npx sdd-es update re-copia commands/agents/skills/hooks sin tocar .sdd/ ni settings
13
+ * npx sdd-es doctor diagnostica la instalación
14
+ * npx sdd-es --version muestra la versión
15
+ */
16
+
17
+ import {
18
+ cpSync,
19
+ existsSync,
20
+ mkdirSync,
21
+ readdirSync,
22
+ readFileSync,
23
+ writeFileSync,
24
+ } from "node:fs";
25
+ import { join, dirname } from "node:path";
26
+ import { fileURLToPath } from "node:url";
27
+ import { homedir } from "node:os";
28
+ import { execSync } from "node:child_process";
29
+
30
+ // ─── Paths ────────────────────────────────────────────────────────────────────
31
+
32
+ const __dirname = dirname(fileURLToPath(import.meta.url));
33
+ /** Raíz del plugin (un nivel arriba de cli/). */
34
+ const PLUGIN_DIR = join(__dirname, "..");
35
+
36
+ // ─── Colores (degradan a texto plano si no hay TTY) ─────────────────────────────
37
+
38
+ const tty = process.stdout.isTTY;
39
+ const c = {
40
+ verde: (s) => (tty ? `\x1b[0;32m${s}\x1b[0m` : s),
41
+ amarillo: (s) => (tty ? `\x1b[1;33m${s}\x1b[0m` : s),
42
+ rojo: (s) => (tty ? `\x1b[0;31m${s}\x1b[0m` : s),
43
+ azul: (s) => (tty ? `\x1b[0;34m${s}\x1b[0m` : s),
44
+ };
45
+
46
+ const info = (msg) => console.log(`${c.verde("✓")} ${msg}`);
47
+ const aviso = (msg) => console.log(`${c.amarillo("⚠")} ${msg}`);
48
+ const titulo = (msg) => console.log(`${c.azul("❯")} ${msg}`);
49
+ function error(msg) {
50
+ console.error(`${c.rojo("✗")} ${msg}`);
51
+ process.exit(1);
52
+ }
53
+
54
+ // ─── Utilidades de copia ────────────────────────────────────────────────────────
55
+
56
+ /** Copia todos los .md de un directorio origen a uno destino. Devuelve cuántos. */
57
+ function copyMd(srcDir, destDir) {
58
+ mkdirSync(destDir, { recursive: true });
59
+ const archivos = readdirSync(srcDir).filter((f) => f.endsWith(".md"));
60
+ for (const f of archivos) {
61
+ cpSync(join(srcDir, f), join(destDir, f));
62
+ }
63
+ return archivos.length;
64
+ }
65
+
66
+ /** Copia un directorio completo (recursivo). */
67
+ function copyDir(srcDir, destDir) {
68
+ mkdirSync(destDir, { recursive: true });
69
+ cpSync(srcDir, destDir, { recursive: true });
70
+ }
71
+
72
+ /** Lee la versión declarada en package.json del plugin. */
73
+ function pluginVersion() {
74
+ try {
75
+ const pkg = JSON.parse(
76
+ readFileSync(join(PLUGIN_DIR, "package.json"), "utf8")
77
+ );
78
+ return pkg.version || "desconocida";
79
+ } catch {
80
+ return "desconocida";
81
+ }
82
+ }
83
+
84
+ /** ¿Está `claude` en el PATH? */
85
+ function claudeEnPath() {
86
+ try {
87
+ execSync(process.platform === "win32" ? "where claude" : "command -v claude", {
88
+ stdio: "ignore",
89
+ });
90
+ return true;
91
+ } catch {
92
+ return false;
93
+ }
94
+ }
95
+
96
+ // ─── Banner ─────────────────────────────────────────────────────────────────────
97
+
98
+ function banner() {
99
+ console.log("");
100
+ console.log(" ╔══════════════════════════════════════════════════════╗");
101
+ console.log(" ║ ║");
102
+ console.log(" ║ SDD-ES — Instalación del Plugin v2.0 ║");
103
+ console.log(" ║ ║");
104
+ console.log(" ╚══════════════════════════════════════════════════════╝");
105
+ console.log("");
106
+ }
107
+
108
+ // ─── Copia núcleo (commands/agents/skills/hooks) ────────────────────────────────
109
+
110
+ /**
111
+ * Copia los artefactos del plugin a CLAUDE_DIR.
112
+ * @param {string} claudeDir destino (.claude del proyecto o global)
113
+ */
114
+ function copiarNucleo(claudeDir) {
115
+ titulo("Copiando comandos...");
116
+ const nCmd = copyMd(join(PLUGIN_DIR, "commands"), join(claudeDir, "commands"));
117
+ info(`Comandos instalados (${nCmd} archivos)`);
118
+
119
+ titulo("Copiando agentes...");
120
+ const nAg = copyMd(join(PLUGIN_DIR, "agents"), join(claudeDir, "agents"));
121
+ info(`Agentes instalados (${nAg} archivos)`);
122
+
123
+ titulo("Copiando skills...");
124
+ // Skills planas (.md) + skills en formato carpeta (SKILL.md)
125
+ const skillsSrc = join(PLUGIN_DIR, "skills");
126
+ const skillsDest = join(claudeDir, "skills");
127
+ mkdirSync(skillsDest, { recursive: true });
128
+ let nSk = 0;
129
+ for (const entry of readdirSync(skillsSrc, { withFileTypes: true })) {
130
+ if (entry.isFile() && entry.name.endsWith(".md")) {
131
+ cpSync(join(skillsSrc, entry.name), join(skillsDest, entry.name));
132
+ nSk++;
133
+ } else if (entry.isDirectory()) {
134
+ // skill en formato carpeta (contiene SKILL.md + recursos)
135
+ copyDir(join(skillsSrc, entry.name), join(skillsDest, entry.name));
136
+ nSk++;
137
+ }
138
+ }
139
+ info(`Skills instaladas (${nSk} entradas)`);
140
+
141
+ titulo("Instalando hooks de Claude Code...");
142
+ const hooksSrc = join(PLUGIN_DIR, "claude-hooks");
143
+ const hooksDest = join(claudeDir, "hooks");
144
+ mkdirSync(hooksDest, { recursive: true });
145
+ for (const f of readdirSync(hooksSrc).filter((f) => f.endsWith(".js"))) {
146
+ cpSync(join(hooksSrc, f), join(hooksDest, f));
147
+ }
148
+ info(`Hooks instalados (${hooksDest})`);
149
+ }
150
+
151
+ // ─── settings.json (no sobreescribe) ────────────────────────────────────────────
152
+
153
+ function copiarSettings(claudeDir) {
154
+ const dest = join(claudeDir, "settings.json");
155
+ const src = join(PLUGIN_DIR, ".claude", "settings.json");
156
+ if (!existsSync(dest)) {
157
+ cpSync(src, dest);
158
+ info(`Settings de seguridad instalados (${dest})`);
159
+ } else {
160
+ aviso(`settings.json ya existe en ${claudeDir} — no se sobreescribe`);
161
+ aviso(`Revisa manualmente: ${src}`);
162
+ }
163
+ }
164
+
165
+ // ─── Estructura .sdd/ del proyecto ──────────────────────────────────────────────
166
+
167
+ const HOOKS_README = `# Hooks personalizados de SDD-ES
168
+
169
+ Coloca aquí tus scripts ejecutables para integrar tu workflow con SDD.
170
+
171
+ ## Hooks por fase
172
+
173
+ - \`antes_constitucion.sh\` \`despues_constitucion.sh\`
174
+ - \`antes_especificar.sh\` \`despues_especificar.sh\`
175
+ - \`antes_aclarar.sh\` \`despues_aclarar.sh\`
176
+ - \`antes_planificar.sh\` \`despues_planificar.sh\`
177
+ - \`antes_tareas.sh\` \`despues_tareas.sh\`
178
+ - \`antes_analizar.sh\` \`despues_analizar.sh\`
179
+ - \`antes_implementar.sh\` \`despues_implementar.sh\`
180
+ - \`antes_cada_tarea.sh\` \`despues_cada_tarea.sh\` (recibe T_ID como arg)
181
+ - \`antes_verificar.sh\` \`despues_verificar.sh\`
182
+ - \`antes_importar.sh\`
183
+
184
+ Recuerda dar permiso de ejecución a tus hooks en sistemas Unix:
185
+ \`chmod +x .sdd/hooks/tu-hook.sh\`
186
+
187
+ Ver más ejemplos en docs/EJEMPLOS.md del plugin.
188
+ `;
189
+
190
+ function configurarSdd(claudeDir) {
191
+ titulo("Configurando estructura .sdd/ del proyecto...");
192
+
193
+ const sub = [
194
+ "memoria",
195
+ "especificaciones",
196
+ "cambios",
197
+ "arquitectura",
198
+ join("dominio", "definiciones"),
199
+ "hooks",
200
+ "plantillas",
201
+ ];
202
+ for (const d of sub) {
203
+ mkdirSync(join(process.cwd(), ".sdd", d), { recursive: true });
204
+ }
205
+
206
+ // Plantillas
207
+ copyMd(join(PLUGIN_DIR, "plantillas"), join(process.cwd(), ".sdd", "plantillas"));
208
+ info("Plantillas copiadas a .sdd/plantillas/");
209
+
210
+ // Config (no sobreescribe)
211
+ const configDest = join(process.cwd(), ".sdd", "sdd.config.yaml");
212
+ if (!existsSync(configDest)) {
213
+ cpSync(
214
+ join(PLUGIN_DIR, "configuracion-ejemplo", "sdd.config.yaml"),
215
+ configDest
216
+ );
217
+ info("Configuración por defecto copiada (.sdd/sdd.config.yaml)");
218
+ } else {
219
+ aviso("Configuración ya existe — no se sobreescribe");
220
+ }
221
+
222
+ // README de hooks (no sobreescribe)
223
+ const hooksReadme = join(process.cwd(), ".sdd", "hooks", "README.md");
224
+ if (!existsSync(hooksReadme)) {
225
+ writeFileSync(hooksReadme, HOOKS_README);
226
+ info("README de hooks creado (.sdd/hooks/README.md)");
227
+ }
228
+
229
+ // Documentación local (opcional)
230
+ const docsDest = join(process.cwd(), ".sdd", "docs");
231
+ if (!existsSync(docsDest)) {
232
+ copyMd(join(PLUGIN_DIR, "docs"), docsDest);
233
+ info("Documentación copiada a .sdd/docs/");
234
+ }
235
+ }
236
+
237
+ // ─── Fin ─────────────────────────────────────────────────────────────────────────
238
+
239
+ function pasosFinales() {
240
+ console.log("");
241
+ console.log(" ╔══════════════════════════════════════════════════════╗");
242
+ console.log(" ║ ✅ Instalación completada ║");
243
+ console.log(" ╠══════════════════════════════════════════════════════╣");
244
+ console.log(" ║ PRÓXIMO PASO: ║");
245
+ console.log(" ║ Abre Claude Code y ejecuta: /sdd.constitucion ║");
246
+ console.log(" ║ Para ver todos los comandos: /sdd.ayuda ║");
247
+ console.log(" ╚══════════════════════════════════════════════════════╝");
248
+ console.log("");
249
+ }
250
+
251
+ // ─── Comandos ───────────────────────────────────────────────────────────────────
252
+
253
+ function cmdInit(global) {
254
+ banner();
255
+ const claudeDir = global
256
+ ? join(homedir(), ".claude")
257
+ : join(process.cwd(), ".claude");
258
+ console.log(` Modo: ${global ? "GLOBAL" : "PROYECTO"} (${claudeDir})`);
259
+ console.log("");
260
+
261
+ if (!claudeEnPath()) {
262
+ aviso("Claude Code CLI no detectado en PATH. Los archivos se instalarán de todos modos.");
263
+ }
264
+
265
+ copiarNucleo(claudeDir);
266
+ copiarSettings(claudeDir);
267
+
268
+ if (!global) {
269
+ configurarSdd(claudeDir);
270
+ }
271
+
272
+ pasosFinales();
273
+ }
274
+
275
+ function cmdUpdate(global) {
276
+ banner();
277
+ const claudeDir = global
278
+ ? join(homedir(), ".claude")
279
+ : join(process.cwd(), ".claude");
280
+ console.log(` Actualizando núcleo en: ${claudeDir}`);
281
+ console.log(" (.sdd/ y settings.json del usuario NO se tocan)");
282
+ console.log("");
283
+
284
+ if (!existsSync(claudeDir)) {
285
+ error(`No existe ${claudeDir}. Ejecuta 'npx sdd-es init' primero.`);
286
+ }
287
+
288
+ copiarNucleo(claudeDir);
289
+ info("Núcleo actualizado. Tu .sdd/ y settings.json se conservan.");
290
+ console.log("");
291
+ }
292
+
293
+ function cmdDoctor() {
294
+ banner();
295
+ console.log(" Diagnóstico de SDD-ES");
296
+ console.log("");
297
+
298
+ let problemas = 0;
299
+
300
+ // Node
301
+ const nodeMajor = Number(process.versions.node.split(".")[0]);
302
+ if (nodeMajor >= 18) {
303
+ info(`Node ${process.versions.node} (>=18 requerido) ✓`);
304
+ } else {
305
+ aviso(`Node ${process.versions.node} es < 18. Actualiza Node.`);
306
+ problemas++;
307
+ }
308
+
309
+ // Claude CLI
310
+ if (claudeEnPath()) {
311
+ info("Claude Code CLI detectado en PATH ✓");
312
+ } else {
313
+ aviso("Claude Code CLI no está en PATH (instala desde claude.ai/code)");
314
+ }
315
+
316
+ // Versión del plugin
317
+ info(`Versión del plugin: ${pluginVersion()}`);
318
+
319
+ // Integridad del plugin
320
+ titulo("Verificando integridad del plugin...");
321
+ for (const dir of ["commands", "agents", "skills", "plantillas", "claude-hooks"]) {
322
+ const p = join(PLUGIN_DIR, dir);
323
+ if (existsSync(p)) {
324
+ const n = readdirSync(p).length;
325
+ info(`${dir}/ (${n} entradas)`);
326
+ } else {
327
+ aviso(`falta ${dir}/ en el plugin`);
328
+ problemas++;
329
+ }
330
+ }
331
+
332
+ // ¿Instalado en el proyecto actual?
333
+ titulo("Instalación en el proyecto actual...");
334
+ const localClaude = join(process.cwd(), ".claude", "commands");
335
+ const localSdd = join(process.cwd(), ".sdd");
336
+ if (existsSync(localClaude)) {
337
+ info(`.claude/commands/ presente (${readdirSync(localClaude).length} comandos)`);
338
+ } else {
339
+ aviso(".claude/commands/ no encontrado — ejecuta 'npx sdd-es init'");
340
+ }
341
+ if (existsSync(localSdd)) {
342
+ info(".sdd/ presente");
343
+ } else {
344
+ aviso(".sdd/ no encontrado — ejecuta 'npx sdd-es init'");
345
+ }
346
+
347
+ console.log("");
348
+ if (problemas === 0) {
349
+ info("Diagnóstico OK — sin problemas críticos.");
350
+ } else {
351
+ aviso(`${problemas} problema(s) detectado(s). Revisa arriba.`);
352
+ }
353
+ console.log("");
354
+ }
355
+
356
+ function uso() {
357
+ console.log(`
358
+ SDD-ES — CLI de instalación (v${pluginVersion()})
359
+
360
+ Uso:
361
+ npx sdd-es init [--global] Instala el plugin (proyecto o global)
362
+ npx sdd-es update [--global] Re-copia núcleo sin tocar tu .sdd/ ni settings
363
+ npx sdd-es doctor Diagnostica la instalación
364
+ npx sdd-es --version Muestra la versión
365
+
366
+ Tras instalar, abre Claude Code y ejecuta /sdd.constitucion
367
+ `);
368
+ }
369
+
370
+ // ─── Entry point ────────────────────────────────────────────────────────────────
371
+
372
+ function main() {
373
+ const args = process.argv.slice(2);
374
+ const comando = args[0];
375
+ const global = args.includes("--global") || args.includes("-g");
376
+
377
+ switch (comando) {
378
+ case "init":
379
+ cmdInit(global);
380
+ break;
381
+ case "update":
382
+ cmdUpdate(global);
383
+ break;
384
+ case "doctor":
385
+ cmdDoctor();
386
+ break;
387
+ case "--version":
388
+ case "-v":
389
+ console.log(pluginVersion());
390
+ break;
391
+ case "--help":
392
+ case "-h":
393
+ case undefined:
394
+ uso();
395
+ break;
396
+ default:
397
+ error(`Comando desconocido: '${comando}'. Usa 'npx sdd-es --help'.`);
398
+ }
399
+ }
400
+
401
+ main();
@@ -0,0 +1,200 @@
1
+ ---
2
+ description: Identifica y resuelve marcadores [NECESITA_ACLARACION] y ambigüedades en la spec activa. Hace preguntas estructuradas por categoría.
3
+ allowed-tools: Read, Write, Edit, Bash
4
+ handoffs:
5
+ - etiqueta: "Validar calidad"
6
+ comando: sdd.checklist
7
+ - etiqueta: "Planificar"
8
+ comando: sdd.planificar
9
+ ---
10
+
11
+ # /sdd.aclarar — Resolver Ambigüedades
12
+
13
+ Eres el **Analista de Ambigüedad**. Tu trabajo es encontrar TODO lo que pueda interpretarse de múltiples formas y resolverlo antes de pasar al plan.
14
+
15
+ ## PASO 1 — Cargar contexto
16
+
17
+ ```bash
18
+ SPEC_ID=$(grep -o '"especificacion_activa": "[^"]*"' .sdd/estado.json | cut -d'"' -f4)
19
+
20
+ if [ -z "$SPEC_ID" ]; then
21
+ echo "ERROR: no hay especificación activa. Ejecuta /sdd.especificar primero."
22
+ exit 1
23
+ fi
24
+
25
+ SPEC_FILE=".sdd/especificaciones/${SPEC_ID}/spec.md"
26
+ cat "$SPEC_FILE"
27
+ cat .sdd/memoria/constitucion.md | head -50
28
+ cat .sdd/dominio/glosario.md 2>/dev/null
29
+ ```
30
+
31
+ ## PASO 2 — Detección sistemática de ambigüedad
32
+
33
+ Analiza la spec aplicando estas categorías. **Sé exhaustivo** — encontrar 0 ambigüedades en una spec compleja es señal de revisión superficial.
34
+
35
+ ### Categoría A — Marcadores explícitos (críticos)
36
+ Busca con `grep`: `[NECESITA_ACLARACION]`, `[POR_DECIDIR]`, `[PENDIENTE]`, `[TODO]`, `???`.
37
+
38
+ ### Categoría B — Criterios no testeables (críticos)
39
+ Busca palabras que sugieren ambigüedad en CAs y requisitos:
40
+ - "rápido", "lento", "eficiente", "óptimo"
41
+ - "fácil", "intuitivo", "amigable", "moderno"
42
+ - "muchos", "varios", "algunos", "pocos"
43
+ - "generalmente", "usualmente", "a veces"
44
+ - "etc.", "entre otros"
45
+ - "debería" sin métrica (¿debería cuándo? ¿cómo medirlo?)
46
+
47
+ ### Categoría C — Actores y permisos (importante)
48
+ - ¿Quién PUEDE ejecutar cada acción?
49
+ - ¿Quién NO puede?
50
+ - ¿Qué pasa con usuarios no autenticados / con permisos parciales?
51
+
52
+ ### Categoría D — Datos y formatos (importante)
53
+ - Validaciones de input (longitud, formato, rangos)
54
+ - Comportamiento ante valores nulos/vacíos/cero/negativos
55
+ - Codificación, idioma, zonas horarias
56
+ - Unidades de medida
57
+
58
+ ### Categoría E — Estados y transiciones (importante)
59
+ - Estados posibles del recurso
60
+ - Transiciones válidas entre estados
61
+ - Comportamiento ante transiciones inválidas
62
+
63
+ ### Categoría F — Casos borde (importante)
64
+ - ¿Qué pasa con el primer elemento? ¿El último?
65
+ - ¿Qué pasa cuando la lista está vacía?
66
+ - ¿Concurrencia? ¿Qué si dos usuarios hacen X al mismo tiempo?
67
+ - ¿Idempotencia? ¿Qué si se repite la operación?
68
+
69
+ ### Categoría G — Integraciones externas (importante)
70
+ - ¿Qué pasa si el servicio externo está caído?
71
+ - Timeouts, reintentos, fallbacks
72
+ - Consistencia eventual vs fuerte
73
+
74
+ ### Categoría H — Performance y escala (medio)
75
+ - Volumen esperado de datos
76
+ - Frecuencia de uso
77
+ - Latencia aceptable
78
+
79
+ ## PASO 3 — Formular preguntas
80
+
81
+ Agrupa las preguntas por categoría. **Máximo 5 preguntas por ronda** para no abrumar.
82
+
83
+ Formato:
84
+
85
+ ```
86
+ 🔴 ACLARACIONES CRÍTICAS
87
+
88
+ **1. [Categoría]** — [pregunta concreta]
89
+
90
+ ¿Cuál se aplica?
91
+ a) [opción A con consecuencia]
92
+ b) [opción B con consecuencia]
93
+ c) [opción C con consecuencia]
94
+ d) Otra (describe)
95
+
96
+ **2. [Categoría]** — [pregunta]
97
+ ...
98
+
99
+ Por favor responde con el número y la letra (ej: "1.a, 2.c").
100
+ ```
101
+
102
+ Para preguntas no críticas, indica "(puedes responder 'cualquiera' si no te importa)".
103
+
104
+ ### Adaptación al perfil guiado
105
+
106
+ ```bash
107
+ PERFIL=$(grep -o '"perfil": *"[^"]*"' .sdd/estado.json 2>/dev/null | cut -d'"' -f4)
108
+ [ -z "$PERFIL" ] && PERFIL=$(grep '^perfil:' .sdd/sdd.config.yaml 2>/dev/null | cut -d':' -f2 | tr -d ' ')
109
+ ```
110
+
111
+ Si `PERFIL=guiado`, activa la skill `modo-guiado` y reformula **todas** las preguntas sin jerga técnica:
112
+
113
+ - Nada de "ISO 8601", "endpoint", "payload", "nullable", "idempotencia". Traduce a lenguaje cotidiano con opciones concretas.
114
+ - Reduce el número de preguntas al mínimo imprescindible (resuelve tú lo técnico con defaults sensatos; pregunta solo lo que afecta lo que el usuario *quiere*, no lo que afecta *cómo se implementa*).
115
+ - Una pregunta a la vez si son varias.
116
+
117
+ Ejemplo de traducción:
118
+
119
+ > ❌ Técnico: "¿El campo `vence_en` acepta null y qué zona horaria usa?"
120
+ > ✅ Guiado: "¿Las tareas tienen fecha límite? a) Sí b) No, solo si está hecha o no"
121
+
122
+ ## PASO 4 — Aplicar respuestas a la spec
123
+
124
+ Por cada respuesta:
125
+
126
+ 1. Localiza la sección de la spec donde se aplica
127
+ 2. Reemplaza el marcador `[NECESITA_ACLARACION]` o frase ambigua con texto preciso
128
+ 3. Si la respuesta agrega un criterio de aceptación nuevo, añádelo a la sección correspondiente con ID nuevo
129
+ 4. Si introduce un término del dominio, márcalo para añadir al glosario
130
+
131
+ ## PASO 5 — Documentar las decisiones
132
+
133
+ Añade o actualiza la sección "Aclaraciones" al final de la spec:
134
+
135
+ ```markdown
136
+ ## 14. Historial de Aclaraciones
137
+
138
+ | # | Categoría | Pregunta | Decisión | Fecha |
139
+ |---|-----------|----------|----------|-------|
140
+ | 1 | [cat] | [pregunta resumida] | [respuesta] | {FECHA} |
141
+ | 2 | [cat] | [pregunta] | [respuesta] | {FECHA} |
142
+ ```
143
+
144
+ ## PASO 6 — Detección de términos del dominio
145
+
146
+ Si las aclaraciones introdujeron términos nuevos del dominio (ej: "membresía Gold", "factura cerrada"), pregunta:
147
+
148
+ > Detecté nuevos términos del dominio: [lista].
149
+ > ¿Quieres añadirlos al glosario? Ejecutaré `/sdd.glosario` para cada uno.
150
+
151
+ ## PASO 7 — Verificar completitud
152
+
153
+ Después de aplicar todas las respuestas:
154
+
155
+ ```bash
156
+ # Verificar que no quedan marcadores críticos
157
+ grep -c "[NECESITA_ACLARACION]" "$SPEC_FILE"
158
+ ```
159
+
160
+ Si quedan marcadores, hay 2 opciones:
161
+ 1. **Nueva ronda** de preguntas (si son críticos)
162
+ 2. **Diferir** explícitamente (si son menores) — convertir a `[POR_DECIDIR]` con justificación
163
+
164
+ ## PASO 8 — Resumen
165
+
166
+ ```
167
+ ✅ Spec aclarada
168
+ 📁 .sdd/especificaciones/{ID}/spec.md
169
+ 📋 {N} preguntas respondidas
170
+ 🏷️ {M} términos del dominio identificados
171
+ ⚠️ {K} pendientes diferidos (no críticos)
172
+
173
+ SIGUIENTES PASOS:
174
+ /sdd.checklist — validar calidad formal
175
+ /sdd.planificar — pasar al plan técnico
176
+ /sdd.glosario — añadir términos al glosario
177
+ ```
178
+
179
+ ## VALIDACIÓN DE SALIDA
180
+
181
+ Antes de entregar al usuario, verifica que la spec quedó limpia:
182
+
183
+ ```bash
184
+ SPEC_ID=$(grep -o '"especificacion_activa": "[^"]*"' .sdd/estado.json 2>/dev/null | cut -d'"' -f4)
185
+ SPEC_FILE=".sdd/especificaciones/${SPEC_ID}/spec.md"
186
+
187
+ # No deben quedar marcadores críticos sin resolver
188
+ PENDIENTES=$(grep -c "\[NECESITA_ACLARACION\]" "$SPEC_FILE" 2>/dev/null || echo 0)
189
+ [ "$PENDIENTES" -gt 0 ] && echo "ADVERTENCIA: $PENDIENTES marcadores [NECESITA_ACLARACION] sin resolver"
190
+
191
+ # El historial de aclaraciones debe existir
192
+ grep -q "Historial de Aclaraciones" "$SPEC_FILE" || echo "FALTA: sección Historial de Aclaraciones"
193
+
194
+ echo "Validación completada — marcadores pendientes: $PENDIENTES"
195
+ ```
196
+
197
+ Si quedan marcadores críticos, hacer nueva ronda de preguntas antes de habilitar el handoff a `/sdd.planificar`.
198
+
199
+ ---
200
+ **HOOK:** `.sdd/hooks/despues_aclarar.sh`