@girardmedia/bootspring 2.0.8 → 2.0.10

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/cli/build.js CHANGED
@@ -341,7 +341,7 @@ function generateTaskPrompt(task, projectRoot) {
341
341
  lines.push('');
342
342
 
343
343
  if (task.description) {
344
- lines.push(`**Description:**`);
344
+ lines.push('**Description:**');
345
345
  lines.push(task.description);
346
346
  lines.push('');
347
347
  }
@@ -363,7 +363,7 @@ function generateTaskPrompt(task, projectRoot) {
363
363
  lines.push('1. Implement the task as described above');
364
364
  lines.push('2. Ensure all acceptance criteria are met');
365
365
  lines.push('3. Test your implementation');
366
- lines.push(`4. When done, run: bootspring build done`);
366
+ lines.push('4. When done, run: bootspring build done');
367
367
 
368
368
  return lines.join('\n');
369
369
  }
package/cli/generate.js CHANGED
@@ -11,6 +11,7 @@ const config = require('../core/config');
11
11
  const context = require('../core/context');
12
12
  const utils = require('../core/utils');
13
13
  const gitMemory = require('../intelligence/git-memory');
14
+ const agentsTemplate = require('../generators/templates/agents.template');
14
15
 
15
16
  /**
16
17
  * Generate CLAUDE.md content from config and project state
@@ -283,6 +284,17 @@ ${utils.COLORS.bold}Score:${utils.COLORS.reset} ${validation.score}/${validation
283
284
  utils.writeFile(todoPath, todoContent);
284
285
  todoSpinner.succeed('Generated todo.md');
285
286
  }
287
+
288
+ // Generate AGENTS.md for multi-tool compatibility
289
+ const agentsPath = path.join(cfg._projectRoot, 'AGENTS.md');
290
+ const agentsSpinner = utils.createSpinner('Generating AGENTS.md').start();
291
+ try {
292
+ const agentsContent = agentsTemplate.generate(cfg);
293
+ utils.writeFile(agentsPath, agentsContent);
294
+ agentsSpinner.succeed('Generated AGENTS.md (Codex, Cursor, Amp, Kilo)');
295
+ } catch (error) {
296
+ agentsSpinner.fail(`Failed to generate AGENTS.md: ${error.message}`);
297
+ }
286
298
  }
287
299
 
288
300
  console.log(`
package/cli/seed.js CHANGED
@@ -25,6 +25,8 @@ const config = require('../core/config');
25
25
  const utils = require('../core/utils');
26
26
  const { runQuestionnaire } = require('../generators/questionnaire');
27
27
  const seedTemplate = require('../generators/templates/seed.template');
28
+ const claudeTemplate = require('../generators/templates/claude.template');
29
+ const agentsTemplate = require('../generators/templates/agents.template');
28
30
  const scaffold = require('../core/scaffold');
29
31
  const projectState = require('../core/project-state');
30
32
  const checkpointEngine = require('../core/checkpoint-engine');
@@ -39,6 +41,79 @@ function getIngest() {
39
41
  return ingest;
40
42
  }
41
43
 
44
+ /**
45
+ * Generate AI context files (CLAUDE.md and AGENTS.md)
46
+ * @param {string} projectRoot - Project root path
47
+ * @param {object} scaffoldConfig - Configuration object
48
+ * @returns {object} Results of generation
49
+ */
50
+ function generateAIContextFiles(projectRoot, scaffoldConfig) {
51
+ const results = { claudeMd: false, agentsMd: false, planningAgentsMd: false };
52
+
53
+ // Build a config object suitable for the templates
54
+ const cfg = {
55
+ project: {
56
+ name: scaffoldConfig.project?.name || 'My Project',
57
+ description: scaffoldConfig.project?.description || '',
58
+ version: scaffoldConfig.project?.version || '0.1.0',
59
+ status: 'development'
60
+ },
61
+ stack: scaffoldConfig.stack || {
62
+ framework: 'nextjs',
63
+ language: 'typescript',
64
+ database: 'postgresql',
65
+ hosting: 'vercel'
66
+ },
67
+ frontend: scaffoldConfig.frontend || {
68
+ uiLibrary: 'shadcn',
69
+ styling: 'tailwind'
70
+ },
71
+ backend: scaffoldConfig.backend || {
72
+ orm: 'prisma',
73
+ auth: 'clerk'
74
+ },
75
+ plugins: scaffoldConfig.plugins || {},
76
+ instructions: scaffoldConfig.instructions || {},
77
+ workflow: scaffoldConfig.workflow || {},
78
+ business: scaffoldConfig.business || {}
79
+ };
80
+
81
+ // Generate CLAUDE.md
82
+ try {
83
+ const claudePath = path.join(projectRoot, 'CLAUDE.md');
84
+ const claudeContent = claudeTemplate.generate(cfg);
85
+ fs.writeFileSync(claudePath, claudeContent);
86
+ results.claudeMd = true;
87
+ } catch (error) {
88
+ utils.print.debug(`Failed to generate CLAUDE.md: ${error.message}`);
89
+ }
90
+
91
+ // Generate AGENTS.md (root level)
92
+ try {
93
+ const agentsPath = path.join(projectRoot, 'AGENTS.md');
94
+ const agentsContent = agentsTemplate.generate(cfg);
95
+ fs.writeFileSync(agentsPath, agentsContent);
96
+ results.agentsMd = true;
97
+ } catch (error) {
98
+ utils.print.debug(`Failed to generate AGENTS.md: ${error.message}`);
99
+ }
100
+
101
+ // Generate planning/AGENTS.md if planning folder exists
102
+ const planningDir = path.join(projectRoot, 'planning');
103
+ if (fs.existsSync(planningDir)) {
104
+ try {
105
+ const planningAgentsPath = path.join(planningDir, 'AGENTS.md');
106
+ const planningAgentsContent = agentsTemplate.generatePlanningAgents(cfg);
107
+ fs.writeFileSync(planningAgentsPath, planningAgentsContent);
108
+ results.planningAgentsMd = true;
109
+ } catch (error) {
110
+ utils.print.debug(`Failed to generate planning/AGENTS.md: ${error.message}`);
111
+ }
112
+ }
113
+
114
+ return results;
115
+ }
116
+
42
117
  /**
43
118
  * Seed folder structure
44
119
  */
@@ -382,9 +457,27 @@ ${utils.COLORS.dim}Interactive project configuration${utils.COLORS.reset}
382
457
  utils.print.debug(`Auto-tagging failed: ${err.message}`);
383
458
  }
384
459
 
460
+ // Generate AI context files
461
+ const aiSpinner = utils.createSpinner('Generating AI context files...').start();
462
+ const aiResults = generateAIContextFiles(projectRoot, projectConfig);
463
+ const aiFilesGenerated = [];
464
+ if (aiResults.claudeMd) aiFilesGenerated.push('CLAUDE.md');
465
+ if (aiResults.agentsMd) aiFilesGenerated.push('AGENTS.md');
466
+ if (aiFilesGenerated.length > 0) {
467
+ aiSpinner.succeed(`Generated ${aiFilesGenerated.join(', ')}`);
468
+ } else {
469
+ aiSpinner.warn('Could not generate AI context files');
470
+ }
471
+
385
472
  console.log(`
386
473
  ${utils.COLORS.green}${utils.COLORS.bold}✓ Seed initialized successfully!${utils.COLORS.reset}
387
474
 
475
+ ${utils.COLORS.bold}Generated Files:${utils.COLORS.reset}
476
+ ${utils.COLORS.cyan}SEED.md${utils.COLORS.reset} - Project specification
477
+ ${utils.COLORS.cyan}bootspring.config.js${utils.COLORS.reset} - Configuration
478
+ ${utils.COLORS.cyan}CLAUDE.md${utils.COLORS.reset} - Claude Code context
479
+ ${utils.COLORS.cyan}AGENTS.md${utils.COLORS.reset} - Codex/Cursor/Amp context
480
+
388
481
  ${utils.COLORS.bold}Next steps:${utils.COLORS.reset}
389
482
  1. Review ${utils.COLORS.cyan}SEED.md${utils.COLORS.reset} for your project configuration
390
483
  2. Run ${utils.COLORS.cyan}bootspring seed scaffold${utils.COLORS.reset} to generate project structure
@@ -549,13 +642,33 @@ ${utils.COLORS.bold}Available presets:${utils.COLORS.reset}`);
549
642
  }
550
643
  }
551
644
 
645
+ // Generate AI context files (CLAUDE.md and AGENTS.md)
646
+ const aiSpinner = utils.createSpinner('Generating AI context files...').start();
647
+ const aiResults = generateAIContextFiles(projectRoot, scaffoldConfig);
648
+
649
+ const aiFilesGenerated = [];
650
+ if (aiResults.claudeMd) aiFilesGenerated.push('CLAUDE.md');
651
+ if (aiResults.agentsMd) aiFilesGenerated.push('AGENTS.md');
652
+ if (aiResults.planningAgentsMd) aiFilesGenerated.push('planning/AGENTS.md');
653
+
654
+ if (aiFilesGenerated.length > 0) {
655
+ aiSpinner.succeed(`Generated ${aiFilesGenerated.join(', ')}`);
656
+ } else {
657
+ aiSpinner.warn('Could not generate AI context files');
658
+ }
659
+
552
660
  console.log(`
553
661
  ${utils.COLORS.green}${utils.COLORS.bold}✓ Scaffold complete!${utils.COLORS.reset}
554
662
 
663
+ ${utils.COLORS.bold}Generated Files:${utils.COLORS.reset}
664
+ ${utils.COLORS.cyan}CLAUDE.md${utils.COLORS.reset} - Context for Claude Code
665
+ ${utils.COLORS.cyan}AGENTS.md${utils.COLORS.reset} - Context for Codex, Cursor, Amp, Kilo
666
+
555
667
  ${utils.COLORS.bold}Next steps:${utils.COLORS.reset}
556
668
  1. Run ${utils.COLORS.cyan}npm install${utils.COLORS.reset} to install dependencies
557
669
  2. Copy ${utils.COLORS.cyan}.env.example${utils.COLORS.reset} to ${utils.COLORS.cyan}.env.local${utils.COLORS.reset}
558
670
  3. Run ${utils.COLORS.cyan}npm run dev${utils.COLORS.reset} to start development
671
+ 4. Run ${utils.COLORS.cyan}bootspring build${utils.COLORS.reset} to start building tasks
559
672
  `);
560
673
  } catch (error) {
561
674
  spinner.fail(`Scaffold failed: ${error.message}`);
@@ -807,15 +920,73 @@ ${utils.COLORS.dim}Create SEED.md from your preseed documents${utils.COLORS.rese
807
920
  fs.writeFileSync(seedPath, seedContent);
808
921
  spinner.succeed('Created SEED.md');
809
922
 
923
+ // Extract tech stack for AI context files
924
+ const techStack = extractTechStack(docs);
925
+ const projectName = extractProjectName(docs) || preseedConfig.identity?.name || 'My Project';
926
+ const tagline = extractTagline(docs) || '';
927
+
928
+ const synthConfig = {
929
+ project: {
930
+ name: projectName,
931
+ description: tagline,
932
+ version: '0.1.0',
933
+ status: 'development'
934
+ },
935
+ stack: {
936
+ framework: techStack.framework,
937
+ language: techStack.language,
938
+ database: techStack.database,
939
+ hosting: techStack.hosting
940
+ },
941
+ frontend: {
942
+ uiLibrary: techStack.uiLibrary,
943
+ styling: techStack.styling
944
+ },
945
+ backend: {
946
+ orm: techStack.orm,
947
+ auth: techStack.auth
948
+ }
949
+ };
950
+
951
+ // Generate AI context files
952
+ const aiSpinner = utils.createSpinner('Generating AI context files...').start();
953
+ const aiResults = generateAIContextFiles(projectRoot, synthConfig);
954
+ const aiFilesGenerated = [];
955
+ if (aiResults.claudeMd) aiFilesGenerated.push('CLAUDE.md');
956
+ if (aiResults.agentsMd) aiFilesGenerated.push('AGENTS.md');
957
+ if (aiFilesGenerated.length > 0) {
958
+ aiSpinner.succeed(`Generated ${aiFilesGenerated.join(', ')}`);
959
+ } else {
960
+ aiSpinner.warn('Could not generate AI context files');
961
+ }
962
+
963
+ // Also create bootspring.config.js for CLI compatibility
964
+ const configPath = path.join(projectRoot, 'bootspring.config.js');
965
+ if (!fs.existsSync(configPath)) {
966
+ const configSpinner = utils.createSpinner('Creating bootspring.config.js...').start();
967
+ if (config.save(synthConfig, configPath)) {
968
+ configSpinner.succeed('Created bootspring.config.js');
969
+ } else {
970
+ configSpinner.warn('Could not create bootspring.config.js');
971
+ }
972
+ }
973
+
810
974
  console.log(`
811
975
  ${utils.COLORS.green}${utils.COLORS.bold}✓ SEED.md synthesized successfully!${utils.COLORS.reset}
812
976
 
813
977
  ${utils.COLORS.bold}Extracted from:${utils.COLORS.reset}
814
978
  ${preseedFiles.map(f => ` • ${f}`).join('\n')}
815
979
 
980
+ ${utils.COLORS.bold}Generated Files:${utils.COLORS.reset}
981
+ ${utils.COLORS.cyan}SEED.md${utils.COLORS.reset} - Project specification
982
+ ${utils.COLORS.cyan}bootspring.config.js${utils.COLORS.reset} - Configuration
983
+ ${utils.COLORS.cyan}CLAUDE.md${utils.COLORS.reset} - Claude Code context
984
+ ${utils.COLORS.cyan}AGENTS.md${utils.COLORS.reset} - Codex/Cursor/Amp context
985
+
816
986
  ${utils.COLORS.bold}Next steps:${utils.COLORS.reset}
817
987
  1. Review ${utils.COLORS.cyan}SEED.md${utils.COLORS.reset} - your project spec is ready
818
988
  2. Run ${utils.COLORS.cyan}bootspring seed scaffold --preset=nextjs${utils.COLORS.reset} to generate project
989
+ 3. Or run ${utils.COLORS.cyan}bootspring build${utils.COLORS.reset} to start building tasks
819
990
  `);
820
991
  } catch (error) {
821
992
  spinner.fail(`Synthesis failed: ${error.message}`);
@@ -0,0 +1,192 @@
1
+ /**
2
+ * AGENTS.md Template Generator
3
+ *
4
+ * Generates AGENTS.md following the open standard for AI coding agents.
5
+ * Supported by: OpenAI Codex, Cursor, Amp, Jules (Google), Factory, Kilo Code
6
+ *
7
+ * Best practices from https://github.blog/ai-and-ml/github-copilot/how-to-write-a-great-agents-md-lessons-from-over-2500-repositories/
8
+ *
9
+ * @package bootspring
10
+ * @module generators/templates/agents
11
+ */
12
+
13
+ /**
14
+ * Generate root AGENTS.md content from config
15
+ */
16
+ function generate(config) {
17
+ const sections = [];
18
+
19
+ // Persona - Be specific, not vague
20
+ sections.push(`# ${config.project.name}`);
21
+ sections.push('');
22
+ sections.push(`You are a senior full-stack developer working on ${config.project.name}, a ${formatFramework(config.stack.framework)} application with ${config.stack.language}.`);
23
+ sections.push('');
24
+
25
+ // Commands - Put early
26
+ sections.push('## Commands');
27
+ sections.push('');
28
+ sections.push('```bash');
29
+ sections.push('# Development');
30
+ sections.push('npm run dev');
31
+ sections.push('');
32
+ sections.push('# Quality checks (run before committing)');
33
+ sections.push('npm run lint');
34
+ sections.push('npm run test');
35
+ sections.push('npm run typecheck');
36
+ sections.push('');
37
+ sections.push('# Build tasks');
38
+ sections.push('bootspring build next # Get next task');
39
+ sections.push('bootspring build done # Mark task complete');
40
+ sections.push('bootspring build status # Check progress');
41
+ sections.push('```');
42
+ sections.push('');
43
+
44
+ // Task System
45
+ sections.push('## Task System');
46
+ sections.push('');
47
+ sections.push('This project uses Bootspring for autonomous task management.');
48
+ sections.push('');
49
+ sections.push('1. Check `planning/CURRENT_TASK.md` for the current task');
50
+ sections.push('2. Implement the task following acceptance criteria');
51
+ sections.push('3. Run quality checks');
52
+ sections.push('4. Commit changes');
53
+ sections.push('5. Run `bootspring build done` to get the next task');
54
+ sections.push('');
55
+
56
+ // Stack - Be specific about versions
57
+ sections.push('## Stack');
58
+ sections.push('');
59
+ sections.push(`- **Framework**: ${formatFramework(config.stack.framework)}`);
60
+ sections.push(`- **Language**: ${config.stack.language}`);
61
+ if (config.stack.database !== 'none') {
62
+ sections.push(`- **Database**: ${config.stack.database}`);
63
+ if (config.stack.orm) {
64
+ sections.push(`- **ORM**: ${config.stack.orm}`);
65
+ }
66
+ }
67
+ sections.push('');
68
+
69
+ // Code Style
70
+ sections.push('## Code Style');
71
+ sections.push('');
72
+ if (config.stack.framework === 'nextjs') {
73
+ sections.push('- Use Server Components by default');
74
+ sections.push('- Use Server Actions for mutations (not API routes)');
75
+ }
76
+ sections.push('- Use Zod for input validation');
77
+ sections.push('- Keep files under 300 lines');
78
+ sections.push('- Use meaningful variable and function names');
79
+ sections.push('- Follow existing patterns in the codebase');
80
+ sections.push('');
81
+
82
+ // Git Workflow
83
+ sections.push('## Git Workflow');
84
+ sections.push('');
85
+ sections.push('- Conventional commits: `feat:`, `fix:`, `docs:`, `refactor:`');
86
+ sections.push('- Atomic commits - one logical change per commit');
87
+ sections.push('- Run quality checks before committing');
88
+ sections.push('- Never force push to main');
89
+ sections.push('');
90
+
91
+ // Boundaries - Critical
92
+ sections.push('## Boundaries');
93
+ sections.push('');
94
+ sections.push('**Never:**');
95
+ sections.push('- Commit secrets, API keys, or credentials');
96
+ sections.push('- Modify `.env` files with real values');
97
+ sections.push('- Delete or overwrite migration files');
98
+ sections.push('- Skip tests to make code compile');
99
+ sections.push('- Introduce breaking changes without discussion');
100
+ sections.push('');
101
+ sections.push('**Always:**');
102
+ sections.push('- Read existing code before modifying');
103
+ sections.push('- Run tests after changes');
104
+ sections.push('- Check for TypeScript errors');
105
+ sections.push('- Follow the acceptance criteria exactly');
106
+ sections.push('');
107
+
108
+ // Testing
109
+ sections.push('## Testing');
110
+ sections.push('');
111
+ sections.push('- Write tests for new features');
112
+ sections.push('- Ensure existing tests still pass');
113
+ sections.push('- Test edge cases and error conditions');
114
+ sections.push('');
115
+
116
+ return sections.join('\n');
117
+ }
118
+
119
+ /**
120
+ * Generate planning-specific AGENTS.md for the planning directory
121
+ */
122
+ function generatePlanningAgents(_config) {
123
+ const sections = [];
124
+
125
+ sections.push('# Planning Directory');
126
+ sections.push('');
127
+ sections.push('You are working on build tasks for the MVP. This directory contains build planning files.');
128
+ sections.push('');
129
+
130
+ sections.push('## Current Task');
131
+ sections.push('');
132
+ sections.push('Read `CURRENT_TASK.md` for the task you should implement now.');
133
+ sections.push('');
134
+
135
+ sections.push('## Commands');
136
+ sections.push('');
137
+ sections.push('```bash');
138
+ sections.push('bootspring build done # Mark task complete, get next');
139
+ sections.push('bootspring build skip # Skip task, get next');
140
+ sections.push('bootspring build status # View progress');
141
+ sections.push('```');
142
+ sections.push('');
143
+
144
+ sections.push('## Files');
145
+ sections.push('');
146
+ sections.push('| File | Purpose |');
147
+ sections.push('|------|---------|');
148
+ sections.push('| `CURRENT_TASK.md` | Task to implement now |');
149
+ sections.push('| `TODO.md` | Full task checklist |');
150
+ sections.push('| `BUILD_STATE.json` | Build state (do not edit) |');
151
+ sections.push('| `IMPLEMENTATION_QUEUE.md` | Ordered task queue |');
152
+ sections.push('| `CONTEXT.md` | Build context summary |');
153
+ sections.push('');
154
+
155
+ sections.push('## Workflow');
156
+ sections.push('');
157
+ sections.push('1. Read `CURRENT_TASK.md`');
158
+ sections.push('2. Implement in the main codebase (not here)');
159
+ sections.push('3. Ensure acceptance criteria met');
160
+ sections.push('4. Run `npm run lint && npm run test`');
161
+ sections.push('5. Commit changes');
162
+ sections.push('6. Run `bootspring build done`');
163
+ sections.push('');
164
+
165
+ sections.push('## Boundaries');
166
+ sections.push('');
167
+ sections.push('- Do NOT edit `BUILD_STATE.json` directly');
168
+ sections.push('- Do NOT skip quality checks');
169
+ sections.push('- Do NOT work on multiple tasks at once');
170
+ sections.push('');
171
+
172
+ return sections.join('\n');
173
+ }
174
+
175
+ // Helper functions
176
+ function formatFramework(framework) {
177
+ const names = {
178
+ 'nextjs': 'Next.js 14+ (App Router)',
179
+ 'remix': 'Remix',
180
+ 'nuxt': 'Nuxt 3',
181
+ 'sveltekit': 'SvelteKit',
182
+ 'express': 'Express.js',
183
+ 'fastify': 'Fastify',
184
+ 'hono': 'Hono'
185
+ };
186
+ return names[framework] || framework;
187
+ }
188
+
189
+ module.exports = {
190
+ generate,
191
+ generatePlanningAgents
192
+ };
@@ -56,9 +56,72 @@ function generateAll(projectRoot, docs, tasks, options = {}) {
56
56
  fs.writeFileSync(contextPath, context);
57
57
  files.context = contextPath;
58
58
 
59
+ // Generate planning/AGENTS.md for AI coding agents
60
+ const agentsContent = generatePlanningAgents(options);
61
+ const agentsPath = path.join(planningDir, 'AGENTS.md');
62
+ fs.writeFileSync(agentsPath, agentsContent);
63
+ files.agents = agentsPath;
64
+
59
65
  return files;
60
66
  }
61
67
 
68
+ /**
69
+ * Generate AGENTS.md for the planning directory
70
+ * Following the open standard for AI coding agents
71
+ */
72
+ function generatePlanningAgents(options = {}) {
73
+ const projectName = options.projectName || 'Project';
74
+
75
+ return `# Planning Directory
76
+
77
+ You are working on build tasks for ${projectName}. This directory contains build planning files.
78
+
79
+ ## Commands
80
+
81
+ \`\`\`bash
82
+ bootspring build done # Mark task complete, get next
83
+ bootspring build skip # Skip current task
84
+ bootspring build status # View progress
85
+ \`\`\`
86
+
87
+ ## Current Task
88
+
89
+ Read \`CURRENT_TASK.md\` for the task you should implement now.
90
+
91
+ ## Workflow
92
+
93
+ 1. Read \`CURRENT_TASK.md\`
94
+ 2. Implement in the main codebase (not in this folder)
95
+ 3. Ensure acceptance criteria are met
96
+ 4. Run quality checks: \`npm run lint && npm run test\`
97
+ 5. Commit your changes
98
+ 6. Run \`bootspring build done\`
99
+
100
+ ## Files
101
+
102
+ | File | Purpose |
103
+ |------|---------|
104
+ | \`CURRENT_TASK.md\` | Task to implement now |
105
+ | \`TODO.md\` | Full task checklist |
106
+ | \`BUILD_STATE.json\` | Build state (do not edit directly) |
107
+ | \`IMPLEMENTATION_QUEUE.md\` | Ordered task queue |
108
+ | \`CONTEXT.md\` | Build context summary |
109
+
110
+ ## Boundaries
111
+
112
+ **Never:**
113
+ - Edit \`BUILD_STATE.json\` directly
114
+ - Skip quality checks
115
+ - Work on multiple tasks simultaneously
116
+ - Implement tasks in this planning folder
117
+
118
+ **Always:**
119
+ - Follow acceptance criteria exactly
120
+ - Run tests before marking complete
121
+ - Commit with conventional commit format
122
+ `;
123
+ }
124
+
62
125
  /**
63
126
  * Generate MASTER_PLAN.md
64
127
  * @param {object} docs - Source documents
@@ -160,6 +160,36 @@ function generate(config) {
160
160
  sections.push('');
161
161
  }
162
162
 
163
+ // Autonomous Build System
164
+ sections.push('## Autonomous Build System');
165
+ sections.push('');
166
+ sections.push('Bootspring manages an autonomous task queue for building your MVP.');
167
+ sections.push('');
168
+ sections.push('### Check for Tasks');
169
+ sections.push('');
170
+ sections.push('If `planning/CURRENT_TASK.md` exists, read it and implement the task.');
171
+ sections.push('When complete, run `bootspring build done` to get the next task.');
172
+ sections.push('');
173
+ sections.push('### MCP Commands (Claude Code)');
174
+ sections.push('');
175
+ sections.push('```');
176
+ sections.push('bootspring_build action=next # Get next task');
177
+ sections.push('bootspring_build action=done # Mark complete, get next');
178
+ sections.push('bootspring_build action=status # Check progress');
179
+ sections.push('```');
180
+ sections.push('');
181
+ sections.push('### CLI Commands (Terminal)');
182
+ sections.push('');
183
+ sections.push('```bash');
184
+ sections.push('bootspring build # Interactive menu');
185
+ sections.push('bootspring build next # Get next task');
186
+ sections.push('bootspring build done # Mark complete');
187
+ sections.push('bootspring build status # Check progress');
188
+ sections.push('```');
189
+ sections.push('');
190
+ sections.push('---');
191
+ sections.push('');
192
+
163
193
  // Bootspring Commands
164
194
  sections.push('## Bootspring Commands');
165
195
  sections.push('');
@@ -167,12 +197,13 @@ function generate(config) {
167
197
  sections.push('');
168
198
  sections.push('| Command | Description |');
169
199
  sections.push('|---------|-------------|');
200
+ sections.push('| `bootspring_build action=next` | Get next build task |');
201
+ sections.push('| `bootspring_build action=done` | Mark task complete |');
170
202
  sections.push('| `bootspring_assist` | Natural language help |');
171
203
  sections.push('| `bootspring_agent action=list` | List available experts |');
172
204
  sections.push('| `bootspring_todo action=list` | List todos |');
173
205
  sections.push('| `bootspring_skill action=search query="..."` | Find patterns |');
174
206
  sections.push('| `bootspring_quality gate=pre-commit` | Run quality checks |');
175
- sections.push('| `bootspring_orchestrator action=workflow workflow=...` | Start workflow |');
176
207
  sections.push('');
177
208
  sections.push('---');
178
209
  sections.push('');
@@ -7,7 +7,9 @@
7
7
 
8
8
  module.exports = {
9
9
  claude: require('./claude.template'),
10
+ agents: require('./agents.template'),
10
11
  seed: require('./seed.template'),
11
12
  planning: require('./planning.template'),
12
- content: require('./content.template')
13
+ content: require('./content.template'),
14
+ buildPlanning: require('./build-planning.template')
13
15
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "contractVersion": "v1",
3
3
  "packageName": "@girardmedia/bootspring",
4
- "packageVersion": "2.0.8",
4
+ "packageVersion": "2.0.10",
5
5
  "tools": [
6
6
  {
7
7
  "name": "bootspring_assist",
@@ -841,6 +841,39 @@
841
841
  },
842
842
  "required": []
843
843
  }
844
+ },
845
+ {
846
+ "name": "bootspring_build",
847
+ "description": "Autonomous MVP build system. Get tasks, execute them, mark complete.\n\nUse this tool to:\n- Get the current/next task to work on\n- Mark tasks as complete when done\n- Check build progress\n- Run tasks in a loop until MVP is complete\n\nTypical workflow:\n1. action=next → Get the next task\n2. Implement the task in the codebase\n3. action=done → Mark complete and get next task\n4. Repeat until all tasks done",
848
+ "inputSchema": {
849
+ "type": "object",
850
+ "properties": {
851
+ "action": {
852
+ "type": "string",
853
+ "enum": [
854
+ "next",
855
+ "current",
856
+ "done",
857
+ "skip",
858
+ "status",
859
+ "list",
860
+ "init"
861
+ ],
862
+ "description": "Action to perform:\n- next: Get the next pending task (marks it in_progress)\n- current: Get the current in_progress task\n- done: Mark current task complete, get next task\n- skip: Skip current task, get next task\n- status: Get build progress summary\n- list: List all tasks with status\n- init: Initialize build from seed docs"
863
+ },
864
+ "reason": {
865
+ "type": "string",
866
+ "description": "Reason for skipping (only for skip action)"
867
+ },
868
+ "projectRoot": {
869
+ "type": "string",
870
+ "description": "Project root path (auto-detected if not provided)"
871
+ }
872
+ },
873
+ "required": [
874
+ "action"
875
+ ]
876
+ }
844
877
  }
845
878
  ],
846
879
  "resources": [
package/mcp/registry.js CHANGED
@@ -83,6 +83,7 @@ const prdTool = require('./tools/prd-tool');
83
83
  const autopilotTool = require('./tools/autopilot-tool');
84
84
  const contentTool = require('./tools/content-tool');
85
85
  const suggestTool = require('./tools/suggest-tool');
86
+ const buildTool = require('./tools/build-tool');
86
87
 
87
88
  // Natural language and context detection
88
89
  const natural = require('../natural');
@@ -123,7 +124,8 @@ const TOOLS = [
123
124
  prdTool.getToolDefinition(),
124
125
  autopilotTool.getToolDefinition(),
125
126
  contentTool.getToolDefinition(),
126
- suggestTool.getToolDefinition()
127
+ suggestTool.getToolDefinition(),
128
+ buildTool.getToolDefinition()
127
129
  ];
128
130
 
129
131
  const RESOURCES = [
@@ -283,6 +285,9 @@ const toolHandlers = {
283
285
  config,
284
286
  format
285
287
  }),
288
+ bootspring_build: buildTool.createHandler({
289
+ configModule: config
290
+ }),
286
291
  };
287
292
 
288
293
  const resourceHandlers = {
@@ -0,0 +1,504 @@
1
+ /**
2
+ * MCP Build Tool
3
+ *
4
+ * Provides Claude Code with direct access to the bootspring build system.
5
+ * This enables autonomous task execution within Claude Code sessions.
6
+ */
7
+
8
+ const path = require('path');
9
+ const fs = require('fs');
10
+
11
+ function getToolDefinition() {
12
+ return {
13
+ name: 'bootspring_build',
14
+ description: `Autonomous MVP build system. Get tasks, execute them, mark complete.
15
+
16
+ Use this tool to:
17
+ - Get the current/next task to work on
18
+ - Mark tasks as complete when done
19
+ - Check build progress
20
+ - Run tasks in a loop until MVP is complete
21
+
22
+ Typical workflow:
23
+ 1. action=next → Get the next task
24
+ 2. Implement the task in the codebase
25
+ 3. action=done → Mark complete and get next task
26
+ 4. Repeat until all tasks done`,
27
+ inputSchema: {
28
+ type: 'object',
29
+ properties: {
30
+ action: {
31
+ type: 'string',
32
+ enum: ['next', 'current', 'done', 'skip', 'status', 'list', 'init'],
33
+ description: `Action to perform:
34
+ - next: Get the next pending task (marks it in_progress)
35
+ - current: Get the current in_progress task
36
+ - done: Mark current task complete, get next task
37
+ - skip: Skip current task, get next task
38
+ - status: Get build progress summary
39
+ - list: List all tasks with status
40
+ - init: Initialize build from seed docs`
41
+ },
42
+ reason: {
43
+ type: 'string',
44
+ description: 'Reason for skipping (only for skip action)'
45
+ },
46
+ projectRoot: {
47
+ type: 'string',
48
+ description: 'Project root path (auto-detected if not provided)'
49
+ }
50
+ },
51
+ required: ['action']
52
+ }
53
+ };
54
+ }
55
+
56
+ function createHandler({ configModule }) {
57
+ return async (args) => {
58
+ const { action, reason, projectRoot: providedRoot } = args;
59
+
60
+ // Get project root
61
+ const cfg = configModule.load();
62
+ const projectRoot = providedRoot || cfg._projectRoot || process.cwd();
63
+
64
+ // Load build modules
65
+ const buildState = require('../../core/build-state');
66
+ const { BuildOrchestrator } = require('../../core/build-orchestrator');
67
+
68
+ switch (action) {
69
+ case 'init': {
70
+ // Initialize build from seed docs
71
+ const orchestrator = new BuildOrchestrator(projectRoot);
72
+ await orchestrator.initialize();
73
+ const state = buildState.load(projectRoot);
74
+ const stats = buildState.getStats(projectRoot);
75
+
76
+ return {
77
+ content: [{
78
+ type: 'text',
79
+ text: JSON.stringify({
80
+ success: true,
81
+ message: 'Build initialized from seed documents',
82
+ project: state?.projectName || 'Unknown',
83
+ phase: state?.currentPhase || 'foundation',
84
+ totalTasks: stats?.total || 0,
85
+ planningFolder: path.join(projectRoot, 'planning'),
86
+ nextStep: 'Use action=next to get the first task'
87
+ }, null, 2)
88
+ }]
89
+ };
90
+ }
91
+
92
+ case 'status': {
93
+ const state = buildState.load(projectRoot);
94
+ if (!state) {
95
+ return {
96
+ content: [{
97
+ type: 'text',
98
+ text: JSON.stringify({
99
+ initialized: false,
100
+ message: 'No build state found. Use action=init to initialize.',
101
+ hint: 'Run: bootspring seed build'
102
+ }, null, 2)
103
+ }]
104
+ };
105
+ }
106
+
107
+ const stats = buildState.getStats(projectRoot);
108
+ const currentTask = state.implementationQueue.find(t => t.status === 'in_progress');
109
+
110
+ return {
111
+ content: [{
112
+ type: 'text',
113
+ text: JSON.stringify({
114
+ project: state.projectName,
115
+ phase: state.currentPhase,
116
+ status: state.status,
117
+ progress: {
118
+ completed: stats.completed,
119
+ pending: stats.pending,
120
+ inProgress: stats.inProgress,
121
+ total: stats.total,
122
+ percent: Math.round(stats.completedPercent)
123
+ },
124
+ currentTask: currentTask ? {
125
+ id: currentTask.id,
126
+ title: currentTask.title
127
+ } : null,
128
+ nextAction: currentTask
129
+ ? 'Complete the current task, then use action=done'
130
+ : 'Use action=next to get the next task'
131
+ }, null, 2)
132
+ }]
133
+ };
134
+ }
135
+
136
+ case 'list': {
137
+ const state = buildState.load(projectRoot);
138
+ if (!state) {
139
+ return {
140
+ content: [{
141
+ type: 'text',
142
+ text: JSON.stringify({
143
+ error: 'No build state found',
144
+ hint: 'Use action=init to initialize'
145
+ }, null, 2)
146
+ }]
147
+ };
148
+ }
149
+
150
+ const tasks = state.implementationQueue.map(t => ({
151
+ id: t.id,
152
+ title: t.title,
153
+ status: t.status,
154
+ phase: t.phase
155
+ }));
156
+
157
+ return {
158
+ content: [{
159
+ type: 'text',
160
+ text: JSON.stringify({
161
+ project: state.projectName,
162
+ tasks: tasks
163
+ }, null, 2)
164
+ }]
165
+ };
166
+ }
167
+
168
+ case 'current': {
169
+ const state = buildState.load(projectRoot);
170
+ if (!state) {
171
+ return {
172
+ content: [{
173
+ type: 'text',
174
+ text: JSON.stringify({
175
+ error: 'No build state found',
176
+ hint: 'Use action=init to initialize'
177
+ }, null, 2)
178
+ }]
179
+ };
180
+ }
181
+
182
+ const currentTask = state.implementationQueue.find(t => t.status === 'in_progress');
183
+ if (!currentTask) {
184
+ return {
185
+ content: [{
186
+ type: 'text',
187
+ text: JSON.stringify({
188
+ message: 'No task currently in progress',
189
+ hint: 'Use action=next to get the next task'
190
+ }, null, 2)
191
+ }]
192
+ };
193
+ }
194
+
195
+ return {
196
+ content: [{
197
+ type: 'text',
198
+ text: JSON.stringify({
199
+ task: {
200
+ id: currentTask.id,
201
+ title: currentTask.title,
202
+ description: currentTask.description,
203
+ phase: currentTask.phase,
204
+ source: currentTask.source,
205
+ sourceSection: currentTask.sourceSection,
206
+ acceptanceCriteria: currentTask.acceptanceCriteria || []
207
+ },
208
+ instructions: [
209
+ 'Implement this task in the codebase',
210
+ 'Run quality checks (lint, test)',
211
+ 'Commit your changes',
212
+ 'Use action=done to mark complete'
213
+ ]
214
+ }, null, 2)
215
+ }]
216
+ };
217
+ }
218
+
219
+ case 'next': {
220
+ const state = buildState.load(projectRoot);
221
+ if (!state) {
222
+ return {
223
+ content: [{
224
+ type: 'text',
225
+ text: JSON.stringify({
226
+ error: 'No build state found',
227
+ hint: 'Use action=init to initialize'
228
+ }, null, 2)
229
+ }]
230
+ };
231
+ }
232
+
233
+ // Check if there's already an in-progress task
234
+ const inProgress = state.implementationQueue.find(t => t.status === 'in_progress');
235
+ if (inProgress) {
236
+ return {
237
+ content: [{
238
+ type: 'text',
239
+ text: JSON.stringify({
240
+ message: 'Task already in progress',
241
+ task: {
242
+ id: inProgress.id,
243
+ title: inProgress.title,
244
+ description: inProgress.description,
245
+ acceptanceCriteria: inProgress.acceptanceCriteria || []
246
+ },
247
+ hint: 'Complete this task first with action=done, or use action=skip to skip it'
248
+ }, null, 2)
249
+ }]
250
+ };
251
+ }
252
+
253
+ const nextTask = buildState.getNextTask(projectRoot);
254
+ if (!nextTask) {
255
+ const stats = buildState.getStats(projectRoot);
256
+ return {
257
+ content: [{
258
+ type: 'text',
259
+ text: JSON.stringify({
260
+ message: 'All tasks complete!',
261
+ progress: {
262
+ completed: stats.completed,
263
+ total: stats.total
264
+ },
265
+ celebration: 'MVP build finished!'
266
+ }, null, 2)
267
+ }]
268
+ };
269
+ }
270
+
271
+ // Mark as in progress
272
+ buildState.updateProgress(projectRoot, nextTask.id, 'in_progress');
273
+
274
+ // Write to CURRENT_TASK.md
275
+ writeCurrentTaskFile(projectRoot, nextTask);
276
+
277
+ return {
278
+ content: [{
279
+ type: 'text',
280
+ text: JSON.stringify({
281
+ task: {
282
+ id: nextTask.id,
283
+ title: nextTask.title,
284
+ description: nextTask.description,
285
+ phase: nextTask.phase,
286
+ source: nextTask.source,
287
+ sourceSection: nextTask.sourceSection,
288
+ acceptanceCriteria: nextTask.acceptanceCriteria || []
289
+ },
290
+ file: 'planning/CURRENT_TASK.md',
291
+ instructions: [
292
+ 'Read the task details above',
293
+ 'Implement the task in the codebase',
294
+ 'Ensure acceptance criteria are met',
295
+ 'Run quality checks (lint, test)',
296
+ 'Commit changes with descriptive message',
297
+ 'Use action=done when complete'
298
+ ]
299
+ }, null, 2)
300
+ }]
301
+ };
302
+ }
303
+
304
+ case 'done': {
305
+ const state = buildState.load(projectRoot);
306
+ if (!state) {
307
+ return {
308
+ content: [{
309
+ type: 'text',
310
+ text: JSON.stringify({
311
+ error: 'No build state found'
312
+ }, null, 2)
313
+ }]
314
+ };
315
+ }
316
+
317
+ const inProgress = state.implementationQueue.find(t => t.status === 'in_progress');
318
+ if (!inProgress) {
319
+ return {
320
+ content: [{
321
+ type: 'text',
322
+ text: JSON.stringify({
323
+ error: 'No task currently in progress',
324
+ hint: 'Use action=next to get a task first'
325
+ }, null, 2)
326
+ }]
327
+ };
328
+ }
329
+
330
+ // Mark complete
331
+ buildState.updateProgress(projectRoot, inProgress.id, 'completed');
332
+
333
+ const stats = buildState.getStats(projectRoot);
334
+ const nextTask = buildState.getNextTask(projectRoot);
335
+
336
+ // Auto-queue next task
337
+ if (nextTask) {
338
+ buildState.updateProgress(projectRoot, nextTask.id, 'in_progress');
339
+ writeCurrentTaskFile(projectRoot, nextTask);
340
+ } else {
341
+ clearCurrentTaskFile(projectRoot);
342
+ }
343
+
344
+ return {
345
+ content: [{
346
+ type: 'text',
347
+ text: JSON.stringify({
348
+ completed: {
349
+ id: inProgress.id,
350
+ title: inProgress.title
351
+ },
352
+ progress: {
353
+ completed: stats.completed,
354
+ total: stats.total,
355
+ percent: Math.round(stats.completedPercent)
356
+ },
357
+ nextTask: nextTask ? {
358
+ id: nextTask.id,
359
+ title: nextTask.title,
360
+ description: nextTask.description,
361
+ acceptanceCriteria: nextTask.acceptanceCriteria || []
362
+ } : null,
363
+ allComplete: !nextTask,
364
+ message: nextTask
365
+ ? `Task complete! Next: ${nextTask.title}`
366
+ : 'All tasks complete! MVP build finished!'
367
+ }, null, 2)
368
+ }]
369
+ };
370
+ }
371
+
372
+ case 'skip': {
373
+ const state = buildState.load(projectRoot);
374
+ if (!state) {
375
+ return {
376
+ content: [{
377
+ type: 'text',
378
+ text: JSON.stringify({
379
+ error: 'No build state found'
380
+ }, null, 2)
381
+ }]
382
+ };
383
+ }
384
+
385
+ const inProgress = state.implementationQueue.find(t => t.status === 'in_progress');
386
+ if (!inProgress) {
387
+ return {
388
+ content: [{
389
+ type: 'text',
390
+ text: JSON.stringify({
391
+ error: 'No task to skip',
392
+ hint: 'Use action=next to get a task first'
393
+ }, null, 2)
394
+ }]
395
+ };
396
+ }
397
+
398
+ // Mark as skipped (back to pending with skip flag)
399
+ buildState.updateProgress(projectRoot, inProgress.id, 'skipped');
400
+
401
+ const nextTask = buildState.getNextTask(projectRoot);
402
+
403
+ if (nextTask) {
404
+ buildState.updateProgress(projectRoot, nextTask.id, 'in_progress');
405
+ writeCurrentTaskFile(projectRoot, nextTask);
406
+ }
407
+
408
+ return {
409
+ content: [{
410
+ type: 'text',
411
+ text: JSON.stringify({
412
+ skipped: {
413
+ id: inProgress.id,
414
+ title: inProgress.title,
415
+ reason: reason || 'Skipped by user'
416
+ },
417
+ nextTask: nextTask ? {
418
+ id: nextTask.id,
419
+ title: nextTask.title,
420
+ description: nextTask.description
421
+ } : null,
422
+ message: nextTask
423
+ ? `Skipped. Next: ${nextTask.title}`
424
+ : 'No more tasks available'
425
+ }, null, 2)
426
+ }]
427
+ };
428
+ }
429
+
430
+ default:
431
+ return {
432
+ content: [{
433
+ type: 'text',
434
+ text: JSON.stringify({
435
+ error: `Unknown action: ${action}`,
436
+ validActions: ['next', 'current', 'done', 'skip', 'status', 'list', 'init']
437
+ }, null, 2)
438
+ }]
439
+ };
440
+ }
441
+ };
442
+ }
443
+
444
+ /**
445
+ * Write task to planning/CURRENT_TASK.md
446
+ */
447
+ function writeCurrentTaskFile(projectRoot, task) {
448
+ const planningDir = path.join(projectRoot, 'planning');
449
+ if (!fs.existsSync(planningDir)) {
450
+ fs.mkdirSync(planningDir, { recursive: true });
451
+ }
452
+
453
+ const content = `# Current Task
454
+
455
+ > Auto-generated by bootspring. Implement this task.
456
+
457
+ ---
458
+
459
+ ## ${task.title}
460
+
461
+ ${task.description || ''}
462
+
463
+ ${task.acceptanceCriteria?.length ? `### Acceptance Criteria
464
+
465
+ ${task.acceptanceCriteria.map(ac => `- [ ] ${ac}`).join('\n')}
466
+ ` : ''}
467
+
468
+ ### Source
469
+
470
+ ${task.source}${task.sourceSection ? ` - ${task.sourceSection}` : ''}
471
+
472
+ ---
473
+
474
+ ## Instructions
475
+
476
+ 1. Implement the task as described above
477
+ 2. Ensure all acceptance criteria are met
478
+ 3. Run quality checks (lint, test)
479
+ 4. Commit your changes
480
+ 5. Mark complete with: \`bootspring build done\` or MCP action=done
481
+
482
+ ---
483
+
484
+ *Task ID: ${task.id}*
485
+ *Generated: ${new Date().toISOString()}*
486
+ `;
487
+
488
+ fs.writeFileSync(path.join(planningDir, 'CURRENT_TASK.md'), content);
489
+ }
490
+
491
+ /**
492
+ * Clear CURRENT_TASK.md
493
+ */
494
+ function clearCurrentTaskFile(projectRoot) {
495
+ const taskFile = path.join(projectRoot, 'planning', 'CURRENT_TASK.md');
496
+ if (fs.existsSync(taskFile)) {
497
+ fs.unlinkSync(taskFile);
498
+ }
499
+ }
500
+
501
+ module.exports = {
502
+ getToolDefinition,
503
+ createHandler
504
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@girardmedia/bootspring",
3
- "version": "2.0.8",
3
+ "version": "2.0.10",
4
4
  "description": "Development scaffolding with intelligence - AI-powered context, agents, and workflows for any MCP-compatible assistant",
5
5
  "keywords": [
6
6
  "ai",