refacil-sdd-ai 4.0.1 → 4.0.3
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/README.md +9 -5
- package/bin/cli.js +42 -20
- package/lib/hooks.js +23 -0
- package/package.json +1 -1
- package/skills/setup/SKILL.md +27 -4
- package/skills/update/SKILL.md +7 -6
package/README.md
CHANGED
|
@@ -71,9 +71,10 @@ npm uninstall -g refacil-sdd-ai
|
|
|
71
71
|
|
|
72
72
|
| Comando | Descripcion |
|
|
73
73
|
|---|---|
|
|
74
|
-
| `refacil-sdd-ai check-update` | Verifica nueva version
|
|
75
|
-
| `refacil-sdd-ai
|
|
76
|
-
| `refacil-sdd-ai
|
|
74
|
+
| `refacil-sdd-ai check-update` | (`SessionStart`) Verifica nueva version, sincroniza skills y `compact-guidance` en AGENTS.md |
|
|
75
|
+
| `refacil-sdd-ai notify-update` | (`Stop` / `afterAgentResponse`) Notifica al LLM sobre migraciones pendientes al finalizar la primera respuesta; el agente ejecuta `/refacil:update` automaticamente |
|
|
76
|
+
| `refacil-sdd-ai check-review` | (`PreToolUse`) Bloquea `git push` si falta `.review-passed` en algun cambio activo |
|
|
77
|
+
| `refacil-sdd-ai compact-bash` | (`PreToolUse`) Reescribe comandos Bash bare via `updatedInput` |
|
|
77
78
|
|
|
78
79
|
### Control del rewrite de comandos (`compact-bash`)
|
|
79
80
|
|
|
@@ -232,11 +233,14 @@ Se instalan en `.claude/settings.json` **y** `.cursor/settings.json` durante `in
|
|
|
232
233
|
|
|
233
234
|
| Hook | Evento | Que hace |
|
|
234
235
|
|---|---|---|
|
|
235
|
-
| `check-update` | `SessionStart` | Chequea nueva version en npm, la instala, y **sincroniza el bloque `compact-guidance`** en `AGENTS.md`. |
|
|
236
|
+
| `check-update` | `SessionStart` | Chequea nueva version en npm, la instala, sincroniza skills y **sincroniza el bloque `compact-guidance`** en `AGENTS.md`. Si hubo actualizacion, escribe un flag de notificacion para el siguiente hook. |
|
|
237
|
+
| `notify-update` | `Stop` (Claude Code) / `afterAgentResponse` (Cursor) | Al finalizar la primera respuesta del agente, lee el flag de actualizacion pendiente. Si existe, inyecta la instruccion de ejecutar `/refacil:update` — el agente la ejecuta automaticamente y el usuario ve el resultado al final. El flag se borra tras notificar. |
|
|
236
238
|
| `compact-bash` | `PreToolUse` (Bash) | Reescribe silenciosamente comandos Bash bare via `updatedInput`. Sin turnos extra, sin que el IDE vea el cambio. Requiere Claude Code >= 2.1.89. |
|
|
237
239
|
| `check-review` | `PreToolUse` (Bash) | Intercepta `git push` y bloquea si falta `.review-passed` en algun cambio activo. |
|
|
238
240
|
|
|
239
|
-
Los
|
|
241
|
+
Los cuatro hooks se instalan en `.claude/settings.json` (Claude Code) y `.cursor/settings.json` (Cursor) con la misma logica parametrica.
|
|
242
|
+
|
|
243
|
+
> **Por que dos hooks para el update?** `SessionStart` hace el sync silencioso al abrir la sesion sin interaccion del usuario. `notify-update` en `Stop` se dispara al finalizar la primera respuesta del agente — la instruccion queda al final visible para el usuario y Claude la ejecuta automaticamente en el siguiente turno.
|
|
240
244
|
|
|
241
245
|
### Gate de review en el push
|
|
242
246
|
|
package/bin/cli.js
CHANGED
|
@@ -29,7 +29,38 @@ const { syncIgnoreFiles } = require('../lib/ignore-files');
|
|
|
29
29
|
const packageRoot = path.resolve(__dirname, '..');
|
|
30
30
|
const projectRoot = process.cwd();
|
|
31
31
|
|
|
32
|
-
// ---
|
|
32
|
+
// --- check-update (SessionStart) + notify-update (UserPromptSubmit) ---
|
|
33
|
+
|
|
34
|
+
function ideBaseDir(root) {
|
|
35
|
+
const claudeDir = path.join(root, '.claude');
|
|
36
|
+
const cursorDir = path.join(root, '.cursor');
|
|
37
|
+
return fs.existsSync(claudeDir) ? claudeDir : fs.existsSync(cursorDir) ? cursorDir : null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function writePendingUpdateFlag(root, fromVersion, toVersion) {
|
|
41
|
+
const base = ideBaseDir(root);
|
|
42
|
+
if (!base) return;
|
|
43
|
+
try {
|
|
44
|
+
fs.writeFileSync(path.join(base, '.refacil-pending-update'), JSON.stringify({ from: fromVersion, to: toVersion }));
|
|
45
|
+
} catch (_) {}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function notifyUpdate() {
|
|
49
|
+
const base = ideBaseDir(projectRoot);
|
|
50
|
+
if (!base) return;
|
|
51
|
+
const flagPath = path.join(base, '.refacil-pending-update');
|
|
52
|
+
if (!fs.existsSync(flagPath)) return;
|
|
53
|
+
|
|
54
|
+
let info = {};
|
|
55
|
+
try { info = JSON.parse(fs.readFileSync(flagPath, 'utf8')); } catch (_) {}
|
|
56
|
+
try { fs.unlinkSync(flagPath); } catch (_) {}
|
|
57
|
+
|
|
58
|
+
const fromLabel = info.from ? `v${info.from}` : 'version desconocida';
|
|
59
|
+
console.log(
|
|
60
|
+
`[refacil-sdd-ai] La metodologia fue actualizada (${fromLabel} → v${info.to}). ` +
|
|
61
|
+
'Ejecuta /refacil:update ahora para aplicar las migraciones pendientes e informa el resultado al usuario.',
|
|
62
|
+
);
|
|
63
|
+
}
|
|
33
64
|
|
|
34
65
|
function repoIsInitialized() {
|
|
35
66
|
return (
|
|
@@ -65,9 +96,7 @@ function checkUpdate() {
|
|
|
65
96
|
try {
|
|
66
97
|
syncCompactGuidance(projectRoot, packageRoot);
|
|
67
98
|
} catch (err) {
|
|
68
|
-
process.stderr.write(
|
|
69
|
-
`[refacil-sdd-ai] No se pudo sincronizar compact-guidance: ${err.message}\n`,
|
|
70
|
-
);
|
|
99
|
+
process.stderr.write(`[refacil-sdd-ai] No se pudo sincronizar compact-guidance: ${err.message}\n`);
|
|
71
100
|
}
|
|
72
101
|
|
|
73
102
|
const syncResult = syncRepoSkillsIfStale(localVersion);
|
|
@@ -77,10 +106,7 @@ function checkUpdate() {
|
|
|
77
106
|
`[refacil-sdd-ai] Skills de este repo sincronizadas (${fromLabel} -> v${syncResult.to}). ` +
|
|
78
107
|
'Reinicia la sesion de Claude Code o Cursor para detectar los cambios.',
|
|
79
108
|
);
|
|
80
|
-
|
|
81
|
-
'[refacil-sdd-ai] La metodologia fue actualizada. ' +
|
|
82
|
-
'Pregunta al usuario si desea aplicar las migraciones pendientes y, si confirma, ejecuta /refacil:update.',
|
|
83
|
-
);
|
|
109
|
+
writePendingUpdateFlag(projectRoot, syncResult.from, syncResult.to);
|
|
84
110
|
} else if (syncResult && syncResult.failed) {
|
|
85
111
|
console.log(
|
|
86
112
|
`[refacil-sdd-ai] Skills de este repo estan desactualizadas respecto al paquete global (v${syncResult.to}) ` +
|
|
@@ -98,16 +124,8 @@ function checkUpdate() {
|
|
|
98
124
|
if (!latest || latest === localVersion) return;
|
|
99
125
|
|
|
100
126
|
try {
|
|
101
|
-
execSync('npm update -g refacil-sdd-ai', {
|
|
102
|
-
|
|
103
|
-
timeout: 60000,
|
|
104
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
105
|
-
});
|
|
106
|
-
execSync('refacil-sdd-ai update', {
|
|
107
|
-
encoding: 'utf8',
|
|
108
|
-
timeout: 30000,
|
|
109
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
110
|
-
});
|
|
127
|
+
execSync('npm update -g refacil-sdd-ai', { encoding: 'utf8', timeout: 60000, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
128
|
+
execSync('refacil-sdd-ai update', { encoding: 'utf8', timeout: 30000, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
111
129
|
writeRepoVersion(projectRoot, latest);
|
|
112
130
|
console.log(
|
|
113
131
|
`[refacil-sdd-ai] La metodologia SDD-AI se actualizo automaticamente de v${localVersion} a v${latest}. Skills y hooks sincronizados.`,
|
|
@@ -327,8 +345,9 @@ function help() {
|
|
|
327
345
|
Comandos:
|
|
328
346
|
init Instala skills en .claude/ y .cursor/, crea CLAUDE.md y .cursorrules
|
|
329
347
|
update Re-copia skills (para actualizar a nueva version del paquete)
|
|
330
|
-
check-update
|
|
331
|
-
|
|
348
|
+
check-update Sincroniza skills y compact-guidance al inicio de sesion (hook SessionStart)
|
|
349
|
+
notify-update Notifica al LLM sobre migraciones pendientes (hook UserPromptSubmit)
|
|
350
|
+
check-review Verifica que el review se haya completado (usado por hook PreToolUse)
|
|
332
351
|
compact-bash Reescribe comandos Bash bare para reducir tokens (usado por hook PreToolUse)
|
|
333
352
|
compact Subcomandos del hook compact-bash:
|
|
334
353
|
compact stats - Estadisticas completas (hook + ya-compacto) y ahorro estimado
|
|
@@ -380,6 +399,9 @@ switch (command) {
|
|
|
380
399
|
case 'check-update':
|
|
381
400
|
checkUpdate();
|
|
382
401
|
break;
|
|
402
|
+
case 'notify-update':
|
|
403
|
+
notifyUpdate();
|
|
404
|
+
break;
|
|
383
405
|
case 'check-review':
|
|
384
406
|
checkReview();
|
|
385
407
|
break;
|
package/lib/hooks.js
CHANGED
|
@@ -31,6 +31,20 @@ function installHooks(ideDir, projectRoot) {
|
|
|
31
31
|
changed = true;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// Claude Code usa "Stop"; Cursor usa "afterAgentResponse" (equivalente al fin de turno del agente)
|
|
35
|
+
const notifyEvent = ideDir === '.cursor' ? 'afterAgentResponse' : 'Stop';
|
|
36
|
+
if (!settings.hooks[notifyEvent]) settings.hooks[notifyEvent] = [];
|
|
37
|
+
|
|
38
|
+
const hasNotifyHook = settings.hooks[notifyEvent].some((h) => h._sdd_notify === true);
|
|
39
|
+
if (!hasNotifyHook) {
|
|
40
|
+
settings.hooks[notifyEvent].push({
|
|
41
|
+
_sdd_notify: true,
|
|
42
|
+
matcher: '',
|
|
43
|
+
hooks: [{ type: 'command', command: 'refacil-sdd-ai notify-update' }],
|
|
44
|
+
});
|
|
45
|
+
changed = true;
|
|
46
|
+
}
|
|
47
|
+
|
|
34
48
|
if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
|
|
35
49
|
|
|
36
50
|
// compact-bash debe correr ANTES de check-review para que el rewrite sea visible
|
|
@@ -87,6 +101,15 @@ function uninstallHooks(ideDir, projectRoot) {
|
|
|
87
101
|
if (settings.hooks.SessionStart.length === 0) delete settings.hooks.SessionStart;
|
|
88
102
|
}
|
|
89
103
|
|
|
104
|
+
for (const notifyEvent of ['Stop', 'afterAgentResponse']) {
|
|
105
|
+
if (Array.isArray(settings.hooks[notifyEvent])) {
|
|
106
|
+
const original = settings.hooks[notifyEvent].length;
|
|
107
|
+
settings.hooks[notifyEvent] = settings.hooks[notifyEvent].filter((h) => h._sdd_notify !== true);
|
|
108
|
+
if (settings.hooks[notifyEvent].length !== original) changed = true;
|
|
109
|
+
if (settings.hooks[notifyEvent].length === 0) delete settings.hooks[notifyEvent];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
90
113
|
if (Array.isArray(settings.hooks.PreToolUse)) {
|
|
91
114
|
const original = settings.hooks.PreToolUse.length;
|
|
92
115
|
settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(
|
package/package.json
CHANGED
package/skills/setup/SKILL.md
CHANGED
|
@@ -50,19 +50,42 @@ openspec init --tools claude,cursor
|
|
|
50
50
|
|
|
51
51
|
`openspec init` instala `opsx:*` en `.claude/` y `.cursor/`; convive con `refacil:*` — el equipo usa **refacil:*** como interfaz principal.
|
|
52
52
|
|
|
53
|
-
`openspec init` no elimina skills de instalaciones anteriores. Después del init,
|
|
53
|
+
`openspec init` no elimina skills ni commands de instalaciones anteriores. Después del init, ejecuta este script para limpiar todo lo sobrante:
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
56
|
node -e "
|
|
57
57
|
const fs=require('fs');
|
|
58
58
|
const path=require('path');
|
|
59
|
-
const
|
|
59
|
+
const requiredSkills=new Set(['openspec-propose','openspec-explore','openspec-apply-change','openspec-archive-change','openspec-verify-change']);
|
|
60
|
+
const requiredCmdsOpsx=new Set(['apply.md','archive.md','explore.md','propose.md','verify.md']);
|
|
61
|
+
const requiredCmdsCursor=new Set(['opsx-apply.md','opsx-archive.md','opsx-explore.md','opsx-propose.md','opsx-verify.md']);
|
|
62
|
+
// Skills sobrantes
|
|
60
63
|
for(const base of ['.claude/skills','.cursor/skills']){
|
|
61
64
|
if(!fs.existsSync(base)) continue;
|
|
62
65
|
for(const d of fs.readdirSync(base)){
|
|
63
|
-
if(d.startsWith('openspec-') && !
|
|
66
|
+
if(d.startsWith('openspec-') && !requiredSkills.has(d)){
|
|
64
67
|
fs.rmSync(path.join(base,d),{recursive:true,force:true});
|
|
65
|
-
console.log('Eliminada:',path.join(base,d));
|
|
68
|
+
console.log('Eliminada skill:',path.join(base,d));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Commands sobrantes — .claude/commands/opsx/ (archivos .md)
|
|
73
|
+
const claudeCmds='.claude/commands/opsx';
|
|
74
|
+
if(fs.existsSync(claudeCmds)){
|
|
75
|
+
for(const f of fs.readdirSync(claudeCmds)){
|
|
76
|
+
if(f.endsWith('.md') && !requiredCmdsOpsx.has(f)){
|
|
77
|
+
fs.rmSync(path.join(claudeCmds,f),{force:true});
|
|
78
|
+
console.log('Eliminado command:',path.join(claudeCmds,f));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Commands sobrantes — .cursor/commands/ (archivos opsx-*.md)
|
|
83
|
+
const cursorCmds='.cursor/commands';
|
|
84
|
+
if(fs.existsSync(cursorCmds)){
|
|
85
|
+
for(const f of fs.readdirSync(cursorCmds)){
|
|
86
|
+
if(f.startsWith('opsx-') && f.endsWith('.md') && !requiredCmdsCursor.has(f)){
|
|
87
|
+
fs.rmSync(path.join(cursorCmds,f),{force:true});
|
|
88
|
+
console.log('Eliminado command:',path.join(cursorCmds,f));
|
|
66
89
|
}
|
|
67
90
|
}
|
|
68
91
|
}
|
package/skills/update/SKILL.md
CHANGED
|
@@ -17,7 +17,7 @@ Evalua cada condicion y marca si aplica:
|
|
|
17
17
|
| 1 | `AGENTS.md` existe y no existe carpeta `.agents/` | Reestructurar en `.agents/` + reescribir como indice |
|
|
18
18
|
| 2 | `CLAUDE.md` tiene mas de 5 lineas o no apunta a `AGENTS.md` | Reemplazar por indice minimo |
|
|
19
19
|
| 3 | `.cursorrules` tiene mas de 5 lineas o no apunta a `AGENTS.md` | Reemplazar por indice minimo |
|
|
20
|
-
| 4 | Existen skills `openspec-*` en `.claude
|
|
20
|
+
| 4 | Existen skills `openspec-*` o commands `opsx-*` fuera del conjunto requerido en `.claude/` o `.cursor/` | Eliminar los sobrantes y reconfigurar OpenSpec |
|
|
21
21
|
|
|
22
22
|
Si ninguna condicion aplica: informar al usuario que todo esta al dia y salir sin cambios.
|
|
23
23
|
|
|
@@ -72,12 +72,13 @@ Contexto completo del proyecto: ver `AGENTS.md`.
|
|
|
72
72
|
Si no existe, ejecuta `/refacil:setup`.
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
### Migracion 4 — Skills OpenSpec sobrantes
|
|
75
|
+
### Migracion 4 — Skills y commands OpenSpec sobrantes
|
|
76
76
|
|
|
77
|
-
Conjunto requerido: `openspec-propose`, `openspec-explore`, `openspec-apply-change`, `openspec-archive-change`, `openspec-verify-change`.
|
|
77
|
+
Conjunto requerido de skills: `openspec-propose`, `openspec-explore`, `openspec-apply-change`, `openspec-archive-change`, `openspec-verify-change`.
|
|
78
|
+
Conjunto requerido de commands: `.claude/commands/opsx/` → `apply.md`, `archive.md`, `explore.md`, `propose.md`, `verify.md`; `.cursor/commands/` → `opsx-apply.md`, `opsx-archive.md`, `opsx-explore.md`, `opsx-propose.md`, `opsx-verify.md`.
|
|
78
79
|
|
|
79
|
-
1. Detecta
|
|
80
|
-
2.
|
|
80
|
+
1. Detecta skills `openspec-*` en `.claude/skills/` y `.cursor/skills/` fuera del conjunto requerido.
|
|
81
|
+
2. Detecta commands `opsx-*.md` en `.cursor/commands/` y archivos `.md` en `.claude/commands/opsx/` fuera del conjunto requerido.
|
|
81
82
|
3. Reconfigura OpenSpec con solo los 5 workflows necesarios:
|
|
82
83
|
```bash
|
|
83
84
|
node -e "
|
|
@@ -90,7 +91,7 @@ Conjunto requerido: `openspec-propose`, `openspec-explore`, `openspec-apply-chan
|
|
|
90
91
|
fs.writeFileSync(p,JSON.stringify(c,null,2));
|
|
91
92
|
"
|
|
92
93
|
```
|
|
93
|
-
4. Elimina
|
|
94
|
+
4. Elimina skills y commands sobrantes.
|
|
94
95
|
5. Ejecuta `openspec init --tools claude,cursor` para dejar el estado limpio.
|
|
95
96
|
|
|
96
97
|
## Paso 4: Resumen
|