@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.
- package/README.md +107 -14
- package/bin/bootspring.js +166 -27
- package/cli/agent.js +189 -17
- package/cli/analyze.js +499 -0
- package/cli/audit.js +557 -0
- package/cli/auth.js +495 -38
- package/cli/billing.js +302 -0
- package/cli/build.js +695 -0
- package/cli/business.js +109 -26
- package/cli/checkpoint-utils.js +168 -0
- package/cli/checkpoint.js +639 -0
- package/cli/cloud-sync.js +447 -0
- package/cli/content.js +198 -0
- package/cli/context.js +1 -1
- package/cli/deploy.js +543 -0
- package/cli/fundraise.js +112 -50
- package/cli/github-cmd.js +435 -0
- package/cli/health.js +477 -0
- package/cli/init.js +84 -13
- package/cli/legal.js +107 -95
- package/cli/log.js +2 -2
- package/cli/loop.js +976 -73
- package/cli/manager.js +711 -0
- package/cli/metrics.js +480 -0
- package/cli/monitor.js +812 -0
- package/cli/onboard.js +521 -0
- package/cli/orchestrator.js +12 -24
- package/cli/prd.js +594 -0
- package/cli/preseed-start.js +1483 -0
- package/cli/preseed.js +2302 -0
- package/cli/project.js +436 -0
- package/cli/quality.js +233 -0
- package/cli/security.js +913 -0
- package/cli/seed.js +1441 -5
- package/cli/skill.js +273 -211
- package/cli/suggest.js +989 -0
- package/cli/switch.js +453 -0
- package/cli/visualize.js +527 -0
- package/cli/watch.js +769 -0
- package/cli/workspace.js +607 -0
- package/core/analyze-workflow.js +1134 -0
- package/core/api-client.js +535 -22
- package/core/audit-workflow.js +1350 -0
- package/core/build-orchestrator.js +480 -0
- package/core/build-state.js +577 -0
- package/core/checkpoint-engine.js +408 -0
- package/core/config.js +1109 -26
- package/core/context-loader.js +21 -1
- package/core/deploy-workflow.js +836 -0
- package/core/entitlements.js +93 -22
- package/core/github-sync.js +610 -0
- package/core/index.js +8 -1
- package/core/ingest.js +1111 -0
- package/core/metrics-engine.js +768 -0
- package/core/onboard-workflow.js +1007 -0
- package/core/preseed-workflow.js +934 -0
- package/core/preseed.js +1617 -0
- package/core/project-context.js +325 -0
- package/core/project-state.js +694 -0
- package/core/r2-sync.js +583 -0
- package/core/scaffold.js +525 -7
- package/core/session.js +258 -0
- package/core/task-extractor.js +758 -0
- package/core/telemetry.js +28 -6
- package/core/tier-enforcement.js +737 -0
- package/core/utils.js +38 -14
- package/generators/questionnaire.js +15 -12
- package/generators/sections/ai.js +7 -7
- package/generators/sections/content.js +300 -0
- package/generators/sections/index.js +3 -0
- package/generators/sections/plugins.js +7 -6
- package/generators/templates/build-planning.template.js +596 -0
- package/generators/templates/content.template.js +819 -0
- package/generators/templates/index.js +2 -1
- package/hooks/git-autopilot.js +1250 -0
- package/hooks/index.js +9 -0
- package/intelligence/agent-collab.js +2057 -0
- package/intelligence/auto-suggest.js +634 -0
- package/intelligence/content-gen.js +1589 -0
- package/intelligence/cross-project.js +1647 -0
- package/intelligence/index.js +184 -0
- package/intelligence/learning/insights.json +517 -7
- package/intelligence/learning/pattern-learner.js +1008 -14
- package/intelligence/memory/decision-tracker.js +1431 -31
- package/intelligence/memory/decisions.jsonl +0 -0
- package/intelligence/orchestrator.js +2896 -1
- package/intelligence/prd.js +92 -1
- package/intelligence/recommendation-weights.json +14 -2
- package/intelligence/recommendations.js +463 -9
- package/intelligence/workflow-composer.js +1451 -0
- package/marketplace/index.d.ts +324 -0
- package/marketplace/index.js +1921 -0
- package/mcp/contracts/mcp-contract.v1.json +342 -4
- package/mcp/registry.js +680 -3
- package/mcp/response-formatter.js +23 -0
- package/mcp/tools/assist-tool.js +78 -4
- package/mcp/tools/autopilot-tool.js +408 -0
- package/mcp/tools/content-tool.js +571 -0
- package/mcp/tools/dashboard-tool.js +251 -5
- package/mcp/tools/mvp-tool.js +344 -0
- package/mcp/tools/plugin-tool.js +23 -1
- package/mcp/tools/prd-tool.js +579 -0
- package/mcp/tools/seed-tool.js +447 -0
- package/mcp/tools/skill-tool.js +43 -14
- package/mcp/tools/suggest-tool.js +147 -0
- package/package.json +15 -6
- package/agents/README.md +0 -93
- package/agents/ai-integration-expert/context.md +0 -386
- package/agents/api-expert/context.md +0 -416
- package/agents/architecture-expert/context.md +0 -454
- package/agents/auth-expert/context.md +0 -399
- package/agents/backend-expert/context.md +0 -483
- package/agents/business-strategy-expert/context.md +0 -180
- package/agents/code-review-expert/context.md +0 -365
- package/agents/competitive-analysis-expert/context.md +0 -239
- package/agents/data-modeling-expert/context.md +0 -352
- package/agents/database-expert/context.md +0 -250
- package/agents/devops-expert/context.md +0 -446
- package/agents/email-expert/context.md +0 -379
- package/agents/financial-expert/context.md +0 -213
- package/agents/frontend-expert/context.md +0 -364
- package/agents/fundraising-expert/context.md +0 -257
- package/agents/growth-expert/context.md +0 -249
- package/agents/index.js +0 -140
- package/agents/investor-relations-expert/context.md +0 -266
- package/agents/legal-expert/context.md +0 -284
- package/agents/marketing-expert/context.md +0 -236
- package/agents/monitoring-expert/context.md +0 -362
- package/agents/operations-expert/context.md +0 -279
- package/agents/partnerships-expert/context.md +0 -286
- package/agents/payment-expert/context.md +0 -340
- package/agents/performance-expert/context.md +0 -377
- package/agents/private-equity-expert/context.md +0 -246
- package/agents/railway-expert/context.md +0 -284
- package/agents/research-expert/context.md +0 -245
- package/agents/sales-expert/context.md +0 -241
- package/agents/security-expert/context.md +0 -343
- package/agents/testing-expert/context.md +0 -414
- package/agents/ui-ux-expert/context.md +0 -448
- package/agents/vercel-expert/context.md +0 -426
- package/skills/index.js +0 -787
- package/skills/patterns/README.md +0 -163
- package/skills/patterns/ai/agents.md +0 -281
- package/skills/patterns/ai/claude.md +0 -138
- package/skills/patterns/ai/embeddings.md +0 -150
- package/skills/patterns/ai/rag.md +0 -266
- package/skills/patterns/ai/streaming.md +0 -170
- package/skills/patterns/ai/structured-output.md +0 -162
- package/skills/patterns/ai/tools.md +0 -154
- package/skills/patterns/analytics/tracking.md +0 -220
- package/skills/patterns/api/errors.md +0 -296
- package/skills/patterns/api/graphql.md +0 -440
- package/skills/patterns/api/middleware.md +0 -279
- package/skills/patterns/api/openapi.md +0 -285
- package/skills/patterns/api/rate-limiting.md +0 -231
- package/skills/patterns/api/route-handler.md +0 -217
- package/skills/patterns/api/server-action.md +0 -249
- package/skills/patterns/api/versioning.md +0 -443
- package/skills/patterns/api/webhooks.md +0 -247
- package/skills/patterns/auth/clerk.md +0 -132
- package/skills/patterns/auth/mfa.md +0 -313
- package/skills/patterns/auth/nextauth.md +0 -140
- package/skills/patterns/auth/oauth.md +0 -237
- package/skills/patterns/auth/rbac.md +0 -152
- package/skills/patterns/auth/session-management.md +0 -367
- package/skills/patterns/auth/session.md +0 -120
- package/skills/patterns/database/audit.md +0 -177
- package/skills/patterns/database/migrations.md +0 -177
- package/skills/patterns/database/pagination.md +0 -230
- package/skills/patterns/database/pooling.md +0 -357
- package/skills/patterns/database/prisma.md +0 -180
- package/skills/patterns/database/relations.md +0 -187
- package/skills/patterns/database/seeding.md +0 -246
- package/skills/patterns/database/soft-delete.md +0 -153
- package/skills/patterns/database/transactions.md +0 -162
- package/skills/patterns/deployment/ci-cd.md +0 -231
- package/skills/patterns/deployment/docker.md +0 -188
- package/skills/patterns/deployment/monitoring.md +0 -387
- package/skills/patterns/deployment/vercel.md +0 -160
- package/skills/patterns/email/resend.md +0 -143
- package/skills/patterns/email/templates.md +0 -245
- package/skills/patterns/email/transactional.md +0 -503
- package/skills/patterns/email/verification.md +0 -176
- package/skills/patterns/files/download.md +0 -243
- package/skills/patterns/files/upload.md +0 -239
- package/skills/patterns/i18n/nextintl.md +0 -188
- package/skills/patterns/logging/structured.md +0 -292
- package/skills/patterns/notifications/email-queue.md +0 -248
- package/skills/patterns/notifications/push.md +0 -279
- package/skills/patterns/payments/checkout.md +0 -303
- package/skills/patterns/payments/invoices.md +0 -287
- package/skills/patterns/payments/portal.md +0 -245
- package/skills/patterns/payments/stripe.md +0 -272
- package/skills/patterns/payments/subscriptions.md +0 -300
- package/skills/patterns/payments/usage.md +0 -279
- package/skills/patterns/performance/caching.md +0 -276
- package/skills/patterns/performance/code-splitting.md +0 -233
- package/skills/patterns/performance/edge.md +0 -254
- package/skills/patterns/performance/isr.md +0 -266
- package/skills/patterns/performance/lazy-loading.md +0 -281
- package/skills/patterns/realtime/sse.md +0 -327
- package/skills/patterns/realtime/websockets.md +0 -336
- package/skills/patterns/search/filtering.md +0 -329
- package/skills/patterns/search/fulltext.md +0 -260
- package/skills/patterns/security/audit-logging.md +0 -444
- package/skills/patterns/security/csrf.md +0 -234
- package/skills/patterns/security/headers.md +0 -252
- package/skills/patterns/security/sanitization.md +0 -258
- package/skills/patterns/security/secrets.md +0 -261
- package/skills/patterns/security/validation.md +0 -268
- package/skills/patterns/security/xss.md +0 -229
- package/skills/patterns/seo/metadata.md +0 -252
- package/skills/patterns/state/context.md +0 -349
- package/skills/patterns/state/react-query.md +0 -313
- package/skills/patterns/state/url-state.md +0 -482
- package/skills/patterns/state/zustand.md +0 -262
- package/skills/patterns/testing/api.md +0 -259
- package/skills/patterns/testing/component.md +0 -233
- package/skills/patterns/testing/coverage.md +0 -207
- package/skills/patterns/testing/fixtures.md +0 -225
- package/skills/patterns/testing/integration.md +0 -436
- package/skills/patterns/testing/mocking.md +0 -177
- package/skills/patterns/testing/playwright.md +0 -162
- package/skills/patterns/testing/snapshot.md +0 -175
- package/skills/patterns/testing/vitest.md +0 -307
- package/skills/patterns/ui/accordions.md +0 -395
- package/skills/patterns/ui/cards.md +0 -299
- package/skills/patterns/ui/dropdowns.md +0 -476
- package/skills/patterns/ui/empty-states.md +0 -320
- package/skills/patterns/ui/forms.md +0 -405
- package/skills/patterns/ui/inputs.md +0 -319
- package/skills/patterns/ui/layouts.md +0 -282
- package/skills/patterns/ui/loading.md +0 -291
- package/skills/patterns/ui/modals.md +0 -338
- package/skills/patterns/ui/navigation.md +0 -374
- package/skills/patterns/ui/tables.md +0 -407
- package/skills/patterns/ui/toasts.md +0 -300
- package/skills/patterns/ui/tooltips.md +0 -396
- package/skills/patterns/utils/dates.md +0 -435
- package/skills/patterns/utils/errors.md +0 -451
- package/skills/patterns/utils/formatting.md +0 -345
- package/skills/patterns/utils/validation.md +0 -434
- package/templates/bootspring.config.js +0 -83
- package/templates/business/business-model-canvas.md +0 -246
- package/templates/business/business-plan.md +0 -266
- package/templates/business/competitive-analysis.md +0 -312
- package/templates/fundraising/data-room-checklist.md +0 -300
- package/templates/fundraising/investor-research.md +0 -243
- package/templates/fundraising/pitch-deck-outline.md +0 -253
- package/templates/legal/gdpr-checklist.md +0 -339
- package/templates/legal/privacy-policy.md +0 -285
- package/templates/legal/terms-of-service.md +0 -222
- package/templates/mcp.json +0 -9
package/core/preseed.js
ADDED
|
@@ -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
|
+
};
|