refacil-sdd-ai 4.3.0 → 4.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/agents/auditor.md CHANGED
@@ -157,10 +157,17 @@ Next step: [/refacil:archive | /refacil:verify]
157
157
  "summary": "<1-line summary>",
158
158
  "failCount": <integer count of FAILs in NEW code>,
159
159
  "preexistingCount": <integer count of pre-existing FAILs found>,
160
- "blockers": <true|false — new code only>
160
+ "blockers": <true|false — new code only>,
161
+ "failedFiles": ["path/to/file-1.ts", "path/to/file-2.ts"]
161
162
  }
162
163
  ```
163
164
 
165
+ **`failedFiles` rules**:
166
+ - On `REQUIERE CORRECCIONES`: list the relative paths (from repo root) of every file in the **blocking scope** (`changedFiles`) that had at least one CRITICAL or HIGH FAIL.
167
+ - On `APROBADO` or `APROBADO CON OBSERVACIONES`: emit `"failedFiles": []`.
168
+ - Files with only MEDIUM/LOW findings do NOT appear in `failedFiles`.
169
+ - Pre-existing context files do NOT appear in `failedFiles` — only blocking scope.
170
+
164
171
  **IMPORTANT about the JSON block**:
165
172
  - Use the literal fence ` ```refacil-review-result ` (not ` ```json `).
166
173
  - Emit it ALWAYS, even if the verdict is `REQUIERE CORRECCIONES`.
@@ -2,7 +2,7 @@
2
2
  name: refacil-proposer
3
3
  description: Generates SDD-AI planning artifacts (proposal, specs, design, tasks) for any codebase. Delegated by /refacil:propose — do not invoke directly.
4
4
  tools: Read, Grep, Glob, Bash, Edit, Write
5
- model: opusplan
5
+ model: sonnet
6
6
  ---
7
7
 
8
8
  # refacil-proposer — Planning Artifact Generator
package/bin/cli.js CHANGED
@@ -25,12 +25,12 @@ const {
25
25
  const { installHooks, uninstallHooks, cleanLegacySettingsHooks } = require('../lib/hooks');
26
26
  const { handleCompact } = require('../lib/commands/compact');
27
27
  const { handleBus } = require('../lib/commands/bus');
28
- const { handleSdd, autoMigrateOpenspec } = require('../lib/commands/sdd');
28
+ const { handleSdd, autoMigrateOpenspec, findProjectRoot } = require('../lib/commands/sdd');
29
29
  const { syncIgnoreFiles } = require('../lib/ignore-files');
30
30
  const { methodologyMigrationPending } = require('../lib/methodology-migration-pending');
31
31
 
32
32
  const packageRoot = path.resolve(__dirname, '..');
33
- const projectRoot = process.cwd();
33
+ const projectRoot = findProjectRoot();
34
34
 
35
35
  // --- check-update (SessionStart) + notify-update (UserPromptSubmit) ---
36
36
 
@@ -506,7 +506,7 @@ function help() {
506
506
  4. Run: /refacil:setup (generates AGENTS.md for your project)
507
507
 
508
508
  Requirements:
509
- - Node.js >= 20.19.0
509
+ - Node.js >= 20.0.0
510
510
  - Claude Code >= 2.1.89 (required by compact-bash for silent rewrite) or Cursor
511
511
  `);
512
512
  }
@@ -549,7 +549,7 @@ switch (command) {
549
549
  handleBus(process.argv[3], process.argv.slice(4), packageRoot);
550
550
  break;
551
551
  case 'sdd':
552
- handleSdd(process.argv[3], process.argv.slice(4));
552
+ handleSdd(process.argv[3], process.argv.slice(4), projectRoot);
553
553
  break;
554
554
  case 'clean':
555
555
  clean();
@@ -3,7 +3,19 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
- const projectRoot = process.cwd();
6
+ function findProjectRoot() {
7
+ let dir = process.cwd();
8
+ const { root } = path.parse(dir);
9
+ while (dir !== root) {
10
+ if (fs.existsSync(path.join(dir, 'refacil-sdd')) || fs.existsSync(path.join(dir, '.git'))) {
11
+ return dir;
12
+ }
13
+ const parent = path.dirname(dir);
14
+ if (parent === dir) break;
15
+ dir = parent;
16
+ }
17
+ return process.cwd();
18
+ }
7
19
 
8
20
  // --- Helpers ---
9
21
 
@@ -65,6 +77,65 @@ function autoMigrateOpenspec(root) {
65
77
  // Si ambos existen o ninguno existe → no hacer nada
66
78
  }
67
79
 
80
+ // --- Minimal YAML parser/serializer for memory.yaml ---
81
+ // Supports: string values and string-array values only.
82
+
83
+ function parseMemoryYaml(content) {
84
+ const result = {};
85
+ const lines = content.split('\n');
86
+ let currentKey = null;
87
+ let currentList = null;
88
+
89
+ for (const line of lines) {
90
+ if (!line.trim() || line.trim().startsWith('#')) continue;
91
+
92
+ // List item: " - value"
93
+ if (/^\s{2,}- /.test(line)) {
94
+ const value = line.replace(/^\s*- /, '').trim();
95
+ if (currentKey && currentList !== null) {
96
+ currentList.push(value);
97
+ }
98
+ continue;
99
+ }
100
+
101
+ // Key-value: "key: value" or "key:" (empty/start of list)
102
+ const kvMatch = line.match(/^([a-zA-Z][a-zA-Z0-9_-]*):\s*(.*)/);
103
+ if (kvMatch) {
104
+ currentKey = kvMatch[1];
105
+ const val = kvMatch[2].trim();
106
+ if (val === '') {
107
+ // Could be a list
108
+ currentList = [];
109
+ result[currentKey] = currentList;
110
+ } else {
111
+ currentList = null;
112
+ result[currentKey] = val;
113
+ }
114
+ continue;
115
+ }
116
+
117
+ currentKey = null;
118
+ currentList = null;
119
+ }
120
+
121
+ return result;
122
+ }
123
+
124
+ function serializeMemoryYaml(obj) {
125
+ const lines = [];
126
+ for (const [key, value] of Object.entries(obj)) {
127
+ if (Array.isArray(value)) {
128
+ lines.push(`${key}:`);
129
+ for (const item of value) {
130
+ lines.push(` - ${item}`);
131
+ }
132
+ } else {
133
+ lines.push(`${key}: ${value}`);
134
+ }
135
+ }
136
+ return lines.join('\n') + '\n';
137
+ }
138
+
68
139
  // --- Subcomandos ---
69
140
 
70
141
  function cmdValidateName(argv) {
@@ -79,7 +150,7 @@ function cmdValidateName(argv) {
79
150
  }
80
151
  }
81
152
 
82
- function cmdNewChange(argv) {
153
+ function cmdNewChange(argv, projectRoot) {
83
154
  const args = parseArgs(argv);
84
155
  const name = args._positional[0];
85
156
 
@@ -107,7 +178,7 @@ function cmdNewChange(argv) {
107
178
  console.log(`Cambio '${name}' creado en refacil-sdd/changes/${name}/`);
108
179
  }
109
180
 
110
- function cmdArchive(argv) {
181
+ function cmdArchive(argv, projectRoot) {
111
182
  const args = parseArgs(argv);
112
183
  const name = args._positional[0];
113
184
 
@@ -134,6 +205,10 @@ function cmdArchive(argv) {
134
205
  process.exit(1);
135
206
  }
136
207
 
208
+ // Delete memory.yaml before archiving (CA-18)
209
+ const memoryFile = path.join(sourceDir, 'memory.yaml');
210
+ if (fs.existsSync(memoryFile)) fs.unlinkSync(memoryFile);
211
+
137
212
  fs.mkdirSync(archiveDir, { recursive: true });
138
213
  fs.renameSync(sourceDir, destDir);
139
214
 
@@ -145,7 +220,138 @@ function cmdArchive(argv) {
145
220
  console.log(`Cambio '${name}' archivado en refacil-sdd/changes/archive/${date}-${name}/`);
146
221
  }
147
222
 
148
- function cmdList(argv) {
223
+ function cmdSetMemory(argv, projectRoot) {
224
+ const args = parseArgs(argv);
225
+ const name = args._positional[0];
226
+
227
+ if (!name) {
228
+ console.error('Uso: refacil-sdd-ai sdd set-memory <nombre-cambio> [--last-step <value>] [--stack-detected <value>] [--touched-files <csv>] [--commands-run <value>] [--criteria-run <csv>]');
229
+ process.exit(1);
230
+ }
231
+
232
+ const root = projectRoot;
233
+
234
+ // Guard: ensure the change directory exists before any file operation
235
+ const changeDir = path.join(root, 'refacil-sdd', 'changes', name);
236
+ if (!fs.existsSync(changeDir)) {
237
+ console.error(`No existe el cambio '${name}' en refacil-sdd/changes/${name}/`);
238
+ process.exit(1);
239
+ }
240
+
241
+ // Require at least one field flag
242
+ const knownFlags = ['last-step', 'stack-detected', 'touched-files', 'commands-run', 'criteria-run'];
243
+ if (!knownFlags.some((f) => args[f] !== undefined)) {
244
+ console.error('set-memory: debe especificar al menos un campo (--last-step, --stack-detected, --touched-files, --commands-run, --criteria-run)');
245
+ process.exit(1);
246
+ }
247
+
248
+ const memoryPath = path.join(changeDir, 'memory.yaml');
249
+
250
+ // Read existing memory to merge
251
+ let existing = {};
252
+ if (fs.existsSync(memoryPath)) {
253
+ try {
254
+ existing = parseMemoryYaml(fs.readFileSync(memoryPath, 'utf8'));
255
+ } catch (_) {
256
+ existing = {};
257
+ }
258
+ }
259
+
260
+ // Apply flags
261
+ if (args['last-step']) existing['lastStep'] = args['last-step'];
262
+ if (args['stack-detected']) existing['stackDetected'] = args['stack-detected'];
263
+ if (args['touched-files']) {
264
+ existing['touchedFiles'] = args['touched-files'].split(',').map((s) => s.trim()).filter(Boolean);
265
+ }
266
+ if (args['commands-run']) existing['commandsRun'] = args['commands-run'];
267
+ if (args['criteria-run']) {
268
+ existing['criteriaRun'] = args['criteria-run'].split(',').map((s) => s.trim()).filter(Boolean);
269
+ }
270
+
271
+ fs.writeFileSync(memoryPath, serializeMemoryYaml(existing), 'utf8');
272
+ console.log(`memory.yaml actualizado para '${name}'`);
273
+ }
274
+
275
+ function cmdGetMemory(argv, projectRoot) {
276
+ const args = parseArgs(argv);
277
+ const name = args._positional[0];
278
+ const wantJson = args.json === true;
279
+
280
+ if (!name) {
281
+ console.error('Uso: refacil-sdd-ai sdd get-memory <nombre-cambio> [--json]');
282
+ process.exit(1);
283
+ }
284
+
285
+ const root = projectRoot;
286
+ const memoryPath = path.join(root, 'refacil-sdd', 'changes', name, 'memory.yaml');
287
+
288
+ if (!fs.existsSync(memoryPath)) {
289
+ if (wantJson) {
290
+ process.stdout.write('{}\n');
291
+ }
292
+ process.exit(0);
293
+ }
294
+
295
+ const content = fs.readFileSync(memoryPath, 'utf8');
296
+
297
+ if (wantJson) {
298
+ let parsed = {};
299
+ try {
300
+ parsed = parseMemoryYaml(content);
301
+ } catch (_) {
302
+ parsed = {};
303
+ }
304
+ process.stdout.write(JSON.stringify(parsed) + '\n');
305
+ } else {
306
+ process.stdout.write(content);
307
+ }
308
+ }
309
+
310
+ function cmdSetReviewFails(argv, projectRoot) {
311
+ const args = parseArgs(argv);
312
+ const name = args._positional[0];
313
+
314
+ if (!name) {
315
+ console.error('Uso: refacil-sdd-ai sdd set-review-fails <nombre-cambio> --files <csv>');
316
+ process.exit(1);
317
+ }
318
+
319
+ const root = projectRoot;
320
+ const changeDir = path.join(root, 'refacil-sdd', 'changes', name);
321
+ if (!fs.existsSync(changeDir)) {
322
+ console.error(`No existe el cambio '${name}' en refacil-sdd/changes/${name}/`);
323
+ process.exit(1);
324
+ }
325
+
326
+ const files = args.files
327
+ ? args.files.split(',').map((s) => s.trim()).filter(Boolean)
328
+ : [];
329
+
330
+ const reviewFailsPath = path.join(changeDir, '.review-last-fails.json');
331
+ fs.writeFileSync(reviewFailsPath, JSON.stringify({ failedFiles: files }, null, 2), 'utf8');
332
+ console.log(`.review-last-fails.json actualizado para '${name}'`);
333
+ }
334
+
335
+ function cmdClearReviewFails(argv, projectRoot) {
336
+ const args = parseArgs(argv);
337
+ const name = args._positional[0];
338
+
339
+ if (!name) {
340
+ console.error('Uso: refacil-sdd-ai sdd clear-review-fails <nombre-cambio>');
341
+ process.exit(1);
342
+ }
343
+
344
+ const root = projectRoot;
345
+ const reviewFailsPath = path.join(root, 'refacil-sdd', 'changes', name, '.review-last-fails.json');
346
+
347
+ if (fs.existsSync(reviewFailsPath)) {
348
+ fs.unlinkSync(reviewFailsPath);
349
+ console.log(`.review-last-fails.json eliminado para '${name}'`);
350
+ }
351
+ // Silent exit 0 if not exists
352
+ }
353
+
354
+ function cmdList(argv, projectRoot) {
149
355
  const args = parseArgs(argv);
150
356
  const wantJson = args.json === true;
151
357
 
@@ -184,7 +390,7 @@ function cmdList(argv) {
184
390
  }
185
391
  }
186
392
 
187
- function cmdStatus(argv) {
393
+ function cmdStatus(argv, projectRoot) {
188
394
  const args = parseArgs(argv);
189
395
  const name = args._positional[0];
190
396
  const wantJson = args.json === true;
@@ -273,7 +479,7 @@ function cmdStatus(argv) {
273
479
  }
274
480
  }
275
481
 
276
- function cmdMarkReviewed(argv) {
482
+ function cmdMarkReviewed(argv, projectRoot) {
277
483
  const args = parseArgs(argv);
278
484
  const name = args._positional[0];
279
485
 
@@ -314,7 +520,7 @@ function cmdMarkReviewed(argv) {
314
520
  console.log(`Review marcado como aprobado para '${name}' (verdict: ${payload.verdict})`);
315
521
  }
316
522
 
317
- function cmdTasksUpdate(argv) {
523
+ function cmdTasksUpdate(argv, projectRoot) {
318
524
  const args = parseArgs(argv);
319
525
  const name = args._positional[0];
320
526
 
@@ -390,44 +596,69 @@ function sddHelp() {
390
596
  --task N Número de task (1-indexed)
391
597
  --done Confirma que la task está hecha
392
598
  sdd validate-name <nombre> Valida el formato del nombre de un cambio
599
+ sdd set-memory <nombre> Escribe o fusiona campos en memory.yaml del cambio
600
+ [--last-step <value>] Último paso ejecutado (apply, test, etc.)
601
+ [--stack-detected <value>] Stack tecnológico detectado
602
+ [--touched-files <csv>] Archivos modificados (separados por coma)
603
+ [--commands-run <value>] Comando de test ejecutado
604
+ [--criteria-run <csv>] Criterios CA/CR ejecutados (separados por coma)
605
+ sdd get-memory <nombre> Lee memory.yaml del cambio
606
+ [--json] Salida en JSON (por defecto: YAML raw)
607
+ sdd set-review-fails <nombre> Escribe .review-last-fails.json con archivos fallidos
608
+ --files <csv> Archivos con fallos (separados por coma)
609
+ sdd clear-review-fails <nombre> Elimina .review-last-fails.json del cambio
393
610
 
394
611
  Notas:
395
612
  - Los nombres de cambio deben empezar con minúscula y usar solo [a-z0-9-]
396
613
  - Si existe openspec/ y no existe refacil-sdd/, se migra automáticamente
614
+ - sdd archive elimina memory.yaml automáticamente antes de mover el cambio
397
615
  `);
398
616
  }
399
617
 
400
618
  // --- Dispatcher ---
401
619
 
402
- function handleSdd(sub, argv) {
620
+ function handleSdd(sub, argv, projectRoot) {
403
621
  const args = argv || [];
622
+ const root = projectRoot || findProjectRoot();
404
623
 
405
624
  switch (sub) {
406
625
  case 'new-change':
407
- cmdNewChange(args);
626
+ cmdNewChange(args, root);
408
627
  break;
409
628
  case 'archive':
410
- cmdArchive(args);
629
+ cmdArchive(args, root);
411
630
  break;
412
631
  case 'list':
413
- cmdList(args);
632
+ cmdList(args, root);
414
633
  break;
415
634
  case 'status':
416
- cmdStatus(args);
635
+ cmdStatus(args, root);
417
636
  break;
418
637
  case 'mark-reviewed':
419
- cmdMarkReviewed(args);
638
+ cmdMarkReviewed(args, root);
420
639
  break;
421
640
  case 'tasks-update':
422
- cmdTasksUpdate(args);
641
+ cmdTasksUpdate(args, root);
423
642
  break;
424
643
  case 'validate-name':
425
644
  cmdValidateName(args);
426
645
  break;
646
+ case 'set-memory':
647
+ cmdSetMemory(args, root);
648
+ break;
649
+ case 'get-memory':
650
+ cmdGetMemory(args, root);
651
+ break;
652
+ case 'set-review-fails':
653
+ cmdSetReviewFails(args, root);
654
+ break;
655
+ case 'clear-review-fails':
656
+ cmdClearReviewFails(args, root);
657
+ break;
427
658
  default:
428
659
  sddHelp();
429
660
  process.exit(1);
430
661
  }
431
662
  }
432
663
 
433
- module.exports = { handleSdd, parseArgs, autoMigrateOpenspec, validateChangeName };
664
+ module.exports = { handleSdd, parseArgs, autoMigrateOpenspec, validateChangeName, findProjectRoot };
package/lib/installer.js CHANGED
@@ -71,10 +71,12 @@ function installSkills(packageRoot, projectRoot) {
71
71
  // Claude Code: tools allowlist granular, model: sonnet|opus|haiku
72
72
  // Cursor: readonly: true|false (booleano), model: inherit (default)
73
73
  function transformFrontmatterForCursor(content) {
74
- const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
74
+ const normalized = content.replace(/\r\n/g, '\n');
75
+ const match = normalized.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
75
76
  if (!match) return content;
76
77
 
77
78
  const [, frontmatterRaw, body] = match;
79
+ // work with normalized content from here on
78
80
  const lines = frontmatterRaw.split('\n');
79
81
  const out = [];
80
82
  let toolsLine = null;
@@ -295,9 +297,9 @@ function checkNodeVersion() {
295
297
  const major = parseInt(version.split('.')[0].replace('v', ''));
296
298
  const minor = parseInt(version.split('.')[1]);
297
299
 
298
- if (major < 20 || (major === 20 && minor < 19)) {
300
+ if (major < 20) {
299
301
  console.log(`\n ADVERTENCIA: Node.js ${version} detectado.`);
300
- console.log(' refacil-sdd-ai requiere Node.js >= 20.19.0.');
302
+ console.log(' refacil-sdd-ai requiere Node.js >= 20.0.0.');
301
303
  console.log(' Las skills se instalaran pero /refacil:setup podria fallar al instalar OpenSpec.\n');
302
304
  return false;
303
305
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "refacil-sdd-ai",
3
- "version": "4.3.0",
3
+ "version": "4.4.1",
4
4
  "description": "SDD-AI: Specification-Driven Development with AI — development methodology using AI with Claude Code and Cursor",
5
5
  "bin": {
6
6
  "refacil-sdd-ai": "./bin/cli.js"
@@ -35,7 +35,7 @@
35
35
  "node": ">=20.0.0"
36
36
  },
37
37
  "scripts": {
38
- "test": "node --test test/hooks.test.js test/installer.test.js test/ignore-files.test.js test/methodology-migration-pending.test.js test/sdd.test.js test/refactor-integrar-openspec-nativo.test.js test/refactor-rutas-refacil-sdd.test.js test/refactor-agents-english.test.js test/remove-openspec-legacy.test.js"
38
+ "test": "node --test test/hooks.test.js test/installer.test.js test/ignore-files.test.js test/methodology-migration-pending.test.js test/sdd.test.js test/refactor-integrar-openspec-nativo.test.js test/refactor-rutas-refacil-sdd.test.js test/refactor-agents-english.test.js test/remove-openspec-legacy.test.js test/find-project-root.test.js"
39
39
  },
40
40
  "dependencies": {
41
41
  "ws": "^8.18.0"
@@ -99,6 +99,22 @@ The sub-agent will use the briefing as the primary guide and will only read the
99
99
 
100
100
  Returns ONE single message with the report + JSON block fenced as ` ```refacil-apply-result `.
101
101
 
102
+ ### Step 2.5: Save cross-skill memory (memory.yaml)
103
+
104
+ After parsing the `refacil-apply-result` block and only if `result` is not `"FAILED"`:
105
+ - Extract `touchedFiles` from the result (list of files actually created/modified by the implementer).
106
+
107
+ Run:
108
+ ```bash
109
+ refacil-sdd-ai sdd set-memory <changeName> \
110
+ --last-step apply \
111
+ --touched-files "<comma-separated list of modified files>"
112
+ ```
113
+
114
+ This command merges into memory.yaml at the repo root using `findProjectRoot()` — no manual path construction needed.
115
+
116
+ If `result` is `"FAILED"`, skip and wait for user instructions.
117
+
102
118
  ### Step 3: Present result and next step
103
119
 
104
120
  Show the user the **report** (everything before the `refacil-apply-result` block). Do not show the JSON block — it is internal metadata.
@@ -61,23 +61,19 @@ Inspect the change folder in `refacil-sdd/changes/`:
61
61
 
62
62
  Depending on the type, follow the corresponding step:
63
63
 
64
- ### Step 2A: Bug fix → Manual archiving (no `sdd archive` CLI)
64
+ ### Step 2A: Bug fix → Archive with native CLI
65
65
 
66
- Bug fixes only contain `summary.md` (and optionally `.review-passed`); they are not full proposal/spec/design/task trees. Archive them with **git mv** / **mv** per the steps below **do not** use `refacil-sdd-ai sdd archive` for these folders.
66
+ Bug fixes only contain `summary.md` (and optionally `.review-passed`). The CLI `refacil-sdd-ai sdd archive` handles the folder move internally with its own `findProjectRoot()`, so there is no need for manual `mv` use the CLI for the move, and only write specs/review.yaml manually (the CLI does not cover those).
67
67
 
68
- 1. **Move to archive (move operation, not copy)**: transfer the full fix folder from `refacil-sdd/changes/[fix-name]/` to `refacil-sdd/changes/archive/[ISO-date]-[fix-name]/`. The source folder **must be eliminated** when done.
68
+ 0. **Read artifacts before archiving**: read `summary.md` and `.review-passed` from `refacil-sdd/changes/[fix-name]/` **now**, before the CLI moves the folder. The archived path will be different.
69
69
 
70
- Recommended execution order (deterministic and cross-platform):
71
- 1. Ensure the `refacil-sdd/changes/archive/` directory exists (create if not present).
72
- 2. Prefer an **atomic move** with `git mv "refacil-sdd/changes/[fix-name]" "refacil-sdd/changes/archive/[ISO-date]-[fix-name]"` when the fix is already under git control (moves and stages at once).
73
- 3. If `git mv` does not apply (untracked files or error), use instead:
74
- - Linux/macOS/Windows: `mv "refacil-sdd/changes/[fix-name]" "refacil-sdd/changes/archive/[ISO-date]-[fix-name]"`. Always use `mv` (not `cp -r`) verify source no longer exists after the move.
75
- 4. **Mandatory post-move verification**: run a listing/existence test to confirm:
76
- - `refacil-sdd/changes/[fix-name]/` **no longer exists**.
77
- - `refacil-sdd/changes/archive/[ISO-date]-[fix-name]/` **does exist** and contains `summary.md` (+ `.review-passed` if it existed).
78
- 5. If the verification fails (source folder survived), explicitly delete it with `rm -rf "refacil-sdd/changes/[fix-name]"` and re-verify. Do not continue to step 2 until the state is consistent.
70
+ 1. **Archive with the CLI** — let the CLI handle the move (it automatically deletes memory.yaml if present):
71
+ ```bash
72
+ refacil-sdd-ai sdd archive [fix-name]
73
+ ```
74
+ The CLI resolves the repo root internally, deletes memory.yaml if present, moves the folder to `refacil-sdd/changes/archive/[ISO-date]-[fix-name]/`, and exits with code 0 on success. If it exits non-zero, stop and report the error to the user.
79
75
 
80
- 2. **Document in specs**: Read the `summary.md` and `.review-passed` from the fix (from the **archived** path at `refacil-sdd/changes/archive/[ISO-date]-[fix-name]/`, no longer from the original path), and create an individual spec for the bug at `refacil-sdd/specs/[descriptive-name]/spec.md`.
76
+ 2. **Document in specs**: using the content read in step 0, create an individual spec at `$(git rev-parse --show-toplevel)/refacil-sdd/specs/[descriptive-name]/spec.md`.
81
77
 
82
78
  **Spec folder name**: Use a short, clear kebab-case description of the bug (e.g. `fix-session-timeout-redis`, `fix-null-pointer-payment-callback`). **Do NOT use ticket IDs** (REF-123, JIRA-456, etc.) — the name must be descriptive so `/refacil:explore` can find and understand the fix without external context.
83
79
 
@@ -101,7 +97,7 @@ Bug fixes only contain `summary.md` (and optionally `.review-passed`); they are
101
97
  - **THEN** the system SHALL [normal behavior without regression]
102
98
  ```
103
99
 
104
- 3. **Persist review metadata separately**: create `refacil-sdd/specs/[descriptive-name]/review.yaml` with the fields from `.review-passed` plus the Jira links from Step 1.5:
100
+ 3. **Persist review metadata separately**: create `$(git rev-parse --show-toplevel)/refacil-sdd/specs/[descriptive-name]/review.yaml` with the fields from `.review-passed` plus the Jira links from Step 1.5:
105
101
  ```yaml
106
102
  verdict: APROBADO|APROBADO CON OBSERVACIONES
107
103
  date: 2026-04-10T00:00:00.000Z
@@ -140,7 +136,7 @@ The spec and review evidence are written **before** running the CLI archive comm
140
136
  ```
141
137
  - If `review.yaml` already exists, update only the fields that changed without removing others.
142
138
 
143
- 3. **Run the CLI archive**: `refacil-sdd-ai sdd archive <changeName>` — this moves the change to `refacil-sdd/changes/archive/<date>-<changeName>/`.
139
+ 3. **Run the CLI archive**: `refacil-sdd-ai sdd archive <changeName>` — the CLI automatically deletes memory.yaml if present, then moves the change to `refacil-sdd/changes/archive/<date>-<changeName>/`.
144
140
  4. Verify the command completed successfully (exit 0) and the original folder no longer exists.
145
141
 
146
142
  5. Continue to **Step 3**.
@@ -151,8 +147,8 @@ The goal is for `refacil-sdd/specs/` to document how the system works TODAY.
151
147
 
152
148
  Before showing the summary, run a **final cleanup verification** (applies to both bug fixes and regular changes):
153
149
 
154
- - `refacil-sdd/changes/[original-name]/` **must NOT exist** (only the archived version must survive in `refacil-sdd/changes/archive/...`).
155
- - If the source folder survived for any reason (failed move, partial copy, interrupted move), explicitly delete it with `rm -rf "refacil-sdd/changes/[original-name]"` before confirming to the user.
150
+ - `$(git rev-parse --show-toplevel)/refacil-sdd/changes/[original-name]/` **must NOT exist** (only the archived version must survive in `refacil-sdd/changes/archive/...`).
151
+ - If the source folder survived for any reason (failed move, partial copy, interrupted move), explicitly delete it with `rm -rf "$(git rev-parse --show-toplevel)/refacil-sdd/changes/[original-name]"` before confirming to the user.
156
152
 
157
153
  ```
158
154
  === Change archived ===
@@ -17,6 +17,18 @@ This skill is a **thin wrapper** that delegates the investigation to the `refaci
17
17
  - If `$ARGUMENTS` is empty, ask the user for the question or topic to explore BEFORE invoking the sub-agent.
18
18
  - If there is a question, continue.
19
19
 
20
+ ### Step 0.1: Duplicate exploration guard (CA-11)
21
+
22
+ Before delegating, check the current session conversation context for a prior complete exploration report with overlapping scope (same modules, files, or question topic):
23
+
24
+ - **If a prior complete exploration exists for the same or highly overlapping topic**: summarize the already-known context in 2-3 sentences and ask:
25
+ ```
26
+ I already explored [topic] earlier in this session. The key findings were: [summary].
27
+ Do you want me to run a targeted follow-up on a specific aspect, or proceed with a new full exploration?
28
+ ```
29
+ Wait for the user's answer before proceeding.
30
+ - **If there is no prior exploration** (or it is on a clearly different topic): continue to Step 1 without interruption.
31
+
20
32
  ### Step 1: Delegate to the refacil-investigator sub-agent
21
33
 
22
34
  Invoke the `refacil-investigator` sub-agent passing it:
@@ -57,19 +57,22 @@ Critical rule:
57
57
 
58
58
  If the current branch is protected and code needs to be written:
59
59
 
60
- 1. Inform and ask for a task identifier (Jira or other).
60
+ 1. **Ask for a task identifier first** before any git action, ask the user:
61
+ *"What is the task number or name for this branch? (e.g. SEGINF-20, REF-123, or a short descriptive name)"*
62
+ Wait for the user's answer before continuing.
61
63
  2. Verify clean working directory (`git status --porcelain`).
62
64
  3. If there are uncommitted changes, ask for approval to `git stash push -m "auto-stash-refacil"`.
63
65
  4. Detect the base branch to create a working branch from:
64
66
  - Prefer `develop`, then `dev`.
65
67
  - Only if neither exists (new repo), use `main` or `master` as a temporary exception.
66
68
  - Switch and update (`git pull origin <base>`). If none exist, stop.
67
- 5. Create the working branch:
68
- - Feature: `feature/<ID>`
69
- - Bugfix: `fix/<ID>`
70
- - Without ID: short descriptive name and recommend creating a ticket.
71
- 6. Restore `stash` if used.
72
- 7. If the user does not approve the process, stop.
69
+ 5. **Propose the branch name and wait for explicit user approval before creating it:**
70
+ - Feature: `feature/<ID>` (e.g. `feature/SEGINF-20`)
71
+ - Bugfix: `fix/<ID>` (e.g. `fix/SEGINF-20`)
72
+ - Without ID: propose a short descriptive name (e.g. `fix/project-root-consistency`) and **require explicit approval** before proceeding.
73
+ Example: *"I'll create branch `fix/SEGINF-20` from `develop`. Shall I proceed?"*
74
+ 6. **Only after explicit user confirmation**: create the branch and restore stash if used.
75
+ 7. If the user does not approve at any point, stop.
73
76
 
74
77
  ## §5 — Output policy (UX)
75
78
 
@@ -12,6 +12,18 @@ This skill is a **wrapper** that prepares the scope, delegates SDD artifact gene
12
12
 
13
13
  ## Flow
14
14
 
15
+ ### Step 0.5: Duplicate exploration guard (CA-11)
16
+
17
+ Before gathering context or delegating, check the current session conversation for a prior complete exploration report with overlapping scope (same modules, files, or described topic):
18
+
19
+ - **If a prior complete exploration exists for the same or highly overlapping topic**: summarize the already-known context in 2-3 sentences and ask:
20
+ ```
21
+ I already explored [topic] earlier in this session. The key findings were: [summary].
22
+ Do you want me to run a targeted follow-up, or proceed with the full exploration for this proposal?
23
+ ```
24
+ Wait for the user's answer. If they confirm "proceed", continue to Step 1 — do not re-invoke a full exploration automatically.
25
+ - **If there is no prior exploration** for this topic: continue to Step 1 without interruption.
26
+
15
27
  ### Step 1: Understand the change
16
28
 
17
29
  If the user did NOT provide sufficient context in `$ARGUMENTS`, ask:
@@ -21,15 +33,15 @@ If the user did NOT provide sufficient context in `$ARGUMENTS`, ask:
21
33
 
22
34
  If `$ARGUMENTS` is already clear, do not ask again.
23
35
 
24
- ### Step 1.5: Change folder identifier (blocking)
36
+ ### Step 1.5: Validate change name (blocking)
25
37
 
26
- The `refacil-sdd/changes/<name>/` directory **cannot** use a `<name>` whose **first character is not an ASCII letter** `[a-zA-Z]` — see **`refacil-prereqs/METHODOLOGY-CONTRACT.md` §9**. Names like `2026-04-17-expose-something` cause `refacil-sdd-ai sdd status <name>` and other SDD commands to fail.
38
+ Before delegating to the sub-agent, agree on or derive the **final slug** of the change (kebab-case: `feat-...`, `expose-...`, `imp-...`, etc.) then validate it with the CLI:
27
39
 
28
- **Before delegating to the sub-agent:**
40
+ Run `refacil-sdd-ai sdd validate-name <slug>`:
41
+ - If exit 0: the name is valid — continue.
42
+ - If exit 1: show the CLI's error message to the user and ask them to provide a corrected slug. Repeat until `sdd validate-name` exits 0.
29
43
 
30
- 1. Agree on or derive the **final slug** of the change (kebab-case: `feat-...`, `expose-...`, `imp-...`, etc.). Dates or tickets do not go at the **start** of the name.
31
- 2. If the user proposes an invalid name, **correct it** (e.g. `feat-` or `change-` prefix) or ask for the correct slug; **do not** delegate until you have a valid name.
32
- 3. Communicate the final agreed slug to the user before generating.
44
+ Communicate the final agreed slug to the user before generating.
33
45
 
34
46
  ### Step 2: Delegate to the refacil-proposer sub-agent
35
47
 
@@ -86,6 +98,6 @@ Do you want me to continue with /refacil:apply?
86
98
 
87
99
  ## Rules
88
100
 
89
- - **Change folder name**: always comply with **§9** of the methodology contract (first character ASCII letter; never start with a digit). Validate BEFORE delegating.
101
+ - **Change folder name**: always validate with `refacil-sdd-ai sdd validate-name <slug>` before delegating. Do not proceed if it exits 1.
90
102
  - **Always delegate generation to the sub-agent**. Do not replicate the codebase exploration or artifact generation logic here.
91
103
  - **Flow continuity**: if the user confirms affirmatively ("yes", "ok", "go", "continue", etc.) the continuity question in Step 4, immediately invoke the **Skill tool** with `skill: "refacil:apply"`. Do not describe it in text or wait for the user to type `/refacil:apply`. (See `METHODOLOGY-CONTRACT.md §5`.)
@@ -43,11 +43,21 @@ If you already have a `changeName`, run `refacil-sdd-ai sdd status <changeName>
43
43
  The change [name] already has an approved review ([verdict] — [date]) and there are no subsequent changes.
44
44
  ```
45
45
 
46
+ ### Step 0.4: Incremental scope (CA-07 — only if re-running after REQUIERE CORRECCIONES)
47
+
48
+ If `changeName` is not null, check whether `refacil-sdd/changes/<changeName>/.review-last-fails.json` exists (read by explicit path — it is NOT a dotfile but may be hidden in listings):
49
+
50
+ - **If the file EXISTS**: read `failedFiles` from it. Compute:
51
+ `incrementalScope = failedFiles ∪ changedFilesUnion`
52
+ - If `incrementalScope` is empty (CR-02): fall back to `changedFilesUnion` and add a comment in the briefing: `"# warning: incremental scope was empty — using full changedFilesUnion"`.
53
+ - Use `incrementalScope` as `changedFiles` in the briefing (Step 0.5), instead of the full `changedFilesUnion`.
54
+ - **If the file does NOT exist**: use `changedFilesUnion` as `changedFiles` normally.
55
+
46
56
  ### Step 0.5: Build briefing for the sub-agent (reduces auditor tool calls)
47
57
 
48
58
  Before invoking the sub-agent, extract the context that the auditor would otherwise calculate on its own:
49
59
 
50
- 1. **Changed files** — use **`changedFilesUnion` from Step 0.3** as the blocking scope. Do not run `git diff` or `git status` again.
60
+ 1. **Changed files** — use the scope resolved in Step 0.4 (`incrementalScope` if available, otherwise `changedFilesUnion`). Do not run `git diff` or `git status` again.
51
61
 
52
62
  2. **Project type** — read `package.json` (if it exists) and inspect the dependencies:
53
63
  - Backend indicators: `@nestjs/*`, `express`, `fastify`, `koa`, `typeorm`, `prisma`, `pg`, `mongoose`, `bullmq`, `amqplib`
@@ -56,6 +66,10 @@ Before invoking the sub-agent, extract the context that the auditor would otherw
56
66
 
57
67
  3. **Change objective** (only if there is an active change in `refacil-sdd/changes/`) — read the first section of `proposal.md`. Extract the objective in 1-2 sentences. If the scope is `git-diff` without an active change → `null`.
58
68
 
69
+ 4. **Cross-skill memory** — run `refacil-sdd-ai sdd get-memory <changeName> --json` and parse the JSON to extract `stackDetected` and `touchedFiles`. Include them in the briefing so the auditor skips re-discovery. If the command outputs `{}` or fails, omit — do not block (CR-04).
70
+
71
+ 5. **Mode** — default `concise`. If re-running after a prior `REQUIERE CORRECCIONES` (i.e., `.review-last-fails.json` was found with non-empty `failedFiles` in Step 0.4): set `mode: focused` — the auditor re-evaluates only the failing checklist items on the `failedFiles` (CR-05: focused mode still reads those files). Otherwise keep `concise`.
72
+
59
73
  Build the BRIEFING block:
60
74
 
61
75
  ```
@@ -64,7 +78,9 @@ scope: <changeName | "git-diff">
64
78
  changedFiles: [path/file-1.ts, path/file-2.ts, ...]
65
79
  projectType: backend | frontend | fullstack | library
66
80
  changeObjective: <objective in 1-2 sentences, or null>
67
- mode: concise | detailed
81
+ mode: concise | detailed | focused
82
+ stackDetected: <from memory.yaml, or omit>
83
+ touchedFiles: [...] # from memory.yaml — omit if not present
68
84
  ```
69
85
 
70
86
  ### Step 1: Delegate to the refacil-auditor sub-agent
@@ -85,6 +101,18 @@ Show the user the **concise report** (everything before the `refacil-review-resu
85
101
 
86
102
  **If the sub-agent returned `SCOPE_ERROR: <reason>`**: propagate the error to the user and ask for clarification. Do not write a marker.
87
103
 
104
+ ### Step 2.5: Persist or clean incremental-scope state (CA-06/CA-08/CA-09/CR-01)
105
+
106
+ Parse the `refacil-review-result` block from the sub-agent.
107
+
108
+ **If `verdict` is `REQUIERE CORRECCIONES`**:
109
+ - Only if `changeName` is not null (CR-01) AND the block includes a `failedFiles` field (CA-09 backward compat):
110
+ - Run: `refacil-sdd-ai sdd set-review-fails <changeName> --files "<comma-separated failedFiles>"`
111
+ - If `changeName` is null or `failedFiles` is absent: skip silently.
112
+
113
+ **If `verdict` is `APROBADO` or `APROBADO CON OBSERVACIONES`**:
114
+ - Run: `refacil-sdd-ai sdd clear-review-fails <changeName>`
115
+
88
116
  ### Step 3: Create `.review-passed` marker (if applicable)
89
117
 
90
118
  Parse the ` ```refacil-review-result ` block from the sub-agent. If `verdict` is **APROBADO** or **APROBADO CON OBSERVACIONES** and `changeName` is not null:
@@ -110,6 +138,24 @@ Where the values are extracted from the sub-agent's `refacil-review-result` bloc
110
138
  - `changeName` is null.
111
139
  - The sub-agent returned `SCOPE_ERROR`.
112
140
 
141
+ ### Step 3.5: Offer to apply corrections (only if REQUIERE CORRECCIONES)
142
+
143
+ If `verdict` is `REQUIERE CORRECCIONES`, after showing the report present a numbered list of the findings (blockers first, then medium/low) and ask:
144
+
145
+ ```
146
+ X corrections are needed. Do you want me to apply them?
147
+ - "yes" / "all" — apply all
148
+ - "1, 3" (numbers) — apply only those items
149
+ - "no" / "skip" — you'll handle them manually
150
+ ```
151
+
152
+ **According to the user's response:**
153
+ - **"yes" / "all"**: apply every correction using Edit/Write/Bash tools directly in this skill. After each correction, note it as applied.
154
+ - **"N, M" (specific items)**: apply only those numbered items.
155
+ - **"no" / "skip" / no answer**: do not apply anything — continue to Step 4.
156
+
157
+ After applying corrections (if any): summarize what was applied, then continue to Step 4.
158
+
113
159
  ### Step 4: Recommend next step
114
160
 
115
161
  According to the parsed `verdict`, add at the end of your response:
@@ -120,9 +166,9 @@ The next step is to archive the change.
120
166
  Do you want me to continue with /refacil:archive?
121
167
  ```
122
168
 
123
- **If REQUIERE CORRECCIONES:**
169
+ **If REQUIERE CORRECCIONES** (after Step 3.5):
124
170
  ```
125
- Once the corrections are applied, the next step is to re-verify the implementation.
171
+ The next step is to re-verify the implementation.
126
172
  Do you want me to continue with /refacil:verify?
127
173
  ```
128
174
 
@@ -132,4 +178,7 @@ Do you want me to continue with /refacil:verify?
132
178
  - **Always delegate to the sub-agent**. Do not replicate checklist or evaluation logic here.
133
179
  - **The marker is created by this skill, not the sub-agent**.
134
180
  - If the sub-agent returned something out of format (no parseable JSON block and not `SCOPE_ERROR`), inform the user: "The reviewer returned an unstructured report — no marker was created. Review the report manually."
135
- - **Flow continuity**: if the user confirms affirmatively ("yes", "ok", "go", "continue", etc.) the continuity question, immediately invoke the corresponding **Skill tool**: `skill: "refacil:archive"` if the verdict is APROBADO/APROBADO CON OBSERVACIONES, or `skill: "refacil:verify"` if REQUIERE CORRECCIONES. (See `METHODOLOGY-CONTRACT.md §5`.)
181
+ - **Flow continuity**:
182
+ - If verdict is APROBADO/APROBADO CON OBSERVACIONES and user confirms → immediately invoke `skill: "refacil:archive"`.
183
+ - If verdict is REQUIERE CORRECCIONES and user confirms verify (Step 4) → immediately invoke `skill: "refacil:verify"`.
184
+ - Do not describe the skill in text or wait for the user to type the command. (See `METHODOLOGY-CONTRACT.md §5`.)
@@ -69,6 +69,23 @@ Parse the `refacil-test-result` block from the sub-agent:
69
69
  - **If `passed: false`** (tests failed): present the `issues` from the JSON and ask the user how to proceed. **Do not continue to Step 4** until the tests pass.
70
70
  - **If `passed: true`**: continue to Step 4.
71
71
 
72
+ ### Step 3.5: Update cross-skill memory (memory.yaml)
73
+
74
+ After parsing the `refacil-test-result` block and only if `passed: true`:
75
+ - Extract from the result or from the briefing: `commandsRun` (test command used), `criteriaRun` (list of CA-XX/CR-XX covered by tests), `stackDetected` (if the tester identified the stack).
76
+
77
+ Run:
78
+ ```bash
79
+ refacil-sdd-ai sdd set-memory <changeName> \
80
+ --last-step test \
81
+ --commands-run "<test command used>" \
82
+ --criteria-run "<comma-separated criteria IDs that were run>"
83
+ ```
84
+
85
+ If `stackDetected` is available, add `--stack-detected "<stack>"` to the command as well.
86
+
87
+ This command merges into memory.yaml, preserving fields from other steps (e.g. `touchedFiles` from apply).
88
+
72
89
  ### Step 4: Flow continuity (only if tests passed)
73
90
 
74
91
  Add:
@@ -27,6 +27,16 @@ If you already have a `changeName`, run `refacil-sdd-ai sdd status <changeName>
27
27
 
28
28
  If **this session** inspects the change directory before or after delegating, apply **`refacil-prereqs/METHODOLOGY-CONTRACT.md` §8**.
29
29
 
30
+ ### Step 0.6: Verify validator agent is installed (blocking — CA-12)
31
+
32
+ Before doing anything else, check that `.claude/agents/refacil-validator.md` exists (read by explicit path or `ls -la .claude/agents/refacil-validator.md`).
33
+
34
+ **If the file does NOT exist**, stop immediately:
35
+ ```
36
+ El agente `refacil-validator` no está instalado. Ejecuta `/refacil:update` y reinicia la sesión antes de volver a correr `/refacil:verify`.
37
+ ```
38
+ Do not continue and do not escalate to any other agent.
39
+
30
40
  ### Step 1: Build briefing for the sub-agent (reduces validator tool calls)
31
41
 
32
42
  Before invoking the sub-agent, extract the context that the validator would otherwise calculate on its own:
@@ -40,6 +50,8 @@ Before invoking the sub-agent, extract the context that the validator would othe
40
50
 
41
51
  3. **Scope files** — run `git diff --name-only HEAD` to get the files modified in this change.
42
52
 
53
+ 4. **Cross-skill memory** — run `refacil-sdd-ai sdd get-memory <changeName> --json` and parse the JSON output to extract `commandsRun` and `criteriaRun`. Include them in the briefing so the validator uses the same command without re-discovering it and can skip re-reading specs for those criteria. If the output is `{}` or the command fails, skip — do not block verification (CR-04).
54
+
43
55
  Build the BRIEFING block:
44
56
 
45
57
  ```
@@ -54,6 +66,8 @@ criteria:
54
66
  - CR-01: <description>
55
67
  changedFiles: [path/file-1.ts, ...]
56
68
  mode: concise | detailed
69
+ commandsRun: [<command>, ...] # from memory.yaml — omit if not present
70
+ criteriaRun: [CA-01, CR-01, ...] # from memory.yaml — omit if not present
57
71
  ```
58
72
 
59
73
  ### Step 2: Delegate to the refacil-validator sub-agent
@@ -73,7 +87,18 @@ The sub-agent:
73
87
 
74
88
  Show the user the **combined report** (everything before the `refacil-verify-result` block). Do not show the JSON block — it is internal metadata.
75
89
 
76
- **If the sub-agent returned a scope error** (without JSON block): propagate to the user and ask for clarification.
90
+ **If the sub-agent failed to load** (tool error, agent type not found, or no response at all): stop immediately and do NOT escalate to any other agent:
91
+ ```
92
+ The validator sub-agent could not be loaded — retry or run `/refacil:verify` again.
93
+ ```
94
+
95
+ **If the sub-agent responded but without a `refacil-verify-result` block** (unstructured output): show the raw report and stop:
96
+ ```
97
+ The validator returned an unstructured report — continue manually.
98
+ ```
99
+ Do not re-invoke a different agent.
100
+
101
+ **If the sub-agent returned a scope error** (`SCOPE_ERROR: <reason>`, without JSON block): propagate to the user and ask for clarification. This is NOT the CA-01 failsafe — the agent loaded correctly but found an ambiguous scope.
77
102
 
78
103
  ### Step 4: Process the result
79
104
 
@@ -124,5 +149,8 @@ Do you want me to apply these corrections? (yes/no)
124
149
  - **Corrections are ONLY applied by this wrapper** (Step 5), after explicit approval.
125
150
  - **Corrections must be surgical**: only what is necessary to resolve the reported issues.
126
151
  - Maximum 2 rounds of automatic correction before escalating to manual.
127
- - If the sub-agent returned something out of format, inform: "The validator returned an unstructured reportcontinue manually."
152
+ - **Sub-agent failsafe (CA-01)**: if the validator fails to load (tool error) or returns no responsestop and inform the user. Do NOT escalate to any other agent.
153
+ - **Unstructured output (CA-02)**: if the validator responds but without a `refacil-verify-result` block — show the raw report and stop. Do NOT re-invoke another agent.
154
+ - **SCOPE_ERROR (CR-03)**: if the validator returns `SCOPE_ERROR: <reason>` — propagate and ask for clarification. CA-01 does NOT apply here.
155
+ - **Agent missing (CA-12)**: checked in Step 0.6 — stop before delegating if `.claude/agents/refacil-validator.md` is absent.
128
156
  - **Flow continuity**: if the result is APPROVED and the user confirms affirmatively, immediately invoke the **Skill tool** with `skill: "refacil:review"`. (See `METHODOLOGY-CONTRACT.md §5`.)