@girardmedia/bootspring 2.0.21 → 2.0.23

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.
Files changed (159) hide show
  1. package/bin/bootspring.js +5 -0
  2. package/cli/org.js +474 -0
  3. package/cli/preseed/index.js +16 -0
  4. package/cli/preseed/interactive.js +143 -0
  5. package/cli/preseed/templates.js +227 -0
  6. package/cli/preseed.js +9 -301
  7. package/cli/seed/builders/ai-context-builder.js +85 -0
  8. package/cli/seed/builders/index.js +13 -0
  9. package/cli/seed/builders/seed-builder.js +272 -0
  10. package/cli/seed/extractors/content-extractors.js +383 -0
  11. package/cli/seed/extractors/index.js +47 -0
  12. package/cli/seed/extractors/metadata-extractors.js +167 -0
  13. package/cli/seed/extractors/section-extractor.js +54 -0
  14. package/cli/seed/extractors/stack-extractors.js +228 -0
  15. package/cli/seed/index.js +18 -0
  16. package/cli/seed/utils/folder-structure.js +84 -0
  17. package/cli/seed/utils/index.js +11 -0
  18. package/cli/seed.js +23 -1074
  19. package/core/api-client.js +77 -0
  20. package/core/entitlements.js +36 -0
  21. package/core/organizations.js +223 -0
  22. package/core/policies.js +51 -6
  23. package/core/policy-matrix.js +303 -0
  24. package/core/project-context.js +1 -0
  25. package/dist/cli/index.d.ts +3 -0
  26. package/dist/cli/index.js +3220 -0
  27. package/dist/cli/index.js.map +1 -0
  28. package/dist/context-McpJQa_2.d.ts +5710 -0
  29. package/dist/core/index.d.ts +635 -0
  30. package/dist/core/index.js +2593 -0
  31. package/dist/core/index.js.map +1 -0
  32. package/dist/index-QqbeEiDm.d.ts +857 -0
  33. package/dist/index-UiYCgwiH.d.ts +174 -0
  34. package/dist/index.d.ts +453 -0
  35. package/dist/index.js +44228 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/mcp/index.d.ts +1 -0
  38. package/dist/mcp/index.js +41173 -0
  39. package/dist/mcp/index.js.map +1 -0
  40. package/generators/index.ts +82 -0
  41. package/intelligence/orchestrator/config/failure-signatures.js +48 -0
  42. package/intelligence/orchestrator/config/index.js +23 -0
  43. package/intelligence/orchestrator/config/pack-lifecycle.js +262 -0
  44. package/intelligence/orchestrator/config/phases.js +111 -0
  45. package/intelligence/orchestrator/config/remediation.js +150 -0
  46. package/intelligence/orchestrator/config/workflows.js +168 -0
  47. package/intelligence/orchestrator/core/index.js +16 -0
  48. package/intelligence/orchestrator/core/state-manager.js +88 -0
  49. package/intelligence/orchestrator/core/telemetry.js +24 -0
  50. package/intelligence/orchestrator/index.js +17 -0
  51. package/intelligence/orchestrator.js +17 -512
  52. package/mcp/contracts/mcp-contract.v1.json +1 -1
  53. package/package.json +16 -3
  54. package/src/cli/agent.ts +703 -0
  55. package/src/cli/analyze.ts +640 -0
  56. package/src/cli/audit.ts +707 -0
  57. package/src/cli/auth.ts +930 -0
  58. package/src/cli/billing.ts +364 -0
  59. package/src/cli/build.ts +1089 -0
  60. package/src/cli/business.ts +508 -0
  61. package/src/cli/checkpoint-utils.ts +236 -0
  62. package/src/cli/checkpoint.ts +757 -0
  63. package/src/cli/cloud-sync.ts +534 -0
  64. package/src/cli/content.ts +273 -0
  65. package/src/cli/context.ts +667 -0
  66. package/src/cli/dashboard.ts +133 -0
  67. package/src/cli/deploy.ts +704 -0
  68. package/src/cli/doctor.ts +480 -0
  69. package/src/cli/fundraise.ts +494 -0
  70. package/src/cli/generate.ts +346 -0
  71. package/src/cli/github-cmd.ts +566 -0
  72. package/src/cli/health.ts +599 -0
  73. package/src/cli/index.ts +113 -0
  74. package/src/cli/init.ts +838 -0
  75. package/src/cli/legal.ts +495 -0
  76. package/src/cli/log.ts +316 -0
  77. package/src/cli/loop.ts +1660 -0
  78. package/src/cli/manager.ts +878 -0
  79. package/src/cli/mcp.ts +275 -0
  80. package/src/cli/memory.ts +346 -0
  81. package/src/cli/metrics.ts +590 -0
  82. package/src/cli/monitor.ts +960 -0
  83. package/src/cli/mvp.ts +662 -0
  84. package/src/cli/onboard.ts +663 -0
  85. package/src/cli/orchestrator.ts +622 -0
  86. package/src/cli/plugin.ts +483 -0
  87. package/src/cli/prd.ts +671 -0
  88. package/src/cli/preseed-start.ts +1633 -0
  89. package/src/cli/preseed.ts +2434 -0
  90. package/src/cli/project.ts +526 -0
  91. package/src/cli/quality.ts +885 -0
  92. package/src/cli/security.ts +1079 -0
  93. package/src/cli/seed.ts +1224 -0
  94. package/src/cli/skill.ts +537 -0
  95. package/src/cli/suggest.ts +1225 -0
  96. package/src/cli/switch.ts +518 -0
  97. package/src/cli/task.ts +780 -0
  98. package/src/cli/telemetry.ts +172 -0
  99. package/src/cli/todo.ts +627 -0
  100. package/src/cli/types.ts +15 -0
  101. package/src/cli/update.ts +334 -0
  102. package/src/cli/visualize.ts +609 -0
  103. package/src/cli/watch.ts +895 -0
  104. package/src/cli/workspace.ts +709 -0
  105. package/src/core/action-recorder.ts +673 -0
  106. package/src/core/analyze-workflow.ts +1453 -0
  107. package/src/core/api-client.ts +1120 -0
  108. package/src/core/audit-workflow.ts +1681 -0
  109. package/src/core/auth.ts +471 -0
  110. package/src/core/build-orchestrator.ts +509 -0
  111. package/src/core/build-state.ts +621 -0
  112. package/src/core/checkpoint-engine.ts +482 -0
  113. package/src/core/config.ts +1285 -0
  114. package/src/core/context-loader.ts +694 -0
  115. package/src/core/context.ts +410 -0
  116. package/src/core/deploy-workflow.ts +1085 -0
  117. package/src/core/entitlements.ts +322 -0
  118. package/src/core/github-sync.ts +720 -0
  119. package/src/core/index.ts +981 -0
  120. package/src/core/ingest.ts +1186 -0
  121. package/src/core/metrics-engine.ts +886 -0
  122. package/src/core/mvp.ts +847 -0
  123. package/src/core/onboard-workflow.ts +1293 -0
  124. package/src/core/policies.ts +81 -0
  125. package/src/core/preseed-workflow.ts +1163 -0
  126. package/src/core/preseed.ts +1826 -0
  127. package/src/core/project-context.ts +380 -0
  128. package/src/core/project-state.ts +699 -0
  129. package/src/core/r2-sync.ts +691 -0
  130. package/src/core/scaffold.ts +1715 -0
  131. package/src/core/session.ts +286 -0
  132. package/src/core/task-extractor.ts +799 -0
  133. package/src/core/telemetry.ts +371 -0
  134. package/src/core/tier-enforcement.ts +737 -0
  135. package/src/core/utils.ts +437 -0
  136. package/src/index.ts +29 -0
  137. package/src/intelligence/agent-collab.ts +2376 -0
  138. package/src/intelligence/auto-suggest.ts +713 -0
  139. package/src/intelligence/content-gen.ts +1351 -0
  140. package/src/intelligence/cross-project.ts +1692 -0
  141. package/src/intelligence/git-memory.ts +529 -0
  142. package/src/intelligence/index.ts +318 -0
  143. package/src/intelligence/orchestrator.ts +534 -0
  144. package/src/intelligence/prd.ts +466 -0
  145. package/src/intelligence/recommendations.ts +982 -0
  146. package/src/intelligence/workflow-composer.ts +1472 -0
  147. package/src/mcp/capabilities.ts +233 -0
  148. package/src/mcp/index.ts +37 -0
  149. package/src/mcp/registry.ts +1268 -0
  150. package/src/mcp/response-formatter.ts +797 -0
  151. package/src/mcp/server.ts +240 -0
  152. package/src/types/agent.ts +69 -0
  153. package/src/types/config.ts +86 -0
  154. package/src/types/context.ts +77 -0
  155. package/src/types/index.ts +53 -0
  156. package/src/types/mcp.ts +91 -0
  157. package/src/types/skills.ts +47 -0
  158. package/src/types/workflow.ts +155 -0
  159. package/generators/index.js +0 -18
package/src/cli/prd.ts ADDED
@@ -0,0 +1,671 @@
1
+ /**
2
+ * Bootspring PRD Command
3
+ * Product Requirements Document management
4
+ *
5
+ * Commands:
6
+ * create Create a new PRD
7
+ * list List all PRDs
8
+ * show Show a specific PRD
9
+ * import Import PRDs from .bootspring/inputs/prd
10
+ * to-tasks Convert PRD to todo items
11
+ * status Show PRD system status
12
+ *
13
+ * @package bootspring
14
+ * @command prd
15
+ */
16
+
17
+ import * as fs from 'fs';
18
+ import * as path from 'path';
19
+
20
+ // Type interfaces for JS modules
21
+ interface Colors {
22
+ reset: string;
23
+ bold: string;
24
+ dim: string;
25
+ cyan: string;
26
+ green: string;
27
+ yellow: string;
28
+ red: string;
29
+ }
30
+
31
+ interface PrintModule {
32
+ error(msg: string): void;
33
+ success(msg: string): void;
34
+ warning(msg: string): void;
35
+ dim(msg: string): void;
36
+ }
37
+
38
+ interface Spinner {
39
+ start(): Spinner;
40
+ stop(): void;
41
+ succeed(text: string): void;
42
+ fail(text: string): void;
43
+ }
44
+
45
+ interface UtilsModule {
46
+ COLORS: Colors;
47
+ print: PrintModule;
48
+ createSpinner(text: string): Spinner;
49
+ parseArgs(args: string[]): ParsedArgs;
50
+ formatRelativeTime(date: Date): string;
51
+ }
52
+
53
+ interface ConfigModule {
54
+ findProjectRoot(): string;
55
+ }
56
+
57
+ interface ParsedArgs {
58
+ _: string[];
59
+ name?: string | undefined;
60
+ title?: string | undefined;
61
+ description?: string | undefined;
62
+ force?: boolean | undefined;
63
+ [key: string]: string | boolean | string[] | undefined;
64
+ }
65
+
66
+ interface PrdInfo {
67
+ name: string;
68
+ title: string;
69
+ source: 'generated' | 'input';
70
+ status: string;
71
+ modified: Date;
72
+ }
73
+
74
+ interface IngestedPrd {
75
+ file: string;
76
+ userStories?: string[] | undefined;
77
+ features?: string[] | undefined;
78
+ requirements?: {
79
+ mustHave?: string[] | undefined;
80
+ shouldHave?: string[] | undefined;
81
+ niceToHave?: string[] | undefined;
82
+ } | undefined;
83
+ }
84
+
85
+ interface IngestModule {
86
+ ingestPRD(projectRoot: string): Promise<IngestedPrd[]>;
87
+ }
88
+
89
+ interface TodoModule {
90
+ add(options: { text: string; priority: number }): void;
91
+ }
92
+
93
+ interface Task {
94
+ text: string;
95
+ type: 'story' | 'requirement';
96
+ priority: number;
97
+ }
98
+
99
+ const utils = require('../core/utils') as UtilsModule;
100
+ const config = require('../core/config') as ConfigModule;
101
+
102
+ // Lazy load ingest module
103
+ let ingest: IngestModule | null = null;
104
+ function getIngest(): IngestModule {
105
+ if (!ingest) {
106
+ ingest = require('../core/ingest') as IngestModule;
107
+ }
108
+ return ingest;
109
+ }
110
+
111
+ /**
112
+ * Run PRD command
113
+ */
114
+ export async function run(args: string[]): Promise<void> {
115
+ const parsedArgs = utils.parseArgs(args);
116
+ const subcommand = parsedArgs._[0] || 'status';
117
+
118
+ switch (subcommand) {
119
+ case 'create':
120
+ return prdCreate(parsedArgs);
121
+ case 'list':
122
+ return prdList(parsedArgs);
123
+ case 'show':
124
+ return prdShow(parsedArgs);
125
+ case 'import':
126
+ return prdImport(parsedArgs);
127
+ case 'to-tasks':
128
+ return prdToTasks(parsedArgs);
129
+ case 'status':
130
+ return prdStatus(parsedArgs);
131
+ case 'help':
132
+ case '-h':
133
+ case '--help':
134
+ return showHelp();
135
+ default:
136
+ utils.print.error(`Unknown subcommand: ${subcommand}`);
137
+ showHelp();
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Create a new PRD
143
+ */
144
+ function prdCreate(args: ParsedArgs): void {
145
+ const projectRoot = config.findProjectRoot();
146
+ const name = args._[1] || args.name;
147
+ const title = args.title;
148
+ const description = args.description;
149
+
150
+ console.log(`
151
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Bootspring PRD Create${utils.COLORS.reset}
152
+ ${utils.COLORS.dim}Create a new Product Requirements Document${utils.COLORS.reset}
153
+ `);
154
+
155
+ if (!name) {
156
+ utils.print.error('PRD name required');
157
+ utils.print.dim('Usage: bootspring prd create <name> [--title="..."] [--description="..."]');
158
+ return;
159
+ }
160
+
161
+ const prdDir = path.join(projectRoot, '.bootspring', 'generated', 'prd');
162
+ if (!fs.existsSync(prdDir)) {
163
+ fs.mkdirSync(prdDir, { recursive: true });
164
+ }
165
+
166
+ const fileName = `${name.toLowerCase().replace(/\s+/g, '-')}.md`;
167
+ const filePath = path.join(prdDir, fileName);
168
+
169
+ if (fs.existsSync(filePath) && !args.force) {
170
+ utils.print.warning(`PRD already exists: ${fileName}`);
171
+ utils.print.dim('Use --force to overwrite');
172
+ return;
173
+ }
174
+
175
+ const date = new Date().toISOString().split('T')[0] || '';
176
+ const content = `# ${title || name}
177
+
178
+ **Status:** Draft
179
+ **Created:** ${date}
180
+ **Author:** ${process.env.USER || 'Bootspring'}
181
+
182
+ ## Overview
183
+
184
+ ${description || 'Add description here.'}
185
+
186
+ ## Problem Statement
187
+
188
+ Describe the problem being solved and why it matters.
189
+
190
+ ## Goals
191
+
192
+ 1. Primary goal
193
+ 2. Secondary goal
194
+ 3. Success criteria
195
+
196
+ ## User Stories
197
+
198
+ ### As a user, I want to...
199
+
200
+ - [ ] User story 1
201
+ - [ ] User story 2
202
+ - [ ] User story 3
203
+
204
+ ## Requirements
205
+
206
+ ### Must Have (P0)
207
+ - Requirement 1
208
+ - Requirement 2
209
+
210
+ ### Should Have (P1)
211
+ - Requirement 3
212
+ - Requirement 4
213
+
214
+ ### Nice to Have (P2)
215
+ - Requirement 5
216
+
217
+ ## Technical Considerations
218
+
219
+ - Architecture notes
220
+ - Dependencies
221
+ - Performance requirements
222
+
223
+ ## Success Metrics
224
+
225
+ | Metric | Target | Current |
226
+ |--------|--------|---------|
227
+ | Metric 1 | X | - |
228
+ | Metric 2 | Y | - |
229
+
230
+ ## Timeline
231
+
232
+ | Phase | Duration | Status |
233
+ |-------|----------|--------|
234
+ | Research | 1 week | Not started |
235
+ | Design | 1 week | Not started |
236
+ | Implementation | 2 weeks | Not started |
237
+ | Testing | 1 week | Not started |
238
+
239
+ ## Open Questions
240
+
241
+ 1. Question 1?
242
+ 2. Question 2?
243
+
244
+ ## References
245
+
246
+ - Link 1
247
+ - Link 2
248
+
249
+ ---
250
+ *Generated by Bootspring PRD System*
251
+ `;
252
+
253
+ fs.writeFileSync(filePath, content);
254
+ utils.print.success(`Created PRD: ${fileName}`);
255
+
256
+ console.log(`
257
+ ${utils.COLORS.bold}Next steps:${utils.COLORS.reset}
258
+ 1. Edit ${utils.COLORS.cyan}.bootspring/generated/prd/${fileName}${utils.COLORS.reset}
259
+ 2. Run ${utils.COLORS.cyan}bootspring prd to-tasks ${name}${utils.COLORS.reset} to create todos
260
+ `);
261
+ }
262
+
263
+ /**
264
+ * List all PRDs
265
+ */
266
+ function prdList(_args: ParsedArgs): void {
267
+ const projectRoot = config.findProjectRoot();
268
+ const prdDir = path.join(projectRoot, '.bootspring', 'generated', 'prd');
269
+ const inputPrdDir = path.join(projectRoot, '.bootspring', 'inputs', 'prd');
270
+
271
+ console.log(`
272
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Bootspring PRD List${utils.COLORS.reset}
273
+ `);
274
+
275
+ const prds: PrdInfo[] = [];
276
+
277
+ // Check generated PRDs
278
+ if (fs.existsSync(prdDir)) {
279
+ const files = fs.readdirSync(prdDir).filter(f => f.endsWith('.md'));
280
+ for (const file of files) {
281
+ const filePath = path.join(prdDir, file);
282
+ const stats = fs.statSync(filePath);
283
+ const content = fs.readFileSync(filePath, 'utf-8');
284
+ const titleMatch = content.match(/^#\s+(.+)/m);
285
+ const statusMatch = content.match(/\*\*Status:\*\*\s*(.+)/i);
286
+
287
+ prds.push({
288
+ name: file.replace('.md', ''),
289
+ title: titleMatch?.[1] || file,
290
+ source: 'generated',
291
+ status: statusMatch?.[1]?.trim() || 'Unknown',
292
+ modified: stats.mtime
293
+ });
294
+ }
295
+ }
296
+
297
+ // Check input PRDs
298
+ if (fs.existsSync(inputPrdDir)) {
299
+ const files = fs.readdirSync(inputPrdDir).filter(f =>
300
+ f.endsWith('.md') || f.endsWith('.txt')
301
+ );
302
+ for (const file of files) {
303
+ if (file === 'README.md') continue;
304
+ const filePath = path.join(inputPrdDir, file);
305
+ const stats = fs.statSync(filePath);
306
+
307
+ prds.push({
308
+ name: file.replace(/\.(md|txt)$/, ''),
309
+ title: file,
310
+ source: 'input',
311
+ status: 'Imported',
312
+ modified: stats.mtime
313
+ });
314
+ }
315
+ }
316
+
317
+ if (prds.length === 0) {
318
+ utils.print.dim('No PRDs found');
319
+ console.log(`
320
+ ${utils.COLORS.bold}To create a PRD:${utils.COLORS.reset}
321
+ bootspring prd create <name> --title="My Feature"
322
+
323
+ ${utils.COLORS.bold}To import PRDs:${utils.COLORS.reset}
324
+ 1. Drop files in ${utils.COLORS.cyan}.bootspring/inputs/prd/${utils.COLORS.reset}
325
+ 2. Run ${utils.COLORS.cyan}bootspring prd import${utils.COLORS.reset}
326
+ `);
327
+ return;
328
+ }
329
+
330
+ console.log(`${utils.COLORS.bold}PRDs:${utils.COLORS.reset}
331
+ `);
332
+
333
+ for (const prd of prds) {
334
+ const modified = utils.formatRelativeTime(prd.modified);
335
+ const sourceIcon = prd.source === 'generated' ? utils.COLORS.green + '+' : utils.COLORS.cyan + '>';
336
+ console.log(` ${sourceIcon}${utils.COLORS.reset} ${utils.COLORS.bold}${prd.name}${utils.COLORS.reset}`);
337
+ console.log(` ${utils.COLORS.dim}${prd.title.slice(0, 50)}${utils.COLORS.reset}`);
338
+ console.log(` ${utils.COLORS.dim}Status: ${prd.status} | Modified: ${modified}${utils.COLORS.reset}`);
339
+ console.log();
340
+ }
341
+
342
+ console.log(`${utils.COLORS.dim}Total: ${prds.length} PRDs (${prds.filter(p => p.source === 'generated').length} generated, ${prds.filter(p => p.source === 'input').length} imported)${utils.COLORS.reset}`);
343
+ }
344
+
345
+ /**
346
+ * Show a specific PRD
347
+ */
348
+ function prdShow(args: ParsedArgs): void {
349
+ const projectRoot = config.findProjectRoot();
350
+ const name = args._[1] || args.name;
351
+
352
+ console.log(`
353
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Bootspring PRD Show${utils.COLORS.reset}
354
+ `);
355
+
356
+ if (!name) {
357
+ utils.print.error('PRD name required');
358
+ utils.print.dim('Usage: bootspring prd show <name>');
359
+ return;
360
+ }
361
+
362
+ // Try generated first, then input
363
+ const paths = [
364
+ path.join(projectRoot, '.bootspring', 'generated', 'prd', `${name}.md`),
365
+ path.join(projectRoot, '.bootspring', 'inputs', 'prd', `${name}.md`),
366
+ path.join(projectRoot, '.bootspring', 'inputs', 'prd', name)
367
+ ];
368
+
369
+ let content: string | null = null;
370
+ let foundPath: string | null = null;
371
+
372
+ for (const p of paths) {
373
+ if (fs.existsSync(p)) {
374
+ content = fs.readFileSync(p, 'utf-8');
375
+ foundPath = p;
376
+ break;
377
+ }
378
+ }
379
+
380
+ if (!content) {
381
+ utils.print.error(`PRD not found: ${name}`);
382
+ utils.print.dim('Run "bootspring prd list" to see available PRDs');
383
+ return;
384
+ }
385
+
386
+ console.log(`${utils.COLORS.dim}File: ${foundPath}${utils.COLORS.reset}
387
+ `);
388
+ console.log(content);
389
+ }
390
+
391
+ /**
392
+ * Import PRDs from inputs folder
393
+ */
394
+ async function prdImport(_args: ParsedArgs): Promise<void> {
395
+ const projectRoot = config.findProjectRoot();
396
+ const inputPrdDir = path.join(projectRoot, '.bootspring', 'inputs', 'prd');
397
+
398
+ console.log(`
399
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Bootspring PRD Import${utils.COLORS.reset}
400
+ ${utils.COLORS.dim}Importing PRDs from .bootspring/inputs/prd${utils.COLORS.reset}
401
+ `);
402
+
403
+ if (!fs.existsSync(inputPrdDir)) {
404
+ utils.print.warning('No .bootspring/inputs/prd folder found');
405
+ utils.print.dim('Run "bootspring seed setup" to create the folder structure');
406
+ return;
407
+ }
408
+
409
+ const spinner = utils.createSpinner('Analyzing PRD files...').start();
410
+
411
+ try {
412
+ const Ingest = getIngest();
413
+ const ingested = await Ingest.ingestPRD(projectRoot);
414
+
415
+ spinner.succeed('Analyzed PRD files');
416
+
417
+ if (!ingested || ingested.length === 0) {
418
+ utils.print.dim('No PRD files found in .bootspring/inputs/prd/');
419
+ return;
420
+ }
421
+
422
+ console.log(`
423
+ ${utils.COLORS.bold}Imported ${ingested.length} PRD files:${utils.COLORS.reset}
424
+ `);
425
+
426
+ for (const prd of ingested) {
427
+ console.log(` ${utils.COLORS.green}+${utils.COLORS.reset} ${prd.file}`);
428
+ if (prd.userStories && prd.userStories.length > 0) {
429
+ console.log(` ${utils.COLORS.dim}User Stories: ${prd.userStories.length}${utils.COLORS.reset}`);
430
+ }
431
+ if (prd.features && prd.features.length > 0) {
432
+ console.log(` ${utils.COLORS.dim}Features: ${prd.features.length}${utils.COLORS.reset}`);
433
+ }
434
+ if (prd.requirements?.mustHave && prd.requirements.mustHave.length > 0) {
435
+ console.log(` ${utils.COLORS.dim}Requirements: ${prd.requirements.mustHave.length} must-have${utils.COLORS.reset}`);
436
+ }
437
+ }
438
+
439
+ console.log(`
440
+ ${utils.COLORS.bold}Next steps:${utils.COLORS.reset}
441
+ 1. Run ${utils.COLORS.cyan}bootspring prd to-tasks${utils.COLORS.reset} to create todos
442
+ 2. Run ${utils.COLORS.cyan}bootspring seed generate${utils.COLORS.reset} to update context
443
+ `);
444
+ } catch (error) {
445
+ spinner.fail(`Import failed: ${(error as Error).message}`);
446
+ }
447
+ }
448
+
449
+ /**
450
+ * Convert PRD to tasks
451
+ */
452
+ async function prdToTasks(args: ParsedArgs): Promise<void> {
453
+ const projectRoot = config.findProjectRoot();
454
+ const name = args._[1] || args.name;
455
+
456
+ console.log(`
457
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Bootspring PRD to Tasks${utils.COLORS.reset}
458
+ ${utils.COLORS.dim}Converting PRD requirements to todos${utils.COLORS.reset}
459
+ `);
460
+
461
+ let content: string | null = null;
462
+
463
+ if (name) {
464
+ // Load specific PRD
465
+ const paths = [
466
+ path.join(projectRoot, '.bootspring', 'generated', 'prd', `${name}.md`),
467
+ path.join(projectRoot, '.bootspring', 'inputs', 'prd', `${name}.md`)
468
+ ];
469
+
470
+ for (const p of paths) {
471
+ if (fs.existsSync(p)) {
472
+ content = fs.readFileSync(p, 'utf-8');
473
+ break;
474
+ }
475
+ }
476
+
477
+ if (!content) {
478
+ utils.print.error(`PRD not found: ${name}`);
479
+ utils.print.dim('Run "bootspring prd list" to see available PRDs');
480
+ return;
481
+ }
482
+ } else {
483
+ // Load all PRDs
484
+ const prdDir = path.join(projectRoot, '.bootspring', 'generated', 'prd');
485
+ if (fs.existsSync(prdDir)) {
486
+ const files = fs.readdirSync(prdDir).filter(f => f.endsWith('.md'));
487
+ content = files.map(f => fs.readFileSync(path.join(prdDir, f), 'utf-8')).join('\n\n');
488
+ }
489
+ }
490
+
491
+ if (!content) {
492
+ utils.print.warning('No PRDs to process');
493
+ return;
494
+ }
495
+
496
+ // Extract tasks from content
497
+ const tasks: Task[] = [];
498
+
499
+ // Extract user stories (lines with - [ ])
500
+ const storyMatches = content.matchAll(/^\s*-\s*\[\s*\]\s*(.+)$/gm);
501
+ for (const match of storyMatches) {
502
+ const text = match[1]?.trim();
503
+ if (text && text.length > 5) {
504
+ tasks.push({ text, type: 'story', priority: 2 });
505
+ }
506
+ }
507
+
508
+ // Extract requirements from Must Have section
509
+ const mustHaveSection = content.match(/### Must Have.*?\n([\s\S]*?)(?=###|##|$)/i);
510
+ if (mustHaveSection?.[1]) {
511
+ const reqMatches = mustHaveSection[1].matchAll(/^\s*-\s*(.+)$/gm);
512
+ for (const match of reqMatches) {
513
+ const text = match[1]?.trim();
514
+ if (text && text.length > 5 && !text.startsWith('[')) {
515
+ tasks.push({ text, type: 'requirement', priority: 1 });
516
+ }
517
+ }
518
+ }
519
+
520
+ // Extract requirements from Should Have section
521
+ const shouldHaveSection = content.match(/### Should Have.*?\n([\s\S]*?)(?=###|##|$)/i);
522
+ if (shouldHaveSection?.[1]) {
523
+ const reqMatches = shouldHaveSection[1].matchAll(/^\s*-\s*(.+)$/gm);
524
+ for (const match of reqMatches) {
525
+ const text = match[1]?.trim();
526
+ if (text && text.length > 5 && !text.startsWith('[')) {
527
+ tasks.push({ text, type: 'requirement', priority: 2 });
528
+ }
529
+ }
530
+ }
531
+
532
+ if (tasks.length === 0) {
533
+ utils.print.warning('No actionable items found in PRD');
534
+ utils.print.dim('Add user stories with "- [ ] ..." or requirements with "- ..."');
535
+ return;
536
+ }
537
+
538
+ console.log(`
539
+ ${utils.COLORS.bold}Found ${tasks.length} tasks:${utils.COLORS.reset}
540
+ `);
541
+
542
+ // Load todo module
543
+ const todoModule = require('./todo') as TodoModule;
544
+
545
+ let added = 0;
546
+ for (const task of tasks.slice(0, 20)) {
547
+ const prefix = task.type === 'story' ? '[Story]' : '[Req]';
548
+ const priority = task.priority === 1 ? 'P0' : 'P1';
549
+ const todoText = `${prefix} ${priority}: ${task.text.slice(0, 80)}`;
550
+
551
+ console.log(` ${utils.COLORS.green}+${utils.COLORS.reset} ${todoText}`);
552
+
553
+ try {
554
+ todoModule.add({ text: todoText, priority: task.priority });
555
+ added++;
556
+ } catch {
557
+ // Skip if todo fails
558
+ }
559
+ }
560
+
561
+ if (tasks.length > 20) {
562
+ console.log(` ${utils.COLORS.dim}... and ${tasks.length - 20} more (run with --all to add all)${utils.COLORS.reset}`);
563
+ }
564
+
565
+ console.log(`
566
+ ${utils.COLORS.green}${utils.COLORS.bold}Added ${added} tasks${utils.COLORS.reset}
567
+
568
+ Run ${utils.COLORS.cyan}bootspring todo list${utils.COLORS.reset} to see all tasks
569
+ `);
570
+ }
571
+
572
+ /**
573
+ * Show PRD status
574
+ */
575
+ function prdStatus(_args: ParsedArgs): void {
576
+ const projectRoot = config.findProjectRoot();
577
+ const prdDir = path.join(projectRoot, '.bootspring', 'generated', 'prd');
578
+ const inputPrdDir = path.join(projectRoot, '.bootspring', 'inputs', 'prd');
579
+
580
+ console.log(`
581
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Bootspring PRD Status${utils.COLORS.reset}
582
+ `);
583
+
584
+ const stats = {
585
+ generated: 0,
586
+ input: 0,
587
+ draft: 0,
588
+ approved: 0,
589
+ totalFeatures: 0,
590
+ totalStories: 0
591
+ };
592
+
593
+ // Count generated PRDs
594
+ if (fs.existsSync(prdDir)) {
595
+ const files = fs.readdirSync(prdDir).filter(f => f.endsWith('.md'));
596
+ stats.generated = files.length;
597
+
598
+ for (const file of files) {
599
+ const content = fs.readFileSync(path.join(prdDir, file), 'utf-8');
600
+ if (content.includes('Status:** Draft')) stats.draft++;
601
+ if (content.includes('Status:** Approved')) stats.approved++;
602
+
603
+ // Count user stories
604
+ const storyCount = (content.match(/-\s*\[\s*\]/g) || []).length;
605
+ stats.totalStories += storyCount;
606
+ }
607
+ }
608
+
609
+ // Count input PRDs
610
+ if (fs.existsSync(inputPrdDir)) {
611
+ const files = fs.readdirSync(inputPrdDir).filter(f =>
612
+ (f.endsWith('.md') || f.endsWith('.txt')) && f !== 'README.md'
613
+ );
614
+ stats.input = files.length;
615
+ }
616
+
617
+ console.log(`${utils.COLORS.bold}Summary:${utils.COLORS.reset}`);
618
+ console.log(` Generated PRDs: ${utils.COLORS.cyan}${stats.generated}${utils.COLORS.reset}`);
619
+ console.log(` Input PRDs: ${utils.COLORS.cyan}${stats.input}${utils.COLORS.reset}`);
620
+ console.log(` Draft: ${stats.draft}`);
621
+ console.log(` Approved: ${stats.approved}`);
622
+ console.log(` Total User Stories: ${stats.totalStories}`);
623
+
624
+ const hasStructure = fs.existsSync(path.join(projectRoot, '.bootspring', 'inputs', 'prd'));
625
+
626
+ console.log(`
627
+ ${utils.COLORS.bold}Status:${utils.COLORS.reset}`);
628
+ console.log(` ${hasStructure ? utils.COLORS.green + '✓' : utils.COLORS.yellow + '○'}${utils.COLORS.reset} Input folder exists`);
629
+ console.log(` ${stats.generated > 0 ? utils.COLORS.green + '✓' : utils.COLORS.yellow + '○'}${utils.COLORS.reset} PRDs created`);
630
+
631
+ console.log(`
632
+ ${utils.COLORS.bold}Commands:${utils.COLORS.reset}
633
+ bootspring prd create <name> ${utils.COLORS.dim}# Create new PRD${utils.COLORS.reset}
634
+ bootspring prd list ${utils.COLORS.dim}# List all PRDs${utils.COLORS.reset}
635
+ bootspring prd show <name> ${utils.COLORS.dim}# View a PRD${utils.COLORS.reset}
636
+ bootspring prd import ${utils.COLORS.dim}# Import from inputs${utils.COLORS.reset}
637
+ bootspring prd to-tasks ${utils.COLORS.dim}# Convert to todos${utils.COLORS.reset}
638
+ `);
639
+ }
640
+
641
+ /**
642
+ * Show help
643
+ */
644
+ function showHelp(): void {
645
+ console.log(`
646
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Bootspring PRD${utils.COLORS.reset}
647
+ ${utils.COLORS.dim}Product Requirements Document management${utils.COLORS.reset}
648
+
649
+ ${utils.COLORS.bold}Usage:${utils.COLORS.reset}
650
+ bootspring prd <command> [options]
651
+
652
+ ${utils.COLORS.bold}Commands:${utils.COLORS.reset}
653
+ ${utils.COLORS.cyan}create${utils.COLORS.reset} Create a new PRD
654
+ ${utils.COLORS.cyan}list${utils.COLORS.reset} List all PRDs
655
+ ${utils.COLORS.cyan}show${utils.COLORS.reset} Show a specific PRD
656
+ ${utils.COLORS.cyan}import${utils.COLORS.reset} Import PRDs from .bootspring/inputs/prd
657
+ ${utils.COLORS.cyan}to-tasks${utils.COLORS.reset} Convert PRD to todo items
658
+ ${utils.COLORS.cyan}status${utils.COLORS.reset} Show PRD system status (default)
659
+
660
+ ${utils.COLORS.bold}Create Options:${utils.COLORS.reset}
661
+ --title="..." PRD title
662
+ --description="..." PRD description
663
+ --force Overwrite existing PRD
664
+
665
+ ${utils.COLORS.bold}Examples:${utils.COLORS.reset}
666
+ bootspring prd create auth --title="User Authentication"
667
+ bootspring prd list
668
+ bootspring prd show auth
669
+ bootspring prd to-tasks auth
670
+ `);
671
+ }