@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
@@ -0,0 +1,1224 @@
1
+ /**
2
+ * Bootspring Seed Command
3
+ * Project scaffolding and configuration management
4
+ *
5
+ * Commands:
6
+ * setup Create .bootspring/inputs folder structure
7
+ * init Full Q&A, generate SEED.md
8
+ * synthesize Create SEED.md from preseed documents
9
+ * generate Ingest files and generate documents
10
+ * scaffold Generate project structure from SEED.md
11
+ * build Build from seed docs (--loop for continuous)
12
+ * update Re-run questionnaire, update SEED.md
13
+ * status Show current seed configuration
14
+ * export Export seed config as JSON/YAML
15
+ *
16
+ * @package bootspring
17
+ * @command seed
18
+ */
19
+
20
+ import * as path from 'path';
21
+ import * as fs from 'fs';
22
+ import * as readline from 'readline';
23
+ import type { ParsedArgs } from './types';
24
+
25
+ // Module interfaces
26
+ interface Utils {
27
+ parseArgs: (args: string[]) => ParsedArgs;
28
+ COLORS: {
29
+ cyan: string;
30
+ bold: string;
31
+ reset: string;
32
+ dim: string;
33
+ green: string;
34
+ yellow: string;
35
+ red: string;
36
+ };
37
+ createSpinner: (text: string) => Spinner;
38
+ print: {
39
+ dim: (msg: string) => void;
40
+ warning: (msg: string) => void;
41
+ error: (msg: string) => void;
42
+ success: (msg: string) => void;
43
+ info: (msg: string) => void;
44
+ debug: (msg: string) => void;
45
+ };
46
+ fileExists: (path: string) => boolean;
47
+ writeFile: (path: string, content: string) => boolean;
48
+ formatRelativeTime: (date: Date) => string;
49
+ }
50
+
51
+ interface Spinner {
52
+ start: () => Spinner;
53
+ succeed: (text?: string) => Spinner;
54
+ fail: (text?: string) => Spinner;
55
+ warn: (text?: string) => Spinner;
56
+ }
57
+
58
+ interface Config {
59
+ findProjectRoot: () => string;
60
+ load: () => ProjectConfig | null;
61
+ save: (config: ProjectConfig, path: string) => boolean;
62
+ }
63
+
64
+ interface ProjectConfig {
65
+ _preset?: string | undefined;
66
+ project?: {
67
+ name?: string | undefined;
68
+ description?: string | undefined;
69
+ version?: string | undefined;
70
+ status?: string | undefined;
71
+ } | undefined;
72
+ stack?: {
73
+ framework?: string | undefined;
74
+ language?: string | undefined;
75
+ database?: string | undefined;
76
+ hosting?: string | undefined;
77
+ } | undefined;
78
+ frontend?: {
79
+ uiLibrary?: string | undefined;
80
+ styling?: string | undefined;
81
+ } | undefined;
82
+ backend?: {
83
+ orm?: string | undefined;
84
+ auth?: string | undefined;
85
+ } | undefined;
86
+ }
87
+
88
+ interface Scaffold {
89
+ getPresets: () => string[];
90
+ getPresetConfig: (preset: string) => ProjectConfig;
91
+ getPresetInfo: (preset: string) => { description: string } | null;
92
+ planScaffold: (config: ProjectConfig, projectRoot: string) => ScaffoldPlan;
93
+ execute: (plan: ScaffoldPlan, projectRoot: string) => Promise<ScaffoldResult>;
94
+ }
95
+
96
+ interface ScaffoldPlan {
97
+ preset?: string | undefined;
98
+ directories: string[];
99
+ files: { path: string; content?: string }[];
100
+ }
101
+
102
+ interface ScaffoldResult {
103
+ filesCreated: number;
104
+ dirsCreated: number;
105
+ warnings: string[];
106
+ }
107
+
108
+ interface SeedTemplate {
109
+ generate: (config: ProjectConfig) => string;
110
+ }
111
+
112
+ interface ProjectState {
113
+ PROJECT_TYPES: { DEVELOPMENT: string };
114
+ setProjectType: (root: string, type: string, meta: Record<string, unknown>) => void;
115
+ }
116
+
117
+ interface CheckpointEngine {
118
+ syncCheckpoints: (root: string, options: { verbose: boolean }) => void;
119
+ }
120
+
121
+ interface TierEnforcement {
122
+ requireSeedAccess: (command: string) => void;
123
+ }
124
+
125
+ interface Questionnaire {
126
+ runQuestionnaire: (preset: string) => Promise<ProjectConfig>;
127
+ }
128
+
129
+ interface TechStack {
130
+ framework?: string | undefined;
131
+ language?: string | undefined;
132
+ database?: string | undefined;
133
+ hosting?: string | undefined;
134
+ uiLibrary?: string | undefined;
135
+ styling?: string | undefined;
136
+ orm?: string | undefined;
137
+ auth?: string | undefined;
138
+ }
139
+
140
+ interface AIContextResults {
141
+ claudeMd?: boolean | undefined;
142
+ agentsMd?: boolean | undefined;
143
+ planningAgentsMd?: boolean | undefined;
144
+ }
145
+
146
+ interface SeedBuilders {
147
+ generateAIContextFiles: (root: string, config: ProjectConfig) => AIContextResults;
148
+ generateSeedFromPreseed: (docs: Record<string, string>, config: Record<string, unknown>) => string;
149
+ }
150
+
151
+ interface SeedFolders {
152
+ createSeedFolders: (root: string) => number;
153
+ }
154
+
155
+ interface SeedExtractors {
156
+ extractTechStack: (docs: Record<string, string>) => TechStack;
157
+ extractProjectName: (docs: Record<string, string>) => string | null;
158
+ extractTagline: (docs: Record<string, string>) => string | null;
159
+ }
160
+
161
+ interface IngestModule {
162
+ ingestAll: (root: string) => Promise<IngestedData>;
163
+ generateDocuments: (ingested: IngestedData, config: ProjectConfig, root: string) => Promise<Record<string, string>>;
164
+ updateContextIndex: (root: string, ingested: IngestedData) => Promise<void>;
165
+ }
166
+
167
+ interface IngestedData {
168
+ mvp?: { files?: unknown[] } | undefined;
169
+ business?: unknown[] | undefined;
170
+ prd?: unknown[] | undefined;
171
+ designs?: { files?: unknown[] } | undefined;
172
+ api?: unknown[] | undefined;
173
+ data?: unknown[] | undefined;
174
+ }
175
+
176
+ interface TierError extends Error {
177
+ code?: string | undefined;
178
+ upgradePrompt?: string | undefined;
179
+ }
180
+
181
+ // Lazy-loaded modules
182
+ let _utils: Utils | null = null;
183
+ let _config: Config | null = null;
184
+ let _scaffold: Scaffold | null = null;
185
+ let _seedTemplate: SeedTemplate | null = null;
186
+ let _projectState: ProjectState | null = null;
187
+ let _checkpointEngine: CheckpointEngine | null = null;
188
+ let _tierEnforcement: TierEnforcement | null = null;
189
+ let _questionnaire: Questionnaire | null = null;
190
+ let _ingest: IngestModule | null = null;
191
+ let _yaml: { stringify: (obj: unknown) => string; parse: (str: string) => unknown } | null = null;
192
+
193
+ function getUtils(): Utils {
194
+ if (!_utils) {
195
+ _utils = require('../core/utils') as Utils;
196
+ }
197
+ return _utils;
198
+ }
199
+
200
+ function getConfig(): Config {
201
+ if (!_config) {
202
+ _config = require('../core/config') as Config;
203
+ }
204
+ return _config;
205
+ }
206
+
207
+ function getScaffold(): Scaffold {
208
+ if (!_scaffold) {
209
+ _scaffold = require('../core/scaffold') as Scaffold;
210
+ }
211
+ return _scaffold;
212
+ }
213
+
214
+ function getSeedTemplate(): SeedTemplate {
215
+ if (!_seedTemplate) {
216
+ _seedTemplate = require('../generators/templates/seed.template') as SeedTemplate;
217
+ }
218
+ return _seedTemplate;
219
+ }
220
+
221
+ function getProjectState(): ProjectState {
222
+ if (!_projectState) {
223
+ _projectState = require('../core/project-state') as ProjectState;
224
+ }
225
+ return _projectState;
226
+ }
227
+
228
+ function getCheckpointEngine(): CheckpointEngine {
229
+ if (!_checkpointEngine) {
230
+ _checkpointEngine = require('../core/checkpoint-engine') as CheckpointEngine;
231
+ }
232
+ return _checkpointEngine;
233
+ }
234
+
235
+ function getTierEnforcement(): TierEnforcement {
236
+ if (!_tierEnforcement) {
237
+ _tierEnforcement = require('../core/tier-enforcement') as TierEnforcement;
238
+ }
239
+ return _tierEnforcement;
240
+ }
241
+
242
+ function getQuestionnaire(): Questionnaire {
243
+ if (!_questionnaire) {
244
+ _questionnaire = require('../generators/questionnaire') as Questionnaire;
245
+ }
246
+ return _questionnaire;
247
+ }
248
+
249
+ function getIngest(): IngestModule {
250
+ if (!_ingest) {
251
+ _ingest = require('../core/ingest') as IngestModule;
252
+ }
253
+ return _ingest;
254
+ }
255
+
256
+ function getYaml(): { stringify: (obj: unknown) => string; parse: (str: string) => unknown } {
257
+ if (!_yaml) {
258
+ _yaml = require('yaml') as { stringify: (obj: unknown) => string; parse: (str: string) => unknown };
259
+ }
260
+ return _yaml;
261
+ }
262
+
263
+ // Import extracted modules
264
+ function getSeedBuilders(): SeedBuilders {
265
+ const aiContextBuilder = require('./seed/builders/ai-context-builder') as { generateAIContextFiles: SeedBuilders['generateAIContextFiles'] };
266
+ const seedBuilder = require('./seed/builders/seed-builder') as { generateSeedFromPreseed: SeedBuilders['generateSeedFromPreseed'] };
267
+ return {
268
+ generateAIContextFiles: aiContextBuilder.generateAIContextFiles,
269
+ generateSeedFromPreseed: seedBuilder.generateSeedFromPreseed
270
+ };
271
+ }
272
+
273
+ function getSeedFolders(): SeedFolders {
274
+ return require('./seed/utils/folder-structure') as SeedFolders;
275
+ }
276
+
277
+ function getSeedExtractors(): SeedExtractors {
278
+ return require('./seed/extractors') as SeedExtractors;
279
+ }
280
+
281
+ /**
282
+ * Run seed command
283
+ * @param args - Command arguments
284
+ */
285
+ export async function run(args: string[]): Promise<void> {
286
+ const utils = getUtils();
287
+ const tierEnforcement = getTierEnforcement();
288
+
289
+ const parsedArgs = utils.parseArgs(args);
290
+ const subcommand = (parsedArgs._ as string[])[0] || 'status';
291
+
292
+ // Check tier access for paid commands
293
+ const paidCommands = ['scaffold', 'synthesize', 'from-preseed', 'generate', 'build'];
294
+ if (paidCommands.includes(subcommand)) {
295
+ // Map aliases to actual command names for tier check
296
+ const commandMap: Record<string, string> = {
297
+ 'from-preseed': 'synthesize',
298
+ };
299
+ const actualCommand = commandMap[subcommand] || subcommand;
300
+
301
+ try {
302
+ tierEnforcement.requireSeedAccess(actualCommand);
303
+ } catch (error) {
304
+ const tierError = error as TierError;
305
+ if (tierError.code === 'TIER_REQUIRED') {
306
+ console.log(tierError.upgradePrompt);
307
+ return;
308
+ }
309
+ throw error;
310
+ }
311
+ }
312
+
313
+ switch (subcommand) {
314
+ case 'setup':
315
+ return seedSetup(parsedArgs);
316
+ case 'init':
317
+ return seedInit(parsedArgs);
318
+ case 'generate':
319
+ return seedGenerate(parsedArgs);
320
+ case 'scaffold':
321
+ return seedScaffold(parsedArgs);
322
+ case 'build':
323
+ return seedBuild(parsedArgs);
324
+ case 'update':
325
+ return seedUpdate(parsedArgs);
326
+ case 'status':
327
+ return seedStatus(parsedArgs);
328
+ case 'export':
329
+ return seedExport(parsedArgs);
330
+ case 'synthesize':
331
+ case 'from-preseed':
332
+ return seedSynthesize(parsedArgs);
333
+ case 'help':
334
+ case '-h':
335
+ case '--help':
336
+ return showHelp();
337
+ default:
338
+ utils.print.error(`Unknown subcommand: ${subcommand}`);
339
+ showHelp();
340
+ }
341
+ }
342
+
343
+ /**
344
+ * Setup .bootspring/inputs folder structure
345
+ */
346
+ async function seedSetup(_args: ParsedArgs): Promise<void> {
347
+ const utils = getUtils();
348
+ const config = getConfig();
349
+ const { createSeedFolders } = getSeedFolders();
350
+
351
+ const projectRoot = config.findProjectRoot();
352
+
353
+ console.log(`
354
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Bootspring Seed Setup${utils.COLORS.reset}
355
+ ${utils.COLORS.dim}Creating input folder structure${utils.COLORS.reset}
356
+ `);
357
+
358
+ const spinner = utils.createSpinner('Creating folder structure...').start();
359
+ const dirsCreated = createSeedFolders(projectRoot);
360
+ spinner.succeed(`Created ${dirsCreated} folders`);
361
+
362
+ console.log(`
363
+ ${utils.COLORS.green}${utils.COLORS.bold}Setup complete!${utils.COLORS.reset}
364
+
365
+ ${utils.COLORS.bold}Folder structure:${utils.COLORS.reset}
366
+ .bootspring/
367
+ ├── inputs/ ${utils.COLORS.dim}# Your files go here${utils.COLORS.reset}
368
+ │ ├── mvp/source/ ${utils.COLORS.dim}# MVP code from Lovable, Bolt, V0${utils.COLORS.reset}
369
+ │ ├── business/ ${utils.COLORS.dim}# Business plans, pitch decks${utils.COLORS.reset}
370
+ │ ├── prd/ ${utils.COLORS.dim}# Product requirements${utils.COLORS.reset}
371
+ │ ├── designs/ ${utils.COLORS.dim}# Figma exports, wireframes${utils.COLORS.reset}
372
+ │ ├── legal/ ${utils.COLORS.dim}# Terms, privacy policies${utils.COLORS.reset}
373
+ │ ├── api/ ${utils.COLORS.dim}# OpenAPI specs${utils.COLORS.reset}
374
+ │ └── data/ ${utils.COLORS.dim}# Sample data, schemas${utils.COLORS.reset}
375
+ ├── generated/ ${utils.COLORS.dim}# Bootspring output${utils.COLORS.reset}
376
+ ├── context/ ${utils.COLORS.dim}# AI context index${utils.COLORS.reset}
377
+ └── logs/ ${utils.COLORS.dim}# Action history${utils.COLORS.reset}
378
+
379
+ ${utils.COLORS.bold}Next steps:${utils.COLORS.reset}
380
+ 1. Drop your files in ${utils.COLORS.cyan}.bootspring/inputs/${utils.COLORS.reset} subfolders
381
+ 2. Run ${utils.COLORS.cyan}bootspring seed generate${utils.COLORS.reset} to process them
382
+ `);
383
+ }
384
+
385
+ /**
386
+ * Generate documents from input files
387
+ */
388
+ async function seedGenerate(_args: ParsedArgs): Promise<void> {
389
+ const utils = getUtils();
390
+ const config = getConfig();
391
+
392
+ const projectRoot = config.findProjectRoot();
393
+ const inputsDir = path.join(projectRoot, '.bootspring', 'inputs');
394
+ const generatedDir = path.join(projectRoot, '.bootspring', 'generated');
395
+
396
+ console.log(`
397
+ ${utils.COLORS.cyan}${utils.COLORS.bold}Bootspring Seed Generate${utils.COLORS.reset}
398
+ ${utils.COLORS.dim}Processing input files and generating documents${utils.COLORS.reset}
399
+ `);
400
+
401
+ // Check if inputs folder exists
402
+ if (!fs.existsSync(inputsDir)) {
403
+ utils.print.warning('.bootspring/inputs folder not found');
404
+ utils.print.dim('Run "bootspring seed setup" first');
405
+ return;
406
+ }
407
+
408
+ // Ensure generated folder exists
409
+ if (!fs.existsSync(generatedDir)) {
410
+ fs.mkdirSync(generatedDir, { recursive: true });
411
+ }
412
+
413
+ try {
414
+ const Ingest = getIngest();
415
+ const spinner = utils.createSpinner('Analyzing input files...').start();
416
+
417
+ // Ingest all input files
418
+ const ingested = await Ingest.ingestAll(projectRoot);
419
+
420
+ spinner.succeed('Analyzed input files');
421
+
422
+ // Show what was found
423
+ console.log(`
424
+ ${utils.COLORS.bold}Files Found:${utils.COLORS.reset}`);
425
+
426
+ const counts = {
427
+ mvp: ingested.mvp?.files?.length || 0,
428
+ business: ingested.business?.length || 0,
429
+ prd: ingested.prd?.length || 0,
430
+ designs: ingested.designs?.files?.length || 0,
431
+ api: ingested.api?.length || 0,
432
+ data: ingested.data?.length || 0
433
+ };
434
+
435
+ console.log(` MVP code: ${counts.mvp > 0 ? utils.COLORS.green + counts.mvp + ' files' : utils.COLORS.dim + 'none'}${utils.COLORS.reset}`);
436
+ console.log(` Business docs: ${counts.business > 0 ? utils.COLORS.green + counts.business + ' files' : utils.COLORS.dim + 'none'}${utils.COLORS.reset}`);
437
+ console.log(` PRD docs: ${counts.prd > 0 ? utils.COLORS.green + counts.prd + ' files' : utils.COLORS.dim + 'none'}${utils.COLORS.reset}`);
438
+ console.log(` Design files: ${counts.designs > 0 ? utils.COLORS.green + counts.designs + ' files' : utils.COLORS.dim + 'none'}${utils.COLORS.reset}`);
439
+ console.log(` API specs: ${counts.api > 0 ? utils.COLORS.green + counts.api + ' files' : utils.COLORS.dim + 'none'}${utils.COLORS.reset}`);
440
+ console.log(` Data files: ${counts.data > 0 ? utils.COLORS.green + counts.data + ' files' : utils.COLORS.dim + 'none'}${utils.COLORS.reset}`);
441
+
442
+ // Load SEED.md config if exists
443
+ const seedPath = path.join(projectRoot, 'SEED.md');
444
+ let seedConfig: ProjectConfig = {};
445
+ if (fs.existsSync(seedPath)) {
446
+ seedConfig = parseSeedFile(seedPath) || {};
447
+ }
448
+
449
+ // Generate documents
450
+ console.log(`
451
+ ${utils.COLORS.bold}Generating documents...${utils.COLORS.reset}`);
452
+
453
+ const generateSpinner = utils.createSpinner('Generating...').start();
454
+ const outputs = await Ingest.generateDocuments(ingested, seedConfig, projectRoot);
455
+ generateSpinner.succeed('Generated documents');
456
+
457
+ // Show what was generated
458
+ let generatedCount = 0;
459
+ for (const [name, content] of Object.entries(outputs)) {
460
+ if (content) {
461
+ generatedCount++;
462
+ console.log(` ${utils.COLORS.green}+${utils.COLORS.reset} ${name}`);
463
+ }
464
+ }
465
+
466
+ // Update context index
467
+ const contextSpinner = utils.createSpinner('Updating context index...').start();
468
+ await Ingest.updateContextIndex(projectRoot, ingested);
469
+ contextSpinner.succeed('Updated context index');
470
+
471
+ console.log(`
472
+ ${utils.COLORS.green}${utils.COLORS.bold}Generation complete!${utils.COLORS.reset}
473
+
474
+ Generated ${generatedCount} documents in ${utils.COLORS.cyan}.bootspring/generated/${utils.COLORS.reset}
475
+
476
+ ${utils.COLORS.bold}Next steps:${utils.COLORS.reset}
477
+ 1. Review generated documents
478
+ 2. Run ${utils.COLORS.cyan}bootspring seed scaffold${utils.COLORS.reset} to create project structure
479
+ 3. Your CLAUDE.md is updated with context
480
+ `);
481
+ } catch (error) {
482
+ utils.print.error(`Generation failed: ${(error as Error).message}`);
483
+ if (process.env.DEBUG) {
484
+ console.error((error as Error).stack);
485
+ }
486
+ }
487
+ }
488
+
489
+ /**
490
+ * Initialize seed - run questionnaire and generate SEED.md
491
+ */
492
+ async function seedInit(args: ParsedArgs): Promise<void> {
493
+ const utils = getUtils();
494
+ const config = getConfig();
495
+ const seedTemplate = getSeedTemplate();
496
+ const projectState = getProjectState();
497
+ const checkpointEngine = getCheckpointEngine();
498
+ const { runQuestionnaire } = getQuestionnaire();
499
+ const { createSeedFolders } = getSeedFolders();
500
+ const { generateAIContextFiles } = getSeedBuilders();
501
+
502
+ const projectRoot = config.findProjectRoot();
503
+ const seedPath = path.join(projectRoot, 'SEED.md');
504
+ const force = args.force || args.f;
505
+
506
+ console.log(`
507
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Seed Init${utils.COLORS.reset}
508
+ ${utils.COLORS.dim}Interactive project configuration${utils.COLORS.reset}
509
+ `);
510
+
511
+ // Auto-create folder structure
512
+ const setupSpinner = utils.createSpinner('Setting up folder structure...').start();
513
+ createSeedFolders(projectRoot);
514
+ setupSpinner.succeed('Folder structure ready');
515
+
516
+ // Check if SEED.md already exists
517
+ if (utils.fileExists(seedPath) && !force) {
518
+ utils.print.warning('SEED.md already exists');
519
+ utils.print.dim('Use --force to regenerate, or "bootspring seed update" to modify');
520
+ return;
521
+ }
522
+
523
+ // Determine preset
524
+ const preset = (args.preset as string) || 'standard';
525
+ const validPresets = ['minimal', 'standard', 'full', 'startup', 'api'];
526
+
527
+ if (!validPresets.includes(preset)) {
528
+ utils.print.error(`Invalid preset: ${preset}`);
529
+ utils.print.dim(`Valid presets: ${validPresets.join(', ')}`);
530
+ return;
531
+ }
532
+
533
+ try {
534
+ // Run questionnaire
535
+ const projectConfig = await runQuestionnaire(preset);
536
+ projectConfig._preset = preset;
537
+
538
+ // Generate SEED.md
539
+ const seedContent = seedTemplate.generate(projectConfig);
540
+ const spinner = utils.createSpinner('Generating SEED.md').start();
541
+
542
+ if (utils.writeFile(seedPath, seedContent)) {
543
+ spinner.succeed('Created SEED.md');
544
+ } else {
545
+ spinner.fail('Failed to create SEED.md');
546
+ return;
547
+ }
548
+
549
+ // Also save config if not exists
550
+ const configPath = path.join(projectRoot, 'bootspring.config.js');
551
+ if (!utils.fileExists(configPath)) {
552
+ const configSpinner = utils.createSpinner('Creating bootspring.config.js').start();
553
+ if (config.save(projectConfig, configPath)) {
554
+ configSpinner.succeed('Created bootspring.config.js');
555
+ } else {
556
+ configSpinner.warn('Could not create bootspring.config.js');
557
+ }
558
+ }
559
+
560
+ // Auto-tag project as development type
561
+ try {
562
+ projectState.setProjectType(projectRoot, projectState.PROJECT_TYPES.DEVELOPMENT, {
563
+ autoTagged: true,
564
+ taggedBy: 'seed'
565
+ });
566
+ checkpointEngine.syncCheckpoints(projectRoot, { verbose: false });
567
+ utils.print.success('Project tagged as development type');
568
+ } catch (err) {
569
+ utils.print.debug(`Auto-tagging failed: ${(err as Error).message}`);
570
+ }
571
+
572
+ // Generate AI context files
573
+ const aiSpinner = utils.createSpinner('Generating AI context files...').start();
574
+ const aiResults = generateAIContextFiles(projectRoot, projectConfig);
575
+ const aiFilesGenerated: string[] = [];
576
+ if (aiResults.claudeMd) aiFilesGenerated.push('CLAUDE.md');
577
+ if (aiResults.agentsMd) aiFilesGenerated.push('AGENTS.md');
578
+ if (aiFilesGenerated.length > 0) {
579
+ aiSpinner.succeed(`Generated ${aiFilesGenerated.join(', ')}`);
580
+ } else {
581
+ aiSpinner.warn('Could not generate AI context files');
582
+ }
583
+
584
+ console.log(`
585
+ ${utils.COLORS.green}${utils.COLORS.bold}✓ Seed initialized successfully!${utils.COLORS.reset}
586
+
587
+ ${utils.COLORS.bold}Generated Files:${utils.COLORS.reset}
588
+ ${utils.COLORS.cyan}SEED.md${utils.COLORS.reset} - Project specification
589
+ ${utils.COLORS.cyan}bootspring.config.js${utils.COLORS.reset} - Configuration
590
+ ${utils.COLORS.cyan}CLAUDE.md${utils.COLORS.reset} - Claude Code context
591
+ ${utils.COLORS.cyan}AGENTS.md${utils.COLORS.reset} - Codex/Cursor/Amp context
592
+
593
+ ${utils.COLORS.bold}Next steps:${utils.COLORS.reset}
594
+ 1. Review ${utils.COLORS.cyan}SEED.md${utils.COLORS.reset} for your project configuration
595
+ 2. Run ${utils.COLORS.cyan}bootspring seed scaffold${utils.COLORS.reset} to generate project structure
596
+ 3. Or run ${utils.COLORS.cyan}bootspring seed scaffold --preset=nextjs${utils.COLORS.reset} for a preset
597
+ `);
598
+ } catch (error) {
599
+ utils.print.error(`Seed init failed: ${(error as Error).message}`);
600
+ }
601
+ }
602
+
603
+ /**
604
+ * Scaffold project from SEED.md or preset
605
+ */
606
+ async function seedScaffold(args: ParsedArgs): Promise<void> {
607
+ const utils = getUtils();
608
+ const config = getConfig();
609
+ const scaffold = getScaffold();
610
+ const { generateAIContextFiles } = getSeedBuilders();
611
+
612
+ const projectRoot = config.findProjectRoot();
613
+ const preset = (args.preset || args.p) as string | undefined;
614
+ const fromConfig = args['from-config'] || args.c;
615
+ const dryRun = args['dry-run'] || args.d;
616
+ const skipConfirm = args.yes || args.y;
617
+
618
+ console.log(`
619
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Scaffold${utils.COLORS.reset}
620
+ ${utils.COLORS.dim}Generate project structure${utils.COLORS.reset}
621
+ `);
622
+
623
+ let scaffoldConfig: ProjectConfig;
624
+ let detectedPreset: string | null = null;
625
+
626
+ if (preset) {
627
+ // Use preset
628
+ const validPresets = scaffold.getPresets();
629
+ if (!validPresets.includes(preset)) {
630
+ utils.print.error(`Invalid preset: ${preset}`);
631
+ utils.print.dim(`Valid presets: ${validPresets.join(', ')}`);
632
+ return;
633
+ }
634
+
635
+ utils.print.info(`Using preset: ${preset}`);
636
+ scaffoldConfig = scaffold.getPresetConfig(preset);
637
+ detectedPreset = preset;
638
+ } else if (fromConfig) {
639
+ // Load from bootspring.config.js
640
+ const cfg = config.load();
641
+ if (!cfg) {
642
+ utils.print.error('No bootspring.config.js found');
643
+ utils.print.dim('Run "bootspring seed init" first');
644
+ return;
645
+ }
646
+ scaffoldConfig = cfg;
647
+ } else {
648
+ // Try to load from SEED.md
649
+ const seedPath = path.join(projectRoot, 'SEED.md');
650
+ if (!utils.fileExists(seedPath)) {
651
+ utils.print.warning('No SEED.md found');
652
+ utils.print.info('Choose a preset or run "bootspring seed init" first');
653
+
654
+ console.log(`
655
+ ${utils.COLORS.bold}Available presets:${utils.COLORS.reset}
656
+ `);
657
+ const presets = scaffold.getPresets();
658
+ for (const p of presets) {
659
+ const info = scaffold.getPresetInfo(p);
660
+ console.log(` ${utils.COLORS.cyan}${p}${utils.COLORS.reset} - ${info?.description || ''}`);
661
+ }
662
+ console.log(`
663
+ ${utils.COLORS.dim}Example: bootspring seed scaffold --preset=nextjs${utils.COLORS.reset}
664
+ `);
665
+ return;
666
+ }
667
+
668
+ // Parse SEED.md
669
+ const parsed = parseSeedFile(seedPath);
670
+ if (!parsed) {
671
+ utils.print.error('Could not parse SEED.md');
672
+ return;
673
+ }
674
+ scaffoldConfig = parsed;
675
+ }
676
+
677
+ // Show what will be created
678
+ const plan = scaffold.planScaffold(scaffoldConfig, projectRoot);
679
+ detectedPreset = plan.preset || null;
680
+
681
+ console.log(`
682
+ ${utils.COLORS.bold}Scaffold Plan:${utils.COLORS.reset}
683
+ Framework: ${scaffoldConfig.stack?.framework || 'nextjs'}
684
+ Language: ${scaffoldConfig.stack?.language || 'typescript'}
685
+ UI: ${scaffoldConfig.frontend?.uiLibrary || 'shadcn'}
686
+ Styling: ${scaffoldConfig.frontend?.styling || 'tailwind'}
687
+ `);
688
+
689
+ console.log(`${utils.COLORS.bold}Directories to create:${utils.COLORS.reset}`);
690
+ for (const dir of plan.directories.slice(0, 10)) {
691
+ console.log(` ${utils.COLORS.dim}+${utils.COLORS.reset} ${dir}`);
692
+ }
693
+ if (plan.directories.length > 10) {
694
+ console.log(` ${utils.COLORS.dim}... and ${plan.directories.length - 10} more${utils.COLORS.reset}`);
695
+ }
696
+
697
+ console.log(`\n${utils.COLORS.bold}Files to create:${utils.COLORS.reset}`);
698
+ for (const file of plan.files.slice(0, 10)) {
699
+ console.log(` ${utils.COLORS.dim}+${utils.COLORS.reset} ${file.path}`);
700
+ }
701
+ if (plan.files.length > 10) {
702
+ console.log(` ${utils.COLORS.dim}... and ${plan.files.length - 10} more${utils.COLORS.reset}`);
703
+ }
704
+
705
+ if (dryRun) {
706
+ console.log(`
707
+ ${utils.COLORS.yellow}Dry run - no files created${utils.COLORS.reset}
708
+ `);
709
+ return;
710
+ }
711
+
712
+ // Show detected preset and ask for confirmation
713
+ const presetInfo = scaffold.getPresetInfo(detectedPreset || 'nextjs');
714
+ console.log(`
715
+ ${utils.COLORS.bold}Detected preset:${utils.COLORS.reset} ${utils.COLORS.cyan}${detectedPreset || 'nextjs'}${utils.COLORS.reset}
716
+ ${utils.COLORS.dim}${presetInfo?.description || ''}${utils.COLORS.reset}
717
+ `);
718
+
719
+ if (!skipConfirm) {
720
+ const rl = readline.createInterface({
721
+ input: process.stdin,
722
+ output: process.stdout
723
+ });
724
+
725
+ const answer = await new Promise<string>((resolve) => {
726
+ rl.question(`Proceed with ${utils.COLORS.cyan}${detectedPreset || 'nextjs'}${utils.COLORS.reset} preset? [Y/n] `, (ans) => {
727
+ rl.close();
728
+ resolve(ans.trim().toLowerCase());
729
+ });
730
+ });
731
+
732
+ if (answer === 'n' || answer === 'no') {
733
+ console.log(`
734
+ ${utils.COLORS.dim}Scaffold cancelled. To use a different preset:${utils.COLORS.reset}
735
+ bootspring seed scaffold --preset=<preset>
736
+
737
+ ${utils.COLORS.bold}Available presets:${utils.COLORS.reset}`);
738
+ const presets = scaffold.getPresets();
739
+ for (const p of presets) {
740
+ const info = scaffold.getPresetInfo(p);
741
+ console.log(` ${utils.COLORS.cyan}${p}${utils.COLORS.reset} - ${info?.description || ''}`);
742
+ }
743
+ console.log('');
744
+ return;
745
+ }
746
+ }
747
+
748
+ // Execute scaffold
749
+ console.log('');
750
+ const spinner = utils.createSpinner('Scaffolding project...').start();
751
+
752
+ try {
753
+ const result = await scaffold.execute(plan, projectRoot);
754
+ spinner.succeed(`Scaffolded ${result.filesCreated} files, ${result.dirsCreated} directories`);
755
+
756
+ if (result.warnings.length > 0) {
757
+ console.log(`\n${utils.COLORS.yellow}Warnings:${utils.COLORS.reset}`);
758
+ for (const warning of result.warnings) {
759
+ console.log(` ${utils.COLORS.dim}!${utils.COLORS.reset} ${warning}`);
760
+ }
761
+ }
762
+
763
+ // Generate AI context files (CLAUDE.md and AGENTS.md)
764
+ const aiSpinner = utils.createSpinner('Generating AI context files...').start();
765
+ const aiResults = generateAIContextFiles(projectRoot, scaffoldConfig);
766
+
767
+ const aiFilesGenerated: string[] = [];
768
+ if (aiResults.claudeMd) aiFilesGenerated.push('CLAUDE.md');
769
+ if (aiResults.agentsMd) aiFilesGenerated.push('AGENTS.md');
770
+ if (aiResults.planningAgentsMd) aiFilesGenerated.push('planning/AGENTS.md');
771
+
772
+ if (aiFilesGenerated.length > 0) {
773
+ aiSpinner.succeed(`Generated ${aiFilesGenerated.join(', ')}`);
774
+ } else {
775
+ aiSpinner.warn('Could not generate AI context files');
776
+ }
777
+
778
+ console.log(`
779
+ ${utils.COLORS.green}${utils.COLORS.bold}✓ Scaffold complete!${utils.COLORS.reset}
780
+
781
+ ${utils.COLORS.bold}Generated Files:${utils.COLORS.reset}
782
+ ${utils.COLORS.cyan}CLAUDE.md${utils.COLORS.reset} - Context for Claude Code
783
+ ${utils.COLORS.cyan}AGENTS.md${utils.COLORS.reset} - Context for Codex, Cursor, Amp, Kilo
784
+
785
+ ${utils.COLORS.bold}Next steps:${utils.COLORS.reset}
786
+ 1. Run ${utils.COLORS.cyan}npm install${utils.COLORS.reset} to install dependencies
787
+ 2. Copy ${utils.COLORS.cyan}.env.example${utils.COLORS.reset} to ${utils.COLORS.cyan}.env.local${utils.COLORS.reset}
788
+ 3. Run ${utils.COLORS.cyan}npm run dev${utils.COLORS.reset} to start development
789
+ 4. Run ${utils.COLORS.cyan}bootspring build${utils.COLORS.reset} to start building tasks
790
+ `);
791
+ } catch (error) {
792
+ spinner.fail(`Scaffold failed: ${(error as Error).message}`);
793
+ }
794
+ }
795
+
796
+ /**
797
+ * Build from seed documents - redirects to interactive build command
798
+ */
799
+ async function seedBuild(args: ParsedArgs): Promise<void> {
800
+ // Just run the interactive build command
801
+ const build = require('./build') as { run: (args: string[]) => Promise<void> };
802
+ await build.run(((args._ as string[])?.slice(1)) || []);
803
+ }
804
+
805
+ /**
806
+ * Update existing SEED.md
807
+ */
808
+ async function seedUpdate(args: ParsedArgs): Promise<void> {
809
+ const utils = getUtils();
810
+ const config = getConfig();
811
+ const seedTemplate = getSeedTemplate();
812
+ const { runQuestionnaire } = getQuestionnaire();
813
+
814
+ const projectRoot = config.findProjectRoot();
815
+ const seedPath = path.join(projectRoot, 'SEED.md');
816
+
817
+ console.log(`
818
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Seed Update${utils.COLORS.reset}
819
+ ${utils.COLORS.dim}Update project configuration${utils.COLORS.reset}
820
+ `);
821
+
822
+ if (!utils.fileExists(seedPath)) {
823
+ utils.print.warning('No SEED.md found');
824
+ utils.print.dim('Run "bootspring seed init" to create one');
825
+ return;
826
+ }
827
+
828
+ // Load existing config
829
+ const existingConfig = parseSeedFile(seedPath);
830
+ const preset = (args.preset as string) || existingConfig?._preset || 'standard';
831
+
832
+ try {
833
+ // Run questionnaire with existing values as defaults
834
+ const projectConfig = await runQuestionnaire(preset);
835
+ projectConfig._preset = preset;
836
+
837
+ // Generate updated SEED.md
838
+ const seedContent = seedTemplate.generate(projectConfig);
839
+ const spinner = utils.createSpinner('Updating SEED.md').start();
840
+
841
+ if (utils.writeFile(seedPath, seedContent)) {
842
+ spinner.succeed('Updated SEED.md');
843
+ } else {
844
+ spinner.fail('Failed to update SEED.md');
845
+ return;
846
+ }
847
+
848
+ // Update bootspring.config.js
849
+ const configPath = path.join(projectRoot, 'bootspring.config.js');
850
+ const configSpinner = utils.createSpinner('Updating bootspring.config.js').start();
851
+ if (config.save(projectConfig, configPath)) {
852
+ configSpinner.succeed('Updated bootspring.config.js');
853
+ } else {
854
+ configSpinner.warn('Could not update bootspring.config.js');
855
+ }
856
+
857
+ utils.print.success('Seed configuration updated');
858
+ } catch (error) {
859
+ utils.print.error(`Seed update failed: ${(error as Error).message}`);
860
+ }
861
+ }
862
+
863
+ /**
864
+ * Show current seed status
865
+ */
866
+ function seedStatus(_args: ParsedArgs): void {
867
+ const utils = getUtils();
868
+ const config = getConfig();
869
+ const scaffold = getScaffold();
870
+
871
+ const projectRoot = config.findProjectRoot();
872
+ const seedPath = path.join(projectRoot, 'SEED.md');
873
+ const configPath = path.join(projectRoot, 'bootspring.config.js');
874
+
875
+ console.log(`
876
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Seed Status${utils.COLORS.reset}
877
+ `);
878
+
879
+ // Check SEED.md
880
+ if (utils.fileExists(seedPath)) {
881
+ const stats = fs.statSync(seedPath);
882
+ const modified = utils.formatRelativeTime(stats.mtime);
883
+ console.log(`${utils.COLORS.green}✓${utils.COLORS.reset} SEED.md exists (modified ${modified})`);
884
+
885
+ // Parse and show summary
886
+ const seedConfig = parseSeedFile(seedPath);
887
+ if (seedConfig) {
888
+ console.log(`
889
+ ${utils.COLORS.bold}Configuration:${utils.COLORS.reset}
890
+ Project: ${seedConfig.project?.name || 'unknown'}
891
+ Framework: ${seedConfig.stack?.framework || 'unknown'}
892
+ Language: ${seedConfig.stack?.language || 'unknown'}
893
+ Database: ${seedConfig.stack?.database || 'none'}
894
+ Preset: ${seedConfig._preset || 'unknown'}
895
+ `);
896
+ }
897
+ } else {
898
+ console.log(`${utils.COLORS.yellow}○${utils.COLORS.reset} SEED.md not found`);
899
+ }
900
+
901
+ // Check bootspring.config.js
902
+ if (utils.fileExists(configPath)) {
903
+ console.log(`${utils.COLORS.green}✓${utils.COLORS.reset} bootspring.config.js exists`);
904
+ } else {
905
+ console.log(`${utils.COLORS.yellow}○${utils.COLORS.reset} bootspring.config.js not found`);
906
+ }
907
+
908
+ // Show available presets
909
+ console.log(`
910
+ ${utils.COLORS.bold}Available Presets:${utils.COLORS.reset}
911
+ `);
912
+ const presets = scaffold.getPresets();
913
+ for (const p of presets) {
914
+ const info = scaffold.getPresetInfo(p);
915
+ console.log(` ${utils.COLORS.cyan}${p}${utils.COLORS.reset} - ${info?.description || ''}`);
916
+ }
917
+
918
+ console.log(`
919
+ ${utils.COLORS.bold}Commands:${utils.COLORS.reset}
920
+ bootspring seed init ${utils.COLORS.dim}# Create SEED.md${utils.COLORS.reset}
921
+ bootspring seed scaffold ${utils.COLORS.dim}# Generate project structure${utils.COLORS.reset}
922
+ bootspring seed scaffold --preset=nextjs
923
+ `);
924
+ }
925
+
926
+ /**
927
+ * Export seed config
928
+ */
929
+ function seedExport(args: ParsedArgs): void {
930
+ const utils = getUtils();
931
+ const config = getConfig();
932
+ const yaml = getYaml();
933
+
934
+ const projectRoot = config.findProjectRoot();
935
+ const seedPath = path.join(projectRoot, 'SEED.md');
936
+ const format = (args.format || args.f || 'json') as string;
937
+ const output = (args.output || args.o) as string | undefined;
938
+
939
+ console.log(`
940
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Seed Export${utils.COLORS.reset}
941
+ `);
942
+
943
+ if (!utils.fileExists(seedPath)) {
944
+ utils.print.error('No SEED.md found');
945
+ utils.print.dim('Run "bootspring seed init" first');
946
+ return;
947
+ }
948
+
949
+ const seedConfig = parseSeedFile(seedPath);
950
+ if (!seedConfig) {
951
+ utils.print.error('Could not parse SEED.md');
952
+ return;
953
+ }
954
+
955
+ let exported: string;
956
+ if (format === 'yaml' || format === 'yml') {
957
+ exported = yaml.stringify(seedConfig);
958
+ } else {
959
+ exported = JSON.stringify(seedConfig, null, 2);
960
+ }
961
+
962
+ if (output) {
963
+ const outputPath = path.resolve(projectRoot, output);
964
+ if (utils.writeFile(outputPath, exported)) {
965
+ utils.print.success(`Exported to ${outputPath}`);
966
+ } else {
967
+ utils.print.error('Export failed');
968
+ }
969
+ } else {
970
+ console.log(exported);
971
+ }
972
+ }
973
+
974
+ /**
975
+ * Synthesize SEED.md from preseed documents
976
+ */
977
+ async function seedSynthesize(args: ParsedArgs): Promise<void> {
978
+ const utils = getUtils();
979
+ const config = getConfig();
980
+ const { generateAIContextFiles, generateSeedFromPreseed } = getSeedBuilders();
981
+ const { extractTechStack, extractProjectName, extractTagline } = getSeedExtractors();
982
+
983
+ const projectRoot = config.findProjectRoot();
984
+ const preseedDir = path.join(projectRoot, '.bootspring', 'preseed');
985
+ const seedPath = path.join(projectRoot, 'SEED.md');
986
+ const force = args.force || args.f;
987
+
988
+ console.log(`
989
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Seed Synthesize${utils.COLORS.reset}
990
+ ${utils.COLORS.dim}Create SEED.md from your preseed documents${utils.COLORS.reset}
991
+ `);
992
+
993
+ // Check for preseed documents
994
+ if (!fs.existsSync(preseedDir)) {
995
+ utils.print.error('No preseed documents found');
996
+ console.log(`\nRun ${utils.COLORS.cyan}bootspring preseed start${utils.COLORS.reset} first`);
997
+ return;
998
+ }
999
+
1000
+ // Valid preseed document names (only these should be synthesized)
1001
+ const validPreseedDocs = [
1002
+ 'VISION.md',
1003
+ 'AUDIENCE.md',
1004
+ 'MARKET.md',
1005
+ 'COMPETITORS.md',
1006
+ 'BUSINESS_MODEL.md',
1007
+ 'PRD.md',
1008
+ 'TECHNICAL_SPEC.md',
1009
+ 'ROADMAP.md'
1010
+ ];
1011
+
1012
+ // Find all preseed docs (only valid ones, exclude MERGE_INSTRUCTIONS etc)
1013
+ const preseedFiles = fs.readdirSync(preseedDir).filter(f =>
1014
+ validPreseedDocs.includes(f)
1015
+ );
1016
+
1017
+ if (preseedFiles.length === 0) {
1018
+ utils.print.error('No preseed documents found');
1019
+ console.log(`\nRun ${utils.COLORS.cyan}bootspring preseed init${utils.COLORS.reset} first`);
1020
+ return;
1021
+ }
1022
+
1023
+ console.log(`${utils.COLORS.bold}Found ${preseedFiles.length} preseed documents:${utils.COLORS.reset}`);
1024
+ for (const file of preseedFiles) {
1025
+ console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} ${file}`);
1026
+ }
1027
+
1028
+ // Check if SEED.md already exists
1029
+ if (fs.existsSync(seedPath) && !force) {
1030
+ utils.print.warning('SEED.md already exists');
1031
+ console.log(`\nUse ${utils.COLORS.cyan}--force${utils.COLORS.reset} to overwrite`);
1032
+ return;
1033
+ }
1034
+
1035
+ // Read all preseed docs
1036
+ const docs: Record<string, string> = {};
1037
+ for (const file of preseedFiles) {
1038
+ const filePath = path.join(preseedDir, file);
1039
+ docs[file.replace('.md', '')] = fs.readFileSync(filePath, 'utf-8');
1040
+ }
1041
+
1042
+ // Also try to read PRESEED_CONFIG.json
1043
+ let preseedConfig: Record<string, unknown> = {};
1044
+ const preseedConfigPath = path.join(preseedDir, 'PRESEED_CONFIG.json');
1045
+ if (fs.existsSync(preseedConfigPath)) {
1046
+ try {
1047
+ preseedConfig = JSON.parse(fs.readFileSync(preseedConfigPath, 'utf-8')) as Record<string, unknown>;
1048
+ } catch {
1049
+ // Ignore parse errors
1050
+ }
1051
+ }
1052
+
1053
+ // Generate SEED.md from preseed docs
1054
+ const spinner = utils.createSpinner('Synthesizing SEED.md...').start();
1055
+
1056
+ try {
1057
+ const seedContent = generateSeedFromPreseed(docs, preseedConfig);
1058
+ fs.writeFileSync(seedPath, seedContent);
1059
+ spinner.succeed('Created SEED.md');
1060
+
1061
+ // Extract tech stack for AI context files
1062
+ const techStack = extractTechStack(docs);
1063
+ const identity = preseedConfig.identity as { name?: string } | undefined;
1064
+ const projectName = extractProjectName(docs) || identity?.name || 'My Project';
1065
+ const tagline = extractTagline(docs) || '';
1066
+
1067
+ const synthConfig: ProjectConfig = {
1068
+ project: {
1069
+ name: projectName,
1070
+ description: tagline,
1071
+ version: '0.1.0',
1072
+ status: 'development'
1073
+ },
1074
+ stack: {
1075
+ framework: techStack.framework,
1076
+ language: techStack.language,
1077
+ database: techStack.database,
1078
+ hosting: techStack.hosting
1079
+ },
1080
+ frontend: {
1081
+ uiLibrary: techStack.uiLibrary,
1082
+ styling: techStack.styling
1083
+ },
1084
+ backend: {
1085
+ orm: techStack.orm,
1086
+ auth: techStack.auth
1087
+ }
1088
+ };
1089
+
1090
+ // Generate AI context files
1091
+ const aiSpinner = utils.createSpinner('Generating AI context files...').start();
1092
+ const aiResults = generateAIContextFiles(projectRoot, synthConfig);
1093
+ const aiFilesGenerated: string[] = [];
1094
+ if (aiResults.claudeMd) aiFilesGenerated.push('CLAUDE.md');
1095
+ if (aiResults.agentsMd) aiFilesGenerated.push('AGENTS.md');
1096
+ if (aiFilesGenerated.length > 0) {
1097
+ aiSpinner.succeed(`Generated ${aiFilesGenerated.join(', ')}`);
1098
+ } else {
1099
+ aiSpinner.warn('Could not generate AI context files');
1100
+ }
1101
+
1102
+ // Also create bootspring.config.js for CLI compatibility
1103
+ const configPath = path.join(projectRoot, 'bootspring.config.js');
1104
+ if (!fs.existsSync(configPath)) {
1105
+ const configSpinner = utils.createSpinner('Creating bootspring.config.js...').start();
1106
+ if (config.save(synthConfig, configPath)) {
1107
+ configSpinner.succeed('Created bootspring.config.js');
1108
+ } else {
1109
+ configSpinner.warn('Could not create bootspring.config.js');
1110
+ }
1111
+ }
1112
+
1113
+ console.log(`
1114
+ ${utils.COLORS.green}${utils.COLORS.bold}✓ SEED.md synthesized successfully!${utils.COLORS.reset}
1115
+
1116
+ ${utils.COLORS.bold}Extracted from:${utils.COLORS.reset}
1117
+ ${preseedFiles.map(f => ` • ${f}`).join('\n')}
1118
+
1119
+ ${utils.COLORS.bold}Generated Files:${utils.COLORS.reset}
1120
+ ${utils.COLORS.cyan}SEED.md${utils.COLORS.reset} - Project specification
1121
+ ${utils.COLORS.cyan}bootspring.config.js${utils.COLORS.reset} - Configuration
1122
+ ${utils.COLORS.cyan}CLAUDE.md${utils.COLORS.reset} - Claude Code context
1123
+ ${utils.COLORS.cyan}AGENTS.md${utils.COLORS.reset} - Codex/Cursor/Amp context
1124
+
1125
+ ${utils.COLORS.bold}Next steps:${utils.COLORS.reset}
1126
+ 1. Review ${utils.COLORS.cyan}SEED.md${utils.COLORS.reset} - your project spec is ready
1127
+ 2. Run ${utils.COLORS.cyan}bootspring seed scaffold --preset=nextjs${utils.COLORS.reset} to generate project
1128
+ 3. Or run ${utils.COLORS.cyan}bootspring build${utils.COLORS.reset} to start building tasks
1129
+ `);
1130
+ } catch (error) {
1131
+ spinner.fail(`Synthesis failed: ${(error as Error).message}`);
1132
+ }
1133
+ }
1134
+
1135
+ /**
1136
+ * Parse SEED.md file
1137
+ */
1138
+ function parseSeedFile(seedPath: string): ProjectConfig | null {
1139
+ const yaml = getYaml();
1140
+
1141
+ try {
1142
+ const content = fs.readFileSync(seedPath, 'utf-8');
1143
+ const seedConfig: Record<string, unknown> = {};
1144
+
1145
+ // Extract YAML blocks from markdown
1146
+ const yamlBlocks = content.matchAll(/```yaml\n([\s\S]*?)```/g);
1147
+
1148
+ for (const match of yamlBlocks) {
1149
+ const yamlContent = match[1];
1150
+ if (yamlContent) {
1151
+ try {
1152
+ const parsed = yaml.parse(yamlContent) as Record<string, unknown>;
1153
+ Object.assign(seedConfig, parsed);
1154
+ } catch {
1155
+ // Skip invalid YAML blocks
1156
+ }
1157
+ }
1158
+ }
1159
+
1160
+ return Object.keys(seedConfig).length > 0 ? seedConfig as ProjectConfig : null;
1161
+ } catch {
1162
+ return null;
1163
+ }
1164
+ }
1165
+
1166
+ /**
1167
+ * Show help
1168
+ */
1169
+ function showHelp(): void {
1170
+ const utils = getUtils();
1171
+
1172
+ console.log(`
1173
+ ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Seed${utils.COLORS.reset}
1174
+ ${utils.COLORS.dim}Project scaffolding and configuration${utils.COLORS.reset}
1175
+
1176
+ ${utils.COLORS.bold}Usage:${utils.COLORS.reset}
1177
+ bootspring seed <command> [options]
1178
+
1179
+ ${utils.COLORS.bold}Commands:${utils.COLORS.reset}
1180
+ ${utils.COLORS.cyan}setup${utils.COLORS.reset} Create .bootspring/inputs folder structure
1181
+ ${utils.COLORS.cyan}init${utils.COLORS.reset} Run questionnaire, create SEED.md
1182
+ ${utils.COLORS.cyan}synthesize${utils.COLORS.reset} Create SEED.md from preseed documents ${utils.COLORS.yellow}[PRO]${utils.COLORS.reset}
1183
+ ${utils.COLORS.cyan}generate${utils.COLORS.reset} Ingest input files and generate documents ${utils.COLORS.yellow}[PRO]${utils.COLORS.reset}
1184
+ ${utils.COLORS.cyan}scaffold${utils.COLORS.reset} Generate project structure ${utils.COLORS.yellow}[PRO]${utils.COLORS.reset}
1185
+ ${utils.COLORS.cyan}build${utils.COLORS.reset} Build from seed docs (--loop for continuous) ${utils.COLORS.yellow}[PRO]${utils.COLORS.reset}
1186
+ ${utils.COLORS.cyan}update${utils.COLORS.reset} Re-run questionnaire, update SEED.md
1187
+ ${utils.COLORS.cyan}status${utils.COLORS.reset} Show current configuration (default)
1188
+ ${utils.COLORS.cyan}export${utils.COLORS.reset} Export config as JSON/YAML
1189
+
1190
+ ${utils.COLORS.bold}Init Options:${utils.COLORS.reset}
1191
+ --preset=<preset> Questionnaire preset (minimal, standard, full, startup, api)
1192
+ --force Overwrite existing SEED.md
1193
+
1194
+ ${utils.COLORS.bold}Scaffold Options:${utils.COLORS.reset}
1195
+ --preset=<preset> Use preset instead of SEED.md (nextjs, react, node, fullstack)
1196
+ --from-config Use bootspring.config.js instead of SEED.md
1197
+ --dry-run Show plan without creating files
1198
+
1199
+ ${utils.COLORS.bold}Build Options:${utils.COLORS.reset}
1200
+ --loop Start continuous build loop until MVP complete
1201
+ --iterations=<n> Max iterations for loop (default: 50)
1202
+ --force Reinitialize existing build state
1203
+ --live Stream AI output in real-time
1204
+ --verbose Show detailed output
1205
+
1206
+ ${utils.COLORS.bold}Export Options:${utils.COLORS.reset}
1207
+ --format=<fmt> Output format: json (default), yaml
1208
+ --output=<file> Write to file instead of stdout
1209
+
1210
+ ${utils.COLORS.bold}Examples:${utils.COLORS.reset}
1211
+ bootspring seed setup # Create input folder structure
1212
+ bootspring seed generate # Process input files
1213
+ bootspring seed init --preset=startup # Full questionnaire
1214
+ ${utils.COLORS.green}bootspring seed synthesize${utils.COLORS.reset} # Create SEED.md from preseed docs
1215
+ bootspring seed scaffold --preset=nextjs # Generate project
1216
+ ${utils.COLORS.green}bootspring seed build --loop${utils.COLORS.reset} # Start autonomous build
1217
+ bootspring seed export --format=yaml # Export config
1218
+
1219
+ ${utils.COLORS.bold}Complete Workflow:${utils.COLORS.reset}
1220
+ 1. ${utils.COLORS.cyan}bootspring preseed start${utils.COLORS.reset} # Capture your idea
1221
+ 2. ${utils.COLORS.cyan}bootspring seed synthesize${utils.COLORS.reset} # Create SEED.md from preseed
1222
+ 3. ${utils.COLORS.cyan}bootspring seed scaffold${utils.COLORS.reset} # Generate project code
1223
+ `);
1224
+ }