@girardmedia/bootspring 2.5.0 → 2.5.2
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 +9 -403
- package/bin/bootspring.js +1 -96
- package/dist/cli/index.js +65134 -0
- package/dist/cli-launcher.js +92 -0
- package/dist/core/index.d.ts +2110 -5582
- package/dist/core/index.js +2 -0
- package/dist/core.js +21123 -5413
- package/dist/mcp/index.d.ts +357 -1
- package/dist/mcp/index.js +2 -0
- package/dist/mcp-server.js +51948 -1976
- package/package.json +27 -63
- package/scripts/postinstall.cjs +144 -0
- package/LICENSE +0 -29
- package/dist/cli/index.cjs +0 -20776
- package/generators/api-docs.js +0 -827
- package/generators/decisions.js +0 -655
- package/generators/generate.js +0 -595
- package/generators/health.js +0 -942
- package/generators/index.ts +0 -82
- package/generators/presets/full.js +0 -28
- package/generators/presets/index.js +0 -12
- package/generators/presets/minimal.js +0 -29
- package/generators/presets/standard.js +0 -28
- package/generators/questionnaire.js +0 -414
- package/generators/sections/advanced.js +0 -136
- package/generators/sections/ai.js +0 -106
- package/generators/sections/auth.js +0 -89
- package/generators/sections/backend.js +0 -146
- package/generators/sections/business.js +0 -118
- package/generators/sections/content.js +0 -300
- package/generators/sections/deployment.js +0 -139
- package/generators/sections/features.js +0 -122
- package/generators/sections/frontend.js +0 -118
- package/generators/sections/identity.js +0 -76
- package/generators/sections/index.js +0 -40
- package/generators/sections/instructions.js +0 -146
- package/generators/sections/payments.js +0 -104
- package/generators/sections/plugins.js +0 -142
- package/generators/sections/pre-build.js +0 -130
- package/generators/sections/security.js +0 -127
- package/generators/sections/technical.js +0 -171
- package/generators/sections/testing.js +0 -125
- package/generators/sections/workflow.js +0 -104
- package/generators/sprint.js +0 -675
- package/generators/templates/agents.template.js +0 -199
- package/generators/templates/assistant-context.template.js +0 -83
- package/generators/templates/build-planning.template.js +0 -708
- package/generators/templates/claude.template.js +0 -379
- package/generators/templates/content.template.js +0 -819
- package/generators/templates/index.js +0 -16
- package/generators/templates/planning.template.js +0 -515
- package/generators/templates/seed.template.js +0 -109
- package/generators/visual-doc-generator.js +0 -910
- package/scripts/postinstall.js +0 -197
- /package/{claude-commands → assets/claude-commands}/agent.md +0 -0
- /package/{claude-commands → assets/claude-commands}/bs.md +0 -0
- /package/{claude-commands → assets/claude-commands}/build.md +0 -0
- /package/{claude-commands → assets/claude-commands}/skill.md +0 -0
- /package/{claude-commands → assets/claude-commands}/todo.md +0 -0
|
@@ -1,708 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bootspring Build Planning Templates
|
|
3
|
-
*
|
|
4
|
-
* Generates planning artifacts for the autonomous build loop:
|
|
5
|
-
* - MASTER_PLAN.md - Vision + phases + progress tracker
|
|
6
|
-
* - TODO.md - Checkbox list by phase
|
|
7
|
-
* - TASK_QUEUE.md - Legacy compatibility mirror only when explicitly requested
|
|
8
|
-
* - CONTEXT.md - Condensed context for AI iterations
|
|
9
|
-
*
|
|
10
|
-
* @package bootspring
|
|
11
|
-
* @module generators/templates/build-planning
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const fs = require('fs');
|
|
15
|
-
const path = require('path');
|
|
16
|
-
const DEFAULT_QUEUE_VERSION = '3.0.0';
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Generate all planning artifacts
|
|
20
|
-
* @param {string} projectRoot - Project root path
|
|
21
|
-
* @param {object} docs - Source documents
|
|
22
|
-
* @param {array} tasks - Extracted tasks
|
|
23
|
-
* @param {object} options - Generation options
|
|
24
|
-
* @returns {object} Generated file paths
|
|
25
|
-
*/
|
|
26
|
-
function generateAll(projectRoot, docs, tasks, options = {}) {
|
|
27
|
-
const planningDir = path.join(projectRoot, 'planning');
|
|
28
|
-
|
|
29
|
-
// Ensure planning directory exists
|
|
30
|
-
if (!fs.existsSync(planningDir)) {
|
|
31
|
-
fs.mkdirSync(planningDir, { recursive: true });
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const files = {};
|
|
35
|
-
|
|
36
|
-
// Generate MASTER_PLAN.md
|
|
37
|
-
const masterPlan = generateMasterPlan(docs, tasks, options);
|
|
38
|
-
const masterPlanPath = path.join(planningDir, 'MASTER_PLAN.md');
|
|
39
|
-
fs.writeFileSync(masterPlanPath, masterPlan);
|
|
40
|
-
files.masterPlan = masterPlanPath;
|
|
41
|
-
|
|
42
|
-
// Generate TODO.md
|
|
43
|
-
const todo = generateTodo(tasks, options);
|
|
44
|
-
const todoPath = path.join(planningDir, 'TODO.md');
|
|
45
|
-
fs.writeFileSync(todoPath, todo);
|
|
46
|
-
files.todo = todoPath;
|
|
47
|
-
|
|
48
|
-
const legacyQueuePath = path.join(planningDir, 'TASK_QUEUE.md');
|
|
49
|
-
const includeLegacyTaskQueue = options.includeLegacyTaskQueue === true;
|
|
50
|
-
|
|
51
|
-
if (includeLegacyTaskQueue) {
|
|
52
|
-
const queue = generateImplementationQueue(tasks, options);
|
|
53
|
-
fs.writeFileSync(legacyQueuePath, queue);
|
|
54
|
-
files.implementationQueue = legacyQueuePath;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Generate CONTEXT.md
|
|
58
|
-
const context = generateContext(docs, tasks, options);
|
|
59
|
-
const contextPath = path.join(planningDir, 'CONTEXT.md');
|
|
60
|
-
fs.writeFileSync(contextPath, context);
|
|
61
|
-
files.context = contextPath;
|
|
62
|
-
|
|
63
|
-
// Generate planning/AGENTS.md for AI coding agents
|
|
64
|
-
const agentsContent = generatePlanningAgents(options);
|
|
65
|
-
const agentsPath = path.join(planningDir, 'AGENTS.md');
|
|
66
|
-
fs.writeFileSync(agentsPath, agentsContent);
|
|
67
|
-
files.agents = agentsPath;
|
|
68
|
-
|
|
69
|
-
return files;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Generate AGENTS.md for the planning directory
|
|
74
|
-
* Following the open standard for AI coding agents
|
|
75
|
-
*/
|
|
76
|
-
function generatePlanningAgents(options = {}) {
|
|
77
|
-
const projectName = options.projectName || 'Project';
|
|
78
|
-
|
|
79
|
-
return `# Planning Directory
|
|
80
|
-
|
|
81
|
-
You are working on build tasks for ${projectName}. This directory contains build planning files.
|
|
82
|
-
|
|
83
|
-
## Commands
|
|
84
|
-
|
|
85
|
-
\`\`\`bash
|
|
86
|
-
bootspring build task # Show current task
|
|
87
|
-
bootspring build done # Mark task complete, get next
|
|
88
|
-
bootspring build skip # Skip current task
|
|
89
|
-
bootspring build status # View progress
|
|
90
|
-
\`\`\`
|
|
91
|
-
|
|
92
|
-
## Current Task
|
|
93
|
-
|
|
94
|
-
Run \`bootspring build task\` to get the current \`in_progress\` task from \`BUILD_STATE.json\`.
|
|
95
|
-
|
|
96
|
-
## Workflow
|
|
97
|
-
|
|
98
|
-
1. Run \`bootspring build task\` to identify the current task ID
|
|
99
|
-
2. Read the matching task in \`TODO.md\` for acceptance criteria
|
|
100
|
-
3. Implement in the main codebase (not in this folder)
|
|
101
|
-
4. Ensure acceptance criteria are met
|
|
102
|
-
5. Run quality checks: \`npm run lint && npm run test\`
|
|
103
|
-
6. Commit your changes
|
|
104
|
-
7. Run \`bootspring build done\`
|
|
105
|
-
|
|
106
|
-
## Files
|
|
107
|
-
|
|
108
|
-
| File | Purpose |
|
|
109
|
-
|------|---------|
|
|
110
|
-
| \`TODO.md\` | **Source of truth** — task checklist with acceptance criteria |
|
|
111
|
-
| \`BUILD_STATE.json\` | Build state and current task ID (do not edit directly) |
|
|
112
|
-
| \`CONTEXT.md\` | Build context summary |
|
|
113
|
-
|
|
114
|
-
## Boundaries
|
|
115
|
-
|
|
116
|
-
**Never:**
|
|
117
|
-
- Edit \`BUILD_STATE.json\` directly
|
|
118
|
-
- Skip quality checks
|
|
119
|
-
- Work on multiple tasks simultaneously
|
|
120
|
-
- Implement tasks in this planning folder
|
|
121
|
-
|
|
122
|
-
**Always:**
|
|
123
|
-
- Follow acceptance criteria exactly
|
|
124
|
-
- Run tests before marking complete
|
|
125
|
-
- Commit with conventional commit format
|
|
126
|
-
`;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Generate MASTER_PLAN.md
|
|
131
|
-
* @param {object} docs - Source documents
|
|
132
|
-
* @param {array} tasks - Extracted tasks
|
|
133
|
-
* @param {object} options - Options
|
|
134
|
-
* @returns {string} MASTER_PLAN.md content
|
|
135
|
-
*/
|
|
136
|
-
function generateMasterPlan(docs, tasks, options = {}) {
|
|
137
|
-
const projectName = options.projectName || 'Project';
|
|
138
|
-
const phases = groupByPhase(tasks);
|
|
139
|
-
|
|
140
|
-
let content = `# ${projectName} - Master Build Plan
|
|
141
|
-
|
|
142
|
-
> Generated by Bootspring Autonomous Build System
|
|
143
|
-
> Last Updated: ${new Date().toISOString().split('T')[0]}
|
|
144
|
-
|
|
145
|
-
---
|
|
146
|
-
|
|
147
|
-
## Overview
|
|
148
|
-
|
|
149
|
-
This master plan guides the autonomous build process from foundation to MVP completion.
|
|
150
|
-
Each phase contains specific tasks that will be executed sequentially by the build loop.
|
|
151
|
-
|
|
152
|
-
---
|
|
153
|
-
|
|
154
|
-
## Progress Tracker
|
|
155
|
-
|
|
156
|
-
| Phase | Status | Progress | Tasks |
|
|
157
|
-
|-------|--------|----------|-------|
|
|
158
|
-
`;
|
|
159
|
-
|
|
160
|
-
// Add phase progress rows
|
|
161
|
-
for (const [phaseName, phaseTasks] of Object.entries(phases)) {
|
|
162
|
-
const completed = phaseTasks.filter(t => t.status === 'completed').length;
|
|
163
|
-
const total = phaseTasks.length;
|
|
164
|
-
const percent = total > 0 ? Math.round((completed / total) * 100) : 0;
|
|
165
|
-
const status = completed === 0 ? 'Pending' :
|
|
166
|
-
completed === total ? 'Complete' : 'In Progress';
|
|
167
|
-
|
|
168
|
-
content += `| ${formatPhaseName(phaseName)} | ${status} | ${percent}% | ${completed}/${total} |\n`;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
content += `
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
## Phases
|
|
175
|
-
|
|
176
|
-
`;
|
|
177
|
-
|
|
178
|
-
// Add phase details
|
|
179
|
-
for (const [phaseName, phaseTasks] of Object.entries(phases)) {
|
|
180
|
-
const icon = phaseName === 'foundation' ? '1' :
|
|
181
|
-
phaseName === 'mvp' ? '2' :
|
|
182
|
-
phaseName === 'launch' ? '3' : '4';
|
|
183
|
-
|
|
184
|
-
content += `### Phase ${icon}: ${formatPhaseName(phaseName)}
|
|
185
|
-
|
|
186
|
-
`;
|
|
187
|
-
|
|
188
|
-
// Group tasks by source
|
|
189
|
-
const bySource = {};
|
|
190
|
-
for (const task of phaseTasks) {
|
|
191
|
-
const source = task.source || 'Manual';
|
|
192
|
-
if (!bySource[source]) bySource[source] = [];
|
|
193
|
-
bySource[source].push(task);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
for (const [source, sourceTasks] of Object.entries(bySource)) {
|
|
197
|
-
content += `**From ${source}:**\n`;
|
|
198
|
-
for (const task of sourceTasks) {
|
|
199
|
-
const checkbox = task.status === 'completed' ? '[x]' : '[ ]';
|
|
200
|
-
content += `- ${checkbox} ${task.title}\n`;
|
|
201
|
-
}
|
|
202
|
-
content += '\n';
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
content += '---\n\n';
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Add exit criteria
|
|
209
|
-
content += `## MVP Exit Criteria
|
|
210
|
-
|
|
211
|
-
The build loop will exit when:
|
|
212
|
-
|
|
213
|
-
1. All MVP phase tasks are completed
|
|
214
|
-
2. Quality gates pass (tests, lint, typecheck)
|
|
215
|
-
3. Core user journeys are functional
|
|
216
|
-
4. No critical bugs remain
|
|
217
|
-
|
|
218
|
-
---
|
|
219
|
-
|
|
220
|
-
## Build Commands
|
|
221
|
-
|
|
222
|
-
\`\`\`bash
|
|
223
|
-
# Start the build loop
|
|
224
|
-
bootspring seed build --loop
|
|
225
|
-
|
|
226
|
-
# Check progress
|
|
227
|
-
bootspring build status
|
|
228
|
-
|
|
229
|
-
# Pause the build
|
|
230
|
-
bootspring build pause
|
|
231
|
-
|
|
232
|
-
# Resume building
|
|
233
|
-
bootspring build resume
|
|
234
|
-
|
|
235
|
-
# View current task
|
|
236
|
-
bootspring build task
|
|
237
|
-
\`\`\`
|
|
238
|
-
|
|
239
|
-
---
|
|
240
|
-
|
|
241
|
-
*Generated by [Bootspring](https://bootspring.com) Autonomous Build System*
|
|
242
|
-
`;
|
|
243
|
-
|
|
244
|
-
return content;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Generate TODO.md
|
|
249
|
-
* @param {array} tasks - Extracted tasks
|
|
250
|
-
* @param {object} options - Options
|
|
251
|
-
* @returns {string} TODO.md content
|
|
252
|
-
*/
|
|
253
|
-
function generateTodo(tasks, options = {}) {
|
|
254
|
-
const phases = groupByPhase(tasks);
|
|
255
|
-
const projectName = options.projectName || 'Project';
|
|
256
|
-
|
|
257
|
-
let content = `# ${projectName} - Build Todo
|
|
258
|
-
|
|
259
|
-
> Single source of truth for autonomous build execution
|
|
260
|
-
> Updated: ${new Date().toISOString().split('T')[0]}
|
|
261
|
-
|
|
262
|
-
---
|
|
263
|
-
|
|
264
|
-
## Program Status
|
|
265
|
-
|
|
266
|
-
| Metric | Value |
|
|
267
|
-
|---|---:|
|
|
268
|
-
| Total Tasks | ${tasks.length} |
|
|
269
|
-
| Completed | ${tasks.filter(t => t.status === 'completed').length} |
|
|
270
|
-
| Remaining | ${tasks.filter(t => t.status === 'pending').length} |
|
|
271
|
-
| In Progress | ${tasks.filter(t => t.status === 'in_progress').length} |
|
|
272
|
-
|
|
273
|
-
---
|
|
274
|
-
|
|
275
|
-
`;
|
|
276
|
-
|
|
277
|
-
for (const [phaseName, phaseTasks] of Object.entries(phases)) {
|
|
278
|
-
const completed = phaseTasks.filter(t => t.status === 'completed').length;
|
|
279
|
-
const total = phaseTasks.length;
|
|
280
|
-
const percent = total > 0 ? Math.round((completed / total) * 100) : 0;
|
|
281
|
-
|
|
282
|
-
content += `## ${formatPhaseName(phaseName)} (${completed}/${total} — ${percent}%)
|
|
283
|
-
|
|
284
|
-
`;
|
|
285
|
-
|
|
286
|
-
for (const task of phaseTasks) {
|
|
287
|
-
const checkbox = task.status === 'completed' ? '[x]' : '[ ]';
|
|
288
|
-
const statusTag = task.status === 'completed' ? ' (`completed`)' :
|
|
289
|
-
task.status === 'in_progress' ? ' (`in_progress`)' :
|
|
290
|
-
task.status === 'blocked' ? ' (`blocked`)' :
|
|
291
|
-
task.status === 'skipped' ? ' (`skipped`)' : '';
|
|
292
|
-
content += `- ${checkbox} \`${task.id}\` ${task.title}${statusTag}\n`;
|
|
293
|
-
|
|
294
|
-
// Source traceability
|
|
295
|
-
if (task.source) {
|
|
296
|
-
const sourceStr = task.sourceSection
|
|
297
|
-
? `${task.source} (${task.sourceSection})`
|
|
298
|
-
: task.source;
|
|
299
|
-
content += ` - **Source:** ${sourceStr}\n`;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Description
|
|
303
|
-
if (task.description) {
|
|
304
|
-
content += ` - **Description:** ${task.description}\n`;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Complexity and dependencies
|
|
308
|
-
if (task.estimatedComplexity && task.estimatedComplexity !== 'medium') {
|
|
309
|
-
content += ` - **Complexity:** ${task.estimatedComplexity}\n`;
|
|
310
|
-
}
|
|
311
|
-
if (task.dependencies && task.dependencies.length > 0) {
|
|
312
|
-
content += ` - **Dependencies:** ${task.dependencies.join(', ')}\n`;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Acceptance criteria as sub-items (all of them — this is the source of truth)
|
|
316
|
-
if (task.acceptanceCriteria && task.acceptanceCriteria.length > 0) {
|
|
317
|
-
for (const criteria of task.acceptanceCriteria) {
|
|
318
|
-
const subCheckbox = task.status === 'completed' ? '[x]' : '[ ]';
|
|
319
|
-
content += ` - ${subCheckbox} ${criteria}\n`;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
content += '\n---\n\n';
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
content += `*Updated: ${new Date().toISOString()}*
|
|
328
|
-
`;
|
|
329
|
-
|
|
330
|
-
return content;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* Generate TASK_QUEUE.md
|
|
335
|
-
* @param {array} tasks - Extracted tasks
|
|
336
|
-
* @param {object} options - Options
|
|
337
|
-
* @returns {string} TASK_QUEUE.md content
|
|
338
|
-
*/
|
|
339
|
-
function generateImplementationQueue(tasks, options = {}) {
|
|
340
|
-
const projectName = options.projectName || 'Project';
|
|
341
|
-
const lastUpdated = options.lastUpdated || new Date().toISOString().split('T')[0];
|
|
342
|
-
const queueVersion = options.queueVersion || DEFAULT_QUEUE_VERSION;
|
|
343
|
-
|
|
344
|
-
let content = `# ${projectName} - Implementation Queue
|
|
345
|
-
|
|
346
|
-
> Ordered task queue for autonomous execution
|
|
347
|
-
> **Last Updated:** ${lastUpdated}
|
|
348
|
-
> **Current Version:** ${queueVersion}
|
|
349
|
-
|
|
350
|
-
---
|
|
351
|
-
|
|
352
|
-
## Queue Status
|
|
353
|
-
|
|
354
|
-
| Position | ID | Task | Phase | Complexity | Status |
|
|
355
|
-
|----------|----|----- |-------|------------|--------|
|
|
356
|
-
`;
|
|
357
|
-
|
|
358
|
-
tasks.forEach((task, index) => {
|
|
359
|
-
const statusIcon = task.status === 'completed' ? 'Done' :
|
|
360
|
-
task.status === 'in_progress' ? 'Active' :
|
|
361
|
-
task.status === 'blocked' ? 'Blocked' : 'Pending';
|
|
362
|
-
|
|
363
|
-
content += `| ${index + 1} | ${task.id} | ${truncate(task.title, 40)} | ${formatPhaseName(task.phase)} | ${task.estimatedComplexity || 'medium'} | ${statusIcon} |\n`;
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
content += `
|
|
367
|
-
---
|
|
368
|
-
|
|
369
|
-
## Task Details
|
|
370
|
-
|
|
371
|
-
`;
|
|
372
|
-
|
|
373
|
-
for (const task of tasks) {
|
|
374
|
-
content += `### ${task.id}: ${task.title}
|
|
375
|
-
|
|
376
|
-
**Phase:** ${formatPhaseName(task.phase)}
|
|
377
|
-
**Source:** ${task.source || 'Manual'} ${task.sourceSection ? `(${task.sourceSection})` : ''}
|
|
378
|
-
**Complexity:** ${task.estimatedComplexity || 'medium'}
|
|
379
|
-
**Status:** ${task.status || 'pending'}
|
|
380
|
-
|
|
381
|
-
`;
|
|
382
|
-
|
|
383
|
-
if (task.description) {
|
|
384
|
-
content += `**Description:**
|
|
385
|
-
${task.description}
|
|
386
|
-
|
|
387
|
-
`;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
if (task.acceptanceCriteria && task.acceptanceCriteria.length > 0) {
|
|
391
|
-
content += `**Acceptance Criteria:**
|
|
392
|
-
${task.acceptanceCriteria.map(c => `- [ ] ${c}`).join('\n')}
|
|
393
|
-
|
|
394
|
-
`;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
if (task.dependencies && task.dependencies.length > 0) {
|
|
398
|
-
content += `**Dependencies:** ${task.dependencies.join(', ')}
|
|
399
|
-
|
|
400
|
-
`;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
content += '---\n\n';
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
content += `## Execution Guidelines
|
|
407
|
-
|
|
408
|
-
1. **One Task at a Time** - Complete each task fully before moving on
|
|
409
|
-
2. **Quality Gates** - Run tests, lint, and typecheck before marking complete
|
|
410
|
-
3. **Commit Per Task** - Create meaningful commits for each completed task
|
|
411
|
-
4. **Update Status** - Run \`bootspring build done\` to mark the current task complete
|
|
412
|
-
5. **Document Changes** - Update CLAUDE.md with new patterns/decisions
|
|
413
|
-
|
|
414
|
-
---
|
|
415
|
-
|
|
416
|
-
*Generated by Bootspring*
|
|
417
|
-
`;
|
|
418
|
-
|
|
419
|
-
return content;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* Generate CONTEXT.md
|
|
424
|
-
* @param {object} docs - Source documents
|
|
425
|
-
* @param {array} tasks - Extracted tasks
|
|
426
|
-
* @param {object} options - Options
|
|
427
|
-
* @returns {string} CONTEXT.md content
|
|
428
|
-
*/
|
|
429
|
-
function generateContext(docs, tasks, options = {}) {
|
|
430
|
-
const projectName = options.projectName || 'Project';
|
|
431
|
-
const techStack = options.techStack || extractTechStack(docs);
|
|
432
|
-
|
|
433
|
-
let content = `# ${projectName} - Build Context
|
|
434
|
-
|
|
435
|
-
> Condensed context for AI build iterations
|
|
436
|
-
> Read this file at the start of each build task
|
|
437
|
-
|
|
438
|
-
---
|
|
439
|
-
|
|
440
|
-
## Project Identity
|
|
441
|
-
|
|
442
|
-
**Name:** ${projectName}
|
|
443
|
-
${options.tagline ? `**Tagline:** ${options.tagline}` : ''}
|
|
444
|
-
|
|
445
|
-
---
|
|
446
|
-
|
|
447
|
-
## Tech Stack
|
|
448
|
-
|
|
449
|
-
| Component | Technology |
|
|
450
|
-
|-----------|------------|
|
|
451
|
-
| Framework | ${techStack.framework || 'Next.js'} |
|
|
452
|
-
| Language | ${techStack.language || 'TypeScript'} |
|
|
453
|
-
| Database | ${techStack.database || 'PostgreSQL'} |
|
|
454
|
-
| ORM | ${techStack.orm || 'Prisma'} |
|
|
455
|
-
| UI | ${techStack.uiLibrary || 'shadcn/ui'} |
|
|
456
|
-
| Styling | ${techStack.styling || 'Tailwind CSS'} |
|
|
457
|
-
| Auth | ${techStack.auth || 'Clerk'} |
|
|
458
|
-
| Hosting | ${techStack.hosting || 'Vercel'} |
|
|
459
|
-
|
|
460
|
-
---
|
|
461
|
-
|
|
462
|
-
## Current Build Status
|
|
463
|
-
|
|
464
|
-
**Phase:** ${options.currentPhase || 'MVP'}
|
|
465
|
-
**Tasks Remaining:** ${tasks.filter(t => t.status !== 'completed').length}
|
|
466
|
-
**Tasks Completed:** ${tasks.filter(t => t.status === 'completed').length}
|
|
467
|
-
|
|
468
|
-
---
|
|
469
|
-
|
|
470
|
-
## Key Decisions
|
|
471
|
-
|
|
472
|
-
`;
|
|
473
|
-
|
|
474
|
-
// Add any decisions from docs
|
|
475
|
-
if (docs.TECHNICAL_SPEC || docs.technical_spec) {
|
|
476
|
-
content += `### Architecture
|
|
477
|
-
- Use Server Components by default
|
|
478
|
-
- Prefer Server Actions over API routes for mutations
|
|
479
|
-
- Use Zod for input validation
|
|
480
|
-
- Never expose API keys to client
|
|
481
|
-
|
|
482
|
-
`;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
content += `### Code Style
|
|
486
|
-
- TypeScript strict mode enabled
|
|
487
|
-
- ESLint + Prettier for formatting
|
|
488
|
-
- Conventional commits (feat:, fix:, docs:, refactor:)
|
|
489
|
-
- Keep files under 300 lines
|
|
490
|
-
|
|
491
|
-
---
|
|
492
|
-
|
|
493
|
-
## MVP Features
|
|
494
|
-
|
|
495
|
-
`;
|
|
496
|
-
|
|
497
|
-
// Extract unique MVP features
|
|
498
|
-
const mvpTasks = tasks.filter(t => t.phase === 'mvp');
|
|
499
|
-
const uniqueFeatures = [...new Set(mvpTasks.map(t => t.title))].slice(0, 10);
|
|
500
|
-
|
|
501
|
-
uniqueFeatures.forEach((feature, i) => {
|
|
502
|
-
content += `${i + 1}. ${feature}\n`;
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
content += `
|
|
506
|
-
---
|
|
507
|
-
|
|
508
|
-
## File Structure
|
|
509
|
-
|
|
510
|
-
\`\`\`
|
|
511
|
-
${projectName.toLowerCase().replace(/\s+/g, '-')}/
|
|
512
|
-
├── app/ # Next.js App Router
|
|
513
|
-
│ ├── (auth)/ # Auth pages
|
|
514
|
-
│ ├── (dashboard)/ # Main dashboard
|
|
515
|
-
│ ├── (marketing)/ # Landing pages
|
|
516
|
-
│ └── api/ # API routes
|
|
517
|
-
├── components/
|
|
518
|
-
│ ├── ui/ # UI components
|
|
519
|
-
│ └── [feature]/ # Feature components
|
|
520
|
-
├── lib/
|
|
521
|
-
│ ├── db.ts # Database client
|
|
522
|
-
│ ├── auth.ts # Auth utilities
|
|
523
|
-
│ └── utils.ts # Helpers
|
|
524
|
-
├── prisma/
|
|
525
|
-
│ └── schema.prisma # Database schema
|
|
526
|
-
├── planning/ # Build planning (this folder)
|
|
527
|
-
└── public/ # Static assets
|
|
528
|
-
\`\`\`
|
|
529
|
-
|
|
530
|
-
---
|
|
531
|
-
|
|
532
|
-
## Build Loop Rules
|
|
533
|
-
|
|
534
|
-
When executing a task:
|
|
535
|
-
|
|
536
|
-
1. **Read CLAUDE.md** - Understand project patterns
|
|
537
|
-
2. **Check BUILD_STATE.json** - Know current task and status
|
|
538
|
-
3. **Implement ONE task** - Focus on the current task only
|
|
539
|
-
4. **Run quality checks** - \`npm run lint && npm run test\`
|
|
540
|
-
5. **Commit changes** - Use conventional commits
|
|
541
|
-
6. **Update status** - Run \`bootspring build done\` to advance the build loop
|
|
542
|
-
|
|
543
|
-
---
|
|
544
|
-
|
|
545
|
-
## Exit Signals
|
|
546
|
-
|
|
547
|
-
Output these signals for loop control:
|
|
548
|
-
|
|
549
|
-
\`\`\`
|
|
550
|
-
<loop-status>TASK_COMPLETE</loop-status> # Task done successfully
|
|
551
|
-
<loop-status>TASK_BLOCKED</loop-status> # Cannot complete, needs help
|
|
552
|
-
<loop-status>ALL_COMPLETE</loop-status> # All MVP tasks done
|
|
553
|
-
EXIT_SIGNAL # Stop the loop
|
|
554
|
-
\`\`\`
|
|
555
|
-
|
|
556
|
-
---
|
|
557
|
-
|
|
558
|
-
*This context is refreshed at the start of each build iteration*
|
|
559
|
-
`;
|
|
560
|
-
|
|
561
|
-
return content;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
/**
|
|
565
|
-
* Group tasks by phase
|
|
566
|
-
* @param {array} tasks - Tasks to group
|
|
567
|
-
* @returns {object} Tasks grouped by phase
|
|
568
|
-
*/
|
|
569
|
-
function groupByPhase(tasks) {
|
|
570
|
-
const phases = {
|
|
571
|
-
foundation: [],
|
|
572
|
-
mvp: [],
|
|
573
|
-
launch: [],
|
|
574
|
-
other: []
|
|
575
|
-
};
|
|
576
|
-
|
|
577
|
-
for (const task of tasks) {
|
|
578
|
-
const phase = task.phase || 'other';
|
|
579
|
-
if (phases[phase]) {
|
|
580
|
-
phases[phase].push(task);
|
|
581
|
-
} else {
|
|
582
|
-
phases.other.push(task);
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
// Remove empty phases
|
|
587
|
-
for (const [key, value] of Object.entries(phases)) {
|
|
588
|
-
if (value.length === 0) {
|
|
589
|
-
delete phases[key];
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
return phases;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
/**
|
|
597
|
-
* Format phase name for display
|
|
598
|
-
* @param {string} phase - Phase name
|
|
599
|
-
* @returns {string} Formatted name
|
|
600
|
-
*/
|
|
601
|
-
function formatPhaseName(phase) {
|
|
602
|
-
if (!phase) return 'Other';
|
|
603
|
-
|
|
604
|
-
const names = {
|
|
605
|
-
foundation: 'Foundation',
|
|
606
|
-
mvp: 'MVP',
|
|
607
|
-
launch: 'Launch',
|
|
608
|
-
other: 'Other'
|
|
609
|
-
};
|
|
610
|
-
|
|
611
|
-
return names[phase] || phase.charAt(0).toUpperCase() + phase.slice(1);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
/**
|
|
615
|
-
* Truncate string to max length
|
|
616
|
-
* @param {string} str - String to truncate
|
|
617
|
-
* @param {number} maxLength - Maximum length
|
|
618
|
-
* @returns {string} Truncated string
|
|
619
|
-
*/
|
|
620
|
-
function truncate(str, maxLength) {
|
|
621
|
-
if (!str) return '';
|
|
622
|
-
if (str.length <= maxLength) return str;
|
|
623
|
-
return str.slice(0, maxLength - 3) + '...';
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
/**
|
|
627
|
-
* Extract tech stack from docs
|
|
628
|
-
* @param {object} docs - Source documents
|
|
629
|
-
* @returns {object} Tech stack
|
|
630
|
-
*/
|
|
631
|
-
function extractTechStack(docs) {
|
|
632
|
-
const techDoc = docs.TECHNICAL_SPEC || docs.technical_spec || '';
|
|
633
|
-
const seedDoc = docs.SEED || docs.seed || '';
|
|
634
|
-
const allDocs = techDoc + seedDoc;
|
|
635
|
-
|
|
636
|
-
const stack = {
|
|
637
|
-
framework: 'nextjs',
|
|
638
|
-
language: 'typescript',
|
|
639
|
-
database: 'postgresql',
|
|
640
|
-
orm: 'prisma',
|
|
641
|
-
uiLibrary: 'shadcn',
|
|
642
|
-
styling: 'tailwind',
|
|
643
|
-
auth: 'clerk',
|
|
644
|
-
hosting: 'vercel'
|
|
645
|
-
};
|
|
646
|
-
|
|
647
|
-
// Framework detection
|
|
648
|
-
if (allDocs.match(/Next\.?js/i)) stack.framework = 'Next.js';
|
|
649
|
-
else if (allDocs.match(/Remix/i)) stack.framework = 'Remix';
|
|
650
|
-
else if (allDocs.match(/Nuxt/i)) stack.framework = 'Nuxt';
|
|
651
|
-
|
|
652
|
-
// Database
|
|
653
|
-
if (allDocs.match(/Postgres|PostgreSQL/i)) stack.database = 'PostgreSQL';
|
|
654
|
-
else if (allDocs.match(/MongoDB/i)) stack.database = 'MongoDB';
|
|
655
|
-
else if (allDocs.match(/Supabase/i)) stack.database = 'Supabase';
|
|
656
|
-
|
|
657
|
-
// Auth
|
|
658
|
-
if (allDocs.match(/\bClerk\b/i)) stack.auth = 'Clerk';
|
|
659
|
-
else if (allDocs.match(/NextAuth|Auth\.js/i)) stack.auth = 'NextAuth';
|
|
660
|
-
|
|
661
|
-
return stack;
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
/**
|
|
665
|
-
* Update planning files with current state
|
|
666
|
-
* @param {string} projectRoot - Project root path
|
|
667
|
-
* @param {object} state - Current build state
|
|
668
|
-
*/
|
|
669
|
-
function updateFromState(projectRoot, state, options = {}) {
|
|
670
|
-
if (!state || !state.implementationQueue) return;
|
|
671
|
-
|
|
672
|
-
const tasks = state.implementationQueue;
|
|
673
|
-
const planningDir = path.join(projectRoot, 'planning');
|
|
674
|
-
|
|
675
|
-
// Regenerate TODO.md — the single source of truth
|
|
676
|
-
const todo = generateTodo(tasks, { projectName: state.projectName });
|
|
677
|
-
fs.writeFileSync(path.join(planningDir, 'TODO.md'), todo);
|
|
678
|
-
|
|
679
|
-
if (options.includeLegacyTaskQueue === true) {
|
|
680
|
-
const queuePath = path.join(planningDir, 'TASK_QUEUE.md');
|
|
681
|
-
const existingQueue = fs.existsSync(queuePath)
|
|
682
|
-
? fs.readFileSync(queuePath, 'utf-8')
|
|
683
|
-
: '';
|
|
684
|
-
const existingVersionMatch = existingQueue.match(/\*\*Current Version:\*\*\s*([^\n]+)/i)
|
|
685
|
-
|| existingQueue.match(/>\s*Current Version:\s*([^\n]+)/i);
|
|
686
|
-
const queueVersion = existingVersionMatch
|
|
687
|
-
? existingVersionMatch[1].trim()
|
|
688
|
-
: DEFAULT_QUEUE_VERSION;
|
|
689
|
-
|
|
690
|
-
const queue = generateImplementationQueue(tasks, {
|
|
691
|
-
projectName: state.projectName,
|
|
692
|
-
lastUpdated: new Date().toISOString().split('T')[0],
|
|
693
|
-
queueVersion
|
|
694
|
-
});
|
|
695
|
-
fs.writeFileSync(queuePath, queue);
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
module.exports = {
|
|
700
|
-
generateAll,
|
|
701
|
-
generateMasterPlan,
|
|
702
|
-
generateTodo,
|
|
703
|
-
generateImplementationQueue,
|
|
704
|
-
generateContext,
|
|
705
|
-
groupByPhase,
|
|
706
|
-
formatPhaseName,
|
|
707
|
-
updateFromState
|
|
708
|
-
};
|