specflow-cc 1.17.1 → 1.18.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 CHANGED
@@ -5,6 +5,26 @@ 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.18.0] - 2026-04-08
9
+
10
+ ### Added
11
+
12
+ - **Per-file TODO storage** — TODOs migrated from monolithic `TODO.md` to individual `TODO-XXX.md` files with YAML frontmatter, mirroring the established `SPEC-XXX.md` pattern
13
+ - Each TODO is now a standalone file in `.specflow/todos/` with structured metadata (id, title, priority, complexity, status, effort, depends_on, created)
14
+ - Auto-generated `INDEX.md` serves as a human-readable cache, regenerated by `/sf:todos`
15
+ - New `bin/lib/todo.cjs` utility module with `cmdTodoLoad`, `cmdTodoList`, `cmdTodoNextId` functions
16
+ - New CLI commands: `todo load <id>`, `todo list [--all]`, `todo next-id`
17
+ - New `/sf:migrate-todos` command for one-time migration from legacy format (with `--dry-run` support)
18
+ - Backward compatibility: projects with legacy `TODO.md` continue to work transparently
19
+ - Eliminated TODOs use `status: eliminated` soft-delete instead of file deletion, preserving history
20
+ - **Updated commands** — all 10 TODO-touching commands and 3 agents updated to use per-file format:
21
+ `sf:todo`, `sf:todos`, `sf:plan`, `sf:done`, `sf:priority`, `sf:triage`, `sf:revise`, `sf:health`, `sf:status`, `sf:metrics`, `sf:init`, `spec-reviser`, `spec-creator`
22
+ - **New health checks** — W009 (TODO file without valid frontmatter), W010 (legacy TODO.md alongside per-file TODOs)
23
+
24
+ ### Fixed
25
+
26
+ - **W001 health check** — now checks for `todos/` directory instead of `TODO.md` file
27
+
8
28
  ## [1.17.1] - 2026-04-07
9
29
 
10
30
  ### Fixed
@@ -156,6 +156,8 @@ If no specs exist in either directory, start with SPEC-001.
156
156
  Write to `.specflow/specs/SPEC-XXX.md` using the template structure:
157
157
 
158
158
  1. **Frontmatter:** id, type, status (draft), priority, complexity, created, source (if `<todo_context>` provided — set to the TODO ID, e.g., `source: TODO-006`), and optionally `delta: true` (only for brownfield tasks detected in Step 2.7)
159
+
160
+ **Note on `source:` field:** The `source: TODO-XXX` value refers to the identifier of a per-file TODO stored at `.specflow/todos/TODO-XXX.md`. It does NOT refer to an entry in a monolithic `TODO.md`. When `/sf:plan` converts a TODO and then `/sf:done` completes the spec, the cleanup step deletes `.specflow/todos/TODO-XXX.md` based on this field.
159
161
  2. **Title:** Clear, action-oriented
160
162
  3. **Context:** Why this is needed
161
163
  - **If `<prior_discussion>` provided:** Add "Prior Discussion" subsection linking to PRE-XXX or DISC-XXX with key decisions
@@ -143,16 +143,33 @@ After recording the Response, check if any items were marked as deferred (⏸).
143
143
 
144
144
  For each deferred item:
145
145
 
146
- 1. Read `.specflow/todos/TODO.md` to find the highest existing TODO ID
147
- 2. Generate next TODO ID (zero-padded to 3 digits, starting from 001)
148
- 3. Create a new TODO entry with:
149
- - **Description:** `{item description} (deferred from {SPEC-XXX} audit v{N})`
150
- - **Priority:**
151
- - **Notes:** `Origin: {SPEC-XXX} Response v{N}. {reason for deferral}`
152
-
153
- 4. Use the **Edit** tool (NOT Write) to insert the new entry into `.specflow/todos/TODO.md` after the first `---` following `# To-Do List` — never rewrite the entire file, as this would destroy existing todos
154
-
155
- 5. Append a "TODOs Created" subsection to the Response in Audit History:
146
+ 1. Generate next TODO ID:
147
+ ```bash
148
+ node bin/sf-tools.cjs todo next-id --raw
149
+ ```
150
+ 2. Create `.specflow/todos/TODO-{XXX}.md` using the Write tool:
151
+ ```markdown
152
+ ---
153
+ id: TODO-{XXX}
154
+ title: "{item description} (deferred from {SPEC-XXX} audit v{N})"
155
+ priority:
156
+ complexity: —
157
+ status: open
158
+ effort: —
159
+ depends_on: —
160
+ created: {YYYY-MM-DD}
161
+ ---
162
+
163
+ ## Description
164
+
165
+ {item description} (deferred from {SPEC-XXX} audit v{N})
166
+
167
+ ## Notes
168
+
169
+ Origin: {SPEC-XXX} Response v{N}. {reason for deferral}
170
+ ```
171
+
172
+ 3. Append a "TODOs Created" subsection to the Response in Audit History:
156
173
 
157
174
  ```markdown
158
175
  **TODOs Created:**
@@ -209,7 +226,7 @@ Output directly as formatted text (not wrapped in a code block):
209
226
 
210
227
  - .specflow/specs/SPEC-XXX.md
211
228
  {If TODOs created:}
212
- - .specflow/todos/TODO.md
229
+ - .specflow/todos/TODO-{XXX}.md
213
230
 
214
231
  ### Next Step
215
232
 
@@ -226,7 +243,7 @@ Tip: `/clear` recommended — auditor needs fresh context
226
243
  - [ ] User's revision scope understood
227
244
  - [ ] Changes applied precisely
228
245
  - [ ] Revision Response recorded in Audit History
229
- - [ ] Deferred items (if any) created as TODOs in `.specflow/todos/TODO.md`
246
+ - [ ] Deferred items (if any) created as individual `.specflow/todos/TODO-XXX.md` files
230
247
  - [ ] TODOs Created subsection appended to Response (if deferred items exist)
231
248
  - [ ] Frontmatter status updated
232
249
  - [ ] STATE.md updated
@@ -0,0 +1,238 @@
1
+ /**
2
+ * bin/lib/todo.cjs — TODO operations
3
+ *
4
+ * Exports: cmdTodoLoad(), cmdTodoList(), cmdTodoNextId()
5
+ *
6
+ * Mirrors the pattern of bin/lib/spec.cjs.
7
+ * Supports both per-file format (TODO-XXX.md) and legacy monolithic TODO.md.
8
+ * Format detection is based on presence of TODO-*.md files — INDEX.md is NOT
9
+ * the detection signal (it may not exist until sf:todos is first run).
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const { output, error, safeReadFile, parseFrontmatter } = require('./core.cjs');
17
+
18
+ /**
19
+ * Priority sort order (lower number = higher priority in sort).
20
+ */
21
+ const PRIORITY_ORDER = { high: 0, medium: 1, low: 2 };
22
+
23
+ /**
24
+ * Get sort key for a priority value.
25
+ * @param {string} priority
26
+ * @returns {number}
27
+ */
28
+ function priorityKey(priority) {
29
+ if (priority && PRIORITY_ORDER[priority] !== undefined) {
30
+ return PRIORITY_ORDER[priority];
31
+ }
32
+ return 3; // unset / —
33
+ }
34
+
35
+ /**
36
+ * Load and parse a TODO file.
37
+ * @param {string} cwd - Working directory
38
+ * @param {string} id - TODO ID (e.g., "TODO-007")
39
+ * @param {boolean} raw - Output raw string
40
+ */
41
+ function cmdTodoLoad(cwd, id, raw) {
42
+ if (!id) {
43
+ error('Missing TODO ID. Usage: todo load <id>');
44
+ }
45
+
46
+ const todoPath = path.join(cwd, '.specflow', 'todos', id + '.md');
47
+ const content = safeReadFile(todoPath);
48
+
49
+ if (!content) {
50
+ error('TODO not found: ' + todoPath);
51
+ }
52
+
53
+ const parsed = parseFrontmatter(content);
54
+
55
+ output({
56
+ frontmatter: parsed.frontmatter,
57
+ body: parsed.body,
58
+ }, raw, parsed.body);
59
+ }
60
+
61
+ /**
62
+ * List all TODOs.
63
+ *
64
+ * Format detection:
65
+ * 1. If TODO-*.md files exist in .specflow/todos/ — use per-file format
66
+ * 2. If no per-file TODOs but TODO.md exists — use legacy format
67
+ * 3. If neither — return empty list
68
+ *
69
+ * @param {string} cwd - Working directory
70
+ * @param {boolean} raw - Output raw string
71
+ * @param {object} options - Options
72
+ * @param {boolean} options.showAll - If true, include eliminated items
73
+ */
74
+ function cmdTodoList(cwd, raw, { showAll } = {}) {
75
+ const todosDir = path.join(cwd, '.specflow', 'todos');
76
+
77
+ // Check for per-file TODOs
78
+ let perFiles;
79
+ try {
80
+ perFiles = fs.readdirSync(todosDir).filter(f => /^TODO-\d+\.md$/.test(f)).sort();
81
+ } catch (e) {
82
+ perFiles = [];
83
+ }
84
+
85
+ if (perFiles.length > 0) {
86
+ // Per-file format
87
+ const todos = [];
88
+
89
+ for (const file of perFiles) {
90
+ const content = safeReadFile(path.join(todosDir, file));
91
+ if (!content) continue;
92
+
93
+ const parsed = parseFrontmatter(content);
94
+ const fm = parsed.frontmatter;
95
+
96
+ // Filter eliminated unless showAll
97
+ if (!showAll && fm.status === 'eliminated') continue;
98
+
99
+ todos.push({
100
+ id: fm.id || file.replace('.md', ''),
101
+ title: fm.title || '',
102
+ priority: fm.priority || '—',
103
+ status: fm.status || 'open',
104
+ complexity: fm.complexity || '—',
105
+ created: fm.created || '',
106
+ });
107
+ }
108
+
109
+ // Sort by priority (high > medium > low > unset), then by created date (oldest first)
110
+ todos.sort((a, b) => {
111
+ const pa = priorityKey(a.priority);
112
+ const pb = priorityKey(b.priority);
113
+ if (pa !== pb) return pa - pb;
114
+ // Compare dates lexicographically (ISO dates sort correctly as strings)
115
+ if (a.created < b.created) return -1;
116
+ if (a.created > b.created) return 1;
117
+ return 0;
118
+ });
119
+
120
+ output(todos, raw, todos.map(t => t.id).join('\n'));
121
+ return;
122
+ }
123
+
124
+ // Legacy format: check for monolithic TODO.md
125
+ const legacyPath = path.join(todosDir, 'TODO.md');
126
+ const legacyContent = safeReadFile(legacyPath);
127
+
128
+ if (legacyContent) {
129
+ // Parse legacy TODO blocks: ## TODO-XXX — YYYY-MM-DD
130
+ const todos = [];
131
+ const blockRegex = /^## (TODO-\d+) — (\d{4}-\d{2}-\d{2})\s*\n([\s\S]*?)(?=^## TODO-|\Z)/gm;
132
+ let match;
133
+
134
+ // Append sentinel heading so the last block's lazy [\s\S]*? terminates correctly
135
+ while ((match = blockRegex.exec(legacyContent + '\n## TODO-END')) !== null) {
136
+ const id = match[1];
137
+ const created = match[2];
138
+ const body = match[3];
139
+
140
+ // Extract description
141
+ const descMatch = body.match(/\*\*Description:\*\*\s*(.+)/);
142
+ const title = descMatch ? descMatch[1].trim() : '';
143
+
144
+ // Extract priority
145
+ const prioMatch = body.match(/\*\*Priority:\*\*\s*(\S+)/);
146
+ const priority = prioMatch ? prioMatch[1].trim() : '—';
147
+
148
+ if (!showAll) {
149
+ // Legacy format has no eliminated status — always include
150
+ }
151
+
152
+ todos.push({
153
+ id,
154
+ title,
155
+ priority,
156
+ status: 'open',
157
+ complexity: '—',
158
+ created,
159
+ });
160
+ }
161
+
162
+ // Sort same way
163
+ todos.sort((a, b) => {
164
+ const pa = priorityKey(a.priority);
165
+ const pb = priorityKey(b.priority);
166
+ if (pa !== pb) return pa - pb;
167
+ if (a.created < b.created) return -1;
168
+ if (a.created > b.created) return 1;
169
+ return 0;
170
+ });
171
+
172
+ output(todos, raw, todos.map(t => t.id).join('\n'));
173
+ return;
174
+ }
175
+
176
+ // Neither format found — empty list
177
+ output([], raw, '');
178
+ }
179
+
180
+ /**
181
+ * Calculate the next available TODO-XXX number.
182
+ *
183
+ * Scans:
184
+ * 1. .specflow/todos/TODO-*.md filenames using fs.readdirSync() + JS regex
185
+ * 2. .specflow/todos/TODO.md for legacy IDs using fs.readFileSync() + /TODO-(\d+)/g
186
+ *
187
+ * NOTE: Does NOT use grep -oP (GNU-only, unavailable on macOS).
188
+ *
189
+ * @param {string} cwd - Working directory
190
+ * @param {boolean} raw - Output raw string
191
+ */
192
+ function cmdTodoNextId(cwd, raw) {
193
+ const todosDir = path.join(cwd, '.specflow', 'todos');
194
+
195
+ let maxNum = 0;
196
+
197
+ // Scan per-file TODOs
198
+ try {
199
+ const files = fs.readdirSync(todosDir);
200
+ for (const file of files) {
201
+ const match = file.match(/^TODO-(\d+)\.md$/);
202
+ if (match) {
203
+ const num = parseInt(match[1], 10);
204
+ if (num > maxNum) maxNum = num;
205
+ }
206
+ }
207
+ } catch (e) {
208
+ // directory may not exist yet
209
+ }
210
+
211
+ // Scan legacy TODO.md for any IDs referenced there
212
+ const legacyPath = path.join(todosDir, 'TODO.md');
213
+ try {
214
+ const legacyContent = fs.readFileSync(legacyPath, 'utf8');
215
+ const regex = /TODO-(\d+)/g;
216
+ let match;
217
+ while ((match = regex.exec(legacyContent)) !== null) {
218
+ const num = parseInt(match[1], 10);
219
+ if (num > maxNum) maxNum = num;
220
+ }
221
+ } catch (e) {
222
+ // file may not exist — skip
223
+ }
224
+
225
+ const nextNumber = maxNum + 1;
226
+ const nextId = 'TODO-' + String(nextNumber).padStart(3, '0');
227
+
228
+ output({
229
+ next_id: nextId,
230
+ next_number: nextNumber,
231
+ }, raw, nextId);
232
+ }
233
+
234
+ module.exports = {
235
+ cmdTodoLoad,
236
+ cmdTodoList,
237
+ cmdTodoNextId,
238
+ };
package/bin/sf-tools.cjs CHANGED
@@ -9,6 +9,9 @@
9
9
  * spec load <id> Parse spec file, return frontmatter + body
10
10
  * spec list List all specs
11
11
  * spec next-id Next available SPEC-XXX number
12
+ * todo load <id> Parse TODO file, return frontmatter + body
13
+ * todo list [--all] List all TODOs sorted by priority
14
+ * todo next-id Next available TODO-XXX number
12
15
  * queue next First actionable spec from queue
13
16
  * state get Current active spec, status, next step
14
17
  * state set-active <id> <status> [next] Update active spec in STATE.md
@@ -22,6 +25,7 @@
22
25
  const { output, error, generateSlug } = require('./lib/core.cjs');
23
26
  const { cmdStateGet, cmdStateSetActive, cmdQueueNext } = require('./lib/state.cjs');
24
27
  const { cmdSpecLoad, cmdSpecList, cmdSpecNextId } = require('./lib/spec.cjs');
28
+ const { cmdTodoLoad, cmdTodoList, cmdTodoNextId } = require('./lib/todo.cjs');
25
29
  const { cmdResolveModel } = require('./lib/config.cjs');
26
30
  const { cmdVerifyStructure } = require('./lib/verify.cjs');
27
31
 
@@ -34,6 +38,14 @@ const filteredArgs = args.filter(a => a !== '--raw');
34
38
  * Command dispatch table.
35
39
  * Keys are "command subcommand" or just "command".
36
40
  */
41
+ const flags = {};
42
+ for (const arg of filteredArgs) {
43
+ if (arg.startsWith('--')) {
44
+ const key = arg.slice(2).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
45
+ flags[key] = true;
46
+ }
47
+ }
48
+
37
49
  const COMMANDS = {
38
50
  'spec load': () => {
39
51
  if (!filteredArgs[2]) error('Missing spec ID. Usage: spec load <id>');
@@ -41,6 +53,12 @@ const COMMANDS = {
41
53
  },
42
54
  'spec list': () => cmdSpecList(cwd, raw),
43
55
  'spec next-id': () => cmdSpecNextId(cwd, raw),
56
+ 'todo load': () => {
57
+ if (!filteredArgs[2]) error('Missing TODO ID. Usage: todo load <id>');
58
+ cmdTodoLoad(cwd, filteredArgs[2], raw);
59
+ },
60
+ 'todo list': () => cmdTodoList(cwd, raw, { showAll: flags.all ?? false }),
61
+ 'todo next-id': () => cmdTodoNextId(cwd, raw),
44
62
  'queue next': () => cmdQueueNext(cwd, raw),
45
63
  'state get': () => cmdStateGet(cwd, raw),
46
64
  'state set-active': () => {
@@ -69,6 +87,9 @@ Commands:
69
87
  spec load <id> Parse spec file, return frontmatter + body
70
88
  spec list List all specs from .specflow/specs/
71
89
  spec next-id Next available SPEC-XXX number
90
+ todo load <id> Parse TODO file, return frontmatter + body
91
+ todo list [--all] List TODOs sorted by priority (--all includes eliminated)
92
+ todo next-id Next available TODO-XXX number
72
93
  queue next First actionable spec from queue table
73
94
  state get Current active spec, status, next step
74
95
  state set-active <id> <status> [next] Update active spec, status, next step
@@ -277,12 +277,21 @@ Check if the spec frontmatter contains a `source:` field (e.g., `source: TODO-00
277
277
 
278
278
  **If `source:` field exists:**
279
279
 
280
- 1. Read `.specflow/todos/TODO.md`
281
- 2. Check if the referenced TODO-XXX entry still exists in the file
282
- 3. **If it exists** — use the **Edit** tool (NOT Write) to remove the entire block (from `## TODO-XXX` heading through the next `---` separator, inclusive) — replace with empty string
283
- 4. Use the **Edit** tool to update `*Last updated:` timestamp with note: `TODO-XXX cleaned up (completed via SPEC-YYY)`
280
+ 1. Check if `.specflow/todos/{source}.md` exists (per-file format):
284
281
 
285
- **CRITICAL:** Never rewrite the entire file with Write. Use Edit to remove the specific block and update the timestamp — this preserves all other todos.
282
+ ```bash
283
+ [ -f .specflow/todos/{source}.md ] && echo "FOUND" || echo "NOT_FOUND"
284
+ ```
285
+
286
+ 2. **If FOUND:** Delete the file:
287
+
288
+ ```bash
289
+ rm .specflow/todos/{source}.md
290
+ ```
291
+
292
+ 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.
293
+
294
+ No "Last updated" lines to update in per-file format.
286
295
 
287
296
  **If no `source:` field or TODO already removed:** Skip — no action needed.
288
297
 
@@ -431,7 +440,7 @@ git commit -m "docs(sf): complete SPEC-XXX"
431
440
  - [ ] Spec status updated to "done"
432
441
  - [ ] Completion section added
433
442
  - [ ] Decisions extracted (if any)
434
- - [ ] Source TODO cleaned up (if `source:` field exists in spec)
443
+ - [ ] Source TODO file deleted (if `source:` field exists in spec and file exists in todos/)
435
444
  - [ ] Spec moved to archive
436
445
  - [ ] STATE.md updated (cleared active, removed from queue)
437
446
  - [ ] Final commit created
@@ -50,16 +50,22 @@ Initialize collectors:
50
50
  |-------|------|----------|------------|
51
51
  | E001 | `.specflow/STATE.md` missing | error | Yes — regenerate minimal STATE.md |
52
52
  | E002 | `.specflow/STATE.md` missing `## Queue` section | error | No |
53
- | W001 | `.specflow/todos/TODO.md` missing | warning | Yes — create empty TODO.md |
53
+ | W001 | `.specflow/todos/` directory missing | warning | Yes — create directory |
54
54
  | W002 | `.specflow/specs/` directory missing | warning | Yes — create directory |
55
55
  | W003 | `.specflow/archive/` directory missing | warning | Yes — create directory |
56
56
  | W004 | `.specflow/execution/` directory missing | warning | Yes — create directory |
57
+ | W009 | TODO file without valid YAML frontmatter | warning | No — inspect manually |
58
+ | W010 | Legacy `TODO.md` exists alongside per-file TODOs (suggest migration) | info | No — run `/sf:migrate-todos` |
57
59
 
58
60
  For each check:
59
61
  1. Test existence
60
62
  2. If missing and repairable and `--repair`: fix it, add to `repairs[]`
61
63
  3. If missing and not repairable: add to `errors[]` or `warnings[]`
62
64
 
65
+ **For W009:** List all `TODO-*.md` files in `.specflow/todos/` and check each for valid YAML frontmatter (presence of `id:`, `status:`, `created:` fields).
66
+
67
+ **For W010:** If both `TODO-*.md` files AND `TODO.md` exist in `.specflow/todos/`, add info note suggesting `/sf:migrate-todos` to complete migration.
68
+
63
69
  ### 3.2 STATE.md Integrity
64
70
 
65
71
  Read STATE.md and validate:
@@ -181,7 +187,7 @@ Display final status.
181
187
  | E001 | error | STATE.md not found | Yes |
182
188
  | E002 | error | STATE.md missing Queue section | No |
183
189
  | E003 | error | Active spec references non-existent file | Yes |
184
- | W001 | warning | TODO.md not found | Yes |
190
+ | W001 | warning | todos/ directory missing | Yes |
185
191
  | W002 | warning | specs/ directory missing | Yes |
186
192
  | W003 | warning | archive/ directory missing | Yes |
187
193
  | W004 | warning | execution/ directory missing | Yes |
@@ -189,6 +195,8 @@ Display final status.
189
195
  | W006 | warning | Queue has duplicate spec IDs | No |
190
196
  | W007 | warning | STATE.md missing required sections | No |
191
197
  | W008 | warning | Stale execution state files | Yes |
198
+ | W009 | warning | TODO file without valid frontmatter | No |
199
+ | W010 | info | Legacy TODO.md alongside per-file TODOs | No |
192
200
  | I001 | info | Spec not in queue (may be WIP) | No |
193
201
  | I002 | info | Completed spec not archived | No |
194
202
 
@@ -199,7 +207,7 @@ Display final status.
199
207
  | Action | Effect | Risk |
200
208
  |--------|--------|------|
201
209
  | Create STATE.md | Minimal template with empty queue | None |
202
- | Create TODO.md | Empty TODO template | None |
210
+ | Create todos/ directory | Empty directory for per-file TODOs | None |
203
211
  | Create directories | specs/, archive/, execution/ | None |
204
212
  | Clear active spec | Set to "—" if spec file missing | Loses active reference |
205
213
  | Delete stale execution | Remove orphaned .json state files | None |
@@ -39,7 +39,7 @@ Check whether the user invoked `/sf:init --force`. Look at the invocation string
39
39
  [ -f .specflow/PROJECT.md ] && echo "HAS_PROJECT_MD" || true
40
40
  [ -f .specflow/STATE.md ] && echo "HAS_STATE_MD" || true
41
41
  [ -f .specflow/config.json ] && echo "HAS_CONFIG_JSON" || true
42
- [ -f .specflow/todos/TODO.md ] && echo "HAS_TODO_MD" || true
42
+ [ -d .specflow/todos ] && echo "HAS_TODOS_DIR" || true
43
43
  [ "$(ls -A .specflow/specs 2>/dev/null)" ] && echo "HAS_SPECS" || true
44
44
  [ "$(ls -A .specflow/archive 2>/dev/null)" ] && echo "HAS_ARCHIVE" || true
45
45
  ```
@@ -58,7 +58,7 @@ The following files/directories would be overwritten:
58
58
  - .specflow/PROJECT.md
59
59
  - .specflow/STATE.md
60
60
  - .specflow/config.json
61
- - .specflow/todos/TODO.md
61
+ - .specflow/todos/ (directory)
62
62
  - .specflow/specs/ (contains files)
63
63
  - .specflow/archive/ (contains files)
64
64
 
@@ -17,7 +17,7 @@ Calculate and display project statistics including completion rates, quality met
17
17
  @.specflow/STATE.md
18
18
  @.specflow/specs/SPEC-*.md
19
19
  @.specflow/archive/SPEC-*.md
20
- @.specflow/todos/TODO.md
20
+ @.specflow/todos/
21
21
  </context>
22
22
 
23
23
  <workflow>
@@ -79,13 +79,22 @@ For each `.specflow/archive/SPEC-*.md`:
79
79
  ### Parse To-Dos
80
80
 
81
81
  ```bash
82
- # Count todos
83
- TODO_COUNT=$(grep -c "^## TODO-" .specflow/todos/TODO.md 2>/dev/null || echo 0)
82
+ # Count todos via CLI tool (format-agnostic, excludes eliminated by default)
83
+ node bin/sf-tools.cjs todo list --raw
84
+ ```
84
85
 
85
- # Count converted (todos that became specs - check STATE.md decisions)
86
- # This is approximated by todos with "converted" or spec reference
86
+ This returns the list of open TODO IDs. For `--all` (including eliminated):
87
+ ```bash
88
+ node bin/sf-tools.cjs todo list --all --raw
87
89
  ```
88
90
 
91
+ Parse priority breakdown from the JSON output to count by priority level.
92
+
93
+ Also check:
94
+ - `TODO_COUNT` = number of open TODOs (from default list)
95
+ - `TODO_COUNT_ALL` = total including eliminated (from --all list)
96
+ - Count converted (todos that became specs) approximated from decisions table
97
+
89
98
  ### Get Project Start Date
90
99
 
91
100
  Read `.specflow/PROJECT.md` for initialized date, or use earliest spec creation date.
@@ -241,7 +250,7 @@ Based on calculated metrics, generate 2-4 actionable insights:
241
250
 
242
251
  ## To-Do Backlog
243
252
 
244
- {If TODO.md exists and has items:}
253
+ {If TODO count > 0:}
245
254
  | Metric | Value |
246
255
  |----------------|-------|
247
256
  | Total items | {N} |