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.
- package/.claude/settings.json +51 -0
- package/.claude-plugin/marketplace.json +31 -0
- package/.claude-plugin/plugin.json +97 -0
- package/README.md +332 -0
- package/agents/arquitecto.md +148 -0
- package/agents/asesor-datos.md +163 -0
- package/agents/critico.md +142 -0
- package/agents/desarrollador-backend.md +242 -0
- package/agents/desarrollador-frontend.md +120 -0
- package/agents/disenador-api.md +108 -0
- package/agents/documentador.md +177 -0
- package/agents/investigador.md +174 -0
- package/agents/operaciones.md +105 -0
- package/agents/revisor.md +153 -0
- package/agents/seguridad.md +216 -0
- package/agents/tester.md +286 -0
- package/claude-hooks/post-write-conventions.js +412 -0
- package/claude-hooks/pre-tool-guard.js +159 -0
- package/cli/index.js +401 -0
- package/commands/sdd.aclarar.md +200 -0
- package/commands/sdd.analizar.md +241 -0
- package/commands/sdd.ayuda.md +227 -0
- package/commands/sdd.canary.md +60 -0
- package/commands/sdd.checklist.md +174 -0
- package/commands/sdd.comprimir.md +166 -0
- package/commands/sdd.configurar.md +195 -0
- package/commands/sdd.constitucion.md +343 -0
- package/commands/sdd.crear-app.md +168 -0
- package/commands/sdd.crear-mcp.md +174 -0
- package/commands/sdd.descubrir.md +269 -0
- package/commands/sdd.desplegar.md +155 -0
- package/commands/sdd.especificar.md +302 -0
- package/commands/sdd.estado.md +124 -0
- package/commands/sdd.glosario.md +108 -0
- package/commands/sdd.implementar.md +377 -0
- package/commands/sdd.importar.md +91 -0
- package/commands/sdd.mapear.md +120 -0
- package/commands/sdd.md +119 -0
- package/commands/sdd.planificar.md +372 -0
- package/commands/sdd.qa.md +108 -0
- package/commands/sdd.release.md +253 -0
- package/commands/sdd.retro.md +82 -0
- package/commands/sdd.snapshot.md +122 -0
- package/commands/sdd.tareas.md +300 -0
- package/commands/sdd.verificar.md +239 -0
- package/configuracion-ejemplo/hooks-ejemplo/antes_cada_tarea.sh +18 -0
- package/configuracion-ejemplo/hooks-ejemplo/antes_implementar.sh +45 -0
- package/configuracion-ejemplo/hooks-ejemplo/despues_especificar.sh +14 -0
- package/configuracion-ejemplo/hooks-ejemplo/despues_implementar.sh +36 -0
- package/configuracion-ejemplo/hooks-ejemplo/despues_planificar.sh +19 -0
- package/configuracion-ejemplo/hooks-ejemplo/guardia-seguridad.sh +367 -0
- package/configuracion-ejemplo/sdd.config.yaml +310 -0
- package/docs/AGENTES.md +74 -0
- package/docs/COMPRESION.md +155 -0
- package/docs/EJEMPLO-PRACTICA.md +383 -0
- package/docs/EJEMPLOS.md +212 -0
- package/docs/FABRICA.md +185 -0
- package/docs/FILOSOFIA.md +61 -0
- package/docs/FLUJO.md +149 -0
- package/docs/INICIO-RAPIDO.md +116 -0
- package/docs/MAPAS.md +113 -0
- package/docs/MODELOS.md +103 -0
- package/docs/PERSONALIZACION.md +152 -0
- package/instalar.ps1 +39 -0
- package/instalar.sh +22 -0
- package/mcp-figma/README.md +158 -0
- package/mcp-figma/package.json +7 -0
- package/mcp-figma/src/component-generator.js +162 -0
- package/mcp-figma/src/design-system-analyzer.js +247 -0
- package/mcp-figma/src/figma-client.js +75 -0
- package/mcp-figma/src/index.js +114 -0
- package/mcp-figma/src/mcp.js +97 -0
- package/mcp-figma/src/style-mapper.js +85 -0
- package/package.json +50 -0
- package/plantillas/analisis.md +57 -0
- package/plantillas/checklist-especificacion.md +66 -0
- package/plantillas/constitucion.md +104 -0
- package/plantillas/decision-arquitectura.md +39 -0
- package/plantillas/dependencias-mapa.md +89 -0
- package/plantillas/especificacion.md +108 -0
- package/plantillas/estructura-mapa.md +40 -0
- package/plantillas/glosario.md +22 -0
- package/plantillas/index-especificaciones.md +15 -0
- package/plantillas/mcp-server.md +147 -0
- package/plantillas/plan.md +152 -0
- package/plantillas/simbolos-mapa.md +57 -0
- package/plantillas/snapshot.md +54 -0
- package/plantillas/tareas.md +72 -0
- package/presets/enterprise.yaml +69 -0
- package/presets/lean.yaml +63 -0
- package/presets/startup.yaml +67 -0
- package/skills/compresion-tokens.md +264 -0
- package/skills/constitucion-constraint.md +78 -0
- package/skills/deteccion-stack.md +175 -0
- package/skills/enrutador-agentes.md +69 -0
- package/skills/gestion-estado.md +114 -0
- package/skills/indexador.md +199 -0
- package/skills/modo-guiado/SKILL.md +78 -0
- package/skills/orquestacion-ptc/SKILL.md +96 -0
- package/skills/validacion-spec.md +52 -0
- 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`
|