sdd-pipeline 1.2.4 → 1.2.7

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.
@@ -4,54 +4,114 @@ tool: Bash
4
4
  when: always
5
5
  description: Update task status in MASTER-TASKS.md + .sdd/config.json. Args: <T-XXX> <pending|in-progress|completed>. Recalculates progress counters, suggests next task.
6
6
  ---
7
+ $ErrorActionPreference = "Stop"
8
+
9
+ # --- SDD Path Resolution ---
10
+ $SDD_TASKS_DIR = "sdd/tasks"
11
+
12
+ if (Test-Path ".sdd/config.json") {
13
+ try {
14
+ $config = Get-Content ".sdd/config.json" -Raw | ConvertFrom-Json
15
+ if ($config.output -and $config.output.tasksDir) {
16
+ $SDD_TASKS_DIR = $config.output.tasksDir
17
+ }
18
+ } catch {}
19
+ }
20
+
21
+ # --- Parse arguments ---
7
22
  $taskId = $args[0]
8
23
  $newStatus = $args[1]
9
24
 
10
25
  if (-not $taskId -or -not $newStatus) {
11
- Write-Host "Usage: /sdd-task-status T-001 done"
12
- Write-Host "Usage: /sdd-task-status T-001 in-progress"
13
- Write-Host "Usage: /sdd-task-status T-001 pending"
26
+ Write-Host ""
27
+ Write-Host "Usage: /sdd-task-status T-001 done" -ForegroundColor White
28
+ Write-Host " /sdd-task-status T-001 in-progress" -ForegroundColor White
29
+ Write-Host " /sdd-task-status T-001 pending" -ForegroundColor White
14
30
  exit 1
15
31
  }
16
32
 
33
+ # Map status to display format
34
+ $statusMap = @{
35
+ "pending" = "[ ]"
36
+ "in-progress" = "[>]"
37
+ "completed" = "[x]"
38
+ "done" = "[x]"
39
+ "x" = "[x]"
40
+ }
41
+
42
+ $displayStatus = $statusMap[$newStatus]
43
+ if (-not $displayStatus) {
44
+ $displayStatus = "[ ]"
45
+ }
46
+
17
47
  # Validate status
18
- $validStatuses = @("pending", "in-progress", "completed")
48
+ $validStatuses = @("pending", "in-progress", "completed", "done", "x")
19
49
  if ($validStatuses -notcontains $newStatus) {
50
+ Write-Host ""
20
51
  Write-Host "ERROR: Invalid status '$newStatus'. Use: pending, in-progress, completed" -ForegroundColor Red
21
52
  exit 1
22
53
  }
23
54
 
24
- # Find task directory
25
- $taskDirs = Get-ChildItem -Filter "task-*-*" -Directory -ErrorAction SilentlyContinue
26
- if (-not $taskDirs) {
27
- Write-Host "ERROR: No task directory found. Run /sdd-tasks first." -ForegroundColor Red
55
+ # --- Find task directory (check new path first, then legacy) ---
56
+ $taskDir = $null
57
+
58
+ # Check new location
59
+ if (Test-Path $SDD_TASKS_DIR) {
60
+ $taskDirs = Get-ChildItem -Path $SDD_TASKS_DIR -Filter "task-*-*" -Directory -ErrorAction SilentlyContinue
61
+ if ($taskDirs) {
62
+ $taskDir = Join-Path $SDD_TASKS_DIR ($taskDirs | Select-Object -First 1).Name
63
+ }
64
+ }
65
+
66
+ # Fallback to legacy location
67
+ if (-not $taskDir) {
68
+ $legacyTaskDirs = Get-ChildItem -Filter "task-*-*" -Directory -ErrorAction SilentlyContinue
69
+ if ($legacyTaskDirs) {
70
+ $taskDir = ($legacyTaskDirs | Select-Object -First 1).FullName
71
+ Write-Host "[LEGACY] Task directory found at legacy location: $taskDir" -ForegroundColor Yellow
72
+ }
73
+ }
74
+
75
+ if (-not $taskDir) {
76
+ Write-Host ""
77
+ Write-Host "ERROR: No task directory found." -ForegroundColor Red
78
+ Write-Host "Run /sdd-tasks first." -ForegroundColor Yellow
28
79
  exit 1
29
80
  }
30
- $taskDir = ($taskDirs | Select-Object -First 1).Name
31
- $taskFile = Join-Path $taskDir "${taskId}-*.md"
32
81
 
33
- # Find task file
82
+ # --- Find task file ---
34
83
  $taskFiles = Get-ChildItem -Path $taskDir -Filter "${taskId}-*.md" -ErrorAction SilentlyContinue
84
+ $taskFiles = $taskFiles | Where-Object { $_.Name -ne "MASTER-TASKS.md" }
35
85
  $taskFilePath = ($taskFiles | Select-Object -First 1).FullName
36
86
 
37
87
  if (-not $taskFilePath) {
88
+ Write-Host ""
38
89
  Write-Host "ERROR: $taskId not found in $taskDir" -ForegroundColor Red
39
90
  exit 1
40
91
  }
41
92
 
42
- # Update task file status in frontmatter
93
+ # --- Update task file status in frontmatter ---
94
+ $frontmatterStatus = $newStatus
95
+ if ($newStatus -in @("done", "x")) {
96
+ $frontmatterStatus = "completed"
97
+ }
98
+
43
99
  $content = Get-Content $taskFilePath -Raw
44
- $content = $content -replace '(?m)^(status:\s*)\S+', "`$1$newStatus"
100
+ $content = $content -replace '(?m)^(status:\s*)\S+', "`$1$frontmatterStatus"
45
101
  Set-Content $taskFilePath $content -Encoding UTF8
46
102
 
47
- # Update MASTER-TASKS.md
103
+ # --- Update MASTER-TASKS.md ---
48
104
  $masterPath = Join-Path $taskDir "MASTER-TASKS.md"
49
105
  $masterContent = Get-Content $masterPath -Raw
50
106
 
51
- # Update task table row status
52
- $pattern = "(\|\s*T-\d+\s*\|\s*[^\|]+\|\s*)\S+(\s*\|\s*[^\|]+\s*\|)"
53
- $replacement = '${1}' + $newStatus + '${2}'
54
- $masterContent = $masterContent -replace $pattern, $replacement
107
+ # Update task table row status - handle both [ ] and [x] patterns
108
+ # Pattern matches the status column and replaces it
109
+ $pattern = "(\|\s*$taskId\s*\|\s*[^\|]+\|\s*[^\|]+\|\s*)\[[^\]]\](\s*\|)"
110
+ $masterContent = $masterContent -replace $pattern, "`$1$displayStatus`$2"
111
+
112
+ # Also handle T-XXX format with status in brackets
113
+ $pattern2 = "(\|\s*T-\d+\s*\|\s*[^\|]+\|\s*[^\|]+\|\s*)\[[^\]]\](\s*\|)"
114
+ $masterContent = $masterContent -replace $pattern2, "`$1$displayStatus`$2"
55
115
 
56
116
  # Update progress counters
57
117
  $allTaskFiles = Get-ChildItem -Path $taskDir -Filter "T-*.md" | Where-Object { $_.Name -ne "MASTER-TASKS.md" }
@@ -65,31 +125,41 @@ foreach ($f in $allTaskFiles) {
65
125
  elseif ($c -match '(?m)^status:\s*in-progress') { $inProgress++ }
66
126
  }
67
127
 
128
+ # Update "Completed: X / N" pattern
68
129
  $masterContent = $masterContent -replace 'Completed: \d+ / \d+', "Completed: $completed / $total"
130
+ # Update "In Progress: X" pattern
69
131
  $masterContent = $masterContent -replace 'In Progress: \d+', "In Progress: $inProgress"
132
+ # Update "Pending: X" pattern
70
133
  $masterContent = $masterContent -replace 'Pending: \d+', "Pending: $($total - $completed - $inProgress)"
71
134
 
72
135
  Set-Content $masterPath $masterContent -Encoding UTF8
73
136
 
74
- # Update .sdd/config.json
137
+ # --- Update .sdd/config.json ---
75
138
  if (Test-Path ".sdd/config.json") {
76
- $config = Get-Content ".sdd/config.json" | ConvertFrom-Json
77
- if (-not $config.tasks) { $config | Add-Member -NotePropertyName "tasks" -NotePropertyValue ([ordered]@{}) }
78
- $config.tasks.$taskId = $newStatus
79
- $config | ConvertTo-Json | Set-Content ".sdd/config.json"
139
+ try {
140
+ $config = Get-Content ".sdd/config.json" -Raw | ConvertFrom-Json
141
+ if (-not $config.tasks) { $config | Add-Member -NotePropertyName "tasks" -NotePropertyValue ([ordered]@{}) }
142
+ $config.tasks.$taskId = $frontmatterStatus
143
+ $config | ConvertTo-Json -Depth 10 | Set-Content ".sdd/config.json"
144
+ } catch {}
80
145
  }
81
146
 
82
- Write-Host "[OK] $taskId -> $newStatus" -ForegroundColor Green
83
- Write-Host " Progress: $completed/$total completed, $inProgress in-progress"
147
+ # --- Summary ---
148
+ $statusColor = if ($frontmatterStatus -eq "completed") { "Green" } elseif ($frontmatterStatus -eq "in-progress") { "Yellow" } else { "Gray" }
149
+ Write-Host ""
150
+ Write-Host "[OK] $taskId -> $displayStatus" -ForegroundColor Green
151
+ Write-Host " Progress: $completed/$total completed, $inProgress in-progress" -ForegroundColor Gray
152
+ Write-Host " Task file: $taskFilePath" -ForegroundColor Gray
84
153
 
85
154
  # Suggest next task
86
- if ($newStatus -eq "completed") {
155
+ if ($frontmatterStatus -eq "completed") {
87
156
  $pending = Get-ChildItem -Path $taskDir -Filter "T-*.md" | Where-Object {
88
157
  $_.Name -ne "MASTER-TASKS.md" -and
89
158
  ((Get-Content $_.FullName -Raw) -notmatch '(?m)^status:\s*completed')
90
159
  }
91
160
  if ($pending) {
92
161
  $next = ($pending | Select-Object -First 1).Name -replace '\.md$', ''
93
- Write-Host " Next: /sdd-task-status $next in-progress" -ForegroundColor Cyan
162
+ Write-Host ""
163
+ Write-Host "Next: /sdd-task-status $next in-progress" -ForegroundColor Cyan
94
164
  }
95
165
  }
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env node
2
+ // sdd — Spec-Driven Development CLI
3
+ // Unified entry point for both npm package and local development
4
+
5
+ const path = require('path')
6
+ const fs = require('fs')
7
+
8
+ // Determine if we're in npm package or local development
9
+ const isNpmPackage = fs.existsSync(path.join(__dirname, 'lib/bmad.js'))
10
+ const libDir = isNpmPackage ? path.join(__dirname, 'lib') : path.join(__dirname, '..', 'lib')
11
+
12
+ // Dynamic requires
13
+ const init = require(path.join(libDir, 'init'))
14
+ const bmad = require(path.join(libDir, 'bmad/index'))
15
+ const config = require(path.join(libDir, 'config'))
16
+
17
+ const args = process.argv.slice(2)
18
+ const cmd = args[0]
19
+
20
+ // PowerShell detection - use correct command for platform
21
+ function getPowerShellCommand() {
22
+ if (process.platform === 'win32') {
23
+ // Windows: try powershell.exe first (always available), then pwsh
24
+ const system32 = 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'
25
+ if (fs.existsSync(system32)) {
26
+ return 'powershell'
27
+ }
28
+ return 'powershell' // Fallback to whatever is in PATH
29
+ }
30
+ return 'pwsh' // macOS/Linux: PowerShell Core
31
+ }
32
+
33
+ function runPowerShellScript(scriptPath, scriptArgs, callback) {
34
+ const psCmd = getPowerShellCommand()
35
+ const psArgs = ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', scriptPath, ...scriptArgs]
36
+
37
+ const { spawn } = require('child_process')
38
+ const ps = spawn(psCmd, psArgs, {
39
+ stdio: 'inherit',
40
+ shell: false,
41
+ windowsHide: true
42
+ })
43
+
44
+ ps.on('error', (err) => {
45
+ callback(err, null)
46
+ })
47
+
48
+ ps.on('close', (code) => {
49
+ callback(null, code)
50
+ })
51
+ }
52
+
53
+ switch (cmd) {
54
+ case 'init':
55
+ init.run()
56
+ break
57
+
58
+ case 'bmad': {
59
+ const input = args.slice(1).join(' ')
60
+ if (!input) {
61
+ console.error('Usage: sdd bmad <feature description>')
62
+ console.error('Example: sdd bmad "landing page for my SaaS product"')
63
+ console.error('')
64
+ console.error('For interactive mode (9-question interview), use Claude Code:')
65
+ console.error(' claude "/sdd-bmad landing page for my SaaS"')
66
+ process.exit(1)
67
+ }
68
+
69
+ // Try to run PowerShell script if available
70
+ const commandsDir = isNpmPackage
71
+ ? path.join(libDir, 'bundle', '.claude', 'commands')
72
+ : path.join(__dirname, '..', '.claude', 'commands')
73
+ const bmadScript = path.join(commandsDir, 'sdd-bmad.md')
74
+
75
+ // Note: Claude Code commands are .md files designed for Claude Code CLI
76
+ // PowerShell cannot run .md files directly
77
+ // So we always use the Node.js module for fast mode
78
+
79
+ // Generate brief using Node.js module
80
+ const session = bmad.init(input)
81
+ const result = bmad.generateBrief()
82
+
83
+ console.log('[OK] sdd/brief.md created.')
84
+ console.log(`Confidence: ${result.confidence.score}/100 — ${result.confidence.level}`)
85
+ console.log('')
86
+ console.log('Output: sdd/brief.md')
87
+ console.log('')
88
+ if (result.confidence.score < 70) {
89
+ console.log('Tip: For higher confidence, use Claude Code with interactive mode:')
90
+ console.log(' claude "/sdd-bmad ' + input.substring(0, 30) + '..."')
91
+ }
92
+ console.log('')
93
+ console.log('Next: claude "/sdd-spec" to generate SPEC.md')
94
+ console.log(' (or use Claude Code for full interactive experience)')
95
+ break
96
+ }
97
+
98
+ case 'spec':
99
+ console.log('=== SDD SPEC Generation ===')
100
+ console.log('')
101
+ console.log('This command requires Claude Code for best results.')
102
+ console.log('')
103
+ console.log('Run: claude "/sdd-spec"')
104
+ console.log('')
105
+ console.log('Claude Code will:')
106
+ console.log(' 1. Read sdd/brief.md')
107
+ console.log(' 2. Validate confidence score (requires >= 20)')
108
+ console.log(' 3. Generate sdd/SPEC.md from template')
109
+ break
110
+
111
+ case 'tasks':
112
+ console.log('=== SDD Task Generation ===')
113
+ console.log('')
114
+ console.log('This command requires Claude Code.')
115
+ console.log('')
116
+ console.log('Run: claude "/sdd-tasks"')
117
+ console.log('')
118
+ console.log('Claude Code will:')
119
+ console.log(' 1. Read sdd/SPEC.md')
120
+ console.log(' 2. Extract features from Section 4.1')
121
+ console.log(' 3. Generate sdd/tasks/task-*/ directory')
122
+ console.log(' 4. Create MASTER-TASKS.md and T-XXX-*.md files')
123
+ break
124
+
125
+ case 'cook':
126
+ console.log('=== SDD Implementation ===')
127
+ console.log('')
128
+ console.log('This command requires Claude Code.')
129
+ console.log('')
130
+ console.log('Run: claude "/sdd-cook"')
131
+ console.log('')
132
+ console.log('Claude Code will:')
133
+ console.log(' 1. Read sdd/tasks/task-*/MASTER-TASKS.md')
134
+ console.log(' 2. Execute tasks sequentially')
135
+ console.log(' 3. Scout, implement, test, review each task')
136
+ console.log('')
137
+ console.log('Flags:')
138
+ console.log(' --all Execute all pending tasks')
139
+ console.log(' --task T-001 Execute specific task')
140
+ break
141
+
142
+ case 'converge':
143
+ console.log('=== SDD Converge Validation ===')
144
+ console.log('')
145
+ console.log('Full validation (requires dev server):')
146
+ console.log(' pwsh run-converge.ps1 -Url http://localhost:3000 -Strict')
147
+ console.log('')
148
+ console.log('Per-task validation:')
149
+ console.log(' pwsh run-converge.ps1 -TaskId T-001')
150
+ console.log('')
151
+ console.log('Claude Code:')
152
+ console.log(' claude "/sdd-converge"')
153
+ break
154
+
155
+ case 'status': {
156
+ config.printStatus()
157
+ break
158
+ }
159
+
160
+ case 'help':
161
+ default:
162
+ if (cmd && cmd !== 'help') {
163
+ console.error(`Unknown command: ${cmd}\n`)
164
+ }
165
+ console.log(`
166
+ sdd — Spec-Driven Development CLI
167
+
168
+ Output Structure (v1.1.0+):
169
+ sdd/ All generated artifacts
170
+ brief.md Phase 0: BMAD brief
171
+ SPEC.md Phase 2: Specification
172
+ tasks/ Phase 3: Task directories
173
+ converge/ Phase 5: Validation reports
174
+
175
+ Usage:
176
+ sdd init Initialize SDD pipeline
177
+ sdd bmad <desc> Phase 0: Generate brief (fast mode)
178
+ sdd status Show pipeline status
179
+ sdd help Show this help
180
+
181
+ Full Pipeline (requires Claude Code):
182
+ claude "/sdd-bmad" Interactive 9-question interview (high confidence)
183
+ claude "/sdd-spec" Generate SPEC.md
184
+ claude "/sdd-tasks" Generate task directory
185
+ claude "/sdd-cook" Execute implementation
186
+ claude "/sdd-converge" Validate implementation
187
+
188
+ Quick Start:
189
+ 1. sdd init
190
+ 2. sdd bmad "my feature idea"
191
+ 3. claude "/sdd-spec"
192
+ 4. claude "/sdd-tasks"
193
+ 5. claude "/sdd-cook"
194
+ 6. claude "/sdd-converge"
195
+
196
+ Notes:
197
+ - Claude Code CLI provides the best experience with interactive commands
198
+ - Install Claude Code: npm install -g @anthropic-ai/claude-code
199
+ - Without Claude Code, use "sdd bmad" for fast mode (lower confidence)
200
+ `)
201
+ }
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env node
2
+ // sdd — Spec-Driven Development CLI
3
+ // Unified entry point for both npm package and local development
4
+
5
+ const path = require('path')
6
+ const fs = require('fs')
7
+ const { spawn } = require('child_process')
8
+
9
+ // Determine if we're in npm package or local development
10
+ const isNpmPackage = fs.existsSync(path.join(__dirname, 'lib/bmad.js'))
11
+ const libDir = isNpmPackage ? path.join(__dirname, 'lib') : path.join(__dirname, '..', 'lib')
12
+
13
+ // Dynamic requires
14
+ const init = require(path.join(libDir, 'init'))
15
+ const bmad = require(path.join(libDir, 'bmad/index'))
16
+ const config = require(path.join(libDir, 'config'))
17
+
18
+ const args = process.argv.slice(2)
19
+ const cmd = args[0]
20
+
21
+ // PowerShell detection - use correct command for platform
22
+ function getPowerShellCommand() {
23
+ if (process.platform === 'win32') {
24
+ // Windows: try powershell.exe first (always available), then pwsh
25
+ const system32 = 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'
26
+ if (fs.existsSync(system32)) {
27
+ return 'powershell'
28
+ }
29
+ return 'powershell' // Fallback to whatever is in PATH
30
+ }
31
+ return 'pwsh' // macOS/Linux: PowerShell Core
32
+ }
33
+
34
+ async function runPowerShellCommand(scriptPath, args = []) {
35
+ return new Promise((resolve, reject) => {
36
+ const psCmd = getPowerShellCommand()
37
+ const psArgs = ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', scriptPath, ...args]
38
+
39
+ const ps = spawn(psCmd, psArgs, {
40
+ stdio: 'inherit',
41
+ shell: false,
42
+ windowsHide: true
43
+ })
44
+
45
+ ps.on('error', (err) => {
46
+ reject(err)
47
+ })
48
+
49
+ ps.on('close', (code) => {
50
+ resolve(code)
51
+ })
52
+ })
53
+ }
54
+
55
+ switch (cmd) {
56
+ case 'init':
57
+ init.run()
58
+ break
59
+
60
+ case 'bmad': {
61
+ const input = args.slice(1).join(' ')
62
+ if (!input) {
63
+ console.error('Usage: sdd bmad <feature description>')
64
+ console.error('Example: sdd bmad "landing page for my SaaS product"')
65
+ console.error('')
66
+ console.error('For interactive mode (9-question interview), use Claude Code:')
67
+ console.error(' claude "/sdd-bmad landing page for my SaaS"')
68
+ process.exit(1)
69
+ }
70
+
71
+ // Try interactive mode via PowerShell
72
+ const commandsDir = isNpmPackage
73
+ ? path.join(libDir, 'bundle', '.claude', 'commands')
74
+ : path.join(__dirname, '..', '.claude', 'commands')
75
+ const bmadScript = path.join(commandsDir, 'sdd-bmad.md')
76
+
77
+ if (fs.existsSync(bmadScript)) {
78
+ try {
79
+ await runPowerShellCommand(bmadScript, [input])
80
+ process.exit(0)
81
+ } catch (err) {
82
+ // PowerShell not available - fall back to non-interactive
83
+ console.warn('[sdd] PowerShell not available, using fast mode.')
84
+ }
85
+ }
86
+
87
+ // Non-interactive mode: generate brief directly
88
+ const session = bmad.init(input)
89
+ const result = bmad.generateBrief()
90
+ console.log('[OK] sdd/brief.md created.')
91
+ console.log(`Confidence: ${result.confidence.score}/100 — ${result.confidence.level}`)
92
+ console.log('')
93
+ if (result.confidence.score < 70) {
94
+ console.log('Tip: Use Claude Code "/sdd-bmad" for interactive mode with 9 questions.')
95
+ }
96
+ console.log('Next: claude "/sdd-spec" to generate SPEC.md')
97
+ break
98
+ }
99
+
100
+ case 'spec':
101
+ console.log('Run: claude "/sdd-spec" to generate SPEC.md')
102
+ console.log('(SPEC generation requires Claude Code for best results)')
103
+ break
104
+
105
+ case 'tasks':
106
+ console.log('Run: claude "/sdd-tasks" to generate TASKS.md')
107
+ console.log('(Task generation requires Claude Code)')
108
+ break
109
+
110
+ case 'cook':
111
+ console.log('Run: claude "/sdd-cook" to execute tasks')
112
+ console.log('(Implementation requires Claude Code)')
113
+ break
114
+
115
+ case 'converge':
116
+ console.log('Run: pwsh run-converge.ps1 -Strict to run converge')
117
+ console.log('')
118
+ console.log('For full validation:')
119
+ console.log(' pwsh run-converge.ps1 -Url http://localhost:3000 -Strict')
120
+ break
121
+
122
+ case 'status':
123
+ config.printStatus()
124
+ break
125
+
126
+ case 'help':
127
+ default:
128
+ if (cmd && cmd !== 'help') {
129
+ console.error(`Unknown command: ${cmd}\n`)
130
+ }
131
+ console.log(`
132
+ sdd — Spec-Driven Development CLI
133
+
134
+ Output Structure (v1.1.0+):
135
+ sdd/ All generated artifacts
136
+ brief.md Phase 0: BMAD brief
137
+ SPEC.md Phase 2: Specification
138
+ tasks/ Phase 3: Task directories
139
+ converge/ Phase 5: Validation reports
140
+
141
+ Usage:
142
+ sdd init Initialize SDD pipeline
143
+ sdd bmad <desc> Phase 0: Generate brief (fast mode)
144
+ sdd status Show pipeline status
145
+ sdd help Show this help
146
+
147
+ Full Pipeline (requires Claude Code):
148
+ claude "/sdd-bmad" Interactive 9-question interview (high confidence)
149
+ claude "/sdd-spec" Generate SPEC.md
150
+ claude "/sdd-tasks" Generate task directory
151
+ claude "/sdd-cook" Execute implementation
152
+ pwsh run-converge.ps1 Phase 5: Validate
153
+
154
+ Notes:
155
+ - Interactive commands require Claude Code CLI
156
+ - Run "npm install -g @anthropic-ai/claude-code" to enable Claude Code
157
+ - Without Claude Code, use fast mode for quick briefs (lower confidence)
158
+ `)
159
+ }