specflow-cc 1.19.0 → 1.20.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/CHANGELOG.md +48 -0
- package/README.md +9 -1
- package/agents/spec-reviser.md +8 -1
- package/bin/lib/core.cjs +26 -0
- package/bin/lib/lock.cjs +213 -0
- package/bin/lib/migrate-state.cjs +242 -0
- package/bin/lib/resolve.cjs +171 -0
- package/bin/lib/state.cjs +350 -52
- package/bin/lib/todo.cjs +74 -2
- package/bin/sf-tools.cjs +58 -7
- package/commands/sf/audit.md +24 -12
- package/commands/sf/autopilot.md +39 -21
- package/commands/sf/discuss.md +4 -3
- package/commands/sf/done.md +23 -12
- package/commands/sf/fix.md +22 -10
- package/commands/sf/health.md +17 -4
- package/commands/sf/help.md +6 -0
- package/commands/sf/migrate-todos.md +7 -17
- package/commands/sf/pause.md +18 -11
- package/commands/sf/plan.md +15 -1
- package/commands/sf/priority.md +7 -1
- package/commands/sf/review.md +25 -10
- package/commands/sf/revise.md +28 -11
- package/commands/sf/run.md +22 -11
- package/commands/sf/show.md +17 -10
- package/commands/sf/split.md +24 -15
- package/commands/sf/status.md +44 -9
- package/commands/sf/todos.md +9 -21
- package/commands/sf/triage.md +11 -0
- package/commands/sf/verify.md +22 -11
- package/package.json +1 -1
- package/templates/state.md +6 -4
- package/templates/todo-index.md +4 -2
package/bin/lib/todo.cjs
CHANGED
|
@@ -295,8 +295,10 @@ function cmdTodoReindex(cwd, raw) {
|
|
|
295
295
|
const lines = [
|
|
296
296
|
'# To-Do Index',
|
|
297
297
|
'',
|
|
298
|
-
'>
|
|
299
|
-
'>
|
|
298
|
+
'> Cache of individual TODO files. Refreshed when `/sf:todos` runs OR when an',
|
|
299
|
+
'> INDEX-mutating command explicitly invokes the regen helper',
|
|
300
|
+
'> (`node bin/sf-tools.cjs todo reindex`). Do not edit manually — changes will',
|
|
301
|
+
'> be overwritten on the next regen.',
|
|
300
302
|
'',
|
|
301
303
|
'| # | ID | Title | Priority | Status | Created |',
|
|
302
304
|
'|---|-----|-------|----------|--------|---------|',
|
|
@@ -324,9 +326,79 @@ function cmdTodoReindex(cwd, raw) {
|
|
|
324
326
|
output({ reindexed: todos.length, path: indexPath }, raw, `Reindexed ${todos.length} TODOs → INDEX.md`);
|
|
325
327
|
}
|
|
326
328
|
|
|
329
|
+
/**
|
|
330
|
+
* Check whether INDEX.md is stale relative to TODO-*.md files.
|
|
331
|
+
*
|
|
332
|
+
* Stale = the set of TODO-XXX IDs on disk diverges from the set of TODO-XXX IDs
|
|
333
|
+
* listed in INDEX.md (file deleted but still in INDEX, or file present but missing).
|
|
334
|
+
*
|
|
335
|
+
* NOTE: Eliminated TODOs (`status: eliminated`) still appear in `/sf:todos --all`
|
|
336
|
+
* regenerated INDEX.md output, so they are NOT filtered here — both sides see them.
|
|
337
|
+
*
|
|
338
|
+
* Output JSON: { stale, missing_from_index, extra_in_index, index_exists }
|
|
339
|
+
* - missing_from_index: file exists on disk but not in INDEX.md
|
|
340
|
+
* - extra_in_index: ID listed in INDEX.md but no file on disk
|
|
341
|
+
*
|
|
342
|
+
* @param {string} cwd - Working directory
|
|
343
|
+
* @param {boolean} raw - Output mode
|
|
344
|
+
*/
|
|
345
|
+
function cmdTodoCheckStale(cwd, raw) {
|
|
346
|
+
const todosDir = path.join(cwd, '.specflow', 'todos');
|
|
347
|
+
const indexPath = path.join(todosDir, 'INDEX.md');
|
|
348
|
+
|
|
349
|
+
// Collect IDs from disk
|
|
350
|
+
const diskIds = new Set();
|
|
351
|
+
try {
|
|
352
|
+
for (const f of fs.readdirSync(todosDir)) {
|
|
353
|
+
const m = f.match(/^(TODO-\d+)\.md$/);
|
|
354
|
+
if (m) diskIds.add(m[1]);
|
|
355
|
+
}
|
|
356
|
+
} catch (e) {
|
|
357
|
+
// todos dir missing — treat as empty
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Collect IDs referenced in INDEX.md (parse only the table rows)
|
|
361
|
+
const indexIds = new Set();
|
|
362
|
+
const indexContent = safeReadFile(indexPath);
|
|
363
|
+
const indexExists = indexContent !== null;
|
|
364
|
+
|
|
365
|
+
if (indexContent) {
|
|
366
|
+
// Match TODO-XXX in pipe-table cells: "| N | TODO-001 | ..."
|
|
367
|
+
const regex = /\|\s*\d+\s*\|\s*(TODO-\d+)\s*\|/g;
|
|
368
|
+
let m;
|
|
369
|
+
while ((m = regex.exec(indexContent)) !== null) {
|
|
370
|
+
indexIds.add(m[1]);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const missingFromIndex = [...diskIds].filter(id => !indexIds.has(id)).sort();
|
|
375
|
+
const extraInIndex = [...indexIds].filter(id => !diskIds.has(id)).sort();
|
|
376
|
+
|
|
377
|
+
// If INDEX.md does not exist but there are TODO files, INDEX is stale.
|
|
378
|
+
// If INDEX.md does not exist and no TODO files, not stale (nothing to track).
|
|
379
|
+
const stale =
|
|
380
|
+
missingFromIndex.length > 0 ||
|
|
381
|
+
extraInIndex.length > 0 ||
|
|
382
|
+
(!indexExists && diskIds.size > 0);
|
|
383
|
+
|
|
384
|
+
output(
|
|
385
|
+
{
|
|
386
|
+
stale,
|
|
387
|
+
index_exists: indexExists,
|
|
388
|
+
todo_count: diskIds.size,
|
|
389
|
+
index_count: indexIds.size,
|
|
390
|
+
missing_from_index: missingFromIndex,
|
|
391
|
+
extra_in_index: extraInIndex,
|
|
392
|
+
},
|
|
393
|
+
raw,
|
|
394
|
+
stale ? 'STALE' : 'FRESH'
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
|
|
327
398
|
module.exports = {
|
|
328
399
|
cmdTodoLoad,
|
|
329
400
|
cmdTodoList,
|
|
330
401
|
cmdTodoNextId,
|
|
331
402
|
cmdTodoReindex,
|
|
403
|
+
cmdTodoCheckStale,
|
|
332
404
|
};
|
package/bin/sf-tools.cjs
CHANGED
|
@@ -13,9 +13,15 @@
|
|
|
13
13
|
* todo list [--all] List all TODOs sorted by priority
|
|
14
14
|
* todo next-id Next available TODO-XXX number
|
|
15
15
|
* todo reindex Regenerate INDEX.md from TODO files
|
|
16
|
+
* todo check-stale Report drift between TODO-*.md and INDEX.md
|
|
16
17
|
* queue next First actionable spec from queue
|
|
17
|
-
* state get Current active spec, status, next step
|
|
18
|
-
* state set-active <id> <status> [next] Update active spec in STATE.md
|
|
18
|
+
* state get Current active spec, status, next step (legacy shim)
|
|
19
|
+
* state set-active <id> <status> [next] Update active spec in STATE.md (legacy shim)
|
|
20
|
+
* state list-active List all active specs from Active Specifications table
|
|
21
|
+
* state add-active <id> <status> <next> Append/update one row in Active Specifications table
|
|
22
|
+
* state remove-active <id> Remove one row from Active Specifications table
|
|
23
|
+
* state resolve [id] Resolve active spec; emit JSON contract
|
|
24
|
+
* state migrate One-shot idempotent migration to new schema
|
|
19
25
|
* resolve-model <agent-type> Model for agent by current profile
|
|
20
26
|
* verify-structure Check .specflow/ integrity
|
|
21
27
|
* generate-slug <text> Text to URL-safe slug
|
|
@@ -24,9 +30,18 @@
|
|
|
24
30
|
'use strict';
|
|
25
31
|
|
|
26
32
|
const { output, error, generateSlug } = require('./lib/core.cjs');
|
|
27
|
-
const {
|
|
33
|
+
const {
|
|
34
|
+
cmdStateGet,
|
|
35
|
+
cmdStateSetActive,
|
|
36
|
+
cmdStateListActive,
|
|
37
|
+
cmdStateAddActive,
|
|
38
|
+
cmdStateRemoveActive,
|
|
39
|
+
cmdStateResolve,
|
|
40
|
+
cmdStateMigrate,
|
|
41
|
+
cmdQueueNext,
|
|
42
|
+
} = require('./lib/state.cjs');
|
|
28
43
|
const { cmdSpecLoad, cmdSpecList, cmdSpecNextId } = require('./lib/spec.cjs');
|
|
29
|
-
const { cmdTodoLoad, cmdTodoList, cmdTodoNextId, cmdTodoReindex } = require('./lib/todo.cjs');
|
|
44
|
+
const { cmdTodoLoad, cmdTodoList, cmdTodoNextId, cmdTodoReindex, cmdTodoCheckStale } = require('./lib/todo.cjs');
|
|
30
45
|
const { cmdResolveModel } = require('./lib/config.cjs');
|
|
31
46
|
const { cmdVerifyStructure } = require('./lib/verify.cjs');
|
|
32
47
|
|
|
@@ -61,14 +76,44 @@ const COMMANDS = {
|
|
|
61
76
|
'todo list': () => cmdTodoList(cwd, raw, { showAll: flags.all ?? false }),
|
|
62
77
|
'todo next-id': () => cmdTodoNextId(cwd, raw),
|
|
63
78
|
'todo reindex': () => cmdTodoReindex(cwd, raw),
|
|
79
|
+
'todo check-stale': () => cmdTodoCheckStale(cwd, raw),
|
|
64
80
|
'queue next': () => cmdQueueNext(cwd, raw),
|
|
81
|
+
|
|
82
|
+
// Legacy shims (backwards compatible)
|
|
65
83
|
'state get': () => cmdStateGet(cwd, raw),
|
|
66
84
|
'state set-active': () => {
|
|
67
85
|
if (!filteredArgs[2] || !filteredArgs[3]) {
|
|
68
86
|
error('Missing arguments. Usage: state set-active <id> <status> [next_step]');
|
|
69
87
|
}
|
|
70
|
-
cmdStateSetActive(cwd, filteredArgs[2], filteredArgs[3], filteredArgs[4], raw)
|
|
88
|
+
Promise.resolve(cmdStateSetActive(cwd, filteredArgs[2], filteredArgs[3], filteredArgs[4], raw))
|
|
89
|
+
.catch(e => error(e.message));
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// New multi-spec state commands
|
|
93
|
+
'state list-active': () => cmdStateListActive(cwd, raw),
|
|
94
|
+
'state add-active': () => {
|
|
95
|
+
if (!filteredArgs[2] || !filteredArgs[3]) {
|
|
96
|
+
error('Missing arguments. Usage: state add-active <id> <status> <next_step>');
|
|
97
|
+
}
|
|
98
|
+
Promise.resolve(cmdStateAddActive(cwd, filteredArgs[2], filteredArgs[3], filteredArgs[4] || '', raw))
|
|
99
|
+
.catch(e => error(e.message));
|
|
71
100
|
},
|
|
101
|
+
'state remove-active': () => {
|
|
102
|
+
if (!filteredArgs[2]) {
|
|
103
|
+
error('Missing arguments. Usage: state remove-active <id>');
|
|
104
|
+
}
|
|
105
|
+
Promise.resolve(cmdStateRemoveActive(cwd, filteredArgs[2], raw))
|
|
106
|
+
.catch(e => error(e.message));
|
|
107
|
+
},
|
|
108
|
+
'state resolve': () => {
|
|
109
|
+
// Optional specId argument (filteredArgs[2])
|
|
110
|
+
cmdStateResolve(cwd, filteredArgs[2] || undefined, raw);
|
|
111
|
+
},
|
|
112
|
+
'state migrate': () => {
|
|
113
|
+
Promise.resolve(cmdStateMigrate(cwd, raw))
|
|
114
|
+
.catch(e => error(e.message));
|
|
115
|
+
},
|
|
116
|
+
|
|
72
117
|
'resolve-model': () => {
|
|
73
118
|
if (!filteredArgs[1]) error('Missing agent type. Usage: resolve-model <agent-type>');
|
|
74
119
|
cmdResolveModel(cwd, filteredArgs[1], raw);
|
|
@@ -93,9 +138,15 @@ Commands:
|
|
|
93
138
|
todo list [--all] List TODOs sorted by priority (--all includes eliminated)
|
|
94
139
|
todo next-id Next available TODO-XXX number
|
|
95
140
|
todo reindex Regenerate INDEX.md from TODO files
|
|
141
|
+
todo check-stale Report drift between TODO-*.md and INDEX.md
|
|
96
142
|
queue next First actionable spec from queue table
|
|
97
|
-
state get Current active spec, status, next step
|
|
98
|
-
state set-active <id> <status> [next] Update active spec, status, next step
|
|
143
|
+
state get Current active spec, status, next step (legacy shim)
|
|
144
|
+
state set-active <id> <status> [next] Update active spec, status, next step (legacy shim)
|
|
145
|
+
state list-active List all rows in Active Specifications table
|
|
146
|
+
state add-active <id> <status> <next> Append/update one row (under advisory lock)
|
|
147
|
+
state remove-active <id> Remove one row (under advisory lock)
|
|
148
|
+
state resolve [SPEC-ID] Resolve active spec; emit JSON contract
|
|
149
|
+
state migrate One-shot idempotent migration to new schema
|
|
99
150
|
resolve-model <agent-type> Resolve model for agent by current profile
|
|
100
151
|
verify-structure Check .specflow/ directory integrity
|
|
101
152
|
generate-slug <text> Convert text to URL-safe slug
|
package/commands/sf/audit.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: sf:audit
|
|
3
3
|
description: Audit the active specification in a fresh context
|
|
4
4
|
argument-hint: "[SPEC-XXX] [--import \"feedback\"]"
|
|
5
|
+
# SPEC-011: Accepts optional SPEC-XXX as first positional arg; resolves via state resolve
|
|
5
6
|
allowed-tools:
|
|
6
7
|
- Read
|
|
7
8
|
- Write
|
|
@@ -40,17 +41,27 @@ Run `/sf:init` first.
|
|
|
40
41
|
```
|
|
41
42
|
Exit.
|
|
42
43
|
|
|
43
|
-
## Step 2:
|
|
44
|
+
## Step 2: Resolve Active Specification
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
Call `node bin/sf-tools.cjs state resolve $ARGUMENTS` (pass the optional SPEC-XXX arg if provided).
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
Parse the JSON response:
|
|
49
|
+
- `{"action":"use","id":"SPEC-XXX"}` → proceed with SPEC-XXX
|
|
50
|
+
- `{"action":"error","code":"NO_ACTIVE_SPEC"}` → display error and exit:
|
|
51
|
+
```
|
|
52
|
+
No active specification to audit.
|
|
50
53
|
|
|
51
|
-
Run `/sf:new "task description"` to create one.
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
+
Run `/sf:new "task description"` to create one.
|
|
55
|
+
```
|
|
56
|
+
- `{"action":"error","code":"SPEC_NOT_ACTIVE","id":"SPEC-XXX"}` → display error and exit:
|
|
57
|
+
```
|
|
58
|
+
SPEC-XXX is not in the Active Specifications table.
|
|
59
|
+
```
|
|
60
|
+
- `{"action":"ask","options":[...]}` → use AskUserQuestion to show picker:
|
|
61
|
+
```
|
|
62
|
+
Multiple active specifications. Which one to audit?
|
|
63
|
+
Options: {id — title (status)} for each entry
|
|
64
|
+
```
|
|
54
65
|
|
|
55
66
|
## Step 3: Load Specification
|
|
56
67
|
|
|
@@ -143,10 +154,11 @@ In spec frontmatter, set: `status: revision_requested`
|
|
|
143
154
|
|
|
144
155
|
### 4.6 Update STATE.md
|
|
145
156
|
|
|
146
|
-
Update STATE.md:
|
|
147
|
-
|
|
148
|
-
-
|
|
149
|
-
|
|
157
|
+
Update STATE.md via CLI:
|
|
158
|
+
```bash
|
|
159
|
+
node bin/sf-tools.cjs state add-active SPEC-XXX external_review /sf:revise
|
|
160
|
+
```
|
|
161
|
+
Add decision: "Imported external feedback for SPEC-XXX"
|
|
150
162
|
|
|
151
163
|
### 4.7 Display Import Result
|
|
152
164
|
|
package/commands/sf/autopilot.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: sf:autopilot
|
|
3
3
|
description: Run full spec lifecycle autonomously (audit -> run -> review -> done)
|
|
4
4
|
argument-hint: "[SPEC-XXX] [--all]"
|
|
5
|
+
# SPEC-011: Accepts optional SPEC-XXX; resolves via state resolve; FAILS when N>1 and no ID (no picker)
|
|
5
6
|
allowed-tools:
|
|
6
7
|
- Read
|
|
7
8
|
- Write
|
|
@@ -43,25 +44,42 @@ Parse the command argument to determine execution mode:
|
|
|
43
44
|
|
|
44
45
|
| Argument | Mode | Behavior |
|
|
45
46
|
|----------|------|----------|
|
|
46
|
-
| (none) | single |
|
|
47
|
-
| `SPEC-XXX` | single |
|
|
48
|
-
| `--all` | batch | Process all actionable specs in Queue order |
|
|
47
|
+
| (none) | single | Resolve active spec; fail fast if N>1 (no picker) |
|
|
48
|
+
| `SPEC-XXX` | single | Process this explicit spec ID |
|
|
49
|
+
| `--all` | batch | Process all actionable specs in Queue order; still requires explicit SPEC-ID if N>1 |
|
|
49
50
|
|
|
50
|
-
**
|
|
51
|
-
- If SPEC-XXX argument provided: update STATE.md to set it as active spec
|
|
52
|
-
- If no argument and no active spec exists: display error and exit
|
|
51
|
+
**CRITICAL — N>1 guard (autopilot must be unambiguous):**
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
- Identify all actionable specs from Queue (any spec with status: draft, auditing, revision_requested, audited, running, review)
|
|
53
|
+
Call `node bin/sf-tools.cjs state resolve $SPEC_ID_ARG` (pass SPEC-ID arg if provided; omit if not).
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
Parse the JSON response:
|
|
56
|
+
- `{"action":"use","id":"SPEC-XXX"}` → proceed with SPEC-XXX
|
|
57
|
+
- `{"action":"error","code":"NO_ACTIVE_SPEC"}` → display error and exit:
|
|
58
|
+
```
|
|
59
|
+
No active specification to process.
|
|
60
60
|
|
|
61
|
-
Provide a spec ID: `/sf:autopilot SPEC-XXX`
|
|
62
|
-
Or run on all specs: `/sf:autopilot --all`
|
|
63
|
-
```
|
|
64
|
-
|
|
61
|
+
Provide a spec ID: `/sf:autopilot SPEC-XXX`
|
|
62
|
+
Or run on all specs: `/sf:autopilot --all`
|
|
63
|
+
```
|
|
64
|
+
- `{"action":"error","code":"SPEC_NOT_ACTIVE","id":"SPEC-XXX"}` → display error and exit:
|
|
65
|
+
```
|
|
66
|
+
SPEC-XXX is not in the Active Specifications table.
|
|
67
|
+
```
|
|
68
|
+
- `{"action":"ask","options":[...]}` → **FAIL FAST** (do NOT show picker):
|
|
69
|
+
```
|
|
70
|
+
Autopilot requires explicit SPEC-ID when >1 active specs.
|
|
71
|
+
|
|
72
|
+
Active specs: {list SPEC-IDs from options}
|
|
73
|
+
|
|
74
|
+
Provide a spec ID: `/sf:autopilot SPEC-XXX`
|
|
75
|
+
```
|
|
76
|
+
Exit. (This applies to both plain `/sf:autopilot` AND `/sf:autopilot --all` without a SPEC-ID. `--all` controls within-spec behavior, not multi-spec iteration.)
|
|
77
|
+
|
|
78
|
+
**If single mode with explicit SPEC-ID:**
|
|
79
|
+
- SPEC-ID was resolved via state resolve above (action:use)
|
|
80
|
+
|
|
81
|
+
**If batch mode (--all):**
|
|
82
|
+
- Identify all actionable specs from Queue (any spec with status: draft, auditing, revision_requested, audited, running, review)
|
|
65
83
|
|
|
66
84
|
## Step 3: Set Configuration Constants
|
|
67
85
|
|
|
@@ -107,8 +125,7 @@ current_spec = null
|
|
|
107
125
|
|
|
108
126
|
### 5.1 Load Current Spec State
|
|
109
127
|
|
|
110
|
-
Read `.specflow/
|
|
111
|
-
Read `.specflow/specs/SPEC-XXX.md` to get frontmatter status.
|
|
128
|
+
Read `.specflow/specs/SPEC-XXX.md` to get frontmatter status (spec ID resolved in Step 2).
|
|
112
129
|
|
|
113
130
|
### 5.2 Determine Current Phase
|
|
114
131
|
|
|
@@ -255,10 +272,11 @@ mv .specflow/specs/SPEC-XXX.md .specflow/archive/
|
|
|
255
272
|
```
|
|
256
273
|
|
|
257
274
|
5. **Update STATE.md:**
|
|
258
|
-
- Active
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
275
|
+
Remove SPEC-XXX from Active Specifications table:
|
|
276
|
+
```bash
|
|
277
|
+
node bin/sf-tools.cjs state remove-active SPEC-XXX
|
|
278
|
+
```
|
|
279
|
+
Remove SPEC-XXX row from Queue table (using Read+Write).
|
|
262
280
|
|
|
263
281
|
6. **Check STATE.md size and rotate** (same logic as 5.5 step 2)
|
|
264
282
|
|
package/commands/sf/discuss.md
CHANGED
|
@@ -62,9 +62,10 @@ Determine discussion mode from arguments:
|
|
|
62
62
|
- Mode: `requirements-gathering`
|
|
63
63
|
|
|
64
64
|
**Case E: No arguments**
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
65
|
+
- Call `node bin/sf-tools.cjs state resolve` to resolve active spec
|
|
66
|
+
- `{"action":"use","id":"SPEC-XXX"}` → discuss that spec
|
|
67
|
+
- `{"action":"error","code":"NO_ACTIVE_SPEC"}` → ask what to discuss
|
|
68
|
+
- `{"action":"ask","options":[...]}` → use AskUserQuestion to pick which spec to discuss
|
|
68
69
|
|
|
69
70
|
## 3. Load Context
|
|
70
71
|
|
package/commands/sf/done.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sf:done
|
|
3
3
|
description: Finalize specification, archive, and update state
|
|
4
|
+
# SPEC-011: Accepts optional SPEC-XXX as first positional arg; resolves via state resolve
|
|
4
5
|
allowed-tools:
|
|
5
6
|
- Read
|
|
6
7
|
- Write
|
|
@@ -36,17 +37,27 @@ Run `/sf:init` first.
|
|
|
36
37
|
```
|
|
37
38
|
Exit.
|
|
38
39
|
|
|
39
|
-
## Step 2:
|
|
40
|
+
## Step 2: Resolve Active Specification
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
Call `node bin/sf-tools.cjs state resolve $ARGUMENTS` (pass the optional SPEC-XXX arg if provided).
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
Parse the JSON response:
|
|
45
|
+
- `{"action":"use","id":"SPEC-XXX"}` → proceed with SPEC-XXX
|
|
46
|
+
- `{"action":"error","code":"NO_ACTIVE_SPEC"}` → display error and exit:
|
|
47
|
+
```
|
|
48
|
+
No active specification to finalize.
|
|
46
49
|
|
|
47
|
-
Run `/sf:new "task description"` to create one.
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
+
Run `/sf:new "task description"` to create one.
|
|
51
|
+
```
|
|
52
|
+
- `{"action":"error","code":"SPEC_NOT_ACTIVE","id":"SPEC-XXX"}` → display error and exit:
|
|
53
|
+
```
|
|
54
|
+
SPEC-XXX is not in the Active Specifications table.
|
|
55
|
+
```
|
|
56
|
+
- `{"action":"ask","options":[...]}` → use AskUserQuestion to show picker:
|
|
57
|
+
```
|
|
58
|
+
Multiple active specifications. Which one to finalize?
|
|
59
|
+
Options: {id — title (status)} for each entry
|
|
60
|
+
```
|
|
50
61
|
|
|
51
62
|
## Step 3: Load Specification
|
|
52
63
|
|
|
@@ -309,11 +320,11 @@ mv .specflow/specs/SPEC-XXX.md .specflow/archive/
|
|
|
309
320
|
|
|
310
321
|
## Step 9: Update STATE.md
|
|
311
322
|
|
|
312
|
-
###
|
|
323
|
+
### Remove from Active Specifications Table
|
|
313
324
|
|
|
314
|
-
|
|
315
|
-
-
|
|
316
|
-
|
|
325
|
+
```bash
|
|
326
|
+
node bin/sf-tools.cjs state remove-active SPEC-XXX
|
|
327
|
+
```
|
|
317
328
|
|
|
318
329
|
### Remove from Queue
|
|
319
330
|
|
package/commands/sf/fix.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sf:fix
|
|
3
3
|
description: Fix implementation based on review feedback
|
|
4
|
+
# SPEC-011: Accepts optional SPEC-XXX as first positional arg; resolves via state resolve
|
|
4
5
|
allowed-tools:
|
|
5
6
|
- Read
|
|
6
7
|
- Write
|
|
@@ -38,17 +39,27 @@ Run `/sf:init` first.
|
|
|
38
39
|
```
|
|
39
40
|
Exit.
|
|
40
41
|
|
|
41
|
-
## Step 2:
|
|
42
|
+
## Step 2: Resolve Active Specification
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
Call `node bin/sf-tools.cjs state resolve $ARGUMENTS` (pass the optional SPEC-XXX arg if provided).
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
Parse the JSON response:
|
|
47
|
+
- `{"action":"use","id":"SPEC-XXX"}` → proceed with SPEC-XXX
|
|
48
|
+
- `{"action":"error","code":"NO_ACTIVE_SPEC"}` → display error and exit:
|
|
49
|
+
```
|
|
50
|
+
No active specification to fix.
|
|
48
51
|
|
|
49
|
-
Run `/sf:new "task description"` to create one.
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
+
Run `/sf:new "task description"` to create one.
|
|
53
|
+
```
|
|
54
|
+
- `{"action":"error","code":"SPEC_NOT_ACTIVE","id":"SPEC-XXX"}` → display error and exit:
|
|
55
|
+
```
|
|
56
|
+
SPEC-XXX is not in the Active Specifications table.
|
|
57
|
+
```
|
|
58
|
+
- `{"action":"ask","options":[...]}` → use AskUserQuestion to show picker:
|
|
59
|
+
```
|
|
60
|
+
Multiple active specifications. Which one to fix?
|
|
61
|
+
Options: {id — title (status)} for each entry
|
|
62
|
+
```
|
|
52
63
|
|
|
53
64
|
## Step 3: Load Specification
|
|
54
65
|
|
|
@@ -164,8 +175,9 @@ Append to Review History:
|
|
|
164
175
|
|
|
165
176
|
## Step 8: Update STATE.md
|
|
166
177
|
|
|
167
|
-
|
|
168
|
-
-
|
|
178
|
+
```bash
|
|
179
|
+
node bin/sf-tools.cjs state add-active SPEC-XXX review /sf:review
|
|
180
|
+
```
|
|
169
181
|
|
|
170
182
|
## Step 9: Display Result
|
|
171
183
|
|
package/commands/sf/health.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: sf:health
|
|
3
3
|
description: Diagnose .specflow/ directory health and optionally repair issues
|
|
4
4
|
argument-hint: [--repair]
|
|
5
|
+
# SPEC-011: Invokes migrate-state on entry; re-stamps STATE.md header from template after migration (--repair path)
|
|
5
6
|
allowed-tools:
|
|
6
7
|
- Read
|
|
7
8
|
- Bash
|
|
@@ -32,7 +33,19 @@ SpecFlow not initialized. Run `/sf:init` first.
|
|
|
32
33
|
```
|
|
33
34
|
Exit.
|
|
34
35
|
|
|
35
|
-
## Step 2:
|
|
36
|
+
## Step 2: Invoke STATE.md Migration (entry point)
|
|
37
|
+
|
|
38
|
+
Run migration on entry — idempotent, no-op when already migrated:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
node bin/sf-tools.cjs state migrate
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Parse the response:
|
|
45
|
+
- `{"migrated":true,...}` → Migration completed. After migration, re-stamp the `## Active Specifications` header block in STATE.md from `templates/state.md` to reconcile any format drift (non-destructive: Queue, Decisions, Notes sections are preserved; only the Active Specifications block is normalized). This is the `--repair` path.
|
|
46
|
+
- `{"migrated":false,...}` → Already migrated, no action needed.
|
|
47
|
+
|
|
48
|
+
## Step 2.5: Parse Arguments
|
|
36
49
|
|
|
37
50
|
Check if `--repair` flag is present.
|
|
38
51
|
|
|
@@ -71,9 +84,9 @@ For each check:
|
|
|
71
84
|
Read STATE.md and validate:
|
|
72
85
|
|
|
73
86
|
**E003: Active spec references non-existent file**
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
- If missing: error (repairable —
|
|
87
|
+
- List all active specs via `node bin/sf-tools.cjs state list-active`
|
|
88
|
+
- For each SPEC-ID, check `.specflow/specs/{ID}.md` exists
|
|
89
|
+
- If missing: error (repairable — remove that row via `state remove-active`)
|
|
77
90
|
|
|
78
91
|
**W005: Queue references non-existent spec file**
|
|
79
92
|
- Parse queue table rows
|
package/commands/sf/help.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sf:help
|
|
3
3
|
description: Show SpecFlow help and command reference
|
|
4
|
+
# SPEC-011: Uses state list-active to show active specs in overview; no state resolve needed (no single-spec operations)
|
|
4
5
|
allowed-tools:
|
|
5
6
|
- Read
|
|
6
7
|
- Glob
|
|
@@ -203,6 +204,11 @@ Exit.
|
|
|
203
204
|
|
|
204
205
|
## Step 2b: Overview Help
|
|
205
206
|
|
|
207
|
+
Show active specs count (uses list-active for multi-spec awareness):
|
|
208
|
+
```bash
|
|
209
|
+
node bin/sf-tools.cjs state list-active --raw
|
|
210
|
+
```
|
|
211
|
+
|
|
206
212
|
Display full command reference:
|
|
207
213
|
|
|
208
214
|
```
|
|
@@ -13,7 +13,7 @@ Migrate an existing monolithic `.specflow/todos/TODO.md` to the new per-file for
|
|
|
13
13
|
This is a one-time migration command. After migration:
|
|
14
14
|
- `TODO.md` is renamed to `TODO.md.bak` (NOT deleted — safety net)
|
|
15
15
|
- Each TODO becomes its own `TODO-XXX.md` file
|
|
16
|
-
- `INDEX.md` is
|
|
16
|
+
- `INDEX.md` is regenerated from the new files via the shared `todo reindex` helper
|
|
17
17
|
- All other commands will use the new per-file format automatically
|
|
18
18
|
|
|
19
19
|
Use `--dry-run` to preview the migration without writing any files.
|
|
@@ -173,24 +173,14 @@ created: {YYYY-MM-DD}
|
|
|
173
173
|
|
|
174
174
|
## Step 7: Generate INDEX.md
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
Invoke the shared regen helper to build `.specflow/todos/INDEX.md` from the migrated files:
|
|
177
177
|
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
> Auto-generated from individual TODO files. Do not edit manually.
|
|
182
|
-
> Regenerate with `/sf:todos`.
|
|
183
|
-
|
|
184
|
-
| # | ID | Title | Priority | Status | Created |
|
|
185
|
-
|---|-----|-------|----------|--------|---------|
|
|
186
|
-
{one row per TODO, sorted by priority then created date}
|
|
187
|
-
|
|
188
|
-
**Total:** {N} items ({high} high, {medium} medium, {low} low, {unset} unset)
|
|
189
|
-
|
|
190
|
-
---
|
|
191
|
-
*Last regenerated: {YYYY-MM-DD HH:MM}*
|
|
178
|
+
```bash
|
|
179
|
+
node ~/.claude/specflow-cc/bin/sf-tools.cjs todo reindex
|
|
192
180
|
```
|
|
193
181
|
|
|
182
|
+
Do NOT write INDEX.md inline — the helper is the single source of truth for its layout (see `templates/todo-index.md`).
|
|
183
|
+
|
|
194
184
|
## Step 8: Rename Legacy TODO.md
|
|
195
185
|
|
|
196
186
|
```bash
|
|
@@ -245,7 +235,7 @@ Migrated {N} TODOs from TODO.md to per-file format.
|
|
|
245
235
|
- [ ] Individual TODO-XXX.md files created for each block
|
|
246
236
|
- [ ] Each file has valid YAML frontmatter (id, title, priority, status, created)
|
|
247
237
|
- [ ] Title derived from description (first sentence, ~80 chars)
|
|
248
|
-
- [ ] INDEX.md
|
|
238
|
+
- [ ] INDEX.md regenerated via `node ~/.claude/specflow-cc/bin/sf-tools.cjs todo reindex`
|
|
249
239
|
- [ ] TODO.md renamed to TODO.md.bak (NOT deleted)
|
|
250
240
|
- [ ] Clear migration summary shown
|
|
251
241
|
- [ ] Cleanup instructions provided
|
package/commands/sf/pause.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sf:pause
|
|
3
3
|
description: Save current work context for later resumption
|
|
4
|
+
# SPEC-011: Accepts optional SPEC-XXX as first positional arg; resolves via state resolve
|
|
4
5
|
allowed-tools:
|
|
5
6
|
- Read
|
|
6
7
|
- Write
|
|
@@ -33,20 +34,26 @@ Run `/sf:init` to start.
|
|
|
33
34
|
```
|
|
34
35
|
Exit.
|
|
35
36
|
|
|
36
|
-
## Step 2:
|
|
37
|
+
## Step 2: Resolve Active Specification
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
- Active Specification
|
|
40
|
-
- Status
|
|
41
|
-
- Next Step
|
|
39
|
+
Call `node bin/sf-tools.cjs state resolve $ARGUMENTS` (pass the optional SPEC-XXX arg if provided).
|
|
42
40
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
Parse the JSON response:
|
|
42
|
+
- `{"action":"use","id":"SPEC-XXX"}` → proceed with SPEC-XXX
|
|
43
|
+
- `{"action":"error","code":"NO_ACTIVE_SPEC"}` → display info and allow general notes:
|
|
44
|
+
```
|
|
45
|
+
No active work to pause.
|
|
46
46
|
|
|
47
|
-
Current state: idle
|
|
48
|
-
```
|
|
49
|
-
|
|
47
|
+
Current state: idle
|
|
48
|
+
```
|
|
49
|
+
Still allow pausing to capture general notes.
|
|
50
|
+
- `{"action":"ask","options":[...]}` → use AskUserQuestion to show picker:
|
|
51
|
+
```
|
|
52
|
+
Multiple active specifications. Which session to pause?
|
|
53
|
+
Options: {id — title (status)} for each entry
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Also call `node bin/sf-tools.cjs state list-active` to capture all active specs in the pause file.
|
|
50
57
|
|
|
51
58
|
## Step 3: Load Active Specification Details
|
|
52
59
|
|