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