refacil-sdd-ai 4.5.7 → 4.5.8

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.
@@ -62,6 +62,47 @@ function validateChangeName(name) {
62
62
  return { valid: true };
63
63
  }
64
64
 
65
+ function resolveExistingChangeName(projectRoot, inputName) {
66
+ if (!inputName || typeof inputName !== 'string') {
67
+ return { ok: false, reason: 'El nombre del cambio no puede estar vacío.' };
68
+ }
69
+
70
+ const normalizedInput = inputName.trim();
71
+ const lowerInput = normalizedInput.toLowerCase();
72
+ const changesDir = path.join(projectRoot, 'refacil-sdd', 'changes');
73
+
74
+ // Keep backward-compatible behavior when directory doesn't exist yet.
75
+ if (!fs.existsSync(changesDir)) {
76
+ return { ok: true, name: lowerInput };
77
+ }
78
+
79
+ const entries = fs.readdirSync(changesDir, { withFileTypes: true })
80
+ .filter((e) => e.isDirectory() && e.name !== 'archive')
81
+ .map((e) => e.name);
82
+
83
+ if (entries.includes(normalizedInput)) {
84
+ return { ok: true, name: normalizedInput };
85
+ }
86
+
87
+ if (entries.includes(lowerInput)) {
88
+ return { ok: true, name: lowerInput };
89
+ }
90
+
91
+ const ciMatches = entries.filter((n) => n.toLowerCase() === lowerInput);
92
+ if (ciMatches.length === 1) {
93
+ return { ok: true, name: ciMatches[0] };
94
+ }
95
+
96
+ if (ciMatches.length > 1) {
97
+ return {
98
+ ok: false,
99
+ reason: `Nombre de cambio ambiguo: '${inputName}'. Coincidencias: ${ciMatches.join(', ')}`,
100
+ };
101
+ }
102
+
103
+ return { ok: true, name: lowerInput };
104
+ }
105
+
65
106
  function autoMigrateOpenspec(root) {
66
107
  const oldDir = path.join(root, 'openspec');
67
108
  const newDir = path.join(root, 'refacil-sdd');
@@ -180,7 +221,15 @@ function cmdNewChange(argv, projectRoot) {
180
221
 
181
222
  function cmdArchive(argv, projectRoot) {
182
223
  const args = parseArgs(argv);
183
- const name = args._positional[0];
224
+ const rawName = args._positional[0];
225
+
226
+ autoMigrateOpenspec(projectRoot);
227
+ const resolved = resolveExistingChangeName(projectRoot, rawName);
228
+ if (!resolved.ok) {
229
+ console.error(resolved.reason);
230
+ process.exit(1);
231
+ }
232
+ const name = resolved.name;
184
233
 
185
234
  const validation = validateChangeName(name);
186
235
  if (!validation.valid) {
@@ -188,8 +237,6 @@ function cmdArchive(argv, projectRoot) {
188
237
  process.exit(1);
189
238
  }
190
239
 
191
- autoMigrateOpenspec(projectRoot);
192
-
193
240
  const sourceDir = path.join(projectRoot, 'refacil-sdd', 'changes', name);
194
241
  if (!fs.existsSync(sourceDir)) {
195
242
  console.error(`No existe el cambio '${name}' en refacil-sdd/changes/${name}/`);
@@ -222,14 +269,21 @@ function cmdArchive(argv, projectRoot) {
222
269
 
223
270
  function cmdSetMemory(argv, projectRoot) {
224
271
  const args = parseArgs(argv);
225
- const name = args._positional[0];
272
+ const rawName = args._positional[0];
226
273
 
227
- if (!name) {
274
+ if (!rawName) {
228
275
  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
276
  process.exit(1);
230
277
  }
231
278
 
232
279
  const root = projectRoot;
280
+ autoMigrateOpenspec(root);
281
+ const resolved = resolveExistingChangeName(root, rawName);
282
+ if (!resolved.ok) {
283
+ console.error(resolved.reason);
284
+ process.exit(1);
285
+ }
286
+ const name = resolved.name;
233
287
 
234
288
  // Guard: ensure the change directory exists before any file operation
235
289
  const changeDir = path.join(root, 'refacil-sdd', 'changes', name);
@@ -274,15 +328,22 @@ function cmdSetMemory(argv, projectRoot) {
274
328
 
275
329
  function cmdGetMemory(argv, projectRoot) {
276
330
  const args = parseArgs(argv);
277
- const name = args._positional[0];
331
+ const rawName = args._positional[0];
278
332
  const wantJson = args.json === true;
279
333
 
280
- if (!name) {
334
+ if (!rawName) {
281
335
  console.error('Uso: refacil-sdd-ai sdd get-memory <nombre-cambio> [--json]');
282
336
  process.exit(1);
283
337
  }
284
338
 
285
339
  const root = projectRoot;
340
+ autoMigrateOpenspec(root);
341
+ const resolved = resolveExistingChangeName(root, rawName);
342
+ if (!resolved.ok) {
343
+ console.error(resolved.reason);
344
+ process.exit(1);
345
+ }
346
+ const name = resolved.name;
286
347
  const memoryPath = path.join(root, 'refacil-sdd', 'changes', name, 'memory.yaml');
287
348
 
288
349
  if (!fs.existsSync(memoryPath)) {
@@ -309,14 +370,21 @@ function cmdGetMemory(argv, projectRoot) {
309
370
 
310
371
  function cmdSetReviewFails(argv, projectRoot) {
311
372
  const args = parseArgs(argv);
312
- const name = args._positional[0];
373
+ const rawName = args._positional[0];
313
374
 
314
- if (!name) {
375
+ if (!rawName) {
315
376
  console.error('Uso: refacil-sdd-ai sdd set-review-fails <nombre-cambio> --files <csv>');
316
377
  process.exit(1);
317
378
  }
318
379
 
319
380
  const root = projectRoot;
381
+ autoMigrateOpenspec(root);
382
+ const resolved = resolveExistingChangeName(root, rawName);
383
+ if (!resolved.ok) {
384
+ console.error(resolved.reason);
385
+ process.exit(1);
386
+ }
387
+ const name = resolved.name;
320
388
  const changeDir = path.join(root, 'refacil-sdd', 'changes', name);
321
389
  if (!fs.existsSync(changeDir)) {
322
390
  console.error(`No existe el cambio '${name}' en refacil-sdd/changes/${name}/`);
@@ -334,14 +402,21 @@ function cmdSetReviewFails(argv, projectRoot) {
334
402
 
335
403
  function cmdClearReviewFails(argv, projectRoot) {
336
404
  const args = parseArgs(argv);
337
- const name = args._positional[0];
405
+ const rawName = args._positional[0];
338
406
 
339
- if (!name) {
407
+ if (!rawName) {
340
408
  console.error('Uso: refacil-sdd-ai sdd clear-review-fails <nombre-cambio>');
341
409
  process.exit(1);
342
410
  }
343
411
 
344
412
  const root = projectRoot;
413
+ autoMigrateOpenspec(root);
414
+ const resolved = resolveExistingChangeName(root, rawName);
415
+ if (!resolved.ok) {
416
+ console.error(resolved.reason);
417
+ process.exit(1);
418
+ }
419
+ const name = resolved.name;
345
420
  const reviewFailsPath = path.join(root, 'refacil-sdd', 'changes', name, '.review-last-fails.json');
346
421
 
347
422
  if (fs.existsSync(reviewFailsPath)) {
@@ -392,15 +467,21 @@ function cmdList(argv, projectRoot) {
392
467
 
393
468
  function cmdStatus(argv, projectRoot) {
394
469
  const args = parseArgs(argv);
395
- const name = args._positional[0];
470
+ const rawName = args._positional[0];
396
471
  const wantJson = args.json === true;
397
472
 
398
- if (!name) {
473
+ if (!rawName) {
399
474
  console.error('Uso: refacil-sdd-ai sdd status <nombre-cambio> [--json]');
400
475
  process.exit(1);
401
476
  }
402
477
 
403
478
  autoMigrateOpenspec(projectRoot);
479
+ const resolved = resolveExistingChangeName(projectRoot, rawName);
480
+ if (!resolved.ok) {
481
+ console.error(resolved.reason);
482
+ process.exit(1);
483
+ }
484
+ const name = resolved.name;
404
485
 
405
486
  const changeDir = path.join(projectRoot, 'refacil-sdd', 'changes', name);
406
487
  if (!fs.existsSync(changeDir)) {
@@ -481,9 +562,9 @@ function cmdStatus(argv, projectRoot) {
481
562
 
482
563
  function cmdMarkReviewed(argv, projectRoot) {
483
564
  const args = parseArgs(argv);
484
- const name = args._positional[0];
565
+ const rawName = args._positional[0];
485
566
 
486
- if (!name) {
567
+ if (!rawName) {
487
568
  console.error('Uso: refacil-sdd-ai sdd mark-reviewed <nombre-cambio> --verdict <verdict> --summary "<resumen>" [--fail-count N] [--preexisting-count N] [--blockers]');
488
569
  process.exit(1);
489
570
  }
@@ -499,6 +580,12 @@ function cmdMarkReviewed(argv, projectRoot) {
499
580
  }
500
581
 
501
582
  autoMigrateOpenspec(projectRoot);
583
+ const resolved = resolveExistingChangeName(projectRoot, rawName);
584
+ if (!resolved.ok) {
585
+ console.error(resolved.reason);
586
+ process.exit(1);
587
+ }
588
+ const name = resolved.name;
502
589
 
503
590
  const changeDir = path.join(projectRoot, 'refacil-sdd', 'changes', name);
504
591
  if (!fs.existsSync(changeDir)) {
@@ -522,9 +609,9 @@ function cmdMarkReviewed(argv, projectRoot) {
522
609
 
523
610
  function cmdTasksUpdate(argv, projectRoot) {
524
611
  const args = parseArgs(argv);
525
- const name = args._positional[0];
612
+ const rawName = args._positional[0];
526
613
 
527
- if (!name) {
614
+ if (!rawName) {
528
615
  console.error('Uso: refacil-sdd-ai sdd tasks-update <nombre-cambio> --task N --done');
529
616
  process.exit(1);
530
617
  }
@@ -541,6 +628,12 @@ function cmdTasksUpdate(argv, projectRoot) {
541
628
  }
542
629
 
543
630
  autoMigrateOpenspec(projectRoot);
631
+ const resolved = resolveExistingChangeName(projectRoot, rawName);
632
+ if (!resolved.ok) {
633
+ console.error(resolved.reason);
634
+ process.exit(1);
635
+ }
636
+ const name = resolved.name;
544
637
 
545
638
  const tasksFile = path.join(projectRoot, 'refacil-sdd', 'changes', name, 'tasks.md');
546
639
  if (!fs.existsSync(tasksFile)) {
@@ -661,4 +754,4 @@ function handleSdd(sub, argv, projectRoot) {
661
754
  }
662
755
  }
663
756
 
664
- module.exports = { handleSdd, parseArgs, autoMigrateOpenspec, validateChangeName, findProjectRoot };
757
+ module.exports = { handleSdd, parseArgs, autoMigrateOpenspec, validateChangeName, resolveExistingChangeName, findProjectRoot };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "refacil-sdd-ai",
3
- "version": "4.5.7",
3
+ "version": "4.5.8",
4
4
  "description": "SDD-AI: Specification-Driven Development with AI — development methodology using AI with Claude Code, Cursor and OpenCode",
5
5
  "bin": {
6
6
  "refacil-sdd-ai": "./bin/cli.js"
@@ -22,7 +22,7 @@ Verify the change is truly complete:
22
22
 
23
23
  2. **Tests pass**: Resolve and run the test command according to `refacil-prereqs/METHODOLOGY-CONTRACT.md`. If there are failing tests, inform and ask if they want to continue.
24
24
 
25
- 3. **No pending files**: Run `git status` and verify if there are uncommitted changes related to the feature. If there are, suggest committing before archiving.
25
+ 3. **Working tree scope hygiene**: Run `git status` and check whether there are files unrelated to the current change scope. It is expected to have uncommitted changes in this step. If unrelated files are detected, warn the user and ask whether to continue archiving anyway. Do not suggest commit in this step; commit/push decisions are handled in `refacil:up-code`.
26
26
 
27
27
  4. **Review approved (blocking)**: Verify that the `.review-passed` file exists in the change folder (`refacil-sdd/changes/[change-name]/.review-passed`) following **`METHODOLOGY-CONTRACT.md` §8** (dotfile; do not conclude by listings without dotfiles). If it does NOT exist, **stop the archiving** and inform the user:
28
28
  ```
@@ -126,6 +126,8 @@ Bug fixes only contain `summary.md` (and optionally `.review-passed`). The CLI `
126
126
 
127
127
  The spec and review evidence are written **before** running the CLI archive command, while the artifacts are still at their original paths. The CLI only moves the folder — it never syncs specs.
128
128
 
129
+ `refacil-sdd-ai sdd archive` normalizes the provided `changeName` to lowercase before validation/path resolution. Prefer lowercase names for consistency across commands and records.
130
+
129
131
  1. **Sync spec to `refacil-sdd/specs/` (before archiving)**:
130
132
  - Read `refacil-sdd/changes/<changeName>/specs.md` (and all `.md` under `specs/` if that subfolder exists).
131
133
  - Determine the spec folder name: use `<changeName>` unless a more descriptive name is clearly better.