specflow-cc 1.22.1 → 1.23.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/CHANGELOG.md +19 -0
- package/bin/lib/todo.cjs +91 -14
- package/commands/sf/audit.md +19 -4
- package/commands/sf/autopilot.md +17 -2
- package/commands/sf/discuss.md +1 -1
- package/commands/sf/done.md +28 -4
- package/commands/sf/fix.md +23 -6
- package/commands/sf/health.md +2 -2
- package/commands/sf/help.md +1 -1
- package/commands/sf/pause.md +17 -2
- package/commands/sf/plan.md +13 -1
- package/commands/sf/review.md +19 -4
- package/commands/sf/revise.md +24 -7
- package/commands/sf/run.md +20 -5
- package/commands/sf/show.md +1 -1
- package/commands/sf/split.md +3 -3
- package/commands/sf/status.md +5 -5
- package/commands/sf/verify.md +16 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ All notable changes to SpecFlow will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.23.0] - 2026-05-27
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Strict TODO frontmatter validation in `cmdTodoReindex` (SPEC-015)** — the indexing chokepoint at [`bin/lib/todo.cjs`](bin/lib/todo.cjs) now enforces a `REQUIRED_TODO_FIELDS = ['id', 'title', 'created']` invariant per TODO file. Files missing any required field (or lacking a frontmatter block entirely) are no longer silently defaulted into the index — they are surfaced as `MALFORMED: <reason>` sentinel rows in `INDEX.md`, sorted after well-formed entries by `fileId` ascending. Each malformed file emits a per-file warning to stderr, the `**Total:**` summary line gains a `… X malformed` component, and `node bin/sf-tools.cjs todo reindex` exits non-zero (`process.exitCode = 1` after `output()` flushes) so callers can gate on it. Well-formed runs remain byte-identical to the legacy format. Validation lives at the call site, not in shared `parseFrontmatter()` — `cmdTodoLoad` / `cmdTodoList` keep their tolerance. 6 new tests in `tests/todo-index.test.cjs` cover the four malformed shapes, malformed-YAML, and a regression guard for legacy parity.
|
|
13
|
+
- **`todo check-stale` exit gate (TODO-029)** — `cmdTodoCheckStale` in [`bin/lib/todo.cjs`](bin/lib/todo.cjs) now sets `process.exitCode = 1` when `INDEX.md` drifts from disk (ghost rows or missing entries). Previously it only reported drift via JSON/text output and always exited 0, so callers could not gate on it.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **Ghost-row drift after `/sf:done` and `/sf:plan` finalize (TODO-029)** — the LLM-orchestrated `rm .specflow/todos/{source}.md` + `node bin/sf-tools.cjs todo reindex` sequence in [`commands/sf/done.md`](commands/sf/done.md) Step 7.5 and [`commands/sf/plan.md`](commands/sf/plan.md) Step 7 (plus its inline fallback) had no enforced atomicity — any inversion, interruption, or skipped step left `INDEX.md` with a row pointing at a deleted file, and the only enforcement was `/sf:status` running `check-stale` reactively, days later. Both command files now invoke `node ~/.claude/specflow-cc/bin/sf-tools.cjs todo check-stale` immediately after the `rm` + `reindex` block and treat non-zero exit as a finalization failure (re-run reindex once; if still stale, halt and surface `extra_in_index` / `missing_from_index` to the user). Surfaced in a downstream project (`~/Projects/topgun`) where SPEC-275 finalization left TODO-406 as a ghost in `INDEX.md`, then `/sf:plan TODO-406` operated on it.
|
|
18
|
+
|
|
19
|
+
## [1.22.2] - 2026-05-22
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **Installed `/sf:*` commands broken on user projects** — every `/sf:*` command shipped in `commands/sf/*.md` is now safe to run from a project directory with no local `bin/`. Two bugs fixed:
|
|
24
|
+
1. **Bare `bin/sf-tools.cjs` paths in 15 command files** (`audit.md`, `autopilot.md`, `discuss.md`, `done.md`, `fix.md`, `health.md`, `help.md`, `pause.md`, `review.md`, `revise.md`, `run.md`, `show.md`, `split.md`, `status.md`, `verify.md`) were rewritten to `~/.claude/specflow-cc/bin/sf-tools.cjs`. The installer rewrites this literal prefix to the actual install location, so every invocation now resolves correctly. Previously these lines errored with `Cannot find module '/path/to/project/bin/sf-tools.cjs'`.
|
|
25
|
+
2. **Unsafe `state resolve $ARGUMENTS` in 8 commands** (`audit.md`, `autopilot.md`, `done.md`, `fix.md`, `pause.md`, `review.md`, `run.md`, `verify.md`) was replaced with a SPEC-ID parsing guard: `$ARGUMENTS` is split into `FIRST_TOKEN` (matched against `^SPEC-\d{3,}$`) and a per-command scope variable (`FIX_SCOPE`, `DONE_SCOPE`, `AUDIT_SCOPE`, …). Previously `/sf:fix all`, `/sf:done --apply=minor`, etc. produced spurious `SPEC_NOT_ACTIVE` errors because the scope/mode flag was passed to the resolver as if it were a SPEC-ID.
|
|
26
|
+
|
|
8
27
|
## [1.22.1] - 2026-05-20
|
|
9
28
|
|
|
10
29
|
### Fixed
|
package/bin/lib/todo.cjs
CHANGED
|
@@ -15,6 +15,13 @@ const fs = require('fs');
|
|
|
15
15
|
const path = require('path');
|
|
16
16
|
const { output, error, safeReadFile, parseFrontmatter } = require('./core.cjs');
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Required YAML frontmatter fields for each TODO file.
|
|
20
|
+
* When any of these is absent or blank, the reindex records the file as MALFORMED
|
|
21
|
+
* rather than silently defaulting to empty values (which would hide drift).
|
|
22
|
+
*/
|
|
23
|
+
const REQUIRED_TODO_FIELDS = ['id', 'title', 'created'];
|
|
24
|
+
|
|
18
25
|
/**
|
|
19
26
|
* Priority sort order (lower number = higher priority in sort).
|
|
20
27
|
*/
|
|
@@ -284,23 +291,55 @@ function cmdTodoReindex(cwd, raw) {
|
|
|
284
291
|
const parsed = parseFrontmatter(content);
|
|
285
292
|
const fm = parsed.frontmatter;
|
|
286
293
|
|
|
294
|
+
// Determine which required fields are absent or blank.
|
|
295
|
+
const missing = REQUIRED_TODO_FIELDS.filter(k => !fm[k] || String(fm[k]).trim() === '');
|
|
296
|
+
|
|
297
|
+
if (missing.length > 0) {
|
|
298
|
+
// Distinguish "no frontmatter block at all" (fm has no keys) from "some
|
|
299
|
+
// fields present but specific ones missing".
|
|
300
|
+
const hasAnyKey = Object.keys(fm).length > 0;
|
|
301
|
+
const reason = hasAnyKey
|
|
302
|
+
? 'missing fields: ' + missing.join(', ')
|
|
303
|
+
: 'no frontmatter block';
|
|
304
|
+
|
|
305
|
+
process.stderr.write('warn: ' + file + ' — ' + reason + '\n');
|
|
306
|
+
|
|
307
|
+
todos.push({
|
|
308
|
+
malformed: true,
|
|
309
|
+
fileId: file.replace('.md', ''),
|
|
310
|
+
reason,
|
|
311
|
+
});
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
|
|
287
315
|
// Strip surrounding quotes from title (YAML may preserve them)
|
|
288
|
-
let title = fm.title
|
|
316
|
+
let title = String(fm.title);
|
|
289
317
|
if ((title.startsWith('"') && title.endsWith('"')) || (title.startsWith("'") && title.endsWith("'"))) {
|
|
290
318
|
title = title.slice(1, -1);
|
|
291
319
|
}
|
|
292
320
|
|
|
293
321
|
todos.push({
|
|
294
|
-
id: fm.id
|
|
322
|
+
id: fm.id,
|
|
295
323
|
title,
|
|
296
324
|
priority: fm.priority || '—',
|
|
297
325
|
status: fm.status || 'open',
|
|
298
|
-
created: fm.created
|
|
326
|
+
created: fm.created,
|
|
299
327
|
});
|
|
300
328
|
}
|
|
301
329
|
|
|
302
|
-
// Sort by priority
|
|
330
|
+
// Sort: well-formed records by priority then created date; malformed records
|
|
331
|
+
// always come after all well-formed ones, ordered by fileId ascending.
|
|
303
332
|
todos.sort((a, b) => {
|
|
333
|
+
const am = a.malformed ? 1 : 0;
|
|
334
|
+
const bm = b.malformed ? 1 : 0;
|
|
335
|
+
if (am !== bm) return am - bm; // well-formed before malformed
|
|
336
|
+
if (a.malformed && b.malformed) {
|
|
337
|
+
// Both malformed: sort by fileId ascending
|
|
338
|
+
if (a.fileId < b.fileId) return -1;
|
|
339
|
+
if (a.fileId > b.fileId) return 1;
|
|
340
|
+
return 0;
|
|
341
|
+
}
|
|
342
|
+
// Both well-formed: priority then date
|
|
304
343
|
const pa = priorityKey(a.priority);
|
|
305
344
|
const pb = priorityKey(b.priority);
|
|
306
345
|
if (pa !== pb) return pa - pb;
|
|
@@ -309,13 +348,21 @@ function cmdTodoReindex(cwd, raw) {
|
|
|
309
348
|
return 0;
|
|
310
349
|
});
|
|
311
350
|
|
|
312
|
-
// Count by priority
|
|
351
|
+
// Count by priority — malformed records are excluded from priority breakdown.
|
|
313
352
|
const counts = { high: 0, medium: 0, low: 0, unset: 0 };
|
|
353
|
+
let malformedCount = 0;
|
|
314
354
|
for (const t of todos) {
|
|
315
|
-
if (t.
|
|
316
|
-
|
|
317
|
-
else if (t.priority === '
|
|
318
|
-
|
|
355
|
+
if (t.malformed) {
|
|
356
|
+
malformedCount++;
|
|
357
|
+
} else if (t.priority === 'high') {
|
|
358
|
+
counts.high++;
|
|
359
|
+
} else if (t.priority === 'medium') {
|
|
360
|
+
counts.medium++;
|
|
361
|
+
} else if (t.priority === 'low') {
|
|
362
|
+
counts.low++;
|
|
363
|
+
} else {
|
|
364
|
+
counts.unset++;
|
|
365
|
+
}
|
|
319
366
|
}
|
|
320
367
|
|
|
321
368
|
// Build INDEX.md
|
|
@@ -333,13 +380,28 @@ function cmdTodoReindex(cwd, raw) {
|
|
|
333
380
|
|
|
334
381
|
for (let i = 0; i < todos.length; i++) {
|
|
335
382
|
const t = todos[i];
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
383
|
+
if (t.malformed) {
|
|
384
|
+
// Render the MALFORMED sentinel row; truncate the reason if very long.
|
|
385
|
+
let marker = 'MALFORMED: ' + t.reason;
|
|
386
|
+
if (marker.length > 50) marker = marker.slice(0, 50) + '...';
|
|
387
|
+
lines.push(`| ${i + 1} | ${t.fileId} | ${marker} | — | — | — |`);
|
|
388
|
+
} else {
|
|
389
|
+
let title = t.title;
|
|
390
|
+
if (title.length > 50) title = title.slice(0, 50) + '...';
|
|
391
|
+
lines.push(`| ${i + 1} | ${t.id} | ${title} | ${t.priority} | ${t.status} | ${t.created} |`);
|
|
392
|
+
}
|
|
339
393
|
}
|
|
340
394
|
|
|
341
395
|
lines.push('');
|
|
342
|
-
|
|
396
|
+
// Malformed records count toward N items total; they are excluded only from
|
|
397
|
+
// the priority breakdown (H/M/L/unset). When all TODOs are well-formed the
|
|
398
|
+
// summary line is byte-identical to the legacy format so downstream parsers
|
|
399
|
+
// of well-formed runs stay compatible.
|
|
400
|
+
if (malformedCount > 0) {
|
|
401
|
+
lines.push(`**Total:** ${todos.length} items (${counts.high} high, ${counts.medium} medium, ${counts.low} low, ${counts.unset} unset, ${malformedCount} malformed)`);
|
|
402
|
+
} else {
|
|
403
|
+
lines.push(`**Total:** ${todos.length} items (${counts.high} high, ${counts.medium} medium, ${counts.low} low, ${counts.unset} unset)`);
|
|
404
|
+
}
|
|
343
405
|
lines.push('');
|
|
344
406
|
lines.push('---');
|
|
345
407
|
const now = new Date();
|
|
@@ -350,7 +412,15 @@ function cmdTodoReindex(cwd, raw) {
|
|
|
350
412
|
const indexPath = path.join(todosDir, 'INDEX.md');
|
|
351
413
|
fs.writeFileSync(indexPath, lines.join('\n'), 'utf8');
|
|
352
414
|
|
|
353
|
-
|
|
415
|
+
// Signal callers that drift was found — set exit code non-zero AFTER writing
|
|
416
|
+
// INDEX.md (so the file is on disk) and BEFORE the output() call (so JSON
|
|
417
|
+
// still flushes). Do NOT call process.exit() here; Node will use exitCode
|
|
418
|
+
// when the event loop drains.
|
|
419
|
+
if (malformedCount > 0) {
|
|
420
|
+
process.exitCode = 1;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
output({ reindexed: todos.length, malformed: malformedCount, path: indexPath }, raw, `Reindexed ${todos.length} TODOs → INDEX.md`);
|
|
354
424
|
}
|
|
355
425
|
|
|
356
426
|
/**
|
|
@@ -408,6 +478,13 @@ function cmdTodoCheckStale(cwd, raw) {
|
|
|
408
478
|
extraInIndex.length > 0 ||
|
|
409
479
|
(!indexExists && diskIds.size > 0);
|
|
410
480
|
|
|
481
|
+
// Exit non-zero when stale so callers can use this as a gate after
|
|
482
|
+
// delete-and-reindex sequences (per SPEC TODO-029). Set exitCode before
|
|
483
|
+
// output() so JSON still flushes; do not call process.exit() directly.
|
|
484
|
+
if (stale) {
|
|
485
|
+
process.exitCode = 1;
|
|
486
|
+
}
|
|
487
|
+
|
|
411
488
|
output(
|
|
412
489
|
{
|
|
413
490
|
stale,
|
package/commands/sf/audit.md
CHANGED
|
@@ -43,7 +43,22 @@ Exit.
|
|
|
43
43
|
|
|
44
44
|
## Step 2: Resolve Active Specification
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
Parse `$ARGUMENTS`:
|
|
47
|
+
- Let `FIRST_TOKEN` = first whitespace-separated token of `$ARGUMENTS`.
|
|
48
|
+
- If `FIRST_TOKEN` matches `^SPEC-\d{3,}$`:
|
|
49
|
+
- Set `SPEC_ARG="$FIRST_TOKEN"`
|
|
50
|
+
- Set `AUDIT_SCOPE` = remainder of `$ARGUMENTS` after `FIRST_TOKEN` (trimmed)
|
|
51
|
+
- Else:
|
|
52
|
+
- Set `SPEC_ARG=""` (resolver uses Active Specifications table)
|
|
53
|
+
- Set `AUDIT_SCOPE="$ARGUMENTS"`
|
|
54
|
+
|
|
55
|
+
Call:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state resolve $SPEC_ARG
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Use `AUDIT_SCOPE` in the subsequent argument-parsing step (Step 3.5).
|
|
47
62
|
|
|
48
63
|
Parse the JSON response:
|
|
49
64
|
- `{"action":"use","id":"SPEC-XXX"}` → proceed with SPEC-XXX
|
|
@@ -77,7 +92,7 @@ Exit.
|
|
|
77
92
|
|
|
78
93
|
## Step 3.5: Check for --import Flag
|
|
79
94
|
|
|
80
|
-
Parse
|
|
95
|
+
Parse `AUDIT_SCOPE` for `--import "feedback text"` pattern.
|
|
81
96
|
|
|
82
97
|
**If --import flag present:** Go to Step 4-IMPORT
|
|
83
98
|
**Otherwise:** Continue to Step 4 (internal audit)
|
|
@@ -156,7 +171,7 @@ In spec frontmatter, set: `status: revision_requested`
|
|
|
156
171
|
|
|
157
172
|
Update STATE.md via CLI:
|
|
158
173
|
```bash
|
|
159
|
-
node bin/sf-tools.cjs state add-active SPEC-XXX external_review /sf:revise
|
|
174
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state add-active SPEC-XXX external_review /sf:revise
|
|
160
175
|
```
|
|
161
176
|
Add decision: "Imported external feedback for SPEC-XXX"
|
|
162
177
|
|
|
@@ -299,7 +314,7 @@ Tip: `/clear` recommended — executor needs fresh context for implementation
|
|
|
299
314
|
|
|
300
315
|
### If APPROVED (with optional recommendations):
|
|
301
316
|
|
|
302
|
-
The `Recommendation:` line is emitted by the auditor agent (Step 7.5 in `agents/spec-auditor.md`) using `node bin/sf-tools.cjs recommend --source audit --critical 0 --minor N`. The STATE.md Next Step remains `/sf:run` (without the `--apply=minor` suffix) — the suffix is advisory here only.
|
|
317
|
+
The `Recommendation:` line is emitted by the auditor agent (Step 7.5 in `agents/spec-auditor.md`) using `node ~/.claude/specflow-cc/bin/sf-tools.cjs recommend --source audit --critical 0 --minor N`. The STATE.md Next Step remains `/sf:run` (without the `--apply=minor` suffix) — the suffix is advisory here only.
|
|
303
318
|
|
|
304
319
|
```
|
|
305
320
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
package/commands/sf/autopilot.md
CHANGED
|
@@ -50,7 +50,22 @@ Parse the command argument to determine execution mode:
|
|
|
50
50
|
|
|
51
51
|
**CRITICAL — N>1 guard (autopilot must be unambiguous):**
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
Parse `$ARGUMENTS`:
|
|
54
|
+
- Let `FIRST_TOKEN` = first whitespace-separated token of `$ARGUMENTS`.
|
|
55
|
+
- If `FIRST_TOKEN` matches `^SPEC-\d{3,}$`:
|
|
56
|
+
- Set `SPEC_ARG="$FIRST_TOKEN"`
|
|
57
|
+
- Set `AUTOPILOT_SCOPE` = remainder of `$ARGUMENTS` after `FIRST_TOKEN` (trimmed)
|
|
58
|
+
- Else:
|
|
59
|
+
- Set `SPEC_ARG=""` (resolver uses Active Specifications table)
|
|
60
|
+
- Set `AUTOPILOT_SCOPE="$ARGUMENTS"`
|
|
61
|
+
|
|
62
|
+
Call:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state resolve $SPEC_ARG
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Use `AUTOPILOT_SCOPE` in the subsequent mode-determination step (e.g. `--all` flag).
|
|
54
69
|
|
|
55
70
|
Parse the JSON response:
|
|
56
71
|
- `{"action":"use","id":"SPEC-XXX"}` → proceed with SPEC-XXX
|
|
@@ -274,7 +289,7 @@ mv .specflow/specs/SPEC-XXX.md .specflow/archive/
|
|
|
274
289
|
5. **Update STATE.md:**
|
|
275
290
|
Remove SPEC-XXX from Active Specifications table:
|
|
276
291
|
```bash
|
|
277
|
-
node bin/sf-tools.cjs state remove-active SPEC-XXX
|
|
292
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state remove-active SPEC-XXX
|
|
278
293
|
```
|
|
279
294
|
Remove SPEC-XXX row from Queue table (using Read+Write).
|
|
280
295
|
|
package/commands/sf/discuss.md
CHANGED
|
@@ -62,7 +62,7 @@ Determine discussion mode from arguments:
|
|
|
62
62
|
- Mode: `requirements-gathering`
|
|
63
63
|
|
|
64
64
|
**Case E: No arguments**
|
|
65
|
-
- Call `node bin/sf-tools.cjs state resolve` to resolve active spec
|
|
65
|
+
- Call `node ~/.claude/specflow-cc/bin/sf-tools.cjs state resolve` to resolve active spec
|
|
66
66
|
- `{"action":"use","id":"SPEC-XXX"}` → discuss that spec
|
|
67
67
|
- `{"action":"error","code":"NO_ACTIVE_SPEC"}` → ask what to discuss
|
|
68
68
|
- `{"action":"ask","options":[...]}` → use AskUserQuestion to pick which spec to discuss
|
package/commands/sf/done.md
CHANGED
|
@@ -39,7 +39,22 @@ Exit.
|
|
|
39
39
|
|
|
40
40
|
## Step 2: Resolve Active Specification
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
Parse `$ARGUMENTS`:
|
|
43
|
+
- Let `FIRST_TOKEN` = first whitespace-separated token of `$ARGUMENTS`.
|
|
44
|
+
- If `FIRST_TOKEN` matches `^SPEC-\d{3,}$`:
|
|
45
|
+
- Set `SPEC_ARG="$FIRST_TOKEN"`
|
|
46
|
+
- Set `DONE_SCOPE` = remainder of `$ARGUMENTS` after `FIRST_TOKEN` (trimmed)
|
|
47
|
+
- Else:
|
|
48
|
+
- Set `SPEC_ARG=""` (resolver uses Active Specifications table)
|
|
49
|
+
- Set `DONE_SCOPE="$ARGUMENTS"`
|
|
50
|
+
|
|
51
|
+
Call:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state resolve $SPEC_ARG
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Use `DONE_SCOPE` in the subsequent argument-parsing step (Step 2.5).
|
|
43
58
|
|
|
44
59
|
Parse the JSON response:
|
|
45
60
|
- `{"action":"use","id":"SPEC-XXX"}` → proceed with SPEC-XXX
|
|
@@ -61,7 +76,7 @@ Parse the JSON response:
|
|
|
61
76
|
|
|
62
77
|
## Step 2.5: Handle `--apply=minor` Flag
|
|
63
78
|
|
|
64
|
-
**Check if `--apply=minor
|
|
79
|
+
**Check if `DONE_SCOPE` contains `--apply=minor`.**
|
|
65
80
|
|
|
66
81
|
**If `--apply=minor` is NOT present:** Continue to Step 3 (existing behavior unchanged).
|
|
67
82
|
|
|
@@ -85,7 +100,7 @@ Extract Critical, Major, and Minor counts from that entry.
|
|
|
85
100
|
|
|
86
101
|
Run:
|
|
87
102
|
```bash
|
|
88
|
-
node bin/sf-tools.cjs recommend --source review --critical N --major M --minor K
|
|
103
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs recommend --source review --critical N --major M --minor K
|
|
89
104
|
```
|
|
90
105
|
|
|
91
106
|
Parse the JSON response.
|
|
@@ -385,6 +400,15 @@ rm .specflow/todos/{source}.md
|
|
|
385
400
|
node ~/.claude/specflow-cc/bin/sf-tools.cjs todo reindex
|
|
386
401
|
```
|
|
387
402
|
|
|
403
|
+
Then run the stale-check exit gate. The `rm` + `reindex` sequence is LLM-orchestrated and has no machine-enforced atomicity — any inversion, interruption, or skipped step leaves INDEX.md with a ghost row. Treat non-zero exit as a **finalization failure**: re-run `todo reindex`, and if still stale, surface the drift to the user before continuing.
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs todo check-stale
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
- **Exit 0 (FRESH):** proceed to Step 8.
|
|
410
|
+
- **Exit 1 (STALE):** re-run `todo reindex` once; if `check-stale` still exits non-zero, halt finalization and report `extra_in_index` / `missing_from_index` from the JSON output so the user can repair the drift manually.
|
|
411
|
+
|
|
388
412
|
3. **If NOT_FOUND (backward compatibility):** Also check legacy format — look in `.specflow/todos/TODO.md` for the referenced ID. If found there, remove the block using the Edit tool.
|
|
389
413
|
|
|
390
414
|
No "Last updated" lines to update in per-file format.
|
|
@@ -414,7 +438,7 @@ If the command fails (parser cannot extract required fields), log a warning to t
|
|
|
414
438
|
### Remove from Active Specifications Table
|
|
415
439
|
|
|
416
440
|
```bash
|
|
417
|
-
node bin/sf-tools.cjs state remove-active SPEC-XXX
|
|
441
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state remove-active SPEC-XXX
|
|
418
442
|
```
|
|
419
443
|
|
|
420
444
|
### Remove from Queue
|
package/commands/sf/fix.md
CHANGED
|
@@ -43,7 +43,22 @@ Exit.
|
|
|
43
43
|
|
|
44
44
|
## Step 2: Resolve Active Specification
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
Parse `$ARGUMENTS`:
|
|
47
|
+
- Let `FIRST_TOKEN` = first whitespace-separated token of `$ARGUMENTS`.
|
|
48
|
+
- If `FIRST_TOKEN` matches `^SPEC-\d{3,}$`:
|
|
49
|
+
- Set `SPEC_ARG="$FIRST_TOKEN"`
|
|
50
|
+
- Set `FIX_SCOPE` = remainder of `$ARGUMENTS` after `FIRST_TOKEN` (trimmed)
|
|
51
|
+
- Else:
|
|
52
|
+
- Set `SPEC_ARG=""` (resolver uses Active Specifications table)
|
|
53
|
+
- Set `FIX_SCOPE="$ARGUMENTS"`
|
|
54
|
+
|
|
55
|
+
Call:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state resolve $SPEC_ARG
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Use `FIX_SCOPE` in the subsequent argument-parsing step (Step 5).
|
|
47
62
|
|
|
48
63
|
Parse the JSON response:
|
|
49
64
|
- `{"action":"use","id":"SPEC-XXX"}` → proceed with SPEC-XXX
|
|
@@ -93,14 +108,16 @@ Exit.
|
|
|
93
108
|
|
|
94
109
|
## Step 5: Parse Arguments
|
|
95
110
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
|
111
|
+
Parse `FIX_SCOPE` (set in Step 2):
|
|
112
|
+
|
|
113
|
+
| Value of `FIX_SCOPE` | Action |
|
|
114
|
+
|----------------------|--------|
|
|
115
|
+
| (empty) | Interactive mode — show issues, ask what to fix |
|
|
99
116
|
| "all" | Apply all critical AND major AND minor fixes |
|
|
100
117
|
| "1,2,3" | Apply only numbered items |
|
|
101
118
|
| "..." | Treat as custom fix instructions |
|
|
102
119
|
|
|
103
|
-
### If Interactive Mode (
|
|
120
|
+
### If Interactive Mode (FIX_SCOPE is empty):
|
|
104
121
|
|
|
105
122
|
Display review findings:
|
|
106
123
|
|
|
@@ -182,7 +199,7 @@ Append to Review History:
|
|
|
182
199
|
**If `--internal` is NOT set (normal invocation):**
|
|
183
200
|
|
|
184
201
|
```bash
|
|
185
|
-
node bin/sf-tools.cjs state add-active SPEC-XXX review /sf:review
|
|
202
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state add-active SPEC-XXX review /sf:review
|
|
186
203
|
```
|
|
187
204
|
|
|
188
205
|
## Step 9: Display Result
|
package/commands/sf/health.md
CHANGED
|
@@ -38,7 +38,7 @@ Exit.
|
|
|
38
38
|
Run migration on entry — idempotent, no-op when already migrated:
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
|
-
node bin/sf-tools.cjs state migrate
|
|
41
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state migrate
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
Parse the response:
|
|
@@ -84,7 +84,7 @@ For each check:
|
|
|
84
84
|
Read STATE.md and validate:
|
|
85
85
|
|
|
86
86
|
**E003: Active spec references non-existent file**
|
|
87
|
-
- List all active specs via `node bin/sf-tools.cjs state list-active`
|
|
87
|
+
- List all active specs via `node ~/.claude/specflow-cc/bin/sf-tools.cjs state list-active`
|
|
88
88
|
- For each SPEC-ID, check `.specflow/specs/{ID}.md` exists
|
|
89
89
|
- If missing: error (repairable — remove that row via `state remove-active`)
|
|
90
90
|
|
package/commands/sf/help.md
CHANGED
|
@@ -206,7 +206,7 @@ Exit.
|
|
|
206
206
|
|
|
207
207
|
Show active specs count (uses list-active for multi-spec awareness):
|
|
208
208
|
```bash
|
|
209
|
-
node bin/sf-tools.cjs state list-active --raw
|
|
209
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state list-active --raw
|
|
210
210
|
```
|
|
211
211
|
|
|
212
212
|
Display full command reference:
|
package/commands/sf/pause.md
CHANGED
|
@@ -36,7 +36,22 @@ Exit.
|
|
|
36
36
|
|
|
37
37
|
## Step 2: Resolve Active Specification
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
Parse `$ARGUMENTS`:
|
|
40
|
+
- Let `FIRST_TOKEN` = first whitespace-separated token of `$ARGUMENTS`.
|
|
41
|
+
- If `FIRST_TOKEN` matches `^SPEC-\d{3,}$`:
|
|
42
|
+
- Set `SPEC_ARG="$FIRST_TOKEN"`
|
|
43
|
+
- Set `PAUSE_NOTE` = remainder of `$ARGUMENTS` after `FIRST_TOKEN` (trimmed)
|
|
44
|
+
- Else:
|
|
45
|
+
- Set `SPEC_ARG=""` (resolver uses Active Specifications table)
|
|
46
|
+
- Set `PAUSE_NOTE="$ARGUMENTS"`
|
|
47
|
+
|
|
48
|
+
Call:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state resolve $SPEC_ARG
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Use `PAUSE_NOTE` in Step 6.5 (notes prompt — pre-populate if non-empty).
|
|
40
55
|
|
|
41
56
|
Parse the JSON response:
|
|
42
57
|
- `{"action":"use","id":"SPEC-XXX"}` → proceed with SPEC-XXX
|
|
@@ -53,7 +68,7 @@ Parse the JSON response:
|
|
|
53
68
|
Options: {id — title (status)} for each entry
|
|
54
69
|
```
|
|
55
70
|
|
|
56
|
-
Also call `node bin/sf-tools.cjs state list-active` to capture all active specs in the pause file.
|
|
71
|
+
Also call `node ~/.claude/specflow-cc/bin/sf-tools.cjs state list-active` to capture all active specs in the pause file.
|
|
57
72
|
|
|
58
73
|
## Step 3: Load Active Specification Details
|
|
59
74
|
|
package/commands/sf/plan.md
CHANGED
|
@@ -179,6 +179,15 @@ node ~/.claude/specflow-cc/bin/sf-tools.cjs todo reindex
|
|
|
179
179
|
|
|
180
180
|
This is mandatory — skipping it leaves INDEX.md listing a TODO that no longer exists on disk, which trips the `/sf:status` freshness check and breaks downstream consumers.
|
|
181
181
|
|
|
182
|
+
4. **Run the stale-check exit gate.** The `rm` + `reindex` sequence is LLM-orchestrated; this surfaces drift immediately instead of deferring it to the next `/sf:status`.
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs todo check-stale
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
- **Exit 0 (FRESH):** proceed to Step 8.
|
|
189
|
+
- **Exit 1 (STALE):** re-run `todo reindex` once; if `check-stale` still exits non-zero, halt the conversion and report `extra_in_index` / `missing_from_index` from the JSON output so the user can repair the drift manually.
|
|
190
|
+
|
|
182
191
|
## Step 8: Display Result
|
|
183
192
|
|
|
184
193
|
**IMPORTANT:** Output the following directly as formatted text, NOT wrapped in a markdown code block:
|
|
@@ -240,13 +249,16 @@ Use `/sf:new "{todo description}"` logic:
|
|
|
240
249
|
|
|
241
250
|
### Remove Todo
|
|
242
251
|
|
|
243
|
-
Delete the file `.specflow/todos/TODO-{XXX}.md`, then refresh INDEX.md:
|
|
252
|
+
Delete the file `.specflow/todos/TODO-{XXX}.md`, then refresh INDEX.md and run the stale-check exit gate:
|
|
244
253
|
|
|
245
254
|
```bash
|
|
246
255
|
rm .specflow/todos/TODO-{XXX}.md
|
|
247
256
|
node ~/.claude/specflow-cc/bin/sf-tools.cjs todo reindex
|
|
257
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs todo check-stale
|
|
248
258
|
```
|
|
249
259
|
|
|
260
|
+
Treat non-zero exit from `check-stale` as a failure: re-run `todo reindex`, and if still stale, surface the drift to the user before continuing.
|
|
261
|
+
|
|
250
262
|
</fallback>
|
|
251
263
|
|
|
252
264
|
<success_criteria>
|
package/commands/sf/review.md
CHANGED
|
@@ -39,7 +39,22 @@ Exit.
|
|
|
39
39
|
|
|
40
40
|
## Step 2: Resolve Active Specification
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
Parse `$ARGUMENTS`:
|
|
43
|
+
- Let `FIRST_TOKEN` = first whitespace-separated token of `$ARGUMENTS`.
|
|
44
|
+
- If `FIRST_TOKEN` matches `^SPEC-\d{3,}$`:
|
|
45
|
+
- Set `SPEC_ARG="$FIRST_TOKEN"`
|
|
46
|
+
- Set `REVIEW_SCOPE` = remainder of `$ARGUMENTS` after `FIRST_TOKEN` (trimmed)
|
|
47
|
+
- Else:
|
|
48
|
+
- Set `SPEC_ARG=""` (resolver uses Active Specifications table)
|
|
49
|
+
- Set `REVIEW_SCOPE="$ARGUMENTS"`
|
|
50
|
+
|
|
51
|
+
Call:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state resolve $SPEC_ARG
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Use `REVIEW_SCOPE` in subsequent steps for any review flags.
|
|
43
58
|
|
|
44
59
|
Parse the JSON response:
|
|
45
60
|
- `{"action":"use","id":"SPEC-XXX"}` → proceed with SPEC-XXX
|
|
@@ -158,7 +173,7 @@ After the agent updates STATE.md, check if rotation is needed:
|
|
|
158
173
|
|
|
159
174
|
### If APPROVED (no minor issues):
|
|
160
175
|
|
|
161
|
-
The `Recommendation:` line is emitted by the reviewer agent (Step 7.5 in `agents/impl-reviewer.md`) using `node bin/sf-tools.cjs recommend --source review --critical 0 --major 0 --minor 0`. The STATE.md Next Step remains `/sf:done` (canonical).
|
|
176
|
+
The `Recommendation:` line is emitted by the reviewer agent (Step 7.5 in `agents/impl-reviewer.md`) using `node ~/.claude/specflow-cc/bin/sf-tools.cjs recommend --source review --critical 0 --major 0 --minor 0`. The STATE.md Next Step remains `/sf:done` (canonical).
|
|
162
177
|
|
|
163
178
|
```
|
|
164
179
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
@@ -345,9 +360,9 @@ Append Review History to spec.
|
|
|
345
360
|
|
|
346
361
|
```bash
|
|
347
362
|
# If APPROVED:
|
|
348
|
-
node bin/sf-tools.cjs state add-active SPEC-XXX done /sf:done
|
|
363
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state add-active SPEC-XXX done /sf:done
|
|
349
364
|
# If CHANGES_REQUESTED:
|
|
350
|
-
node bin/sf-tools.cjs state add-active SPEC-XXX review /sf:fix
|
|
365
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state add-active SPEC-XXX review /sf:fix
|
|
351
366
|
```
|
|
352
367
|
|
|
353
368
|
</fallback>
|
package/commands/sf/revise.md
CHANGED
|
@@ -42,7 +42,22 @@ Exit.
|
|
|
42
42
|
|
|
43
43
|
## Step 2: Resolve Active Specification
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
Parse `$ARGUMENTS`:
|
|
46
|
+
- Let `FIRST_TOKEN` = first whitespace-separated token of `$ARGUMENTS`.
|
|
47
|
+
- If `FIRST_TOKEN` matches `^SPEC-\d{3,}$`:
|
|
48
|
+
- Set `SPEC_ARG="$FIRST_TOKEN"`
|
|
49
|
+
- Set `REVISE_SCOPE` = remainder of `$ARGUMENTS` after `FIRST_TOKEN` (trimmed)
|
|
50
|
+
- Else:
|
|
51
|
+
- Set `SPEC_ARG=""` (resolver uses Active Specifications table)
|
|
52
|
+
- Set `REVISE_SCOPE="$ARGUMENTS"`
|
|
53
|
+
|
|
54
|
+
Call:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state resolve $SPEC_ARG
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Use `REVISE_SCOPE` in the subsequent argument-parsing step (Step 5).
|
|
46
61
|
|
|
47
62
|
Parse the JSON response:
|
|
48
63
|
- `{"action":"use","id":"SPEC-XXX"}` → proceed with SPEC-XXX
|
|
@@ -160,9 +175,11 @@ Continue to Step 5 with analysis context available.
|
|
|
160
175
|
|
|
161
176
|
## Step 5: Parse Arguments
|
|
162
177
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
|
178
|
+
Parse `REVISE_SCOPE` (set in Step 2):
|
|
179
|
+
|
|
180
|
+
| Value of `REVISE_SCOPE` | Action |
|
|
181
|
+
|-------------------------|--------|
|
|
182
|
+
| (empty) | Interactive mode — show comments, ask what to fix |
|
|
166
183
|
| "all" | Apply all critical issues AND recommendations |
|
|
167
184
|
| "1,2,3" | Apply only numbered items |
|
|
168
185
|
| "--no-analysis" | Skip pre-analysis, go directly to review mode |
|
|
@@ -170,9 +187,9 @@ Continue to Step 5 with analysis context available.
|
|
|
170
187
|
|
|
171
188
|
**Check for `--no-analysis` flag:**
|
|
172
189
|
|
|
173
|
-
If
|
|
190
|
+
If `REVISE_SCOPE` contains `--no-analysis`:
|
|
174
191
|
- Set SKIP_ANALYSIS to true
|
|
175
|
-
- Remove the `--no-analysis` flag from
|
|
192
|
+
- Remove the `--no-analysis` flag from `REVISE_SCOPE` for further processing
|
|
176
193
|
|
|
177
194
|
### If Interactive Mode (no arguments):
|
|
178
195
|
|
|
@@ -532,7 +549,7 @@ In spec frontmatter: `status: auditing`
|
|
|
532
549
|
|
|
533
550
|
In STATE.md:
|
|
534
551
|
```bash
|
|
535
|
-
node bin/sf-tools.cjs state add-active SPEC-XXX auditing /sf:audit
|
|
552
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state add-active SPEC-XXX auditing /sf:audit
|
|
536
553
|
```
|
|
537
554
|
|
|
538
555
|
</fallback>
|
package/commands/sf/run.md
CHANGED
|
@@ -42,7 +42,22 @@ Exit.
|
|
|
42
42
|
|
|
43
43
|
## Step 2: Resolve Active Specification
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
Parse `$ARGUMENTS`:
|
|
46
|
+
- Let `FIRST_TOKEN` = first whitespace-separated token of `$ARGUMENTS`.
|
|
47
|
+
- If `FIRST_TOKEN` matches `^SPEC-\d{3,}$`:
|
|
48
|
+
- Set `SPEC_ARG="$FIRST_TOKEN"`
|
|
49
|
+
- Set `RUN_SCOPE` = remainder of `$ARGUMENTS` after `FIRST_TOKEN` (trimmed)
|
|
50
|
+
- Else:
|
|
51
|
+
- Set `SPEC_ARG=""` (resolver uses Active Specifications table)
|
|
52
|
+
- Set `RUN_SCOPE="$ARGUMENTS"`
|
|
53
|
+
|
|
54
|
+
Call:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state resolve $SPEC_ARG
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Use `RUN_SCOPE` in the subsequent argument-parsing step (Step 3.5).
|
|
46
61
|
|
|
47
62
|
Parse the JSON response:
|
|
48
63
|
- `{"action":"use","id":"SPEC-XXX"}` → proceed with SPEC-XXX
|
|
@@ -68,7 +83,7 @@ Read the active spec file: `.specflow/specs/SPEC-XXX.md`
|
|
|
68
83
|
|
|
69
84
|
## Step 3.5: Handle `--apply=minor` Flag
|
|
70
85
|
|
|
71
|
-
**Check if `--apply=minor
|
|
86
|
+
**Check if `RUN_SCOPE` contains `--apply=minor`.**
|
|
72
87
|
|
|
73
88
|
**If `--apply=minor` is NOT present:** Continue to Step 4 (existing behavior unchanged).
|
|
74
89
|
|
|
@@ -92,7 +107,7 @@ Extract Critical count and Recommendations count from that entry. Map Recommenda
|
|
|
92
107
|
|
|
93
108
|
Run:
|
|
94
109
|
```bash
|
|
95
|
-
node bin/sf-tools.cjs recommend --source audit --critical N --minor M
|
|
110
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs recommend --source audit --critical N --minor M
|
|
96
111
|
```
|
|
97
112
|
|
|
98
113
|
Parse the JSON response.
|
|
@@ -118,7 +133,7 @@ This reuses `/sf:revise`'s existing per-item commit behavior. Do NOT duplicate r
|
|
|
118
133
|
|
|
119
134
|
Run spec structural validation:
|
|
120
135
|
```bash
|
|
121
|
-
node bin/sf-tools.cjs spec validate SPEC-XXX
|
|
136
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs spec validate SPEC-XXX
|
|
122
137
|
```
|
|
123
138
|
|
|
124
139
|
This is the exact gate specified in R2.5: verifies frontmatter parses, required fields present (`id`, `type`, `status`, `priority`), and `## Requirements` heading present. No fallback path.
|
|
@@ -265,7 +280,7 @@ Use model for `spec-executor` or `spec-executor-orchestrator` from selected prof
|
|
|
265
280
|
## Step 7: Update Status
|
|
266
281
|
|
|
267
282
|
```bash
|
|
268
|
-
node bin/sf-tools.cjs state add-active SPEC-XXX running "(in progress)"
|
|
283
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state add-active SPEC-XXX running "(in progress)"
|
|
269
284
|
```
|
|
270
285
|
|
|
271
286
|
Update spec frontmatter:
|
package/commands/sf/show.md
CHANGED
|
@@ -42,7 +42,7 @@ Exit.
|
|
|
42
42
|
Use provided ID (e.g., SPEC-003).
|
|
43
43
|
|
|
44
44
|
**If no argument:**
|
|
45
|
-
Call `node bin/sf-tools.cjs state resolve` to get active spec.
|
|
45
|
+
Call `node ~/.claude/specflow-cc/bin/sf-tools.cjs state resolve` to get active spec.
|
|
46
46
|
|
|
47
47
|
Parse the JSON response:
|
|
48
48
|
- `{"action":"use","id":"SPEC-XXX"}` → use SPEC-XXX
|
package/commands/sf/split.md
CHANGED
|
@@ -58,7 +58,7 @@ Use `/sf:list` to see available specifications.
|
|
|
58
58
|
Exit.
|
|
59
59
|
|
|
60
60
|
**If no ID provided:**
|
|
61
|
-
Call `node bin/sf-tools.cjs state resolve` to get active spec.
|
|
61
|
+
Call `node ~/.claude/specflow-cc/bin/sf-tools.cjs state resolve` to get active spec.
|
|
62
62
|
|
|
63
63
|
Parse the JSON response:
|
|
64
64
|
- `{"action":"use","id":"SPEC-XXX"}` → use SPEC-XXX
|
|
@@ -365,8 +365,8 @@ Add split reference to archived parent.
|
|
|
365
365
|
- Add children to Queue (using Read+Write)
|
|
366
366
|
- Register first child in Active Specifications table:
|
|
367
367
|
```bash
|
|
368
|
-
node bin/sf-tools.cjs state remove-active SPEC-PARENT
|
|
369
|
-
node bin/sf-tools.cjs state add-active SPEC-XXXa draft /sf:audit
|
|
368
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state remove-active SPEC-PARENT
|
|
369
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state add-active SPEC-XXXa draft /sf:audit
|
|
370
370
|
```
|
|
371
371
|
|
|
372
372
|
</fallback>
|
package/commands/sf/status.md
CHANGED
|
@@ -39,12 +39,12 @@ Read `.specflow/STATE.md` and extract Queue, Recent Decisions, Warnings.
|
|
|
39
39
|
|
|
40
40
|
Get active specs:
|
|
41
41
|
```bash
|
|
42
|
-
node bin/sf-tools.cjs state list-active
|
|
42
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state list-active
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
For single-spec display, also call:
|
|
46
46
|
```bash
|
|
47
|
-
node bin/sf-tools.cjs state resolve
|
|
47
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state resolve
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
Parse the resolve response:
|
|
@@ -108,13 +108,13 @@ Fix: Rename the spec in specs/ to next available ID.
|
|
|
108
108
|
Compare the set of TODO files on disk to the IDs listed in `.specflow/todos/INDEX.md`:
|
|
109
109
|
|
|
110
110
|
```bash
|
|
111
|
-
node bin/sf-tools.cjs todo check-stale
|
|
111
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs todo check-stale
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
Parse the JSON response. If `stale: true`, add a warning to the Warnings section:
|
|
115
115
|
|
|
116
116
|
```
|
|
117
|
-
INDEX.md stale — run /sf:todos (or `node bin/sf-tools.cjs todo reindex`).
|
|
117
|
+
INDEX.md stale — run /sf:todos (or `node ~/.claude/specflow-cc/bin/sf-tools.cjs todo reindex`).
|
|
118
118
|
{If missing_from_index is non-empty:}
|
|
119
119
|
Missing from INDEX.md (TODO file exists on disk but not listed):
|
|
120
120
|
{comma-separated list}
|
|
@@ -232,7 +232,7 @@ Based on state, provide additional guidance:
|
|
|
232
232
|
- [ ] STATE.md loaded
|
|
233
233
|
- [ ] PROJECT.md info extracted
|
|
234
234
|
- [ ] Statistics calculated
|
|
235
|
-
- [ ] TODO index freshness checked via `node bin/sf-tools.cjs todo check-stale` (warning surfaced if stale)
|
|
235
|
+
- [ ] TODO index freshness checked via `node ~/.claude/specflow-cc/bin/sf-tools.cjs todo check-stale` (warning surfaced if stale)
|
|
236
236
|
- [ ] Current position displayed
|
|
237
237
|
- [ ] Queue shown
|
|
238
238
|
- [ ] Recommended next step clear
|
package/commands/sf/verify.md
CHANGED
|
@@ -36,7 +36,22 @@ Exit.
|
|
|
36
36
|
|
|
37
37
|
## Step 2: Resolve Active Specification
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
Parse `$ARGUMENTS`:
|
|
40
|
+
- Let `FIRST_TOKEN` = first whitespace-separated token of `$ARGUMENTS`.
|
|
41
|
+
- If `FIRST_TOKEN` matches `^SPEC-\d{3,}$`:
|
|
42
|
+
- Set `SPEC_ARG="$FIRST_TOKEN"`
|
|
43
|
+
- Set `VERIFY_SCOPE` = remainder of `$ARGUMENTS` after `FIRST_TOKEN` (trimmed)
|
|
44
|
+
- Else:
|
|
45
|
+
- Set `SPEC_ARG=""` (resolver uses Active Specifications table)
|
|
46
|
+
- Set `VERIFY_SCOPE="$ARGUMENTS"`
|
|
47
|
+
|
|
48
|
+
Call:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs state resolve $SPEC_ARG
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Use `VERIFY_SCOPE` in subsequent steps for any verify flags.
|
|
40
55
|
|
|
41
56
|
Parse the JSON response:
|
|
42
57
|
- `{"action":"use","id":"SPEC-XXX"}` → proceed with SPEC-XXX
|