@girardmedia/bootspring 1.2.0 → 2.0.3

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 (253) hide show
  1. package/README.md +107 -14
  2. package/bin/bootspring.js +166 -27
  3. package/cli/agent.js +189 -17
  4. package/cli/analyze.js +499 -0
  5. package/cli/audit.js +557 -0
  6. package/cli/auth.js +495 -38
  7. package/cli/billing.js +302 -0
  8. package/cli/build.js +695 -0
  9. package/cli/business.js +109 -26
  10. package/cli/checkpoint-utils.js +168 -0
  11. package/cli/checkpoint.js +639 -0
  12. package/cli/cloud-sync.js +447 -0
  13. package/cli/content.js +198 -0
  14. package/cli/context.js +1 -1
  15. package/cli/deploy.js +543 -0
  16. package/cli/fundraise.js +112 -50
  17. package/cli/github-cmd.js +435 -0
  18. package/cli/health.js +477 -0
  19. package/cli/init.js +84 -13
  20. package/cli/legal.js +107 -95
  21. package/cli/log.js +2 -2
  22. package/cli/loop.js +976 -73
  23. package/cli/manager.js +711 -0
  24. package/cli/metrics.js +480 -0
  25. package/cli/monitor.js +812 -0
  26. package/cli/onboard.js +521 -0
  27. package/cli/orchestrator.js +12 -24
  28. package/cli/prd.js +594 -0
  29. package/cli/preseed-start.js +1483 -0
  30. package/cli/preseed.js +2302 -0
  31. package/cli/project.js +436 -0
  32. package/cli/quality.js +233 -0
  33. package/cli/security.js +913 -0
  34. package/cli/seed.js +1441 -5
  35. package/cli/skill.js +273 -211
  36. package/cli/suggest.js +989 -0
  37. package/cli/switch.js +453 -0
  38. package/cli/visualize.js +527 -0
  39. package/cli/watch.js +769 -0
  40. package/cli/workspace.js +607 -0
  41. package/core/analyze-workflow.js +1134 -0
  42. package/core/api-client.js +535 -22
  43. package/core/audit-workflow.js +1350 -0
  44. package/core/build-orchestrator.js +480 -0
  45. package/core/build-state.js +577 -0
  46. package/core/checkpoint-engine.js +408 -0
  47. package/core/config.js +1109 -26
  48. package/core/context-loader.js +21 -1
  49. package/core/deploy-workflow.js +836 -0
  50. package/core/entitlements.js +93 -22
  51. package/core/github-sync.js +610 -0
  52. package/core/index.js +8 -1
  53. package/core/ingest.js +1111 -0
  54. package/core/metrics-engine.js +768 -0
  55. package/core/onboard-workflow.js +1007 -0
  56. package/core/preseed-workflow.js +934 -0
  57. package/core/preseed.js +1617 -0
  58. package/core/project-context.js +325 -0
  59. package/core/project-state.js +694 -0
  60. package/core/r2-sync.js +583 -0
  61. package/core/scaffold.js +525 -7
  62. package/core/session.js +258 -0
  63. package/core/task-extractor.js +758 -0
  64. package/core/telemetry.js +28 -6
  65. package/core/tier-enforcement.js +737 -0
  66. package/core/utils.js +38 -14
  67. package/generators/questionnaire.js +15 -12
  68. package/generators/sections/ai.js +7 -7
  69. package/generators/sections/content.js +300 -0
  70. package/generators/sections/index.js +3 -0
  71. package/generators/sections/plugins.js +7 -6
  72. package/generators/templates/build-planning.template.js +596 -0
  73. package/generators/templates/content.template.js +819 -0
  74. package/generators/templates/index.js +2 -1
  75. package/hooks/git-autopilot.js +1250 -0
  76. package/hooks/index.js +9 -0
  77. package/intelligence/agent-collab.js +2057 -0
  78. package/intelligence/auto-suggest.js +634 -0
  79. package/intelligence/content-gen.js +1589 -0
  80. package/intelligence/cross-project.js +1647 -0
  81. package/intelligence/index.js +184 -0
  82. package/intelligence/learning/insights.json +517 -7
  83. package/intelligence/learning/pattern-learner.js +1008 -14
  84. package/intelligence/memory/decision-tracker.js +1431 -31
  85. package/intelligence/memory/decisions.jsonl +0 -0
  86. package/intelligence/orchestrator.js +2896 -1
  87. package/intelligence/prd.js +92 -1
  88. package/intelligence/recommendation-weights.json +14 -2
  89. package/intelligence/recommendations.js +463 -9
  90. package/intelligence/workflow-composer.js +1451 -0
  91. package/marketplace/index.d.ts +324 -0
  92. package/marketplace/index.js +1921 -0
  93. package/mcp/contracts/mcp-contract.v1.json +342 -4
  94. package/mcp/registry.js +680 -3
  95. package/mcp/response-formatter.js +23 -0
  96. package/mcp/tools/assist-tool.js +78 -4
  97. package/mcp/tools/autopilot-tool.js +408 -0
  98. package/mcp/tools/content-tool.js +571 -0
  99. package/mcp/tools/dashboard-tool.js +251 -5
  100. package/mcp/tools/mvp-tool.js +344 -0
  101. package/mcp/tools/plugin-tool.js +23 -1
  102. package/mcp/tools/prd-tool.js +579 -0
  103. package/mcp/tools/seed-tool.js +447 -0
  104. package/mcp/tools/skill-tool.js +43 -14
  105. package/mcp/tools/suggest-tool.js +147 -0
  106. package/package.json +15 -6
  107. package/agents/README.md +0 -93
  108. package/agents/ai-integration-expert/context.md +0 -386
  109. package/agents/api-expert/context.md +0 -416
  110. package/agents/architecture-expert/context.md +0 -454
  111. package/agents/auth-expert/context.md +0 -399
  112. package/agents/backend-expert/context.md +0 -483
  113. package/agents/business-strategy-expert/context.md +0 -180
  114. package/agents/code-review-expert/context.md +0 -365
  115. package/agents/competitive-analysis-expert/context.md +0 -239
  116. package/agents/data-modeling-expert/context.md +0 -352
  117. package/agents/database-expert/context.md +0 -250
  118. package/agents/devops-expert/context.md +0 -446
  119. package/agents/email-expert/context.md +0 -379
  120. package/agents/financial-expert/context.md +0 -213
  121. package/agents/frontend-expert/context.md +0 -364
  122. package/agents/fundraising-expert/context.md +0 -257
  123. package/agents/growth-expert/context.md +0 -249
  124. package/agents/index.js +0 -140
  125. package/agents/investor-relations-expert/context.md +0 -266
  126. package/agents/legal-expert/context.md +0 -284
  127. package/agents/marketing-expert/context.md +0 -236
  128. package/agents/monitoring-expert/context.md +0 -362
  129. package/agents/operations-expert/context.md +0 -279
  130. package/agents/partnerships-expert/context.md +0 -286
  131. package/agents/payment-expert/context.md +0 -340
  132. package/agents/performance-expert/context.md +0 -377
  133. package/agents/private-equity-expert/context.md +0 -246
  134. package/agents/railway-expert/context.md +0 -284
  135. package/agents/research-expert/context.md +0 -245
  136. package/agents/sales-expert/context.md +0 -241
  137. package/agents/security-expert/context.md +0 -343
  138. package/agents/testing-expert/context.md +0 -414
  139. package/agents/ui-ux-expert/context.md +0 -448
  140. package/agents/vercel-expert/context.md +0 -426
  141. package/skills/index.js +0 -787
  142. package/skills/patterns/README.md +0 -163
  143. package/skills/patterns/ai/agents.md +0 -281
  144. package/skills/patterns/ai/claude.md +0 -138
  145. package/skills/patterns/ai/embeddings.md +0 -150
  146. package/skills/patterns/ai/rag.md +0 -266
  147. package/skills/patterns/ai/streaming.md +0 -170
  148. package/skills/patterns/ai/structured-output.md +0 -162
  149. package/skills/patterns/ai/tools.md +0 -154
  150. package/skills/patterns/analytics/tracking.md +0 -220
  151. package/skills/patterns/api/errors.md +0 -296
  152. package/skills/patterns/api/graphql.md +0 -440
  153. package/skills/patterns/api/middleware.md +0 -279
  154. package/skills/patterns/api/openapi.md +0 -285
  155. package/skills/patterns/api/rate-limiting.md +0 -231
  156. package/skills/patterns/api/route-handler.md +0 -217
  157. package/skills/patterns/api/server-action.md +0 -249
  158. package/skills/patterns/api/versioning.md +0 -443
  159. package/skills/patterns/api/webhooks.md +0 -247
  160. package/skills/patterns/auth/clerk.md +0 -132
  161. package/skills/patterns/auth/mfa.md +0 -313
  162. package/skills/patterns/auth/nextauth.md +0 -140
  163. package/skills/patterns/auth/oauth.md +0 -237
  164. package/skills/patterns/auth/rbac.md +0 -152
  165. package/skills/patterns/auth/session-management.md +0 -367
  166. package/skills/patterns/auth/session.md +0 -120
  167. package/skills/patterns/database/audit.md +0 -177
  168. package/skills/patterns/database/migrations.md +0 -177
  169. package/skills/patterns/database/pagination.md +0 -230
  170. package/skills/patterns/database/pooling.md +0 -357
  171. package/skills/patterns/database/prisma.md +0 -180
  172. package/skills/patterns/database/relations.md +0 -187
  173. package/skills/patterns/database/seeding.md +0 -246
  174. package/skills/patterns/database/soft-delete.md +0 -153
  175. package/skills/patterns/database/transactions.md +0 -162
  176. package/skills/patterns/deployment/ci-cd.md +0 -231
  177. package/skills/patterns/deployment/docker.md +0 -188
  178. package/skills/patterns/deployment/monitoring.md +0 -387
  179. package/skills/patterns/deployment/vercel.md +0 -160
  180. package/skills/patterns/email/resend.md +0 -143
  181. package/skills/patterns/email/templates.md +0 -245
  182. package/skills/patterns/email/transactional.md +0 -503
  183. package/skills/patterns/email/verification.md +0 -176
  184. package/skills/patterns/files/download.md +0 -243
  185. package/skills/patterns/files/upload.md +0 -239
  186. package/skills/patterns/i18n/nextintl.md +0 -188
  187. package/skills/patterns/logging/structured.md +0 -292
  188. package/skills/patterns/notifications/email-queue.md +0 -248
  189. package/skills/patterns/notifications/push.md +0 -279
  190. package/skills/patterns/payments/checkout.md +0 -303
  191. package/skills/patterns/payments/invoices.md +0 -287
  192. package/skills/patterns/payments/portal.md +0 -245
  193. package/skills/patterns/payments/stripe.md +0 -272
  194. package/skills/patterns/payments/subscriptions.md +0 -300
  195. package/skills/patterns/payments/usage.md +0 -279
  196. package/skills/patterns/performance/caching.md +0 -276
  197. package/skills/patterns/performance/code-splitting.md +0 -233
  198. package/skills/patterns/performance/edge.md +0 -254
  199. package/skills/patterns/performance/isr.md +0 -266
  200. package/skills/patterns/performance/lazy-loading.md +0 -281
  201. package/skills/patterns/realtime/sse.md +0 -327
  202. package/skills/patterns/realtime/websockets.md +0 -336
  203. package/skills/patterns/search/filtering.md +0 -329
  204. package/skills/patterns/search/fulltext.md +0 -260
  205. package/skills/patterns/security/audit-logging.md +0 -444
  206. package/skills/patterns/security/csrf.md +0 -234
  207. package/skills/patterns/security/headers.md +0 -252
  208. package/skills/patterns/security/sanitization.md +0 -258
  209. package/skills/patterns/security/secrets.md +0 -261
  210. package/skills/patterns/security/validation.md +0 -268
  211. package/skills/patterns/security/xss.md +0 -229
  212. package/skills/patterns/seo/metadata.md +0 -252
  213. package/skills/patterns/state/context.md +0 -349
  214. package/skills/patterns/state/react-query.md +0 -313
  215. package/skills/patterns/state/url-state.md +0 -482
  216. package/skills/patterns/state/zustand.md +0 -262
  217. package/skills/patterns/testing/api.md +0 -259
  218. package/skills/patterns/testing/component.md +0 -233
  219. package/skills/patterns/testing/coverage.md +0 -207
  220. package/skills/patterns/testing/fixtures.md +0 -225
  221. package/skills/patterns/testing/integration.md +0 -436
  222. package/skills/patterns/testing/mocking.md +0 -177
  223. package/skills/patterns/testing/playwright.md +0 -162
  224. package/skills/patterns/testing/snapshot.md +0 -175
  225. package/skills/patterns/testing/vitest.md +0 -307
  226. package/skills/patterns/ui/accordions.md +0 -395
  227. package/skills/patterns/ui/cards.md +0 -299
  228. package/skills/patterns/ui/dropdowns.md +0 -476
  229. package/skills/patterns/ui/empty-states.md +0 -320
  230. package/skills/patterns/ui/forms.md +0 -405
  231. package/skills/patterns/ui/inputs.md +0 -319
  232. package/skills/patterns/ui/layouts.md +0 -282
  233. package/skills/patterns/ui/loading.md +0 -291
  234. package/skills/patterns/ui/modals.md +0 -338
  235. package/skills/patterns/ui/navigation.md +0 -374
  236. package/skills/patterns/ui/tables.md +0 -407
  237. package/skills/patterns/ui/toasts.md +0 -300
  238. package/skills/patterns/ui/tooltips.md +0 -396
  239. package/skills/patterns/utils/dates.md +0 -435
  240. package/skills/patterns/utils/errors.md +0 -451
  241. package/skills/patterns/utils/formatting.md +0 -345
  242. package/skills/patterns/utils/validation.md +0 -434
  243. package/templates/bootspring.config.js +0 -83
  244. package/templates/business/business-model-canvas.md +0 -246
  245. package/templates/business/business-plan.md +0 -266
  246. package/templates/business/competitive-analysis.md +0 -312
  247. package/templates/fundraising/data-room-checklist.md +0 -300
  248. package/templates/fundraising/investor-research.md +0 -243
  249. package/templates/fundraising/pitch-deck-outline.md +0 -253
  250. package/templates/legal/gdpr-checklist.md +0 -339
  251. package/templates/legal/privacy-policy.md +0 -285
  252. package/templates/legal/terms-of-service.md +0 -222
  253. package/templates/mcp.json +0 -9
@@ -0,0 +1,1617 @@
1
+ /**
2
+ * Bootspring Preseed Engine
3
+ *
4
+ * Core logic for generating foundational documents from minimal input.
5
+ * Creates "living documents" that can be synced with code changes.
6
+ *
7
+ * @package bootspring
8
+ * @module core/preseed
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ /**
15
+ * Document types and their metadata
16
+ */
17
+ const DOCUMENT_TYPES = {
18
+ vision: {
19
+ name: 'VISION.md',
20
+ title: 'Vision Document',
21
+ description: 'Problem statement, solution overview, and core values',
22
+ priority: 1,
23
+ required: true
24
+ },
25
+ audience: {
26
+ name: 'AUDIENCE.md',
27
+ title: 'Target Audience',
28
+ description: 'Target audience, personas, and ideal customer profile',
29
+ priority: 2,
30
+ required: true
31
+ },
32
+ market: {
33
+ name: 'MARKET.md',
34
+ title: 'Market Analysis',
35
+ description: 'TAM/SAM/SOM analysis and market opportunity',
36
+ priority: 3,
37
+ required: false
38
+ },
39
+ competitors: {
40
+ name: 'COMPETITORS.md',
41
+ title: 'Competitive Analysis',
42
+ description: 'Competitive landscape, positioning, and differentiation',
43
+ priority: 4,
44
+ required: false
45
+ },
46
+ 'business-model': {
47
+ name: 'BUSINESS_MODEL.md',
48
+ title: 'Business Model',
49
+ description: 'Value proposition, revenue model, and pricing strategy',
50
+ priority: 5,
51
+ required: true
52
+ },
53
+ prd: {
54
+ name: 'PRD.md',
55
+ title: 'Product Requirements',
56
+ description: 'Product vision, user stories, and MVP scope',
57
+ priority: 6,
58
+ required: true
59
+ },
60
+ 'technical-spec': {
61
+ name: 'TECHNICAL_SPEC.md',
62
+ title: 'Technical Specification',
63
+ description: 'Architecture, tech stack, and data model',
64
+ priority: 7,
65
+ required: false
66
+ },
67
+ roadmap: {
68
+ name: 'ROADMAP.md',
69
+ title: 'Product Roadmap',
70
+ description: 'Phased development plan and milestones',
71
+ priority: 8,
72
+ required: false
73
+ }
74
+ };
75
+
76
+ /**
77
+ * Preseed presets - different levels of document generation
78
+ */
79
+ const PRESETS = {
80
+ // Essential documents only
81
+ essential: ['vision', 'audience', 'business-model', 'prd'],
82
+ // Standard startup pack
83
+ startup: ['vision', 'audience', 'market', 'competitors', 'business-model', 'prd', 'roadmap'],
84
+ // Full documentation suite
85
+ full: Object.keys(DOCUMENT_TYPES),
86
+ // Technical focus
87
+ technical: ['vision', 'prd', 'technical-spec', 'roadmap'],
88
+ // Investor-ready
89
+ investor: ['vision', 'audience', 'market', 'competitors', 'business-model', 'roadmap']
90
+ };
91
+
92
+ /**
93
+ * Default output directory for preseed documents
94
+ */
95
+ const DEFAULT_OUTPUT_DIR = '.bootspring/preseed';
96
+
97
+ /**
98
+ * Context input directory
99
+ */
100
+ const CONTEXT_INPUT_DIR = '.bootspring/preseed/context';
101
+
102
+ /**
103
+ * Context input folder structure
104
+ * Maps to DOCUMENT_TYPES for 1:1 correspondence between input folders and output documents
105
+ */
106
+ const CONTEXT_FOLDERS = {
107
+ // Universal drop zone
108
+ drop: {
109
+ name: 'drop',
110
+ description: 'Drop anything here - AI will sort it out',
111
+ extensions: ['*'],
112
+ isUniversal: true
113
+ },
114
+ // General input folders
115
+ ideas: {
116
+ name: 'ideas',
117
+ description: 'Rough ideas, brainstorms, notes',
118
+ extensions: ['.md', '.txt', '.pdf']
119
+ },
120
+ research: {
121
+ name: 'research',
122
+ description: 'Market research, industry reports',
123
+ extensions: ['.md', '.txt', '.pdf', '.docx']
124
+ },
125
+ // Document-type specific folders (matches DOCUMENT_TYPES)
126
+ vision: {
127
+ name: 'vision',
128
+ description: 'Vision statements, problem/solution notes',
129
+ extensions: ['.md', '.txt', '.pdf'],
130
+ mapsTo: 'vision'
131
+ },
132
+ audience: {
133
+ name: 'audience',
134
+ description: 'User interviews, surveys, personas',
135
+ extensions: ['.md', '.txt', '.pdf', '.csv'],
136
+ mapsTo: 'audience'
137
+ },
138
+ market: {
139
+ name: 'market',
140
+ description: 'Market size, TAM/SAM/SOM analysis, trends',
141
+ extensions: ['.md', '.txt', '.pdf', '.csv'],
142
+ mapsTo: 'market'
143
+ },
144
+ competitors: {
145
+ name: 'competitors',
146
+ description: 'Competitor analysis, screenshots, comparisons',
147
+ extensions: ['.md', '.txt', '.pdf', '.png', '.jpg'],
148
+ mapsTo: 'competitors'
149
+ },
150
+ business: {
151
+ name: 'business',
152
+ description: 'Business model ideas, pricing notes, revenue',
153
+ extensions: ['.md', '.txt', '.pdf'],
154
+ mapsTo: 'business-model'
155
+ },
156
+ prd: {
157
+ name: 'prd',
158
+ description: 'Product requirements, user stories, features',
159
+ extensions: ['.md', '.txt', '.pdf'],
160
+ mapsTo: 'prd'
161
+ },
162
+ technical: {
163
+ name: 'technical',
164
+ description: 'Technical requirements, architecture notes',
165
+ extensions: ['.md', '.txt', '.pdf'],
166
+ mapsTo: 'technical-spec'
167
+ },
168
+ roadmap: {
169
+ name: 'roadmap',
170
+ description: 'Timeline notes, milestones, phasing plans',
171
+ extensions: ['.md', '.txt', '.pdf'],
172
+ mapsTo: 'roadmap'
173
+ }
174
+ };
175
+
176
+ /**
177
+ * Config file name
178
+ */
179
+ const CONFIG_FILE = 'PRESEED_CONFIG.json';
180
+
181
+ /**
182
+ * Preseed Engine class
183
+ */
184
+ class PreseedEngine {
185
+ constructor(projectRoot, options = {}) {
186
+ this.projectRoot = projectRoot;
187
+ this.outputDir = options.outputDir || path.join(projectRoot, DEFAULT_OUTPUT_DIR);
188
+ this.contextDir = options.contextDir || path.join(projectRoot, CONTEXT_INPUT_DIR);
189
+ this.preset = options.preset || 'startup';
190
+ this.config = null;
191
+ this.documents = new Map();
192
+ this.contextData = null;
193
+ }
194
+
195
+ /**
196
+ * Setup context input folders
197
+ */
198
+ setupContextFolders() {
199
+ const created = [];
200
+
201
+ // Create main context directory
202
+ if (!fs.existsSync(this.contextDir)) {
203
+ fs.mkdirSync(this.contextDir, { recursive: true });
204
+ created.push(this.contextDir);
205
+ }
206
+
207
+ // Create subfolders
208
+ for (const [key, folder] of Object.entries(CONTEXT_FOLDERS)) {
209
+ const folderPath = path.join(this.contextDir, folder.name);
210
+ if (!fs.existsSync(folderPath)) {
211
+ fs.mkdirSync(folderPath, { recursive: true });
212
+ created.push(folderPath);
213
+ }
214
+ }
215
+
216
+ // Create README
217
+ const readmePath = path.join(this.contextDir, 'README.md');
218
+ if (!fs.existsSync(readmePath)) {
219
+ const readme = `# Preseed Context Documents
220
+
221
+ Drop your existing context files here before running \`bootspring preseed init\`.
222
+ These documents will be analyzed to pre-populate and enhance your preseed wizard.
223
+
224
+ ## Quick Start: The Drop Zone
225
+
226
+ **Don't know where to put something?** Just drop it in the \`drop/\` folder.
227
+ AI will analyze your files and extract relevant information for each preseed document.
228
+
229
+ ## Folders
230
+
231
+ ### Universal
232
+ | Folder | What to put here |
233
+ |--------|------------------|
234
+ | \`drop/\` | **Anything!** Pitch decks, notes, docs - AI sorts it out |
235
+
236
+ ### General
237
+ | Folder | What to put here |
238
+ |--------|------------------|
239
+ | \`ideas/\` | Rough ideas, brainstorms, notes |
240
+ | \`research/\` | Market research, industry reports |
241
+
242
+ ### Document-specific (maps 1:1 to output)
243
+ | Folder | Output Document |
244
+ |--------|-----------------|
245
+ | \`vision/\` | → VISION.md |
246
+ | \`audience/\` | → AUDIENCE.md |
247
+ | \`market/\` | → MARKET.md |
248
+ | \`competitors/\` | → COMPETITORS.md |
249
+ | \`business/\` | → BUSINESS_MODEL.md |
250
+ | \`prd/\` | → PRD.md |
251
+ | \`technical/\` | → TECHNICAL_SPEC.md |
252
+ | \`roadmap/\` | → ROADMAP.md |
253
+
254
+ ## Supported Formats
255
+
256
+ - Markdown (.md)
257
+ - Text (.txt)
258
+ - PDF (.pdf)
259
+ - Word (.docx)
260
+ - CSV (.csv) - for structured data
261
+ - Images (.png, .jpg) - for screenshots
262
+
263
+ ## How It Works
264
+
265
+ 1. Drop your files in the matching folder (e.g., \`vision/\` for VISION.md sources)
266
+ 2. Or use \`drop/\` if unsure - AI will sort it out
267
+ 3. Run \`bootspring preseed init\` or \`bootspring preseed start\`
268
+ 4. The wizard will use your context to enhance document generation
269
+ 5. Your context is preserved in PRESEED_CONFIG.json
270
+
271
+ ## Tips
272
+
273
+ - More context = better generated documents
274
+ - Each folder maps 1:1 to an output document for easy organization
275
+ - Even rough notes are valuable
276
+ - **Not sure where something goes? Use \`drop/\`!**
277
+ - Include any competitor URLs or screenshots
278
+ - Add user interview transcripts if available
279
+ `;
280
+ fs.writeFileSync(readmePath, readme);
281
+ }
282
+
283
+ return created;
284
+ }
285
+
286
+ /**
287
+ * Check if context documents exist
288
+ */
289
+ hasContextDocuments() {
290
+ if (!fs.existsSync(this.contextDir)) {
291
+ return false;
292
+ }
293
+
294
+ for (const folder of Object.values(CONTEXT_FOLDERS)) {
295
+ const folderPath = path.join(this.contextDir, folder.name);
296
+ if (fs.existsSync(folderPath)) {
297
+ const files = fs.readdirSync(folderPath).filter(f => !f.startsWith('.'));
298
+ if (files.length > 0) {
299
+ return true;
300
+ }
301
+ }
302
+ }
303
+
304
+ return false;
305
+ }
306
+
307
+ /**
308
+ * Ingest context documents
309
+ */
310
+ ingestContext() {
311
+ const context = {
312
+ files: [],
313
+ // General folders
314
+ drop: [],
315
+ ideas: [],
316
+ research: [],
317
+ // Document-specific folders (maps to output documents)
318
+ vision: [],
319
+ audience: [],
320
+ market: [],
321
+ competitors: [],
322
+ business: [],
323
+ prd: [],
324
+ technical: [],
325
+ roadmap: [],
326
+ summary: {}
327
+ };
328
+
329
+ if (!fs.existsSync(this.contextDir)) {
330
+ return context;
331
+ }
332
+
333
+ for (const [key, folder] of Object.entries(CONTEXT_FOLDERS)) {
334
+ const folderPath = path.join(this.contextDir, folder.name);
335
+ if (!fs.existsSync(folderPath)) {
336
+ continue;
337
+ }
338
+
339
+ const files = fs.readdirSync(folderPath).filter(f => !f.startsWith('.'));
340
+ for (const file of files) {
341
+ const filePath = path.join(folderPath, file);
342
+ const stat = fs.statSync(filePath);
343
+
344
+ if (!stat.isFile()) continue;
345
+
346
+ const ext = path.extname(file).toLowerCase();
347
+ const fileInfo = {
348
+ name: file,
349
+ path: filePath,
350
+ category: key,
351
+ size: stat.size,
352
+ modified: stat.mtime.toISOString()
353
+ };
354
+
355
+ // Read text content for supported formats
356
+ if (['.md', '.txt'].includes(ext)) {
357
+ try {
358
+ fileInfo.content = fs.readFileSync(filePath, 'utf-8');
359
+ fileInfo.preview = fileInfo.content.substring(0, 500);
360
+ } catch (e) {
361
+ fileInfo.error = e.message;
362
+ }
363
+ }
364
+
365
+ context.files.push(fileInfo);
366
+ context[key].push(fileInfo);
367
+ }
368
+ }
369
+
370
+ // Build summary
371
+ context.summary = {
372
+ totalFiles: context.files.length,
373
+ byCategory: {}
374
+ };
375
+
376
+ for (const key of Object.keys(CONTEXT_FOLDERS)) {
377
+ context.summary.byCategory[key] = context[key].length;
378
+ }
379
+
380
+ this.contextData = context;
381
+ return context;
382
+ }
383
+
384
+ /**
385
+ * Extract suggestions from context
386
+ */
387
+ extractSuggestionsFromContext() {
388
+ if (!this.contextData || this.contextData.files.length === 0) {
389
+ return {};
390
+ }
391
+
392
+ const suggestions = {};
393
+
394
+ // Extract from ideas
395
+ if (this.contextData.ideas.length > 0) {
396
+ const ideaContent = this.contextData.ideas
397
+ .filter(f => f.content)
398
+ .map(f => f.content)
399
+ .join('\n\n');
400
+
401
+ if (ideaContent) {
402
+ suggestions.rawIdeas = ideaContent;
403
+ }
404
+ }
405
+
406
+ // Extract from research
407
+ if (this.contextData.research.length > 0) {
408
+ const researchContent = this.contextData.research
409
+ .filter(f => f.content)
410
+ .map(f => f.content)
411
+ .join('\n\n');
412
+
413
+ if (researchContent) {
414
+ suggestions.rawResearch = researchContent;
415
+ }
416
+ }
417
+
418
+ // Extract from competitors
419
+ if (this.contextData.competitors.length > 0) {
420
+ suggestions.competitorFiles = this.contextData.competitors.map(f => f.name);
421
+ const competitorContent = this.contextData.competitors
422
+ .filter(f => f.content)
423
+ .map(f => f.content)
424
+ .join('\n\n');
425
+
426
+ if (competitorContent) {
427
+ suggestions.rawCompetitors = competitorContent;
428
+ }
429
+ }
430
+
431
+ // Extract from audience
432
+ if (this.contextData.audience.length > 0) {
433
+ const audienceContent = this.contextData.audience
434
+ .filter(f => f.content)
435
+ .map(f => f.content)
436
+ .join('\n\n');
437
+
438
+ if (audienceContent) {
439
+ suggestions.rawAudience = audienceContent;
440
+ }
441
+ }
442
+
443
+ // Extract from business
444
+ if (this.contextData.business.length > 0) {
445
+ const businessContent = this.contextData.business
446
+ .filter(f => f.content)
447
+ .map(f => f.content)
448
+ .join('\n\n');
449
+
450
+ if (businessContent) {
451
+ suggestions.rawBusiness = businessContent;
452
+ }
453
+ }
454
+
455
+ // Extract from technical
456
+ if (this.contextData.technical.length > 0) {
457
+ const technicalContent = this.contextData.technical
458
+ .filter(f => f.content)
459
+ .map(f => f.content)
460
+ .join('\n\n');
461
+
462
+ if (technicalContent) {
463
+ suggestions.rawTechnical = technicalContent;
464
+ }
465
+ }
466
+
467
+ // Extract from vision
468
+ if (this.contextData.vision && this.contextData.vision.length > 0) {
469
+ const visionContent = this.contextData.vision
470
+ .filter(f => f.content)
471
+ .map(f => f.content)
472
+ .join('\n\n');
473
+
474
+ if (visionContent) {
475
+ suggestions.rawVision = visionContent;
476
+ }
477
+ }
478
+
479
+ // Extract from market
480
+ if (this.contextData.market && this.contextData.market.length > 0) {
481
+ const marketContent = this.contextData.market
482
+ .filter(f => f.content)
483
+ .map(f => f.content)
484
+ .join('\n\n');
485
+
486
+ if (marketContent) {
487
+ suggestions.rawMarket = marketContent;
488
+ }
489
+ }
490
+
491
+ // Extract from prd
492
+ if (this.contextData.prd && this.contextData.prd.length > 0) {
493
+ const prdContent = this.contextData.prd
494
+ .filter(f => f.content)
495
+ .map(f => f.content)
496
+ .join('\n\n');
497
+
498
+ if (prdContent) {
499
+ suggestions.rawPrd = prdContent;
500
+ }
501
+ }
502
+
503
+ // Extract from roadmap
504
+ if (this.contextData.roadmap && this.contextData.roadmap.length > 0) {
505
+ const roadmapContent = this.contextData.roadmap
506
+ .filter(f => f.content)
507
+ .map(f => f.content)
508
+ .join('\n\n');
509
+
510
+ if (roadmapContent) {
511
+ suggestions.rawRoadmap = roadmapContent;
512
+ }
513
+ }
514
+
515
+ // Extract from drop (universal folder - analyze and distribute)
516
+ if (this.contextData.drop && this.contextData.drop.length > 0) {
517
+ const dropContent = this.contextData.drop
518
+ .filter(f => f.content)
519
+ .map(f => f.content)
520
+ .join('\n\n');
521
+
522
+ if (dropContent) {
523
+ suggestions.rawDrop = dropContent;
524
+ }
525
+ }
526
+
527
+ return suggestions;
528
+ }
529
+
530
+ /**
531
+ * Get context content for a specific document type
532
+ * Maps document types to their corresponding context folders
533
+ */
534
+ getContextForDocument(docType) {
535
+ if (!this.contextData) {
536
+ this.ingestContext();
537
+ }
538
+
539
+ // Map document types to context folder keys
540
+ const docToFolderMap = {
541
+ 'vision': 'vision',
542
+ 'audience': 'audience',
543
+ 'market': 'market',
544
+ 'competitors': 'competitors',
545
+ 'business-model': 'business',
546
+ 'prd': 'prd',
547
+ 'technical-spec': 'technical',
548
+ 'roadmap': 'roadmap'
549
+ };
550
+
551
+ const folderKey = docToFolderMap[docType];
552
+ if (!folderKey || !this.contextData[folderKey]) {
553
+ return null;
554
+ }
555
+
556
+ const files = this.contextData[folderKey];
557
+ if (files.length === 0) {
558
+ return null;
559
+ }
560
+
561
+ // Combine all file contents from this folder
562
+ const contents = files
563
+ .filter(f => f.content)
564
+ .map(f => ({
565
+ filename: f.name,
566
+ content: f.content
567
+ }));
568
+
569
+ if (contents.length === 0) {
570
+ return null;
571
+ }
572
+
573
+ return {
574
+ fileCount: contents.length,
575
+ files: contents,
576
+ combined: contents.map(c => `--- ${c.filename} ---\n${c.content}`).join('\n\n')
577
+ };
578
+ }
579
+
580
+ /**
581
+ * Initialize preseed with minimal input
582
+ */
583
+ async initialize(minimalInput) {
584
+ // Ensure output directory exists
585
+ if (!fs.existsSync(this.outputDir)) {
586
+ fs.mkdirSync(this.outputDir, { recursive: true });
587
+ }
588
+
589
+ // Build configuration from minimal input
590
+ this.config = this.buildConfig(minimalInput);
591
+
592
+ // Save config
593
+ this.saveConfig();
594
+
595
+ return this.config;
596
+ }
597
+
598
+ /**
599
+ * Build full configuration from minimal input
600
+ */
601
+ buildConfig(input) {
602
+ const now = new Date().toISOString();
603
+
604
+ return {
605
+ // Metadata
606
+ _meta: {
607
+ version: '1.0.0',
608
+ created: now,
609
+ updated: now,
610
+ preset: this.preset,
611
+ bootspringVersion: require('../package.json').version
612
+ },
613
+
614
+ // Core identity
615
+ identity: {
616
+ name: input.name || 'Untitled Project',
617
+ tagline: input.tagline || '',
618
+ description: input.description || '',
619
+ category: input.category || 'saas'
620
+ },
621
+
622
+ // Problem/Solution
623
+ problem: {
624
+ statement: input.problem || '',
625
+ painPoints: input.painPoints || [],
626
+ currentSolutions: input.currentSolutions || [],
627
+ whyNow: input.whyNow || ''
628
+ },
629
+
630
+ // Solution
631
+ solution: {
632
+ overview: input.solution || '',
633
+ keyFeatures: input.keyFeatures || [],
634
+ uniqueValue: input.uniqueValue || '',
635
+ coreCapabilities: input.coreCapabilities || []
636
+ },
637
+
638
+ // Target audience
639
+ audience: {
640
+ primary: input.primaryAudience || '',
641
+ segments: input.segments || [],
642
+ personas: input.personas || [],
643
+ icp: input.icp || {}
644
+ },
645
+
646
+ // Market
647
+ market: {
648
+ tam: input.tam || null,
649
+ sam: input.sam || null,
650
+ som: input.som || null,
651
+ trends: input.trends || [],
652
+ growth: input.marketGrowth || ''
653
+ },
654
+
655
+ // Competition
656
+ competitors: {
657
+ direct: input.directCompetitors || [],
658
+ indirect: input.indirectCompetitors || [],
659
+ positioning: input.positioning || '',
660
+ differentiation: input.differentiation || []
661
+ },
662
+
663
+ // Business model
664
+ business: {
665
+ model: input.businessModel || 'subscription',
666
+ revenueStreams: input.revenueStreams || [],
667
+ pricing: input.pricing || {},
668
+ unitEconomics: input.unitEconomics || {}
669
+ },
670
+
671
+ // Product
672
+ product: {
673
+ vision: input.productVision || '',
674
+ mvpFeatures: input.mvpFeatures || [],
675
+ futureFeatures: input.futureFeatures || [],
676
+ userStories: input.userStories || []
677
+ },
678
+
679
+ // Technical
680
+ technical: {
681
+ stack: input.techStack || {},
682
+ architecture: input.architecture || '',
683
+ integrations: input.integrations || [],
684
+ constraints: input.constraints || []
685
+ },
686
+
687
+ // Roadmap
688
+ roadmap: {
689
+ phases: input.phases || [],
690
+ milestones: input.milestones || [],
691
+ timeline: input.timeline || ''
692
+ },
693
+
694
+ // Living document tracking
695
+ _sync: {
696
+ lastSync: null,
697
+ codebaseHash: null,
698
+ linkedFiles: [],
699
+ changeLog: []
700
+ }
701
+ };
702
+ }
703
+
704
+ /**
705
+ * Save configuration to file
706
+ */
707
+ saveConfig() {
708
+ const configPath = path.join(this.outputDir, CONFIG_FILE);
709
+ fs.writeFileSync(configPath, JSON.stringify(this.config, null, 2));
710
+ return configPath;
711
+ }
712
+
713
+ /**
714
+ * Load existing configuration
715
+ */
716
+ loadConfig() {
717
+ const configPath = path.join(this.outputDir, CONFIG_FILE);
718
+ if (fs.existsSync(configPath)) {
719
+ this.config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
720
+ return true;
721
+ }
722
+ return false;
723
+ }
724
+
725
+ /**
726
+ * Get documents to generate based on preset
727
+ */
728
+ getDocumentsToGenerate() {
729
+ const docTypes = PRESETS[this.preset] || PRESETS.startup;
730
+ return docTypes.map(type => ({
731
+ type,
732
+ ...DOCUMENT_TYPES[type]
733
+ }));
734
+ }
735
+
736
+ /**
737
+ * Generate all documents
738
+ */
739
+ async generateAll() {
740
+ if (!this.config) {
741
+ throw new Error('No configuration loaded. Run initialize() first.');
742
+ }
743
+
744
+ const docs = this.getDocumentsToGenerate();
745
+ const results = [];
746
+
747
+ for (const doc of docs) {
748
+ const content = this.generateDocument(doc.type);
749
+ const filePath = path.join(this.outputDir, doc.name);
750
+ fs.writeFileSync(filePath, content);
751
+ this.documents.set(doc.type, filePath);
752
+ results.push({ type: doc.type, path: filePath, title: doc.title });
753
+ }
754
+
755
+ // Update sync metadata
756
+ this.config._sync.lastSync = new Date().toISOString();
757
+ this.saveConfig();
758
+
759
+ return results;
760
+ }
761
+
762
+ /**
763
+ * Generate a single document
764
+ */
765
+ generateDocument(type) {
766
+ const generators = {
767
+ vision: () => this.generateVision(),
768
+ audience: () => this.generateAudience(),
769
+ market: () => this.generateMarket(),
770
+ competitors: () => this.generateCompetitors(),
771
+ 'business-model': () => this.generateBusinessModel(),
772
+ prd: () => this.generatePRD(),
773
+ 'technical-spec': () => this.generateTechnicalSpec(),
774
+ roadmap: () => this.generateRoadmap()
775
+ };
776
+
777
+ const generator = generators[type];
778
+ if (!generator) {
779
+ throw new Error(`Unknown document type: ${type}`);
780
+ }
781
+
782
+ return generator();
783
+ }
784
+
785
+ /**
786
+ * Generate VISION.md
787
+ */
788
+ generateVision() {
789
+ const { identity, problem, solution } = this.config;
790
+ const sections = [];
791
+ const context = this.getContextForDocument('vision');
792
+
793
+ sections.push(`# ${identity.name}`);
794
+ sections.push('');
795
+ if (identity.tagline) {
796
+ sections.push(`> ${identity.tagline}`);
797
+ sections.push('');
798
+ }
799
+ sections.push(`**Category:** ${identity.category}`);
800
+ sections.push(`**Generated:** ${new Date().toISOString().split('T')[0]}`);
801
+ sections.push('');
802
+
803
+ // Problem Statement
804
+ sections.push('## The Problem');
805
+ sections.push('');
806
+ if (problem.statement) {
807
+ sections.push(problem.statement);
808
+ sections.push('');
809
+ }
810
+
811
+ if (problem.painPoints && problem.painPoints.length > 0) {
812
+ sections.push('### Pain Points');
813
+ sections.push('');
814
+ problem.painPoints.forEach(point => {
815
+ sections.push(`- ${point}`);
816
+ });
817
+ sections.push('');
818
+ }
819
+
820
+ if (problem.currentSolutions && problem.currentSolutions.length > 0) {
821
+ sections.push('### Current Solutions (and their limitations)');
822
+ sections.push('');
823
+ problem.currentSolutions.forEach(sol => {
824
+ if (typeof sol === 'object') {
825
+ sections.push(`- **${sol.name}**: ${sol.limitation || 'N/A'}`);
826
+ } else {
827
+ sections.push(`- ${sol}`);
828
+ }
829
+ });
830
+ sections.push('');
831
+ }
832
+
833
+ if (problem.whyNow) {
834
+ sections.push('### Why Now?');
835
+ sections.push('');
836
+ sections.push(problem.whyNow);
837
+ sections.push('');
838
+ }
839
+
840
+ // Solution
841
+ sections.push('## Our Solution');
842
+ sections.push('');
843
+ if (solution.overview) {
844
+ sections.push(solution.overview);
845
+ sections.push('');
846
+ }
847
+
848
+ if (solution.keyFeatures && solution.keyFeatures.length > 0) {
849
+ sections.push('### Key Features');
850
+ sections.push('');
851
+ solution.keyFeatures.forEach(feature => {
852
+ if (typeof feature === 'object') {
853
+ sections.push(`- **${feature.name}**: ${feature.description || ''}`);
854
+ } else {
855
+ sections.push(`- ${feature}`);
856
+ }
857
+ });
858
+ sections.push('');
859
+ }
860
+
861
+ if (solution.uniqueValue) {
862
+ sections.push('### Unique Value Proposition');
863
+ sections.push('');
864
+ sections.push(solution.uniqueValue);
865
+ sections.push('');
866
+ }
867
+
868
+ // Include source context if available
869
+ if (context && context.fileCount > 0) {
870
+ sections.push('## Source Context');
871
+ sections.push('');
872
+ sections.push(`*Generated from ${context.fileCount} source file(s) in \`context/vision/\`:*`);
873
+ context.files.forEach(f => {
874
+ sections.push(`- ${f.filename}`);
875
+ });
876
+ sections.push('');
877
+ }
878
+
879
+ // Footer
880
+ sections.push('---');
881
+ sections.push('');
882
+ sections.push('*This is a living document generated by Bootspring. Run `bootspring preseed sync` to update.*');
883
+
884
+ return sections.join('\n');
885
+ }
886
+
887
+ /**
888
+ * Generate AUDIENCE.md
889
+ */
890
+ generateAudience() {
891
+ const { identity, audience } = this.config;
892
+ const sections = [];
893
+ const context = this.getContextForDocument('audience');
894
+
895
+ sections.push(`# Target Audience: ${identity.name}`);
896
+ sections.push('');
897
+ sections.push(`**Generated:** ${new Date().toISOString().split('T')[0]}`);
898
+ sections.push('');
899
+
900
+ // Primary audience
901
+ sections.push('## Primary Audience');
902
+ sections.push('');
903
+ if (audience.primary) {
904
+ sections.push(audience.primary);
905
+ sections.push('');
906
+ }
907
+
908
+ // Segments
909
+ if (audience.segments && audience.segments.length > 0) {
910
+ sections.push('## Market Segments');
911
+ sections.push('');
912
+ audience.segments.forEach((segment, i) => {
913
+ if (typeof segment === 'object') {
914
+ sections.push(`### ${i + 1}. ${segment.name}`);
915
+ if (segment.description) sections.push(segment.description);
916
+ if (segment.size) sections.push(`- **Size**: ${segment.size}`);
917
+ if (segment.characteristics) {
918
+ sections.push(`- **Characteristics**: ${segment.characteristics.join(', ')}`);
919
+ }
920
+ } else {
921
+ sections.push(`- ${segment}`);
922
+ }
923
+ sections.push('');
924
+ });
925
+ }
926
+
927
+ // Personas
928
+ if (audience.personas && audience.personas.length > 0) {
929
+ sections.push('## User Personas');
930
+ sections.push('');
931
+ audience.personas.forEach((persona, i) => {
932
+ if (typeof persona === 'object') {
933
+ sections.push(`### Persona ${i + 1}: ${persona.name || 'Unnamed'}`);
934
+ sections.push('');
935
+ if (persona.role) sections.push(`**Role:** ${persona.role}`);
936
+ if (persona.demographics) sections.push(`**Demographics:** ${persona.demographics}`);
937
+ if (persona.goals && persona.goals.length > 0) {
938
+ sections.push('');
939
+ sections.push('**Goals:**');
940
+ persona.goals.forEach(goal => sections.push(`- ${goal}`));
941
+ }
942
+ if (persona.painPoints && persona.painPoints.length > 0) {
943
+ sections.push('');
944
+ sections.push('**Pain Points:**');
945
+ persona.painPoints.forEach(pain => sections.push(`- ${pain}`));
946
+ }
947
+ if (persona.quote) {
948
+ sections.push('');
949
+ sections.push(`> "${persona.quote}"`);
950
+ }
951
+ } else {
952
+ sections.push(`- ${persona}`);
953
+ }
954
+ sections.push('');
955
+ });
956
+ }
957
+
958
+ // ICP
959
+ if (audience.icp && Object.keys(audience.icp).length > 0) {
960
+ sections.push('## Ideal Customer Profile (ICP)');
961
+ sections.push('');
962
+ const icp = audience.icp;
963
+ if (icp.company) sections.push(`**Company:** ${icp.company}`);
964
+ if (icp.size) sections.push(`**Size:** ${icp.size}`);
965
+ if (icp.industry) sections.push(`**Industry:** ${icp.industry}`);
966
+ if (icp.budget) sections.push(`**Budget:** ${icp.budget}`);
967
+ if (icp.decisionMaker) sections.push(`**Decision Maker:** ${icp.decisionMaker}`);
968
+ if (icp.buyingProcess) sections.push(`**Buying Process:** ${icp.buyingProcess}`);
969
+ sections.push('');
970
+ }
971
+
972
+ // Include source context if available
973
+ if (context && context.fileCount > 0) {
974
+ sections.push('## Source Context');
975
+ sections.push('');
976
+ sections.push(`*Generated from ${context.fileCount} source file(s) in \`context/audience/\`:*`);
977
+ context.files.forEach(f => {
978
+ sections.push(`- ${f.filename}`);
979
+ });
980
+ sections.push('');
981
+ }
982
+
983
+ // Footer
984
+ sections.push('---');
985
+ sections.push('');
986
+ sections.push('*This is a living document generated by Bootspring. Run `bootspring preseed sync` to update.*');
987
+
988
+ return sections.join('\n');
989
+ }
990
+
991
+ /**
992
+ * Generate MARKET.md
993
+ */
994
+ generateMarket() {
995
+ const { identity, market } = this.config;
996
+ const sections = [];
997
+ const context = this.getContextForDocument('market');
998
+
999
+ sections.push(`# Market Analysis: ${identity.name}`);
1000
+ sections.push('');
1001
+ sections.push(`**Generated:** ${new Date().toISOString().split('T')[0]}`);
1002
+ sections.push('');
1003
+
1004
+ // Market sizing
1005
+ sections.push('## Market Size');
1006
+ sections.push('');
1007
+ sections.push('| Metric | Value | Notes |');
1008
+ sections.push('|--------|-------|-------|');
1009
+ sections.push(`| **TAM** (Total Addressable Market) | ${market.tam || 'TBD'} | Total market demand |`);
1010
+ sections.push(`| **SAM** (Serviceable Addressable Market) | ${market.sam || 'TBD'} | Reachable market |`);
1011
+ sections.push(`| **SOM** (Serviceable Obtainable Market) | ${market.som || 'TBD'} | Target in 1-3 years |`);
1012
+ sections.push('');
1013
+
1014
+ // Growth
1015
+ if (market.growth) {
1016
+ sections.push('## Market Growth');
1017
+ sections.push('');
1018
+ sections.push(market.growth);
1019
+ sections.push('');
1020
+ }
1021
+
1022
+ // Trends
1023
+ if (market.trends && market.trends.length > 0) {
1024
+ sections.push('## Market Trends');
1025
+ sections.push('');
1026
+ market.trends.forEach(trend => {
1027
+ if (typeof trend === 'object') {
1028
+ sections.push(`### ${trend.name}`);
1029
+ if (trend.description) sections.push(trend.description);
1030
+ if (trend.impact) sections.push(`**Impact:** ${trend.impact}`);
1031
+ } else {
1032
+ sections.push(`- ${trend}`);
1033
+ }
1034
+ sections.push('');
1035
+ });
1036
+ }
1037
+
1038
+ // Include source context if available
1039
+ if (context && context.fileCount > 0) {
1040
+ sections.push('## Source Context');
1041
+ sections.push('');
1042
+ sections.push(`*Generated from ${context.fileCount} source file(s) in \`context/market/\`:*`);
1043
+ context.files.forEach(f => {
1044
+ sections.push(`- ${f.filename}`);
1045
+ });
1046
+ sections.push('');
1047
+ }
1048
+
1049
+ // Footer
1050
+ sections.push('---');
1051
+ sections.push('');
1052
+ sections.push('*This is a living document generated by Bootspring. Run `bootspring preseed sync` to update.*');
1053
+
1054
+ return sections.join('\n');
1055
+ }
1056
+
1057
+ /**
1058
+ * Generate COMPETITORS.md
1059
+ */
1060
+ generateCompetitors() {
1061
+ const { identity, competitors } = this.config;
1062
+ const sections = [];
1063
+ const context = this.getContextForDocument('competitors');
1064
+
1065
+ sections.push(`# Competitive Analysis: ${identity.name}`);
1066
+ sections.push('');
1067
+ sections.push(`**Generated:** ${new Date().toISOString().split('T')[0]}`);
1068
+ sections.push('');
1069
+
1070
+ // Direct competitors
1071
+ if (competitors.direct && competitors.direct.length > 0) {
1072
+ sections.push('## Direct Competitors');
1073
+ sections.push('');
1074
+ sections.push('| Company | Strengths | Weaknesses | Pricing |');
1075
+ sections.push('|---------|-----------|------------|---------|');
1076
+ competitors.direct.forEach(comp => {
1077
+ if (typeof comp === 'object') {
1078
+ sections.push(`| ${comp.name || 'N/A'} | ${comp.strengths || 'N/A'} | ${comp.weaknesses || 'N/A'} | ${comp.pricing || 'N/A'} |`);
1079
+ } else {
1080
+ sections.push(`| ${comp} | TBD | TBD | TBD |`);
1081
+ }
1082
+ });
1083
+ sections.push('');
1084
+ }
1085
+
1086
+ // Indirect competitors
1087
+ if (competitors.indirect && competitors.indirect.length > 0) {
1088
+ sections.push('## Indirect Competitors');
1089
+ sections.push('');
1090
+ sections.push('| Solution | Why Used | Limitations |');
1091
+ sections.push('|----------|----------|-------------|');
1092
+ competitors.indirect.forEach(comp => {
1093
+ if (typeof comp === 'object') {
1094
+ sections.push(`| ${comp.name || 'N/A'} | ${comp.whyUsed || 'N/A'} | ${comp.limitations || 'N/A'} |`);
1095
+ } else {
1096
+ sections.push(`| ${comp} | TBD | TBD |`);
1097
+ }
1098
+ });
1099
+ sections.push('');
1100
+ }
1101
+
1102
+ // Positioning
1103
+ if (competitors.positioning) {
1104
+ sections.push('## Our Positioning');
1105
+ sections.push('');
1106
+ sections.push(competitors.positioning);
1107
+ sections.push('');
1108
+ }
1109
+
1110
+ // Differentiation
1111
+ if (competitors.differentiation && competitors.differentiation.length > 0) {
1112
+ sections.push('## Key Differentiators');
1113
+ sections.push('');
1114
+ competitors.differentiation.forEach((diff, i) => {
1115
+ sections.push(`${i + 1}. ${diff}`);
1116
+ });
1117
+ sections.push('');
1118
+ }
1119
+
1120
+ // Include source context if available
1121
+ if (context && context.fileCount > 0) {
1122
+ sections.push('## Source Context');
1123
+ sections.push('');
1124
+ sections.push(`*Generated from ${context.fileCount} source file(s) in \`context/competitors/\`:*`);
1125
+ context.files.forEach(f => {
1126
+ sections.push(`- ${f.filename}`);
1127
+ });
1128
+ sections.push('');
1129
+ }
1130
+
1131
+ // Footer
1132
+ sections.push('---');
1133
+ sections.push('');
1134
+ sections.push('*This is a living document generated by Bootspring. Run `bootspring preseed sync` to update.*');
1135
+
1136
+ return sections.join('\n');
1137
+ }
1138
+
1139
+ /**
1140
+ * Generate BUSINESS_MODEL.md
1141
+ */
1142
+ generateBusinessModel() {
1143
+ const { identity, business } = this.config;
1144
+ const sections = [];
1145
+ const context = this.getContextForDocument('business-model');
1146
+
1147
+ sections.push(`# Business Model: ${identity.name}`);
1148
+ sections.push('');
1149
+ sections.push(`**Generated:** ${new Date().toISOString().split('T')[0]}`);
1150
+ sections.push('');
1151
+
1152
+ // Model type
1153
+ sections.push('## Business Model Type');
1154
+ sections.push('');
1155
+ sections.push(`**Model:** ${business.model || 'Subscription SaaS'}`);
1156
+ sections.push('');
1157
+
1158
+ // Revenue streams
1159
+ if (business.revenueStreams && business.revenueStreams.length > 0) {
1160
+ sections.push('## Revenue Streams');
1161
+ sections.push('');
1162
+ business.revenueStreams.forEach(stream => {
1163
+ if (typeof stream === 'object') {
1164
+ sections.push(`### ${stream.name}`);
1165
+ if (stream.description) sections.push(stream.description);
1166
+ if (stream.percentage) sections.push(`**Percentage of Revenue:** ${stream.percentage}`);
1167
+ } else {
1168
+ sections.push(`- ${stream}`);
1169
+ }
1170
+ sections.push('');
1171
+ });
1172
+ }
1173
+
1174
+ // Pricing
1175
+ if (business.pricing && Object.keys(business.pricing).length > 0) {
1176
+ sections.push('## Pricing Strategy');
1177
+ sections.push('');
1178
+ const p = business.pricing;
1179
+ if (p.model) sections.push(`**Pricing Model:** ${p.model}`);
1180
+ if (p.tiers && p.tiers.length > 0) {
1181
+ sections.push('');
1182
+ sections.push('### Pricing Tiers');
1183
+ sections.push('');
1184
+ sections.push('| Tier | Price | Features |');
1185
+ sections.push('|------|-------|----------|');
1186
+ p.tiers.forEach(tier => {
1187
+ const features = tier.features ? tier.features.join(', ') : 'N/A';
1188
+ sections.push(`| ${tier.name || 'N/A'} | ${tier.price || 'N/A'} | ${features} |`);
1189
+ });
1190
+ }
1191
+ sections.push('');
1192
+ }
1193
+
1194
+ // Unit economics
1195
+ if (business.unitEconomics && Object.keys(business.unitEconomics).length > 0) {
1196
+ sections.push('## Unit Economics');
1197
+ sections.push('');
1198
+ const ue = business.unitEconomics;
1199
+ if (ue.cac) sections.push(`**Customer Acquisition Cost (CAC):** ${ue.cac}`);
1200
+ if (ue.ltv) sections.push(`**Lifetime Value (LTV):** ${ue.ltv}`);
1201
+ if (ue.ltvCacRatio) sections.push(`**LTV:CAC Ratio:** ${ue.ltvCacRatio}`);
1202
+ if (ue.paybackPeriod) sections.push(`**Payback Period:** ${ue.paybackPeriod}`);
1203
+ if (ue.grossMargin) sections.push(`**Gross Margin:** ${ue.grossMargin}`);
1204
+ sections.push('');
1205
+ }
1206
+
1207
+ // Include source context if available
1208
+ if (context && context.fileCount > 0) {
1209
+ sections.push('## Source Context');
1210
+ sections.push('');
1211
+ sections.push(`*Generated from ${context.fileCount} source file(s) in \`context/business/\`:*`);
1212
+ context.files.forEach(f => {
1213
+ sections.push(`- ${f.filename}`);
1214
+ });
1215
+ sections.push('');
1216
+ }
1217
+
1218
+ // Footer
1219
+ sections.push('---');
1220
+ sections.push('');
1221
+ sections.push('*This is a living document generated by Bootspring. Run `bootspring preseed sync` to update.*');
1222
+
1223
+ return sections.join('\n');
1224
+ }
1225
+
1226
+ /**
1227
+ * Generate PRD.md
1228
+ */
1229
+ generatePRD() {
1230
+ const { identity, solution, product } = this.config;
1231
+ const sections = [];
1232
+ const context = this.getContextForDocument('prd');
1233
+
1234
+ sections.push(`# Product Requirements: ${identity.name}`);
1235
+ sections.push('');
1236
+ sections.push(`**Generated:** ${new Date().toISOString().split('T')[0]}`);
1237
+ sections.push('**Version:** 1.0');
1238
+ sections.push('');
1239
+
1240
+ // Product vision
1241
+ sections.push('## Product Vision');
1242
+ sections.push('');
1243
+ if (product.vision) {
1244
+ sections.push(product.vision);
1245
+ } else if (solution.overview) {
1246
+ sections.push(solution.overview);
1247
+ }
1248
+ sections.push('');
1249
+
1250
+ // MVP Features
1251
+ if (product.mvpFeatures && product.mvpFeatures.length > 0) {
1252
+ sections.push('## MVP Features');
1253
+ sections.push('');
1254
+ sections.push('| Feature | Priority | Description |');
1255
+ sections.push('|---------|----------|-------------|');
1256
+ product.mvpFeatures.forEach(feature => {
1257
+ if (typeof feature === 'object') {
1258
+ sections.push(`| ${feature.name || 'N/A'} | ${feature.priority || 'P1'} | ${feature.description || 'N/A'} |`);
1259
+ } else {
1260
+ sections.push(`| ${feature} | P1 | TBD |`);
1261
+ }
1262
+ });
1263
+ sections.push('');
1264
+ } else if (solution.keyFeatures && solution.keyFeatures.length > 0) {
1265
+ sections.push('## MVP Features');
1266
+ sections.push('');
1267
+ solution.keyFeatures.forEach((feature, i) => {
1268
+ if (typeof feature === 'object') {
1269
+ sections.push(`### ${i + 1}. ${feature.name}`);
1270
+ if (feature.description) sections.push(feature.description);
1271
+ } else {
1272
+ sections.push(`${i + 1}. ${feature}`);
1273
+ }
1274
+ sections.push('');
1275
+ });
1276
+ }
1277
+
1278
+ // User stories
1279
+ if (product.userStories && product.userStories.length > 0) {
1280
+ sections.push('## User Stories');
1281
+ sections.push('');
1282
+ product.userStories.forEach((story, i) => {
1283
+ if (typeof story === 'object') {
1284
+ sections.push(`### US-${String(i + 1).padStart(3, '0')}: ${story.title || 'Untitled'}`);
1285
+ sections.push('');
1286
+ sections.push(`**As a** ${story.actor || 'user'}`);
1287
+ sections.push(`**I want to** ${story.action || 'TBD'}`);
1288
+ sections.push(`**So that** ${story.benefit || 'TBD'}`);
1289
+ if (story.acceptanceCriteria && story.acceptanceCriteria.length > 0) {
1290
+ sections.push('');
1291
+ sections.push('**Acceptance Criteria:**');
1292
+ story.acceptanceCriteria.forEach(ac => sections.push(`- [ ] ${ac}`));
1293
+ }
1294
+ } else {
1295
+ sections.push(`- ${story}`);
1296
+ }
1297
+ sections.push('');
1298
+ });
1299
+ }
1300
+
1301
+ // Future features
1302
+ if (product.futureFeatures && product.futureFeatures.length > 0) {
1303
+ sections.push('## Future Features (Post-MVP)');
1304
+ sections.push('');
1305
+ product.futureFeatures.forEach(feature => {
1306
+ if (typeof feature === 'object') {
1307
+ sections.push(`- **${feature.name}**: ${feature.description || ''}`);
1308
+ } else {
1309
+ sections.push(`- ${feature}`);
1310
+ }
1311
+ });
1312
+ sections.push('');
1313
+ }
1314
+
1315
+ // Include source context if available
1316
+ if (context && context.fileCount > 0) {
1317
+ sections.push('## Source Context');
1318
+ sections.push('');
1319
+ sections.push(`*Generated from ${context.fileCount} source file(s) in \`context/prd/\`:*`);
1320
+ context.files.forEach(f => {
1321
+ sections.push(`- ${f.filename}`);
1322
+ });
1323
+ sections.push('');
1324
+ }
1325
+
1326
+ // Footer
1327
+ sections.push('---');
1328
+ sections.push('');
1329
+ sections.push('*This is a living document generated by Bootspring. Run `bootspring preseed sync` to update.*');
1330
+
1331
+ return sections.join('\n');
1332
+ }
1333
+
1334
+ /**
1335
+ * Generate TECHNICAL_SPEC.md
1336
+ */
1337
+ generateTechnicalSpec() {
1338
+ const { identity, technical, product } = this.config;
1339
+ const sections = [];
1340
+ const context = this.getContextForDocument('technical-spec');
1341
+
1342
+ sections.push(`# Technical Specification: ${identity.name}`);
1343
+ sections.push('');
1344
+ sections.push(`**Generated:** ${new Date().toISOString().split('T')[0]}`);
1345
+ sections.push('');
1346
+
1347
+ // Tech stack
1348
+ if (technical.stack && Object.keys(technical.stack).length > 0) {
1349
+ sections.push('## Technology Stack');
1350
+ sections.push('');
1351
+ sections.push('| Component | Technology |');
1352
+ sections.push('|-----------|------------|');
1353
+ for (const [key, value] of Object.entries(technical.stack)) {
1354
+ sections.push(`| ${key.charAt(0).toUpperCase() + key.slice(1)} | ${value} |`);
1355
+ }
1356
+ sections.push('');
1357
+ }
1358
+
1359
+ // Architecture
1360
+ if (technical.architecture) {
1361
+ sections.push('## Architecture Overview');
1362
+ sections.push('');
1363
+ sections.push(technical.architecture);
1364
+ sections.push('');
1365
+ }
1366
+
1367
+ // Integrations
1368
+ if (technical.integrations && technical.integrations.length > 0) {
1369
+ sections.push('## Third-Party Integrations');
1370
+ sections.push('');
1371
+ technical.integrations.forEach(integration => {
1372
+ if (typeof integration === 'object') {
1373
+ sections.push(`- **${integration.name}**: ${integration.purpose || ''}`);
1374
+ } else {
1375
+ sections.push(`- ${integration}`);
1376
+ }
1377
+ });
1378
+ sections.push('');
1379
+ }
1380
+
1381
+ // Constraints
1382
+ if (technical.constraints && technical.constraints.length > 0) {
1383
+ sections.push('## Constraints & Requirements');
1384
+ sections.push('');
1385
+ technical.constraints.forEach(constraint => {
1386
+ sections.push(`- ${constraint}`);
1387
+ });
1388
+ sections.push('');
1389
+ }
1390
+
1391
+ // Include source context if available
1392
+ if (context && context.fileCount > 0) {
1393
+ sections.push('## Source Context');
1394
+ sections.push('');
1395
+ sections.push(`*Generated from ${context.fileCount} source file(s) in \`context/technical/\`:*`);
1396
+ context.files.forEach(f => {
1397
+ sections.push(`- ${f.filename}`);
1398
+ });
1399
+ sections.push('');
1400
+ }
1401
+
1402
+ // Footer
1403
+ sections.push('---');
1404
+ sections.push('');
1405
+ sections.push('*This is a living document generated by Bootspring. Run `bootspring preseed sync` to update.*');
1406
+
1407
+ return sections.join('\n');
1408
+ }
1409
+
1410
+ /**
1411
+ * Generate ROADMAP.md
1412
+ */
1413
+ generateRoadmap() {
1414
+ const { identity, roadmap, product } = this.config;
1415
+ const sections = [];
1416
+ const context = this.getContextForDocument('roadmap');
1417
+
1418
+ sections.push(`# Product Roadmap: ${identity.name}`);
1419
+ sections.push('');
1420
+ sections.push(`**Generated:** ${new Date().toISOString().split('T')[0]}`);
1421
+ sections.push('');
1422
+
1423
+ // Timeline
1424
+ if (roadmap.timeline) {
1425
+ sections.push('## Timeline Overview');
1426
+ sections.push('');
1427
+ sections.push(roadmap.timeline);
1428
+ sections.push('');
1429
+ }
1430
+
1431
+ // Phases
1432
+ if (roadmap.phases && roadmap.phases.length > 0) {
1433
+ sections.push('## Development Phases');
1434
+ sections.push('');
1435
+ roadmap.phases.forEach((phase, i) => {
1436
+ if (typeof phase === 'object') {
1437
+ sections.push(`### Phase ${i + 1}: ${phase.name || 'Unnamed'}`);
1438
+ sections.push('');
1439
+ if (phase.duration) sections.push(`**Duration:** ${phase.duration}`);
1440
+ if (phase.goals && phase.goals.length > 0) {
1441
+ sections.push('');
1442
+ sections.push('**Goals:**');
1443
+ phase.goals.forEach(goal => sections.push(`- ${goal}`));
1444
+ }
1445
+ if (phase.deliverables && phase.deliverables.length > 0) {
1446
+ sections.push('');
1447
+ sections.push('**Deliverables:**');
1448
+ phase.deliverables.forEach(d => sections.push(`- [ ] ${d}`));
1449
+ }
1450
+ } else {
1451
+ sections.push(`${i + 1}. ${phase}`);
1452
+ }
1453
+ sections.push('');
1454
+ });
1455
+ }
1456
+
1457
+ // Milestones
1458
+ if (roadmap.milestones && roadmap.milestones.length > 0) {
1459
+ sections.push('## Key Milestones');
1460
+ sections.push('');
1461
+ sections.push('| Milestone | Target Date | Status |');
1462
+ sections.push('|-----------|-------------|--------|');
1463
+ roadmap.milestones.forEach(milestone => {
1464
+ if (typeof milestone === 'object') {
1465
+ sections.push(`| ${milestone.name || 'N/A'} | ${milestone.date || 'TBD'} | ${milestone.status || 'Planned'} |`);
1466
+ } else {
1467
+ sections.push(`| ${milestone} | TBD | Planned |`);
1468
+ }
1469
+ });
1470
+ sections.push('');
1471
+ }
1472
+
1473
+ // Include source context if available
1474
+ if (context && context.fileCount > 0) {
1475
+ sections.push('## Source Context');
1476
+ sections.push('');
1477
+ sections.push(`*Generated from ${context.fileCount} source file(s) in \`context/roadmap/\`:*`);
1478
+ context.files.forEach(f => {
1479
+ sections.push(`- ${f.filename}`);
1480
+ });
1481
+ sections.push('');
1482
+ }
1483
+
1484
+ // Footer
1485
+ sections.push('---');
1486
+ sections.push('');
1487
+ sections.push('*This is a living document generated by Bootspring. Run `bootspring preseed sync` to update.*');
1488
+
1489
+ return sections.join('\n');
1490
+ }
1491
+
1492
+ /**
1493
+ * Sync documents with codebase changes
1494
+ */
1495
+ async sync() {
1496
+ if (!this.loadConfig()) {
1497
+ throw new Error('No preseed configuration found. Run preseed init first.');
1498
+ }
1499
+
1500
+ // Record sync
1501
+ this.config._sync.lastSync = new Date().toISOString();
1502
+ this.config._sync.changeLog.push({
1503
+ timestamp: new Date().toISOString(),
1504
+ action: 'sync',
1505
+ changes: []
1506
+ });
1507
+
1508
+ // Regenerate all documents
1509
+ const results = await this.generateAll();
1510
+
1511
+ return {
1512
+ synced: results.length,
1513
+ documents: results
1514
+ };
1515
+ }
1516
+
1517
+ /**
1518
+ * Update a specific field in the config
1519
+ */
1520
+ updateConfig(path, value) {
1521
+ if (!this.config) {
1522
+ this.loadConfig();
1523
+ }
1524
+
1525
+ // Navigate to the path and set value
1526
+ const parts = path.split('.');
1527
+ let current = this.config;
1528
+ for (let i = 0; i < parts.length - 1; i++) {
1529
+ if (!current[parts[i]]) {
1530
+ current[parts[i]] = {};
1531
+ }
1532
+ current = current[parts[i]];
1533
+ }
1534
+ current[parts[parts.length - 1]] = value;
1535
+
1536
+ // Record change
1537
+ this.config._meta.updated = new Date().toISOString();
1538
+ this.config._sync.changeLog.push({
1539
+ timestamp: new Date().toISOString(),
1540
+ action: 'update',
1541
+ path,
1542
+ value
1543
+ });
1544
+
1545
+ this.saveConfig();
1546
+ }
1547
+
1548
+ /**
1549
+ * Get status of preseed documents
1550
+ */
1551
+ getStatus() {
1552
+ const configExists = this.loadConfig();
1553
+ const docs = this.getDocumentsToGenerate();
1554
+
1555
+ // Check for merged documents (may exist without config)
1556
+ let hasMergedDocs = false;
1557
+ let mergedDocCount = 0;
1558
+ const mergedDocNames = ['VISION.md', 'AUDIENCE.md', 'MARKET.md', 'COMPETITORS.md',
1559
+ 'BUSINESS_MODEL.md', 'PRD.md', 'TECHNICAL_SPEC.md', 'ROADMAP.md'];
1560
+
1561
+ for (const docName of mergedDocNames) {
1562
+ const docPath = path.join(this.outputDir, docName);
1563
+ if (fs.existsSync(docPath)) {
1564
+ hasMergedDocs = true;
1565
+ mergedDocCount++;
1566
+ }
1567
+ }
1568
+
1569
+ const status = {
1570
+ initialized: configExists || hasMergedDocs,
1571
+ initMode: configExists ? 'config' : (hasMergedDocs ? 'merged' : null),
1572
+ preset: this.preset,
1573
+ outputDir: this.outputDir,
1574
+ documents: []
1575
+ };
1576
+
1577
+ if (configExists) {
1578
+ status.lastSync = this.config._sync.lastSync;
1579
+ status.projectName = this.config.identity.name;
1580
+ } else if (hasMergedDocs) {
1581
+ status.projectName = '(Merged documents)';
1582
+ status.mergedDocCount = mergedDocCount;
1583
+ }
1584
+
1585
+ for (const doc of docs) {
1586
+ const filePath = path.join(this.outputDir, doc.name);
1587
+ const exists = fs.existsSync(filePath);
1588
+ let modified = null;
1589
+
1590
+ if (exists) {
1591
+ const stats = fs.statSync(filePath);
1592
+ modified = stats.mtime.toISOString();
1593
+ }
1594
+
1595
+ status.documents.push({
1596
+ type: doc.type,
1597
+ name: doc.name,
1598
+ title: doc.title,
1599
+ exists,
1600
+ modified,
1601
+ required: doc.required
1602
+ });
1603
+ }
1604
+
1605
+ return status;
1606
+ }
1607
+ }
1608
+
1609
+ module.exports = {
1610
+ PreseedEngine,
1611
+ DOCUMENT_TYPES,
1612
+ PRESETS,
1613
+ DEFAULT_OUTPUT_DIR,
1614
+ CONTEXT_INPUT_DIR,
1615
+ CONTEXT_FOLDERS,
1616
+ CONFIG_FILE
1617
+ };