@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
|
@@ -0,0 +1,1483 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring Preseed Start
|
|
3
|
+
* Intelligent, context-aware entry point for idea-to-app journey
|
|
4
|
+
*
|
|
5
|
+
* Detects:
|
|
6
|
+
* - Environment (CLI, Claude Code/MCP, VS Code)
|
|
7
|
+
* - Auth status and tier
|
|
8
|
+
* - Existing context (files, config, previous progress)
|
|
9
|
+
* - Best path forward based on all signals
|
|
10
|
+
*
|
|
11
|
+
* @package bootspring
|
|
12
|
+
* @module cli/preseed-start
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const readline = require('readline');
|
|
18
|
+
const os = require('os');
|
|
19
|
+
const config = require('../core/config');
|
|
20
|
+
const utils = require('../core/utils');
|
|
21
|
+
const auth = require('../core/auth');
|
|
22
|
+
const session = require('../core/session');
|
|
23
|
+
const apiClient = require('../core/api-client');
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Context Detection
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Detect the current environment and capabilities
|
|
31
|
+
*/
|
|
32
|
+
function detectEnvironment() {
|
|
33
|
+
const env = {
|
|
34
|
+
// Environment type
|
|
35
|
+
isClaudeCode: !!process.env.CLAUDE_CODE || !!process.env.MCP_SERVER,
|
|
36
|
+
isVSCode: !!process.env.VSCODE_PID || !!process.env.TERM_PROGRAM?.includes('vscode'),
|
|
37
|
+
isCursor: !!process.env.CURSOR_TRACE_ID,
|
|
38
|
+
isInteractive: process.stdin.isTTY && process.stdout.isTTY,
|
|
39
|
+
|
|
40
|
+
// Auth and tier
|
|
41
|
+
isAuthenticated: auth.isAuthenticated(),
|
|
42
|
+
tier: auth.getTier(),
|
|
43
|
+
user: auth.getUser(),
|
|
44
|
+
|
|
45
|
+
// Capabilities based on tier
|
|
46
|
+
capabilities: {
|
|
47
|
+
aiGeneration: false,
|
|
48
|
+
dashboardAccess: false,
|
|
49
|
+
teamCollaboration: false,
|
|
50
|
+
prioritySupport: false,
|
|
51
|
+
unlimitedProjects: false
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
// Platform
|
|
55
|
+
platform: os.platform(),
|
|
56
|
+
shell: process.env.SHELL || 'unknown'
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Set capabilities based on tier
|
|
60
|
+
const tier = env.tier;
|
|
61
|
+
if (tier === 'pro' || tier === 'team' || tier === 'enterprise') {
|
|
62
|
+
env.capabilities.aiGeneration = true;
|
|
63
|
+
env.capabilities.dashboardAccess = true;
|
|
64
|
+
}
|
|
65
|
+
if (tier === 'team' || tier === 'enterprise') {
|
|
66
|
+
env.capabilities.teamCollaboration = true;
|
|
67
|
+
}
|
|
68
|
+
if (tier === 'enterprise') {
|
|
69
|
+
env.capabilities.prioritySupport = true;
|
|
70
|
+
env.capabilities.unlimitedProjects = true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return env;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Detect existing project context
|
|
78
|
+
*/
|
|
79
|
+
function detectProjectContext(projectRoot) {
|
|
80
|
+
const context = {
|
|
81
|
+
hasBootspringFolder: false,
|
|
82
|
+
hasPreseedConfig: false,
|
|
83
|
+
hasPreseedDocs: [],
|
|
84
|
+
hasContextFiles: [],
|
|
85
|
+
hasSeedMd: false,
|
|
86
|
+
hasClaudeMd: false,
|
|
87
|
+
hasPackageJson: false,
|
|
88
|
+
projectName: null,
|
|
89
|
+
previousProgress: null,
|
|
90
|
+
|
|
91
|
+
// What we can work with
|
|
92
|
+
existingAssets: []
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// Check .bootspring folder
|
|
96
|
+
const bootspringDir = path.join(projectRoot, '.bootspring');
|
|
97
|
+
context.hasBootspringFolder = fs.existsSync(bootspringDir);
|
|
98
|
+
|
|
99
|
+
// Check preseed config
|
|
100
|
+
const preseedConfigPath = path.join(bootspringDir, 'preseed', 'PRESEED_CONFIG.json');
|
|
101
|
+
if (fs.existsSync(preseedConfigPath)) {
|
|
102
|
+
context.hasPreseedConfig = true;
|
|
103
|
+
try {
|
|
104
|
+
const cfg = JSON.parse(fs.readFileSync(preseedConfigPath, 'utf-8'));
|
|
105
|
+
context.projectName = cfg.identity?.name;
|
|
106
|
+
} catch {}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Check for preseed documents
|
|
110
|
+
const preseedDir = path.join(bootspringDir, 'preseed');
|
|
111
|
+
if (fs.existsSync(preseedDir)) {
|
|
112
|
+
const files = fs.readdirSync(preseedDir).filter(f => f.endsWith('.md'));
|
|
113
|
+
context.hasPreseedDocs = files;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Check for context files (user uploads, research, etc.)
|
|
117
|
+
const contextDir = path.join(bootspringDir, 'preseed', 'context');
|
|
118
|
+
context.hasDropFiles = [];
|
|
119
|
+
if (fs.existsSync(contextDir)) {
|
|
120
|
+
// Check drop folder first (universal inbox)
|
|
121
|
+
const dropDir = path.join(contextDir, 'drop');
|
|
122
|
+
if (fs.existsSync(dropDir)) {
|
|
123
|
+
const dropFiles = fs.readdirSync(dropDir).filter(f => !f.startsWith('.'));
|
|
124
|
+
if (dropFiles.length > 0) {
|
|
125
|
+
context.hasDropFiles = dropFiles;
|
|
126
|
+
context.existingAssets.push(`${dropFiles.length} file(s) in drop zone`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Check categorized folders (includes document-specific folders)
|
|
131
|
+
const categories = [
|
|
132
|
+
// General folders
|
|
133
|
+
'ideas', 'research',
|
|
134
|
+
// Document-specific folders (maps 1:1 to output)
|
|
135
|
+
'vision', 'audience', 'market', 'competitors', 'business', 'prd', 'technical', 'roadmap'
|
|
136
|
+
];
|
|
137
|
+
for (const cat of categories) {
|
|
138
|
+
const catDir = path.join(contextDir, cat);
|
|
139
|
+
if (fs.existsSync(catDir)) {
|
|
140
|
+
const files = fs.readdirSync(catDir).filter(f => !f.startsWith('.'));
|
|
141
|
+
if (files.length > 0) {
|
|
142
|
+
context.hasContextFiles.push({ category: cat, files });
|
|
143
|
+
context.existingAssets.push(`${files.length} ${cat} file(s)`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Check for SEED.md
|
|
150
|
+
context.hasSeedMd = fs.existsSync(path.join(projectRoot, 'SEED.md'));
|
|
151
|
+
|
|
152
|
+
// Check for CLAUDE.md
|
|
153
|
+
context.hasClaudeMd = fs.existsSync(path.join(projectRoot, 'CLAUDE.md'));
|
|
154
|
+
|
|
155
|
+
// Check for package.json (existing project)
|
|
156
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
157
|
+
if (fs.existsSync(pkgPath)) {
|
|
158
|
+
context.hasPackageJson = true;
|
|
159
|
+
try {
|
|
160
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
161
|
+
context.projectName = context.projectName || pkg.name;
|
|
162
|
+
} catch {}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Check for previous wizard progress
|
|
166
|
+
const progressPath = path.join(bootspringDir, 'preseed', '.wizard-progress.json');
|
|
167
|
+
if (fs.existsSync(progressPath)) {
|
|
168
|
+
try {
|
|
169
|
+
context.previousProgress = JSON.parse(fs.readFileSync(progressPath, 'utf-8'));
|
|
170
|
+
} catch {}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return context;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Determine the best starting point based on context
|
|
178
|
+
*/
|
|
179
|
+
function determineStartingPoint(env, projectContext) {
|
|
180
|
+
const recommendations = [];
|
|
181
|
+
|
|
182
|
+
// If they have previous progress, offer to resume
|
|
183
|
+
if (projectContext.previousProgress) {
|
|
184
|
+
recommendations.push({
|
|
185
|
+
id: 'resume',
|
|
186
|
+
label: 'Resume where you left off',
|
|
187
|
+
description: `Continue from ${projectContext.previousProgress.lastStep || 'previous session'}`,
|
|
188
|
+
priority: 1
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// If they have preseed docs, offer to continue to seed
|
|
193
|
+
if (projectContext.hasPreseedDocs.length >= 3) {
|
|
194
|
+
recommendations.push({
|
|
195
|
+
id: 'synthesize',
|
|
196
|
+
label: 'Generate SEED.md from your preseed docs',
|
|
197
|
+
description: `You have ${projectContext.hasPreseedDocs.length} preseed documents ready`,
|
|
198
|
+
priority: 2
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// If they have files in the drop zone, offer to analyze them
|
|
203
|
+
if (projectContext.hasDropFiles && projectContext.hasDropFiles.length > 0) {
|
|
204
|
+
recommendations.push({
|
|
205
|
+
id: 'analyze-drop',
|
|
206
|
+
label: 'Analyze your drop zone files',
|
|
207
|
+
description: `Process ${projectContext.hasDropFiles.length} file(s) and extract insights`,
|
|
208
|
+
priority: 2
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// If they have context files, offer to use them
|
|
213
|
+
if (projectContext.hasContextFiles.length > 0) {
|
|
214
|
+
recommendations.push({
|
|
215
|
+
id: 'from-context',
|
|
216
|
+
label: 'Build from your existing materials',
|
|
217
|
+
description: `Use your ${projectContext.existingAssets.join(', ')}`,
|
|
218
|
+
priority: 3
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// If in Claude Code, highlight MCP capabilities
|
|
223
|
+
if (env.isClaudeCode) {
|
|
224
|
+
recommendations.push({
|
|
225
|
+
id: 'claude-code',
|
|
226
|
+
label: 'Use Claude to build your preseed',
|
|
227
|
+
description: 'Just describe your idea - Claude will create the documents',
|
|
228
|
+
priority: env.capabilities.aiGeneration ? 1 : 4
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// If they have AI generation capability
|
|
233
|
+
if (env.capabilities.aiGeneration) {
|
|
234
|
+
recommendations.push({
|
|
235
|
+
id: 'ai-wizard',
|
|
236
|
+
label: 'AI-Powered Wizard',
|
|
237
|
+
description: 'Answer questions, AI generates professional documents',
|
|
238
|
+
priority: 2
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Always available options
|
|
243
|
+
recommendations.push({
|
|
244
|
+
id: 'guided-wizard',
|
|
245
|
+
label: 'Guided Wizard',
|
|
246
|
+
description: 'Step-by-step questions to capture your idea',
|
|
247
|
+
priority: 5
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
recommendations.push({
|
|
251
|
+
id: 'ai-prompts',
|
|
252
|
+
label: 'Get AI Prompts',
|
|
253
|
+
description: 'Copy prompts to use with Claude, ChatGPT, or any LLM',
|
|
254
|
+
priority: 6
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
recommendations.push({
|
|
258
|
+
id: 'idea-dump',
|
|
259
|
+
label: 'Brain Dump',
|
|
260
|
+
description: 'Just write freely - we\'ll structure it for you',
|
|
261
|
+
priority: 7
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
if (env.capabilities.dashboardAccess) {
|
|
265
|
+
recommendations.push({
|
|
266
|
+
id: 'dashboard',
|
|
267
|
+
label: 'Open Dashboard Studio',
|
|
268
|
+
description: 'Visual editor with AI generation in your browser',
|
|
269
|
+
priority: 4
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Sort by priority
|
|
274
|
+
recommendations.sort((a, b) => a.priority - b.priority);
|
|
275
|
+
|
|
276
|
+
return recommendations;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ============================================================================
|
|
280
|
+
// UI Components
|
|
281
|
+
// ============================================================================
|
|
282
|
+
|
|
283
|
+
function createInterface() {
|
|
284
|
+
return readline.createInterface({
|
|
285
|
+
input: process.stdin,
|
|
286
|
+
output: process.stdout
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function askChoice(rl, options) {
|
|
291
|
+
return new Promise((resolve) => {
|
|
292
|
+
options.forEach((opt, i) => {
|
|
293
|
+
const num = `${i + 1}`.padStart(2, ' ');
|
|
294
|
+
console.log(` ${utils.COLORS.cyan}${num}${utils.COLORS.reset} ${opt.label}`);
|
|
295
|
+
if (opt.description) {
|
|
296
|
+
console.log(` ${utils.COLORS.dim}${opt.description}${utils.COLORS.reset}`);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
console.log();
|
|
301
|
+
rl.question(`${utils.COLORS.dim}Select [1-${options.length}]:${utils.COLORS.reset} `, (answer) => {
|
|
302
|
+
const index = parseInt(answer, 10) - 1;
|
|
303
|
+
if (index >= 0 && index < options.length) {
|
|
304
|
+
resolve(options[index]);
|
|
305
|
+
} else {
|
|
306
|
+
resolve(options[0]); // Default to first
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function askText(rl, question, hint = '') {
|
|
313
|
+
return new Promise((resolve) => {
|
|
314
|
+
const hintText = hint ? ` ${utils.COLORS.dim}${hint}${utils.COLORS.reset}` : '';
|
|
315
|
+
rl.question(`${question}${hintText}: `, (answer) => {
|
|
316
|
+
resolve(answer.trim());
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function displayHeader(env, projectContext) {
|
|
322
|
+
console.log();
|
|
323
|
+
console.log(`${utils.COLORS.cyan}${utils.COLORS.bold} ╔══════════════════════════════════════════════════════════╗${utils.COLORS.reset}`);
|
|
324
|
+
console.log(`${utils.COLORS.cyan}${utils.COLORS.bold} ║${utils.COLORS.reset} ${utils.COLORS.bold}BOOTSPRING PRESEED STUDIO${utils.COLORS.reset} ${utils.COLORS.cyan}${utils.COLORS.bold}║${utils.COLORS.reset}`);
|
|
325
|
+
console.log(`${utils.COLORS.cyan}${utils.COLORS.bold} ║${utils.COLORS.reset} ${utils.COLORS.dim}Transform your idea into a production app${utils.COLORS.reset} ${utils.COLORS.cyan}${utils.COLORS.bold}║${utils.COLORS.reset}`);
|
|
326
|
+
console.log(`${utils.COLORS.cyan}${utils.COLORS.bold} ╚══════════════════════════════════════════════════════════╝${utils.COLORS.reset}`);
|
|
327
|
+
console.log();
|
|
328
|
+
|
|
329
|
+
// Show context badges
|
|
330
|
+
const badges = [];
|
|
331
|
+
|
|
332
|
+
if (env.isAuthenticated) {
|
|
333
|
+
const tierBadge = env.tier === 'free'
|
|
334
|
+
? `${utils.COLORS.dim}FREE${utils.COLORS.reset}`
|
|
335
|
+
: `${utils.COLORS.green}${env.tier.toUpperCase()}${utils.COLORS.reset}`;
|
|
336
|
+
badges.push(tierBadge);
|
|
337
|
+
} else {
|
|
338
|
+
badges.push(`${utils.COLORS.yellow}NOT LOGGED IN${utils.COLORS.reset}`);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (env.isClaudeCode) {
|
|
342
|
+
badges.push(`${utils.COLORS.magenta}CLAUDE CODE${utils.COLORS.reset}`);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (projectContext.projectName) {
|
|
346
|
+
badges.push(`${utils.COLORS.cyan}${projectContext.projectName}${utils.COLORS.reset}`);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
console.log(` ${badges.join(' │ ')}`);
|
|
350
|
+
console.log();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function displayJourney(projectContext) {
|
|
354
|
+
const stages = [
|
|
355
|
+
{ id: 'idea', label: 'Idea', done: true }, // They're here, so they have an idea
|
|
356
|
+
{ id: 'preseed', label: 'Preseed', done: projectContext.hasPreseedDocs.length >= 3 },
|
|
357
|
+
{ id: 'seed', label: 'Seed', done: projectContext.hasSeedMd },
|
|
358
|
+
{ id: 'scaffold', label: 'App', done: projectContext.hasPackageJson && projectContext.hasClaudeMd }
|
|
359
|
+
];
|
|
360
|
+
|
|
361
|
+
const journeyLine = stages.map((stage, i) => {
|
|
362
|
+
const icon = stage.done ? `${utils.COLORS.green}●${utils.COLORS.reset}` : `${utils.COLORS.dim}○${utils.COLORS.reset}`;
|
|
363
|
+
const label = stage.done ? `${utils.COLORS.green}${stage.label}${utils.COLORS.reset}` : `${utils.COLORS.dim}${stage.label}${utils.COLORS.reset}`;
|
|
364
|
+
const connector = i < stages.length - 1 ? ` ${utils.COLORS.dim}───${utils.COLORS.reset} ` : '';
|
|
365
|
+
return `${icon} ${label}${connector}`;
|
|
366
|
+
}).join('');
|
|
367
|
+
|
|
368
|
+
console.log(` ${utils.COLORS.bold}Your Journey:${utils.COLORS.reset}`);
|
|
369
|
+
console.log(` ${journeyLine}`);
|
|
370
|
+
console.log();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ============================================================================
|
|
374
|
+
// Action Handlers
|
|
375
|
+
// ============================================================================
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Handle the guided wizard flow
|
|
379
|
+
*/
|
|
380
|
+
async function handleGuidedWizard(rl, projectRoot, env) {
|
|
381
|
+
console.log(`\n${utils.COLORS.bold}Starting Guided Wizard...${utils.COLORS.reset}\n`);
|
|
382
|
+
|
|
383
|
+
// Import the preseed module and run wizard
|
|
384
|
+
const preseed = require('./preseed');
|
|
385
|
+
await preseed.run(['init']);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Handle AI prompts generation
|
|
390
|
+
*/
|
|
391
|
+
async function handleAIPrompts(rl, projectRoot, env, projectContext) {
|
|
392
|
+
console.log(`\n${utils.COLORS.bold}AI Prompt Generator${utils.COLORS.reset}`);
|
|
393
|
+
console.log(`${utils.COLORS.dim}Get ready-to-use prompts for Claude, ChatGPT, or any LLM${utils.COLORS.reset}\n`);
|
|
394
|
+
|
|
395
|
+
// Check if we have wizard data to personalize prompts
|
|
396
|
+
let wizardData = {};
|
|
397
|
+
const wizardPath = path.join(projectRoot, '.bootspring', 'preseed', 'PRESEED_CONFIG.json');
|
|
398
|
+
if (fs.existsSync(wizardPath)) {
|
|
399
|
+
try {
|
|
400
|
+
wizardData = JSON.parse(fs.readFileSync(wizardPath, 'utf-8'));
|
|
401
|
+
} catch {}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Ask which document they want to generate
|
|
405
|
+
console.log(`${utils.COLORS.bold}Which document do you want to create?${utils.COLORS.reset}\n`);
|
|
406
|
+
|
|
407
|
+
const documents = [
|
|
408
|
+
{ id: 'vision', label: 'Vision Document', description: 'Problem, solution, and unique value' },
|
|
409
|
+
{ id: 'audience', label: 'Target Audience', description: 'User personas and market segments' },
|
|
410
|
+
{ id: 'prd', label: 'Product Requirements', description: 'Features, user stories, MVP scope' },
|
|
411
|
+
{ id: 'technical', label: 'Technical Spec', description: 'Architecture and tech stack' },
|
|
412
|
+
{ id: 'roadmap', label: 'Roadmap', description: 'Development phases and milestones' },
|
|
413
|
+
{ id: 'all', label: 'All Documents', description: 'Get prompts for everything' }
|
|
414
|
+
];
|
|
415
|
+
|
|
416
|
+
const selected = await askChoice(rl, documents);
|
|
417
|
+
|
|
418
|
+
// Generate the prompt
|
|
419
|
+
const prompt = generateAIPrompt(selected.id, wizardData);
|
|
420
|
+
|
|
421
|
+
console.log(`\n${utils.COLORS.green}━━━ COPY THIS PROMPT ━━━${utils.COLORS.reset}\n`);
|
|
422
|
+
console.log(prompt);
|
|
423
|
+
console.log(`\n${utils.COLORS.green}━━━ END OF PROMPT ━━━${utils.COLORS.reset}\n`);
|
|
424
|
+
|
|
425
|
+
// Offer to save or copy
|
|
426
|
+
console.log(`${utils.COLORS.dim}Paste this prompt into Claude, ChatGPT, or your favorite LLM.${utils.COLORS.reset}`);
|
|
427
|
+
console.log(`${utils.COLORS.dim}Then save the response to: .bootspring/preseed/${selected.id.toUpperCase()}.md${utils.COLORS.reset}\n`);
|
|
428
|
+
|
|
429
|
+
// Save prompt to file for easy access
|
|
430
|
+
const promptsDir = path.join(projectRoot, '.bootspring', 'preseed', 'prompts');
|
|
431
|
+
if (!fs.existsSync(promptsDir)) {
|
|
432
|
+
fs.mkdirSync(promptsDir, { recursive: true });
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const promptFile = path.join(promptsDir, `${selected.id}-prompt.md`);
|
|
436
|
+
fs.writeFileSync(promptFile, prompt);
|
|
437
|
+
console.log(`${utils.COLORS.green}✓${utils.COLORS.reset} Prompt saved to: ${utils.COLORS.cyan}${promptFile}${utils.COLORS.reset}`);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Generate AI prompt for a document type
|
|
442
|
+
*/
|
|
443
|
+
function generateAIPrompt(docType, wizardData) {
|
|
444
|
+
const projectName = wizardData.identity?.name || '[YOUR PROJECT NAME]';
|
|
445
|
+
const tagline = wizardData.identity?.tagline || '[YOUR TAGLINE]';
|
|
446
|
+
const problem = wizardData.problem?.statement || '[THE PROBLEM YOU SOLVE]';
|
|
447
|
+
const solution = wizardData.solution?.description || '[YOUR SOLUTION]';
|
|
448
|
+
const audience = wizardData.audience?.primary || '[YOUR TARGET AUDIENCE]';
|
|
449
|
+
|
|
450
|
+
const prompts = {
|
|
451
|
+
vision: `# Generate a Vision Document
|
|
452
|
+
|
|
453
|
+
I'm building ${projectName} - ${tagline}.
|
|
454
|
+
|
|
455
|
+
**The Problem:**
|
|
456
|
+
${problem}
|
|
457
|
+
|
|
458
|
+
**My Solution:**
|
|
459
|
+
${solution}
|
|
460
|
+
|
|
461
|
+
**Target Audience:**
|
|
462
|
+
${audience}
|
|
463
|
+
|
|
464
|
+
Please create a comprehensive Vision Document that includes:
|
|
465
|
+
|
|
466
|
+
1. **Executive Summary** - One paragraph overview
|
|
467
|
+
2. **Problem Statement** - Deep dive into the pain points
|
|
468
|
+
3. **Solution Overview** - How we solve it uniquely
|
|
469
|
+
4. **Key Features** - Top 5-7 features with descriptions
|
|
470
|
+
5. **Value Proposition** - Why users will choose us
|
|
471
|
+
6. **Success Metrics** - How we'll measure success
|
|
472
|
+
|
|
473
|
+
Format as clean Markdown. Be specific and actionable, not generic.`,
|
|
474
|
+
|
|
475
|
+
audience: `# Generate a Target Audience Document
|
|
476
|
+
|
|
477
|
+
I'm building ${projectName} - ${tagline}.
|
|
478
|
+
|
|
479
|
+
**Our Solution:**
|
|
480
|
+
${solution}
|
|
481
|
+
|
|
482
|
+
Please create a comprehensive Target Audience Document that includes:
|
|
483
|
+
|
|
484
|
+
1. **Primary Audience** - Main user segment with demographics
|
|
485
|
+
2. **Secondary Audiences** - Other potential user groups
|
|
486
|
+
3. **User Personas** - Create 2-3 detailed personas with:
|
|
487
|
+
- Name, role, background
|
|
488
|
+
- Goals and motivations
|
|
489
|
+
- Pain points and frustrations
|
|
490
|
+
- How our product helps them
|
|
491
|
+
- Quote that captures their perspective
|
|
492
|
+
4. **Market Segments** - B2B, B2C, enterprise, SMB, etc.
|
|
493
|
+
5. **Ideal Customer Profile (ICP)** - Specific characteristics
|
|
494
|
+
|
|
495
|
+
Format as clean Markdown with tables where appropriate.`,
|
|
496
|
+
|
|
497
|
+
prd: `# Generate a Product Requirements Document (PRD)
|
|
498
|
+
|
|
499
|
+
I'm building ${projectName} - ${tagline}.
|
|
500
|
+
|
|
501
|
+
**The Problem:**
|
|
502
|
+
${problem}
|
|
503
|
+
|
|
504
|
+
**Our Solution:**
|
|
505
|
+
${solution}
|
|
506
|
+
|
|
507
|
+
**Target Audience:**
|
|
508
|
+
${audience}
|
|
509
|
+
|
|
510
|
+
Please create a comprehensive PRD that includes:
|
|
511
|
+
|
|
512
|
+
1. **Product Vision** - The north star
|
|
513
|
+
2. **MVP Scope** - What's in v1.0 vs later
|
|
514
|
+
3. **Feature List** - Prioritized (P0, P1, P2) with:
|
|
515
|
+
- Feature name
|
|
516
|
+
- Description
|
|
517
|
+
- User value
|
|
518
|
+
- Acceptance criteria
|
|
519
|
+
4. **User Stories** - 5-10 key stories in "As a... I want... So that..." format
|
|
520
|
+
5. **Non-Functional Requirements** - Performance, security, scalability
|
|
521
|
+
6. **Out of Scope** - What we're NOT building in MVP
|
|
522
|
+
7. **Dependencies & Risks**
|
|
523
|
+
|
|
524
|
+
Format as clean Markdown. Be specific enough that a developer could implement from this.`,
|
|
525
|
+
|
|
526
|
+
technical: `# Generate a Technical Specification
|
|
527
|
+
|
|
528
|
+
I'm building ${projectName} - ${tagline}.
|
|
529
|
+
|
|
530
|
+
**Our Solution:**
|
|
531
|
+
${solution}
|
|
532
|
+
|
|
533
|
+
Please create a Technical Specification that includes:
|
|
534
|
+
|
|
535
|
+
1. **Technology Stack Recommendation**
|
|
536
|
+
- Frontend framework and why
|
|
537
|
+
- Backend/API approach
|
|
538
|
+
- Database choice
|
|
539
|
+
- Hosting/deployment
|
|
540
|
+
- Key libraries/tools
|
|
541
|
+
|
|
542
|
+
2. **Architecture Overview**
|
|
543
|
+
- High-level system design
|
|
544
|
+
- Component diagram (describe in text)
|
|
545
|
+
- Data flow
|
|
546
|
+
|
|
547
|
+
3. **Data Models** - Key entities and relationships
|
|
548
|
+
|
|
549
|
+
4. **API Design** - Main endpoints needed
|
|
550
|
+
|
|
551
|
+
5. **Third-Party Integrations** - What external services
|
|
552
|
+
|
|
553
|
+
6. **Security Considerations** - Auth, data protection
|
|
554
|
+
|
|
555
|
+
7. **Scalability Plan** - How it grows
|
|
556
|
+
|
|
557
|
+
Format as clean Markdown with code blocks where helpful.`,
|
|
558
|
+
|
|
559
|
+
roadmap: `# Generate a Product Roadmap
|
|
560
|
+
|
|
561
|
+
I'm building ${projectName} - ${tagline}.
|
|
562
|
+
|
|
563
|
+
**MVP Features:**
|
|
564
|
+
${wizardData.mvp?.features?.join(', ') || '[LIST YOUR MVP FEATURES]'}
|
|
565
|
+
|
|
566
|
+
Please create a Product Roadmap that includes:
|
|
567
|
+
|
|
568
|
+
1. **Phase 1: MVP** (Target: 4-6 weeks)
|
|
569
|
+
- Core features to ship
|
|
570
|
+
- Success criteria
|
|
571
|
+
- Key milestones
|
|
572
|
+
|
|
573
|
+
2. **Phase 2: Growth** (Next 2-3 months)
|
|
574
|
+
- Features to add
|
|
575
|
+
- User acquisition focus
|
|
576
|
+
- Iteration based on feedback
|
|
577
|
+
|
|
578
|
+
3. **Phase 3: Scale** (3-6 months out)
|
|
579
|
+
- Advanced features
|
|
580
|
+
- Enterprise/team features
|
|
581
|
+
- Platform expansion
|
|
582
|
+
|
|
583
|
+
4. **Milestone Timeline** - Visual table format
|
|
584
|
+
|
|
585
|
+
5. **Dependencies & Risks** - What could block progress
|
|
586
|
+
|
|
587
|
+
Format as clean Markdown with a timeline table.`,
|
|
588
|
+
|
|
589
|
+
all: `# Generate Complete Preseed Documentation
|
|
590
|
+
|
|
591
|
+
I'm building ${projectName} - ${tagline}.
|
|
592
|
+
|
|
593
|
+
**The Problem:**
|
|
594
|
+
${problem}
|
|
595
|
+
|
|
596
|
+
**My Solution:**
|
|
597
|
+
${solution}
|
|
598
|
+
|
|
599
|
+
**Target Audience:**
|
|
600
|
+
${audience}
|
|
601
|
+
|
|
602
|
+
Please generate a complete set of preseed documents. For each document, create a separate section:
|
|
603
|
+
|
|
604
|
+
## 1. VISION.md
|
|
605
|
+
[Generate vision document with problem, solution, value prop]
|
|
606
|
+
|
|
607
|
+
## 2. AUDIENCE.md
|
|
608
|
+
[Generate audience doc with personas, segments, ICP]
|
|
609
|
+
|
|
610
|
+
## 3. PRD.md
|
|
611
|
+
[Generate PRD with features, user stories, MVP scope]
|
|
612
|
+
|
|
613
|
+
## 4. TECHNICAL_SPEC.md
|
|
614
|
+
[Generate tech spec with stack, architecture, data models]
|
|
615
|
+
|
|
616
|
+
## 5. ROADMAP.md
|
|
617
|
+
[Generate roadmap with phases, milestones, timeline]
|
|
618
|
+
|
|
619
|
+
For each document, be specific and actionable. Use clean Markdown formatting.
|
|
620
|
+
After generating, I'll split these into separate files.`
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
return prompts[docType] || prompts.vision;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Handle analyzing files dropped in the drop zone
|
|
628
|
+
*/
|
|
629
|
+
async function handleAnalyzeDrop(rl, projectRoot, env, projectContext) {
|
|
630
|
+
console.log(`\n${utils.COLORS.cyan}${utils.COLORS.bold}Drop Zone Analysis${utils.COLORS.reset}`);
|
|
631
|
+
console.log(`${utils.COLORS.dim}Analyzing your files to extract preseed insights${utils.COLORS.reset}\n`);
|
|
632
|
+
|
|
633
|
+
const dropDir = path.join(projectRoot, '.bootspring', 'preseed', 'context', 'drop');
|
|
634
|
+
const files = projectContext.hasDropFiles || [];
|
|
635
|
+
|
|
636
|
+
if (files.length === 0) {
|
|
637
|
+
console.log(`${utils.COLORS.yellow}No files found in drop zone.${utils.COLORS.reset}`);
|
|
638
|
+
console.log(`\nAdd files to: ${utils.COLORS.cyan}${dropDir}${utils.COLORS.reset}\n`);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// List files found
|
|
643
|
+
console.log(`${utils.COLORS.bold}Files found:${utils.COLORS.reset}`);
|
|
644
|
+
files.forEach((file, i) => {
|
|
645
|
+
const ext = path.extname(file).toLowerCase();
|
|
646
|
+
const icon = getFileIcon(ext);
|
|
647
|
+
console.log(` ${utils.COLORS.green}${i + 1}.${utils.COLORS.reset} ${icon} ${file}`);
|
|
648
|
+
});
|
|
649
|
+
console.log();
|
|
650
|
+
|
|
651
|
+
// Read content from text-based files
|
|
652
|
+
const fileContents = [];
|
|
653
|
+
const textExtensions = ['.md', '.txt', '.json', '.yaml', '.yml', '.csv'];
|
|
654
|
+
|
|
655
|
+
for (const file of files) {
|
|
656
|
+
const ext = path.extname(file).toLowerCase();
|
|
657
|
+
const filePath = path.join(dropDir, file);
|
|
658
|
+
|
|
659
|
+
if (textExtensions.includes(ext)) {
|
|
660
|
+
try {
|
|
661
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
662
|
+
const truncated = content.length > 5000 ? content.slice(0, 5000) + '\n...[truncated]' : content;
|
|
663
|
+
fileContents.push({
|
|
664
|
+
name: file,
|
|
665
|
+
type: ext,
|
|
666
|
+
content: truncated
|
|
667
|
+
});
|
|
668
|
+
} catch (err) {
|
|
669
|
+
fileContents.push({
|
|
670
|
+
name: file,
|
|
671
|
+
type: ext,
|
|
672
|
+
content: `[Error reading file: ${err.message}]`
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
} else {
|
|
676
|
+
// For binary files, just note their presence
|
|
677
|
+
fileContents.push({
|
|
678
|
+
name: file,
|
|
679
|
+
type: ext,
|
|
680
|
+
content: `[Binary file - ${ext} - describe what this file likely contains based on filename]`
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// Generate the analysis prompt
|
|
686
|
+
const prompt = generateDropAnalysisPrompt(fileContents);
|
|
687
|
+
|
|
688
|
+
console.log(`${utils.COLORS.green}━━━ COPY THIS PROMPT ━━━${utils.COLORS.reset}\n`);
|
|
689
|
+
console.log(prompt);
|
|
690
|
+
console.log(`\n${utils.COLORS.green}━━━ END OF PROMPT ━━━${utils.COLORS.reset}\n`);
|
|
691
|
+
|
|
692
|
+
// Save prompt to file
|
|
693
|
+
const promptsDir = path.join(projectRoot, '.bootspring', 'preseed', 'prompts');
|
|
694
|
+
if (!fs.existsSync(promptsDir)) {
|
|
695
|
+
fs.mkdirSync(promptsDir, { recursive: true });
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const promptFile = path.join(promptsDir, 'drop-analysis-prompt.md');
|
|
699
|
+
fs.writeFileSync(promptFile, prompt);
|
|
700
|
+
console.log(`${utils.COLORS.green}✓${utils.COLORS.reset} Prompt saved to: ${utils.COLORS.cyan}${promptFile}${utils.COLORS.reset}`);
|
|
701
|
+
|
|
702
|
+
console.log(`\n${utils.COLORS.bold}Next steps:${utils.COLORS.reset}`);
|
|
703
|
+
console.log(' 1. Paste this prompt into Claude, ChatGPT, or your LLM');
|
|
704
|
+
console.log(' 2. The AI will analyze your files and create preseed documents');
|
|
705
|
+
console.log(` 3. Save the outputs to ${utils.COLORS.cyan}.bootspring/preseed/${utils.COLORS.reset}`);
|
|
706
|
+
console.log(` 4. Run ${utils.COLORS.cyan}bootspring seed synthesize${utils.COLORS.reset} to create SEED.md\n`);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* Get icon for file type
|
|
711
|
+
*/
|
|
712
|
+
function getFileIcon(ext) {
|
|
713
|
+
const icons = {
|
|
714
|
+
'.md': '\u{1F4DD}',
|
|
715
|
+
'.txt': '\u{1F4C4}',
|
|
716
|
+
'.pdf': '\u{1F4D5}',
|
|
717
|
+
'.docx': '\u{1F4D8}',
|
|
718
|
+
'.doc': '\u{1F4D8}',
|
|
719
|
+
'.png': '\u{1F5BC}',
|
|
720
|
+
'.jpg': '\u{1F5BC}',
|
|
721
|
+
'.jpeg': '\u{1F5BC}',
|
|
722
|
+
'.csv': '\u{1F4CA}',
|
|
723
|
+
'.json': '\u{2699}',
|
|
724
|
+
'.yaml': '\u{2699}',
|
|
725
|
+
'.yml': '\u{2699}'
|
|
726
|
+
};
|
|
727
|
+
return icons[ext] || '\u{1F4CE}';
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Generate AI prompt to analyze drop zone files
|
|
732
|
+
*/
|
|
733
|
+
function generateDropAnalysisPrompt(fileContents) {
|
|
734
|
+
const fileList = fileContents.map(f => `- ${f.name} (${f.type})`).join('\n');
|
|
735
|
+
|
|
736
|
+
let prompt = `# Analyze My Project Materials
|
|
737
|
+
|
|
738
|
+
I have the following files that describe my project idea. Please analyze them and help me create structured preseed documents.
|
|
739
|
+
|
|
740
|
+
## Files to Analyze
|
|
741
|
+
|
|
742
|
+
${fileList}
|
|
743
|
+
|
|
744
|
+
## File Contents
|
|
745
|
+
|
|
746
|
+
`;
|
|
747
|
+
|
|
748
|
+
for (const file of fileContents) {
|
|
749
|
+
prompt += `### ${file.name}
|
|
750
|
+
|
|
751
|
+
\`\`\`
|
|
752
|
+
${file.content}
|
|
753
|
+
\`\`\`
|
|
754
|
+
|
|
755
|
+
`;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
prompt += `## Your Task
|
|
759
|
+
|
|
760
|
+
Based on these materials, please create the following preseed documents:
|
|
761
|
+
|
|
762
|
+
### 1. VISION.md
|
|
763
|
+
Extract and synthesize:
|
|
764
|
+
- Executive Summary (one paragraph)
|
|
765
|
+
- Problem Statement (what problem does this solve?)
|
|
766
|
+
- Solution Overview (how does it solve the problem?)
|
|
767
|
+
- Key Features (what are the main capabilities?)
|
|
768
|
+
- Value Proposition (why would someone use this?)
|
|
769
|
+
|
|
770
|
+
### 2. AUDIENCE.md
|
|
771
|
+
Extract and synthesize:
|
|
772
|
+
- Primary Audience (who is this for?)
|
|
773
|
+
- User Personas (2-3 detailed personas if enough info)
|
|
774
|
+
- Market Segments (what types of users/companies?)
|
|
775
|
+
|
|
776
|
+
### 3. PRD.md
|
|
777
|
+
Extract and synthesize:
|
|
778
|
+
- MVP Features (prioritized list with P0/P1/P2)
|
|
779
|
+
- User Stories (As a..., I want..., so that...)
|
|
780
|
+
- Requirements (functional and non-functional)
|
|
781
|
+
|
|
782
|
+
### 4. TECHNICAL_SPEC.md (if technical details are present)
|
|
783
|
+
Extract and synthesize:
|
|
784
|
+
- Technology Stack recommendations
|
|
785
|
+
- Architecture overview
|
|
786
|
+
- Integration requirements
|
|
787
|
+
|
|
788
|
+
### 5. ROADMAP.md
|
|
789
|
+
Extract and synthesize:
|
|
790
|
+
- Phase 1 (MVP scope)
|
|
791
|
+
- Phase 2 (Growth features)
|
|
792
|
+
- Key milestones
|
|
793
|
+
|
|
794
|
+
## Output Format
|
|
795
|
+
|
|
796
|
+
Please output each document as a separate markdown section that I can copy into individual files. Use clean, professional formatting with headers and bullet points.
|
|
797
|
+
|
|
798
|
+
If information for a section is missing from my materials, note what additional information would be helpful rather than making assumptions.`;
|
|
799
|
+
|
|
800
|
+
return prompt;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* Handle brain dump / idea dump mode
|
|
805
|
+
*/
|
|
806
|
+
async function handleIdeaDump(rl, projectRoot) {
|
|
807
|
+
console.log(`\n${utils.COLORS.bold}Brain Dump Mode${utils.COLORS.reset}`);
|
|
808
|
+
console.log(`${utils.COLORS.dim}Just write freely about your idea. Press Enter twice when done.${utils.COLORS.reset}`);
|
|
809
|
+
console.log(`${utils.COLORS.dim}Don't worry about structure - we'll organize it for you.${utils.COLORS.reset}\n`);
|
|
810
|
+
|
|
811
|
+
console.log(`${utils.COLORS.cyan}Start typing your idea:${utils.COLORS.reset}\n`);
|
|
812
|
+
|
|
813
|
+
const lines = [];
|
|
814
|
+
let emptyCount = 0;
|
|
815
|
+
|
|
816
|
+
return new Promise((resolve) => {
|
|
817
|
+
const onLine = (line) => {
|
|
818
|
+
if (line.trim() === '') {
|
|
819
|
+
emptyCount++;
|
|
820
|
+
if (emptyCount >= 2) {
|
|
821
|
+
rl.removeListener('line', onLine);
|
|
822
|
+
processIdeaDump(lines.join('\n'), projectRoot);
|
|
823
|
+
resolve();
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
} else {
|
|
827
|
+
emptyCount = 0;
|
|
828
|
+
}
|
|
829
|
+
lines.push(line);
|
|
830
|
+
};
|
|
831
|
+
|
|
832
|
+
rl.on('line', onLine);
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Process the idea dump into structured format
|
|
838
|
+
*/
|
|
839
|
+
function processIdeaDump(text, projectRoot) {
|
|
840
|
+
console.log(`\n${utils.COLORS.bold}Processing your idea...${utils.COLORS.reset}\n`);
|
|
841
|
+
|
|
842
|
+
// Save raw dump
|
|
843
|
+
const dumpDir = path.join(projectRoot, '.bootspring', 'preseed', 'context', 'ideas');
|
|
844
|
+
if (!fs.existsSync(dumpDir)) {
|
|
845
|
+
fs.mkdirSync(dumpDir, { recursive: true });
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
849
|
+
const dumpFile = path.join(dumpDir, `brain-dump-${timestamp}.md`);
|
|
850
|
+
fs.writeFileSync(dumpFile, `# Brain Dump\n\n${text}\n`);
|
|
851
|
+
|
|
852
|
+
console.log(`${utils.COLORS.green}✓${utils.COLORS.reset} Saved to: ${utils.COLORS.cyan}${dumpFile}${utils.COLORS.reset}`);
|
|
853
|
+
|
|
854
|
+
// Generate a prompt for structuring
|
|
855
|
+
const structurePrompt = `# Structure This Idea
|
|
856
|
+
|
|
857
|
+
Here's a brain dump about a product idea. Please extract and organize it into:
|
|
858
|
+
|
|
859
|
+
1. **Project Identity** - Name ideas, tagline options
|
|
860
|
+
2. **Problem** - What problem is being solved
|
|
861
|
+
3. **Solution** - How it's being solved
|
|
862
|
+
4. **Target Audience** - Who it's for
|
|
863
|
+
5. **Key Features** - Main capabilities mentioned
|
|
864
|
+
6. **Unique Value** - What makes it different
|
|
865
|
+
|
|
866
|
+
Here's the brain dump:
|
|
867
|
+
|
|
868
|
+
---
|
|
869
|
+
${text}
|
|
870
|
+
---
|
|
871
|
+
|
|
872
|
+
Please structure this into a JSON format like:
|
|
873
|
+
\`\`\`json
|
|
874
|
+
{
|
|
875
|
+
"identity": { "name": "...", "tagline": "..." },
|
|
876
|
+
"problem": { "statement": "...", "painPoints": [...] },
|
|
877
|
+
"solution": { "description": "...", "keyFeatures": [...] },
|
|
878
|
+
"audience": { "primary": "...", "segments": [...] },
|
|
879
|
+
"uniqueValue": "..."
|
|
880
|
+
}
|
|
881
|
+
\`\`\``;
|
|
882
|
+
|
|
883
|
+
// Save the structuring prompt
|
|
884
|
+
const promptFile = path.join(dumpDir, `structure-prompt-${timestamp}.md`);
|
|
885
|
+
fs.writeFileSync(promptFile, structurePrompt);
|
|
886
|
+
|
|
887
|
+
console.log(`${utils.COLORS.green}✓${utils.COLORS.reset} Generated structuring prompt: ${utils.COLORS.cyan}${promptFile}${utils.COLORS.reset}`);
|
|
888
|
+
console.log();
|
|
889
|
+
console.log(`${utils.COLORS.bold}Next steps:${utils.COLORS.reset}`);
|
|
890
|
+
console.log(` 1. Copy the prompt from: ${utils.COLORS.cyan}${promptFile}${utils.COLORS.reset}`);
|
|
891
|
+
console.log(' 2. Paste into Claude or ChatGPT');
|
|
892
|
+
console.log(` 3. Save the JSON response to: ${utils.COLORS.cyan}.bootspring/preseed/PRESEED_CONFIG.json${utils.COLORS.reset}`);
|
|
893
|
+
console.log(` 4. Run: ${utils.COLORS.cyan}bootspring preseed generate${utils.COLORS.reset}`);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Handle dashboard redirect
|
|
898
|
+
*/
|
|
899
|
+
async function handleDashboard(projectRoot, env) {
|
|
900
|
+
const project = session.getEffectiveProject();
|
|
901
|
+
|
|
902
|
+
let url = 'https://bootspring.com/dashboard/projects';
|
|
903
|
+
if (project?.id) {
|
|
904
|
+
url = `https://bootspring.com/dashboard/projects/${project.id}/preseed/studio`;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
console.log(`\n${utils.COLORS.bold}Opening Dashboard...${utils.COLORS.reset}`);
|
|
908
|
+
console.log(`${utils.COLORS.dim}${url}${utils.COLORS.reset}\n`);
|
|
909
|
+
|
|
910
|
+
// Try to open browser
|
|
911
|
+
const { exec } = require('child_process');
|
|
912
|
+
const command = process.platform === 'darwin' ? 'open' :
|
|
913
|
+
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
914
|
+
|
|
915
|
+
exec(`${command} "${url}"`, (err) => {
|
|
916
|
+
if (err) {
|
|
917
|
+
console.log(`${utils.COLORS.yellow}Could not open browser automatically.${utils.COLORS.reset}`);
|
|
918
|
+
console.log(`Please visit: ${utils.COLORS.cyan}${url}${utils.COLORS.reset}`);
|
|
919
|
+
}
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
/**
|
|
924
|
+
* Handle Claude Code / MCP mode
|
|
925
|
+
*/
|
|
926
|
+
async function handleClaudeCode(rl, projectRoot, env) {
|
|
927
|
+
console.log(`\n${utils.COLORS.magenta}${utils.COLORS.bold}Claude Code Mode${utils.COLORS.reset}`);
|
|
928
|
+
console.log();
|
|
929
|
+
console.log(`${utils.COLORS.dim}You're in Claude Code! You can generate preseed documents directly.${utils.COLORS.reset}`);
|
|
930
|
+
console.log();
|
|
931
|
+
console.log(`${utils.COLORS.bold}Try saying:${utils.COLORS.reset}`);
|
|
932
|
+
console.log();
|
|
933
|
+
console.log(` ${utils.COLORS.cyan}"Help me create preseed documents for my app idea"${utils.COLORS.reset}`);
|
|
934
|
+
console.log(` ${utils.COLORS.cyan}"Generate a vision document for [your idea]"${utils.COLORS.reset}`);
|
|
935
|
+
console.log(` ${utils.COLORS.cyan}"Create a PRD based on my preseed config"${utils.COLORS.reset}`);
|
|
936
|
+
console.log();
|
|
937
|
+
|
|
938
|
+
if (env.capabilities.aiGeneration) {
|
|
939
|
+
console.log(`${utils.COLORS.green}✓${utils.COLORS.reset} AI generation is enabled for your account`);
|
|
940
|
+
} else {
|
|
941
|
+
console.log(`${utils.COLORS.yellow}!${utils.COLORS.reset} Upgrade to Pro for AI-powered document generation`);
|
|
942
|
+
console.log(` ${utils.COLORS.dim}https://bootspring.com/pricing${utils.COLORS.reset}`);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* Handle building from existing context files
|
|
948
|
+
* This synthesizes/merges documents from context folders into preseed output
|
|
949
|
+
*/
|
|
950
|
+
async function handleFromContext(rl, projectRoot, env, projectContext) {
|
|
951
|
+
console.log(`\n${utils.COLORS.cyan}${utils.COLORS.bold}Building from your existing materials${utils.COLORS.reset}`);
|
|
952
|
+
console.log(`${utils.COLORS.dim}Synthesizing documents from your context folders...${utils.COLORS.reset}\n`);
|
|
953
|
+
|
|
954
|
+
const contextDir = path.join(projectRoot, '.bootspring', 'preseed', 'context');
|
|
955
|
+
const outputDir = path.join(projectRoot, '.bootspring', 'preseed');
|
|
956
|
+
|
|
957
|
+
// Document type mappings: folder -> output filename
|
|
958
|
+
const docMappings = {
|
|
959
|
+
'vision': 'VISION.md',
|
|
960
|
+
'audience': 'AUDIENCE.md',
|
|
961
|
+
'market': 'MARKET.md',
|
|
962
|
+
'competitors': 'COMPETITORS.md',
|
|
963
|
+
'business': 'BUSINESS_MODEL.md',
|
|
964
|
+
'prd': 'PRD.md',
|
|
965
|
+
'technical': 'TECHNICAL_SPEC.md',
|
|
966
|
+
'roadmap': 'ROADMAP.md'
|
|
967
|
+
};
|
|
968
|
+
|
|
969
|
+
const synthesized = [];
|
|
970
|
+
const needsSynthesis = [];
|
|
971
|
+
|
|
972
|
+
// Check each document folder
|
|
973
|
+
for (const [folder, outputFile] of Object.entries(docMappings)) {
|
|
974
|
+
const folderPath = path.join(contextDir, folder);
|
|
975
|
+
if (!fs.existsSync(folderPath)) continue;
|
|
976
|
+
|
|
977
|
+
const files = fs.readdirSync(folderPath).filter(f =>
|
|
978
|
+
!f.startsWith('.') && (f.endsWith('.md') || f.endsWith('.txt'))
|
|
979
|
+
);
|
|
980
|
+
|
|
981
|
+
if (files.length === 0) continue;
|
|
982
|
+
|
|
983
|
+
console.log(`${utils.COLORS.green}✓${utils.COLORS.reset} ${folder}/: ${files.length} file(s)`);
|
|
984
|
+
|
|
985
|
+
// Read all files in this folder
|
|
986
|
+
const contents = [];
|
|
987
|
+
for (const file of files) {
|
|
988
|
+
const filePath = path.join(folderPath, file);
|
|
989
|
+
try {
|
|
990
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
991
|
+
contents.push({
|
|
992
|
+
filename: file,
|
|
993
|
+
content: content
|
|
994
|
+
});
|
|
995
|
+
} catch (err) {
|
|
996
|
+
console.log(` ${utils.COLORS.yellow}!${utils.COLORS.reset} Could not read ${file}`);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
if (contents.length === 1) {
|
|
1001
|
+
// Single file - just copy it (with header)
|
|
1002
|
+
const outputPath = path.join(outputDir, outputFile);
|
|
1003
|
+
const header = `<!-- Generated from: ${folder}/${contents[0].filename} -->\n\n`;
|
|
1004
|
+
fs.writeFileSync(outputPath, header + contents[0].content);
|
|
1005
|
+
synthesized.push({ folder, outputFile, files: 1 });
|
|
1006
|
+
} else if (contents.length > 1) {
|
|
1007
|
+
// Multiple files - need to synthesize/merge
|
|
1008
|
+
needsSynthesis.push({
|
|
1009
|
+
folder,
|
|
1010
|
+
outputFile,
|
|
1011
|
+
contents
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// Handle multi-file folders
|
|
1017
|
+
if (needsSynthesis.length > 0) {
|
|
1018
|
+
console.log(`\n${utils.COLORS.green}${utils.COLORS.bold}Multiple source files detected${utils.COLORS.reset}`);
|
|
1019
|
+
console.log(`${utils.COLORS.dim}All files will be read during SEED.md generation - no merge required.${utils.COLORS.reset}\n`);
|
|
1020
|
+
|
|
1021
|
+
for (const item of needsSynthesis) {
|
|
1022
|
+
console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} ${item.outputFile}: ${item.contents.length} sources will be used`);
|
|
1023
|
+
for (const c of item.contents) {
|
|
1024
|
+
console.log(` ${utils.COLORS.dim}└ ${c.filename}${utils.COLORS.reset}`);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// Ask if they want to merge (optional)
|
|
1029
|
+
console.log();
|
|
1030
|
+
const wantMerge = await askText(rl, `${utils.COLORS.dim}Create merged single-file versions? (optional)${utils.COLORS.reset}`, 'y/N');
|
|
1031
|
+
|
|
1032
|
+
if (wantMerge.toLowerCase() === 'y' || wantMerge.toLowerCase() === 'yes') {
|
|
1033
|
+
// Decision engine - ask about merge preferences
|
|
1034
|
+
console.log(`\n${utils.COLORS.cyan}${utils.COLORS.bold}Merge Settings${utils.COLORS.reset}\n`);
|
|
1035
|
+
|
|
1036
|
+
const mergeStrategy = await askChoice(rl, [
|
|
1037
|
+
{ id: 'conservative', label: 'Conservative (Recommended)', description: 'Keep ALL content, only remove exact duplicates' },
|
|
1038
|
+
{ id: 'balanced', label: 'Balanced', description: 'Merge similar sections, keep unique content' },
|
|
1039
|
+
{ id: 'aggressive', label: 'Aggressive', description: 'Synthesize into most concise version' }
|
|
1040
|
+
]);
|
|
1041
|
+
|
|
1042
|
+
const conflictResolution = await askChoice(rl, [
|
|
1043
|
+
{ id: 'keep-both', label: 'Keep both versions', description: 'When content differs, include both with labels' },
|
|
1044
|
+
{ id: 'prefer-detailed', label: 'Prefer more detailed', description: 'When content differs, use the longer/more detailed version' },
|
|
1045
|
+
{ id: 'ask-in-prompt', label: 'Flag for review', description: 'Mark conflicts for manual decision' }
|
|
1046
|
+
]);
|
|
1047
|
+
|
|
1048
|
+
const includeChangelog = await askText(rl, 'Include changelog showing what came from each source?', 'Y/n');
|
|
1049
|
+
const showChangelog = includeChangelog.toLowerCase() !== 'n';
|
|
1050
|
+
|
|
1051
|
+
const mergeConfig = {
|
|
1052
|
+
strategy: mergeStrategy.id,
|
|
1053
|
+
conflictResolution: conflictResolution.id,
|
|
1054
|
+
includeChangelog: showChangelog
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
// Ask how to execute the merge
|
|
1058
|
+
console.log(`\n${utils.COLORS.bold}How do you want to merge?${utils.COLORS.reset}\n`);
|
|
1059
|
+
const mergeMethod = await askChoice(rl, [
|
|
1060
|
+
{ id: 'ai-execute', label: 'Have AI execute now (Recommended)', description: 'Claude Code / Cursor / Codex will merge directly' },
|
|
1061
|
+
{ id: 'prompts', label: 'Generate prompts only', description: 'Copy prompts to paste into another LLM' }
|
|
1062
|
+
]);
|
|
1063
|
+
|
|
1064
|
+
const promptsDir = path.join(outputDir, 'prompts');
|
|
1065
|
+
if (!fs.existsSync(promptsDir)) {
|
|
1066
|
+
fs.mkdirSync(promptsDir, { recursive: true });
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
if (mergeMethod.id === 'ai-execute') {
|
|
1070
|
+
// Create a merge manifest for AI execution
|
|
1071
|
+
const manifest = {
|
|
1072
|
+
created: new Date().toISOString(),
|
|
1073
|
+
settings: mergeConfig,
|
|
1074
|
+
tasks: []
|
|
1075
|
+
};
|
|
1076
|
+
|
|
1077
|
+
for (const item of needsSynthesis) {
|
|
1078
|
+
manifest.tasks.push({
|
|
1079
|
+
folder: item.folder,
|
|
1080
|
+
outputFile: item.outputFile,
|
|
1081
|
+
outputPath: path.join(outputDir, item.outputFile),
|
|
1082
|
+
sources: item.contents.map(c => ({
|
|
1083
|
+
filename: c.filename,
|
|
1084
|
+
path: path.join(contextDir, item.folder, c.filename)
|
|
1085
|
+
}))
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// Save manifest
|
|
1090
|
+
const manifestPath = path.join(outputDir, 'MERGE_MANIFEST.json');
|
|
1091
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
1092
|
+
|
|
1093
|
+
// Also create a human-readable instruction file
|
|
1094
|
+
let instructions = `# Merge Instructions for AI Assistant
|
|
1095
|
+
|
|
1096
|
+
## Settings
|
|
1097
|
+
- **Strategy**: ${mergeStrategy.label}
|
|
1098
|
+
- **Conflicts**: ${conflictResolution.label}
|
|
1099
|
+
- **Changelog**: ${showChangelog ? 'Yes' : 'No'}
|
|
1100
|
+
|
|
1101
|
+
## Rules
|
|
1102
|
+
${mergeStrategy.id === 'conservative' ? `
|
|
1103
|
+
- NEVER delete unique content from any source
|
|
1104
|
+
- ONLY remove exact duplicate sentences/paragraphs
|
|
1105
|
+
- When in doubt, KEEP the content
|
|
1106
|
+
` : mergeStrategy.id === 'balanced' ? `
|
|
1107
|
+
- Merge similar sections covering the same topic
|
|
1108
|
+
- Preserve all unique content
|
|
1109
|
+
- Remove clear redundancies
|
|
1110
|
+
` : `
|
|
1111
|
+
- Synthesize into most concise version
|
|
1112
|
+
- Keep only the strongest version of each point
|
|
1113
|
+
`}
|
|
1114
|
+
|
|
1115
|
+
## Conflict Resolution
|
|
1116
|
+
${conflictResolution.id === 'keep-both' ? `
|
|
1117
|
+
- When sources differ, include BOTH versions with labels
|
|
1118
|
+
` : conflictResolution.id === 'prefer-detailed' ? `
|
|
1119
|
+
- When sources differ, use the MORE DETAILED version
|
|
1120
|
+
` : `
|
|
1121
|
+
- Mark conflicts with ⚠️ for manual review
|
|
1122
|
+
`}
|
|
1123
|
+
|
|
1124
|
+
## Tasks
|
|
1125
|
+
|
|
1126
|
+
Please merge the following documents:
|
|
1127
|
+
|
|
1128
|
+
`;
|
|
1129
|
+
|
|
1130
|
+
for (const item of needsSynthesis) {
|
|
1131
|
+
instructions += `### ${item.outputFile}
|
|
1132
|
+
**Sources:**
|
|
1133
|
+
${item.contents.map(c => `- \`context/${item.folder}/${c.filename}\``).join('\n')}
|
|
1134
|
+
|
|
1135
|
+
**Output:** \`.bootspring/preseed/${item.outputFile}\`
|
|
1136
|
+
|
|
1137
|
+
---
|
|
1138
|
+
|
|
1139
|
+
`;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
instructions += `
|
|
1143
|
+
## How to Execute
|
|
1144
|
+
|
|
1145
|
+
Read each source file, merge according to the rules above, and save to the output path.
|
|
1146
|
+
${showChangelog ? 'Include a changelog section at the end of each merged document.' : ''}
|
|
1147
|
+
`;
|
|
1148
|
+
|
|
1149
|
+
const instructionsPath = path.join(outputDir, 'MERGE_INSTRUCTIONS.md');
|
|
1150
|
+
fs.writeFileSync(instructionsPath, instructions);
|
|
1151
|
+
|
|
1152
|
+
console.log(`\n${utils.COLORS.green}${utils.COLORS.bold}Ready for AI merge!${utils.COLORS.reset}\n`);
|
|
1153
|
+
console.log(`${utils.COLORS.bold}Files created:${utils.COLORS.reset}`);
|
|
1154
|
+
console.log(` ${utils.COLORS.cyan}●${utils.COLORS.reset} MERGE_MANIFEST.json - Machine-readable task list`);
|
|
1155
|
+
console.log(` ${utils.COLORS.cyan}●${utils.COLORS.reset} MERGE_INSTRUCTIONS.md - Human-readable instructions`);
|
|
1156
|
+
console.log();
|
|
1157
|
+
|
|
1158
|
+
// Automatically run the merge command to output tasks for AI
|
|
1159
|
+
console.log(`${utils.COLORS.bold}Executing merge command...${utils.COLORS.reset}\n`);
|
|
1160
|
+
const preseed = require('./preseed');
|
|
1161
|
+
await preseed.run(['merge']);
|
|
1162
|
+
|
|
1163
|
+
} else {
|
|
1164
|
+
// Original behavior - generate prompts only
|
|
1165
|
+
console.log(`\n${utils.COLORS.yellow}Creating merge prompts...${utils.COLORS.reset}\n`);
|
|
1166
|
+
|
|
1167
|
+
for (const item of needsSynthesis) {
|
|
1168
|
+
const prompt = generateMergePrompt(item.folder, item.outputFile, item.contents, mergeConfig);
|
|
1169
|
+
const promptFile = path.join(promptsDir, `merge-${item.folder}-prompt.md`);
|
|
1170
|
+
fs.writeFileSync(promptFile, prompt);
|
|
1171
|
+
|
|
1172
|
+
console.log(` ${utils.COLORS.cyan}●${utils.COLORS.reset} ${item.outputFile}: merge prompt → ${utils.COLORS.dim}${path.basename(promptFile)}${utils.COLORS.reset}`);
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
console.log(`\n${utils.COLORS.bold}Merge prompts saved to:${utils.COLORS.reset} ${utils.COLORS.cyan}.bootspring/preseed/prompts/${utils.COLORS.reset}`);
|
|
1176
|
+
console.log(`${utils.COLORS.dim}Paste prompts into an LLM, review output, then save to .bootspring/preseed/${utils.COLORS.reset}`);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
console.log(`\n${utils.COLORS.bold}Your settings:${utils.COLORS.reset}`);
|
|
1180
|
+
console.log(` Strategy: ${utils.COLORS.cyan}${mergeStrategy.label}${utils.COLORS.reset}`);
|
|
1181
|
+
console.log(` Conflicts: ${utils.COLORS.cyan}${conflictResolution.label}${utils.COLORS.reset}`);
|
|
1182
|
+
console.log(` Changelog: ${utils.COLORS.cyan}${showChangelog ? 'Yes' : 'No'}${utils.COLORS.reset}`);
|
|
1183
|
+
} else {
|
|
1184
|
+
console.log(`\n${utils.COLORS.green}✓${utils.COLORS.reset} Keeping all source files - they'll all be used during SEED.md generation.`);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
// Summary
|
|
1189
|
+
if (synthesized.length > 0) {
|
|
1190
|
+
console.log(`\n${utils.COLORS.green}${utils.COLORS.bold}Documents created:${utils.COLORS.reset}`);
|
|
1191
|
+
for (const doc of synthesized) {
|
|
1192
|
+
console.log(` ${utils.COLORS.green}✓${utils.COLORS.reset} ${doc.outputFile}`);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
// Create/update PRESEED_CONFIG.json with project info extracted from docs
|
|
1197
|
+
console.log(`\n${utils.COLORS.dim}Tip: Run ${utils.COLORS.cyan}bootspring preseed status${utils.COLORS.reset}${utils.COLORS.dim} to see your documents${utils.COLORS.reset}`);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
/**
|
|
1201
|
+
* Generate a prompt to merge multiple source files into one document
|
|
1202
|
+
*/
|
|
1203
|
+
function generateMergePrompt(folder, outputFile, contents, config = {}) {
|
|
1204
|
+
const docType = folder.charAt(0).toUpperCase() + folder.slice(1);
|
|
1205
|
+
const strategy = config.strategy || 'conservative';
|
|
1206
|
+
const conflictRes = config.conflictResolution || 'keep-both';
|
|
1207
|
+
const includeChangelog = config.includeChangelog !== false;
|
|
1208
|
+
|
|
1209
|
+
let prompt = `# Merge ${docType} Documents
|
|
1210
|
+
|
|
1211
|
+
I have ${contents.length} source files for my ${docType} document. Please merge them into a single ${outputFile}.
|
|
1212
|
+
|
|
1213
|
+
## CRITICAL RULES - READ FIRST
|
|
1214
|
+
|
|
1215
|
+
${strategy === 'conservative' ? `
|
|
1216
|
+
**CONSERVATIVE MERGE MODE**
|
|
1217
|
+
1. **NEVER DELETE** unique content from any source
|
|
1218
|
+
2. **ONLY REMOVE** exact duplicate sentences/paragraphs
|
|
1219
|
+
3. **PRESERVE** all unique insights, details, and perspectives
|
|
1220
|
+
4. When in doubt, KEEP the content
|
|
1221
|
+
5. It's better to have slight redundancy than to lose information
|
|
1222
|
+
` : strategy === 'balanced' ? `
|
|
1223
|
+
**BALANCED MERGE MODE**
|
|
1224
|
+
1. Merge similar sections that cover the same topic
|
|
1225
|
+
2. Preserve all unique content that appears in only one source
|
|
1226
|
+
3. When content overlaps but differs in detail, keep the more detailed version
|
|
1227
|
+
4. Remove only clear redundancies
|
|
1228
|
+
` : `
|
|
1229
|
+
**AGGRESSIVE MERGE MODE**
|
|
1230
|
+
1. Synthesize into the most concise version
|
|
1231
|
+
2. Eliminate all redundancy
|
|
1232
|
+
3. Keep only the strongest/most detailed version of each point
|
|
1233
|
+
4. Flag any content you're uncertain about removing
|
|
1234
|
+
`}
|
|
1235
|
+
|
|
1236
|
+
## Conflict Resolution
|
|
1237
|
+
|
|
1238
|
+
${conflictRes === 'keep-both' ? `
|
|
1239
|
+
When sources have DIFFERENT content on the same topic:
|
|
1240
|
+
- Include BOTH versions
|
|
1241
|
+
- Label them: "[Source A view]" and "[Source B view]"
|
|
1242
|
+
- Let the user decide later which to keep
|
|
1243
|
+
` : conflictRes === 'prefer-detailed' ? `
|
|
1244
|
+
When sources have DIFFERENT content on the same topic:
|
|
1245
|
+
- Use the MORE DETAILED version
|
|
1246
|
+
- If detail is similar, use the more specific/concrete version
|
|
1247
|
+
- Note in changelog what was not used
|
|
1248
|
+
` : `
|
|
1249
|
+
When sources have DIFFERENT content on the same topic:
|
|
1250
|
+
- Mark with: "⚠️ CONFLICT - Review needed"
|
|
1251
|
+
- Show both versions inline
|
|
1252
|
+
- User will manually resolve
|
|
1253
|
+
`}
|
|
1254
|
+
|
|
1255
|
+
## Source Files
|
|
1256
|
+
|
|
1257
|
+
`;
|
|
1258
|
+
|
|
1259
|
+
for (const item of contents) {
|
|
1260
|
+
prompt += `### ${item.filename}
|
|
1261
|
+
|
|
1262
|
+
\`\`\`markdown
|
|
1263
|
+
${item.content}
|
|
1264
|
+
\`\`\`
|
|
1265
|
+
|
|
1266
|
+
---
|
|
1267
|
+
|
|
1268
|
+
`;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
prompt += `## Output Format
|
|
1272
|
+
|
|
1273
|
+
Create a single ${outputFile} with:
|
|
1274
|
+
- Clear structure with headers
|
|
1275
|
+
- All unique content from each source preserved
|
|
1276
|
+
- Clean Markdown formatting
|
|
1277
|
+
|
|
1278
|
+
${includeChangelog ? `
|
|
1279
|
+
## REQUIRED: Changelog Section
|
|
1280
|
+
|
|
1281
|
+
At the END of the merged document, include:
|
|
1282
|
+
|
|
1283
|
+
\`\`\`
|
|
1284
|
+
---
|
|
1285
|
+
## Merge Changelog
|
|
1286
|
+
|
|
1287
|
+
### From ${contents[0]?.filename || 'Source 1'}:
|
|
1288
|
+
- [List what was kept from this source]
|
|
1289
|
+
|
|
1290
|
+
### From ${contents[1]?.filename || 'Source 2'}:
|
|
1291
|
+
- [List what was kept from this source]
|
|
1292
|
+
|
|
1293
|
+
### Duplicates Removed:
|
|
1294
|
+
- [List any exact duplicates that were deduplicated]
|
|
1295
|
+
|
|
1296
|
+
### Conflicts Found:
|
|
1297
|
+
- [List any conflicts and how they were resolved]
|
|
1298
|
+
\`\`\`
|
|
1299
|
+
|
|
1300
|
+
This changelog lets the user verify nothing important was lost.
|
|
1301
|
+
` : ''}
|
|
1302
|
+
|
|
1303
|
+
Remember: The user's original files are preserved. This merge creates a NEW combined version.`;
|
|
1304
|
+
|
|
1305
|
+
return prompt;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
/**
|
|
1309
|
+
* Handle synthesis of preseed docs into SEED.md
|
|
1310
|
+
*/
|
|
1311
|
+
async function handleSynthesize(rl, projectRoot, env, projectContext) {
|
|
1312
|
+
console.log(`\n${utils.COLORS.bold}Synthesize SEED.md${utils.COLORS.reset}`);
|
|
1313
|
+
console.log(`${utils.COLORS.dim}Creating SEED.md from your ${projectContext.hasPreseedDocs.length} preseed documents...${utils.COLORS.reset}\n`);
|
|
1314
|
+
|
|
1315
|
+
// Read all preseed docs
|
|
1316
|
+
const preseedDir = path.join(projectRoot, '.bootspring', 'preseed');
|
|
1317
|
+
const docs = {};
|
|
1318
|
+
|
|
1319
|
+
for (const file of projectContext.hasPreseedDocs) {
|
|
1320
|
+
const filePath = path.join(preseedDir, file);
|
|
1321
|
+
docs[file.replace('.md', '')] = fs.readFileSync(filePath, 'utf-8');
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
// Generate synthesis prompt
|
|
1325
|
+
const synthesisPrompt = `# Synthesize SEED.md
|
|
1326
|
+
|
|
1327
|
+
I have the following preseed documents for my project. Please synthesize them into a single SEED.md file that:
|
|
1328
|
+
|
|
1329
|
+
1. Provides a complete project overview
|
|
1330
|
+
2. Defines the technical implementation guide
|
|
1331
|
+
3. Establishes development standards
|
|
1332
|
+
4. Creates a clear path from idea to implementation
|
|
1333
|
+
|
|
1334
|
+
Here are my preseed documents:
|
|
1335
|
+
|
|
1336
|
+
${Object.entries(docs).map(([name, content]) => `## ${name}\n\n${content}`).join('\n\n---\n\n')}
|
|
1337
|
+
|
|
1338
|
+
---
|
|
1339
|
+
|
|
1340
|
+
Please create a comprehensive SEED.md with:
|
|
1341
|
+
|
|
1342
|
+
1. **Project Overview** - What we're building
|
|
1343
|
+
2. **Technical Stack** - Technologies and architecture
|
|
1344
|
+
3. **Core Features** - What to implement first
|
|
1345
|
+
4. **Development Standards** - Code style, patterns
|
|
1346
|
+
5. **File Structure** - How to organize the codebase
|
|
1347
|
+
6. **Implementation Guide** - Step-by-step build order
|
|
1348
|
+
7. **Success Criteria** - How to know when it's done
|
|
1349
|
+
|
|
1350
|
+
Make it actionable for a developer or AI assistant to follow.`;
|
|
1351
|
+
|
|
1352
|
+
// Save prompt
|
|
1353
|
+
const promptPath = path.join(preseedDir, 'prompts', 'synthesize-seed-prompt.md');
|
|
1354
|
+
const promptDir = path.dirname(promptPath);
|
|
1355
|
+
if (!fs.existsSync(promptDir)) {
|
|
1356
|
+
fs.mkdirSync(promptDir, { recursive: true });
|
|
1357
|
+
}
|
|
1358
|
+
fs.writeFileSync(promptPath, synthesisPrompt);
|
|
1359
|
+
|
|
1360
|
+
console.log(`${utils.COLORS.green}✓${utils.COLORS.reset} Generated synthesis prompt`);
|
|
1361
|
+
console.log();
|
|
1362
|
+
console.log(`${utils.COLORS.bold}Next steps:${utils.COLORS.reset}`);
|
|
1363
|
+
console.log(` 1. Copy prompt from: ${utils.COLORS.cyan}${promptPath}${utils.COLORS.reset}`);
|
|
1364
|
+
console.log(' 2. Paste into Claude or ChatGPT');
|
|
1365
|
+
console.log(` 3. Save response as: ${utils.COLORS.cyan}SEED.md${utils.COLORS.reset} in project root`);
|
|
1366
|
+
console.log(` 4. Run: ${utils.COLORS.cyan}bootspring scaffold${utils.COLORS.reset}`);
|
|
1367
|
+
|
|
1368
|
+
if (env.capabilities.aiGeneration) {
|
|
1369
|
+
console.log();
|
|
1370
|
+
console.log(`${utils.COLORS.green}Tip:${utils.COLORS.reset} You can also use the dashboard for AI-powered synthesis!`);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
/**
|
|
1375
|
+
* Handle resume from previous progress
|
|
1376
|
+
*/
|
|
1377
|
+
async function handleResume(rl, projectRoot, projectContext) {
|
|
1378
|
+
console.log(`\n${utils.COLORS.bold}Resuming...${utils.COLORS.reset}`);
|
|
1379
|
+
console.log(`${utils.COLORS.dim}Last step: ${projectContext.previousProgress.lastStep}${utils.COLORS.reset}\n`);
|
|
1380
|
+
|
|
1381
|
+
// Load progress and continue wizard
|
|
1382
|
+
const preseed = require('./preseed');
|
|
1383
|
+
await preseed.run(['workflow', 'resume']);
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
// ============================================================================
|
|
1387
|
+
// Main Entry Point
|
|
1388
|
+
// ============================================================================
|
|
1389
|
+
|
|
1390
|
+
/**
|
|
1391
|
+
* Start the preseed journey
|
|
1392
|
+
*/
|
|
1393
|
+
async function start(args) {
|
|
1394
|
+
const projectRoot = config.findProjectRoot();
|
|
1395
|
+
|
|
1396
|
+
// Detect context
|
|
1397
|
+
const env = detectEnvironment();
|
|
1398
|
+
const projectContext = detectProjectContext(projectRoot);
|
|
1399
|
+
|
|
1400
|
+
// Display UI
|
|
1401
|
+
displayHeader(env, projectContext);
|
|
1402
|
+
displayJourney(projectContext);
|
|
1403
|
+
|
|
1404
|
+
// Get recommendations
|
|
1405
|
+
const recommendations = determineStartingPoint(env, projectContext);
|
|
1406
|
+
|
|
1407
|
+
// Show options
|
|
1408
|
+
console.log(`${utils.COLORS.bold}How would you like to proceed?${utils.COLORS.reset}\n`);
|
|
1409
|
+
|
|
1410
|
+
const rl = createInterface();
|
|
1411
|
+
const selected = await askChoice(rl, recommendations);
|
|
1412
|
+
|
|
1413
|
+
// Handle selection
|
|
1414
|
+
switch (selected.id) {
|
|
1415
|
+
case 'resume':
|
|
1416
|
+
await handleResume(rl, projectRoot, projectContext);
|
|
1417
|
+
break;
|
|
1418
|
+
|
|
1419
|
+
case 'guided-wizard':
|
|
1420
|
+
rl.close();
|
|
1421
|
+
await handleGuidedWizard(rl, projectRoot, env);
|
|
1422
|
+
break;
|
|
1423
|
+
|
|
1424
|
+
case 'ai-prompts':
|
|
1425
|
+
await handleAIPrompts(rl, projectRoot, env, projectContext);
|
|
1426
|
+
rl.close();
|
|
1427
|
+
break;
|
|
1428
|
+
|
|
1429
|
+
case 'idea-dump':
|
|
1430
|
+
await handleIdeaDump(rl, projectRoot);
|
|
1431
|
+
rl.close();
|
|
1432
|
+
break;
|
|
1433
|
+
|
|
1434
|
+
case 'dashboard':
|
|
1435
|
+
rl.close();
|
|
1436
|
+
await handleDashboard(projectRoot, env);
|
|
1437
|
+
break;
|
|
1438
|
+
|
|
1439
|
+
case 'claude-code':
|
|
1440
|
+
await handleClaudeCode(rl, projectRoot, env);
|
|
1441
|
+
rl.close();
|
|
1442
|
+
break;
|
|
1443
|
+
|
|
1444
|
+
case 'synthesize':
|
|
1445
|
+
await handleSynthesize(rl, projectRoot, env, projectContext);
|
|
1446
|
+
rl.close();
|
|
1447
|
+
break;
|
|
1448
|
+
|
|
1449
|
+
case 'from-context':
|
|
1450
|
+
await handleFromContext(rl, projectRoot, env, projectContext);
|
|
1451
|
+
rl.close();
|
|
1452
|
+
break;
|
|
1453
|
+
|
|
1454
|
+
case 'analyze-drop':
|
|
1455
|
+
await handleAnalyzeDrop(rl, projectRoot, env, projectContext);
|
|
1456
|
+
rl.close();
|
|
1457
|
+
break;
|
|
1458
|
+
|
|
1459
|
+
case 'ai-wizard':
|
|
1460
|
+
rl.close();
|
|
1461
|
+
// If they have AI capability, redirect to dashboard or enhanced CLI
|
|
1462
|
+
if (env.capabilities.aiGeneration) {
|
|
1463
|
+
await handleDashboard(projectRoot, env);
|
|
1464
|
+
} else {
|
|
1465
|
+
console.log(`\n${utils.COLORS.yellow}AI Wizard requires Pro tier.${utils.COLORS.reset}`);
|
|
1466
|
+
console.log(`${utils.COLORS.dim}Upgrade at: https://bootspring.com/pricing${utils.COLORS.reset}\n`);
|
|
1467
|
+
}
|
|
1468
|
+
break;
|
|
1469
|
+
|
|
1470
|
+
default:
|
|
1471
|
+
rl.close();
|
|
1472
|
+
console.log(`\n${utils.COLORS.dim}Starting guided wizard...${utils.COLORS.reset}\n`);
|
|
1473
|
+
await handleGuidedWizard(rl, projectRoot, env);
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
module.exports = {
|
|
1478
|
+
start,
|
|
1479
|
+
detectEnvironment,
|
|
1480
|
+
detectProjectContext,
|
|
1481
|
+
determineStartingPoint,
|
|
1482
|
+
generateAIPrompt
|
|
1483
|
+
};
|