@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
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed Builder - Generate SEED.md from preseed documents
|
|
3
|
+
* @package bootspring
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
extractProjectName,
|
|
8
|
+
extractTagline,
|
|
9
|
+
extractProblem,
|
|
10
|
+
extractSolution,
|
|
11
|
+
extractTechStack,
|
|
12
|
+
extractMVPFeatures,
|
|
13
|
+
extractTargetUsers,
|
|
14
|
+
extractDatabaseEntities,
|
|
15
|
+
extractImplementationPhases,
|
|
16
|
+
extractIntegrations,
|
|
17
|
+
extractAPIRoutes,
|
|
18
|
+
extractArchitecture
|
|
19
|
+
} = require('../extractors');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Generate SEED.md content from preseed documents
|
|
23
|
+
* @param {object} docs - Document map (key: doc name, value: content)
|
|
24
|
+
* @param {object} preseedConfig - Optional preseed config for fallback values
|
|
25
|
+
* @returns {string} Generated SEED.md content
|
|
26
|
+
*/
|
|
27
|
+
function generateSeedFromPreseed(docs, preseedConfig = {}) {
|
|
28
|
+
// Extract from documents (preferred) with config fallback
|
|
29
|
+
const projectName = extractProjectName(docs) || preseedConfig.identity?.name || 'My Project';
|
|
30
|
+
const tagline = extractTagline(docs) || preseedConfig.identity?.tagline || '';
|
|
31
|
+
const problem = extractProblem(docs) || preseedConfig.problem?.statement || '';
|
|
32
|
+
const solution = extractSolution(docs) || preseedConfig.solution?.description || '';
|
|
33
|
+
const techStack = extractTechStack(docs);
|
|
34
|
+
const mvpFeatures = extractMVPFeatures(docs);
|
|
35
|
+
const targetUsers = extractTargetUsers(docs);
|
|
36
|
+
const dbEntities = extractDatabaseEntities(docs);
|
|
37
|
+
const phases = extractImplementationPhases(docs);
|
|
38
|
+
const integrations = extractIntegrations(docs);
|
|
39
|
+
const apiRoutes = extractAPIRoutes(docs);
|
|
40
|
+
const architecture = extractArchitecture(docs);
|
|
41
|
+
|
|
42
|
+
// Build tech stack YAML with detected values
|
|
43
|
+
let stackYaml = `stack:
|
|
44
|
+
framework: ${techStack.framework}
|
|
45
|
+
language: ${techStack.language}
|
|
46
|
+
database: ${techStack.database}
|
|
47
|
+
hosting: ${techStack.hosting}
|
|
48
|
+
|
|
49
|
+
frontend:
|
|
50
|
+
uiLibrary: ${techStack.uiLibrary}
|
|
51
|
+
styling: ${techStack.styling}
|
|
52
|
+
|
|
53
|
+
backend:
|
|
54
|
+
orm: ${techStack.orm}
|
|
55
|
+
auth: ${techStack.auth}
|
|
56
|
+
payments: ${techStack.payments}`;
|
|
57
|
+
|
|
58
|
+
if (techStack.vector) {
|
|
59
|
+
stackYaml += `
|
|
60
|
+
vector: ${techStack.vector}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Build features section
|
|
64
|
+
let featuresSection = '';
|
|
65
|
+
if (mvpFeatures.length > 0) {
|
|
66
|
+
featuresSection = mvpFeatures.map((f, i) => `${i + 1}. ${f}`).join('\n');
|
|
67
|
+
} else {
|
|
68
|
+
featuresSection = `1. User authentication and workspace management
|
|
69
|
+
2. Core dashboard with analytics
|
|
70
|
+
3. Data management and CRUD operations
|
|
71
|
+
4. API endpoints for integrations
|
|
72
|
+
5. Admin interface and settings`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Build target users section
|
|
76
|
+
let usersSection = '';
|
|
77
|
+
if (targetUsers.length > 0) {
|
|
78
|
+
usersSection = targetUsers.map(u => `- ${u}`).join('\n');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Build database entities section
|
|
82
|
+
let entitiesSection = '';
|
|
83
|
+
if (dbEntities.length > 0) {
|
|
84
|
+
entitiesSection = dbEntities.map(e => `- \`${e}\``).join('\n');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Build architecture section
|
|
88
|
+
let architectureSection = '';
|
|
89
|
+
if (architecture.length > 0) {
|
|
90
|
+
architectureSection = architecture.map((c, i) => `${i + 1}. ${c}`).join('\n');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Build integrations section
|
|
94
|
+
let integrationsSection = '';
|
|
95
|
+
const intCats = Object.entries(integrations).filter(([_, v]) => v.length > 0);
|
|
96
|
+
if (intCats.length > 0) {
|
|
97
|
+
integrationsSection = intCats.map(([cat, services]) => {
|
|
98
|
+
const catName = cat.charAt(0).toUpperCase() + cat.slice(1);
|
|
99
|
+
return `**${catName}:** ${services.join(', ')}`;
|
|
100
|
+
}).join('\n');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Build phases section
|
|
104
|
+
let phasesSection = '';
|
|
105
|
+
if (phases.length > 0) {
|
|
106
|
+
phasesSection = phases.map(p => {
|
|
107
|
+
let section = `### Phase ${p.phase}: ${p.name}`;
|
|
108
|
+
if (p.deliverables.length > 0) {
|
|
109
|
+
section += '\n' + p.deliverables.map(d => `- ${d}`).join('\n');
|
|
110
|
+
}
|
|
111
|
+
return section;
|
|
112
|
+
}).join('\n\n');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Build API routes section
|
|
116
|
+
let apiRoutesSection = '';
|
|
117
|
+
if (apiRoutes.length > 0) {
|
|
118
|
+
apiRoutesSection = apiRoutes.map(r => `- \`${r}\``).join('\n');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const content = `# ${projectName}
|
|
122
|
+
|
|
123
|
+
${tagline ? `> ${tagline}` : ''}
|
|
124
|
+
|
|
125
|
+
## Overview
|
|
126
|
+
|
|
127
|
+
${problem ? `**The Problem:** ${problem}\n\n` : ''}${solution ? `**Our Solution:** ${solution}` : ''}
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Tech Stack
|
|
132
|
+
|
|
133
|
+
\`\`\`yaml
|
|
134
|
+
${stackYaml}
|
|
135
|
+
\`\`\`
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
${architecture.length > 0 ? `
|
|
139
|
+
## Architecture
|
|
140
|
+
|
|
141
|
+
${architectureSection}
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
` : ''}
|
|
145
|
+
${intCats.length > 0 ? `
|
|
146
|
+
## Integrations
|
|
147
|
+
|
|
148
|
+
${integrationsSection}
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
` : ''}
|
|
152
|
+
## MVP Features
|
|
153
|
+
|
|
154
|
+
${featuresSection}
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
${phases.length > 0 ? `
|
|
158
|
+
## Implementation Phases
|
|
159
|
+
|
|
160
|
+
${phasesSection}
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
` : ''}
|
|
164
|
+
${targetUsers.length > 0 ? `
|
|
165
|
+
## Target Users
|
|
166
|
+
|
|
167
|
+
${usersSection}
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
` : ''}
|
|
171
|
+
${dbEntities.length > 0 ? `
|
|
172
|
+
## Core Data Entities
|
|
173
|
+
|
|
174
|
+
${entitiesSection}
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
` : ''}
|
|
178
|
+
${apiRoutes.length > 0 ? `
|
|
179
|
+
## API Routes
|
|
180
|
+
|
|
181
|
+
${apiRoutesSection}
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
` : ''}
|
|
185
|
+
## Development Standards
|
|
186
|
+
|
|
187
|
+
### Code Style
|
|
188
|
+
- Use ${techStack.language === 'typescript' ? 'TypeScript' : 'JavaScript'} for all new code
|
|
189
|
+
- Follow existing naming conventions in the codebase
|
|
190
|
+
- Keep files focused and under 300 lines when possible
|
|
191
|
+
|
|
192
|
+
### Best Practices
|
|
193
|
+
${techStack.framework === 'nextjs' ? `- Use Server Components by default
|
|
194
|
+
- Prefer Server Actions over API routes for mutations` : `- Follow ${techStack.framework} best practices`}
|
|
195
|
+
- Use Zod for all input validation
|
|
196
|
+
- Never expose API keys to client-side code
|
|
197
|
+
- Write tests for new features
|
|
198
|
+
|
|
199
|
+
### Git Commits
|
|
200
|
+
- Use conventional format: \`feat:\`, \`fix:\`, \`docs:\`, \`refactor:\`
|
|
201
|
+
- Keep commits focused and atomic
|
|
202
|
+
- Never commit sensitive data or API keys
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Project Structure
|
|
207
|
+
|
|
208
|
+
\`\`\`
|
|
209
|
+
${projectName.toLowerCase().replace(/\s+/g, '-')}/
|
|
210
|
+
├── app/ # Next.js App Router
|
|
211
|
+
│ ├── (auth)/ # Auth pages${techStack.auth === 'clerk' ? ' (Clerk)' : ''}
|
|
212
|
+
│ ├── (dashboard)/ # Main dashboard
|
|
213
|
+
│ ├── (marketing)/ # Landing pages
|
|
214
|
+
│ └── api/ # API routes
|
|
215
|
+
├── components/
|
|
216
|
+
│ ├── ui/ # UI components${techStack.uiLibrary === 'shadcn' ? ' (shadcn/ui)' : ''}
|
|
217
|
+
│ └── [feature]/ # Feature components
|
|
218
|
+
├── lib/
|
|
219
|
+
│ ├── db.ts # Database client${techStack.orm === 'prisma' ? ' (Prisma)' : ''}
|
|
220
|
+
│ ├── auth.ts # Auth utilities
|
|
221
|
+
│ └── utils.ts # Helpers
|
|
222
|
+
├── prisma/
|
|
223
|
+
│ └── schema.prisma # Database schema
|
|
224
|
+
└── public/ # Static assets
|
|
225
|
+
\`\`\`
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Source Documents
|
|
230
|
+
|
|
231
|
+
This SEED.md was synthesized from:
|
|
232
|
+
${Object.keys(docs).map(name => `- \`.bootspring/preseed/${name}.md\``).join('\n')}
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
*Generated with [Bootspring](https://bootspring.com)*
|
|
237
|
+
`;
|
|
238
|
+
|
|
239
|
+
return content;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Generate AI enhancement prompt for SEED.md
|
|
244
|
+
* @param {object} docs - Document map
|
|
245
|
+
* @param {object} preseedConfig - Preseed config
|
|
246
|
+
* @returns {string} Enhancement prompt
|
|
247
|
+
*/
|
|
248
|
+
function generateEnhancementPrompt(docs, preseedConfig) {
|
|
249
|
+
return `# Enhance SEED.md
|
|
250
|
+
|
|
251
|
+
I've created a basic SEED.md from my preseed documents. Please review and enhance it with:
|
|
252
|
+
|
|
253
|
+
1. **More specific tech stack recommendations** based on my requirements
|
|
254
|
+
2. **Detailed file structure** for the project
|
|
255
|
+
3. **Implementation order** - which features to build first
|
|
256
|
+
4. **API endpoints** we'll need
|
|
257
|
+
5. **Database schema** suggestions
|
|
258
|
+
6. **Third-party services** recommendations
|
|
259
|
+
|
|
260
|
+
Here are my preseed documents for context:
|
|
261
|
+
|
|
262
|
+
${Object.entries(docs).map(([name, content]) => `## ${name}\n\n${content}`).join('\n\n---\n\n')}
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
Please provide an enhanced SEED.md that a developer (or AI assistant) can follow to build this project from scratch.`;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
module.exports = {
|
|
270
|
+
generateSeedFromPreseed,
|
|
271
|
+
generateEnhancementPrompt
|
|
272
|
+
};
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content Extractors - Extract features, users, entities, phases, routes
|
|
3
|
+
* @package bootspring
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { extractSection } = require('./section-extractor');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Extract MVP features from PRD
|
|
10
|
+
* @param {object} docs - Document map
|
|
11
|
+
* @returns {string[]} Array of feature names
|
|
12
|
+
*/
|
|
13
|
+
function extractMVPFeatures(docs) {
|
|
14
|
+
const prdDoc = docs['PRD'] || docs['prd'];
|
|
15
|
+
if (!prdDoc) return [];
|
|
16
|
+
|
|
17
|
+
const features = [];
|
|
18
|
+
|
|
19
|
+
// Look for MVP Features section
|
|
20
|
+
const mvpSection = extractSection(prdDoc, 'MVP\\s+Features|P1\\s+.*Features|Must\\s+Ship', { maxLength: 5000 });
|
|
21
|
+
if (mvpSection) {
|
|
22
|
+
// Extract feature names from headers like "#### F-001: Feature Name"
|
|
23
|
+
const featureHeaders = mvpSection.match(/^#{1,4}\s+F-\d+:\s*(.+)$/gm);
|
|
24
|
+
if (featureHeaders && featureHeaders.length > 0) {
|
|
25
|
+
featureHeaders.forEach(h => {
|
|
26
|
+
const match = h.match(/F-\d+:\s*(.+)/);
|
|
27
|
+
if (match) features.push(match[1].trim());
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Also look for Feature Requirements section
|
|
33
|
+
const featureReqSection = extractSection(prdDoc, 'Feature\\s+Requirements', { maxLength: 5000 });
|
|
34
|
+
if (featureReqSection && features.length === 0) {
|
|
35
|
+
// Look for **FR-XX:** patterns
|
|
36
|
+
const frMatches = featureReqSection.match(/\*{0,2}FR-\d+:?\*{0,2}\s*(.+)/g);
|
|
37
|
+
if (frMatches) {
|
|
38
|
+
frMatches.forEach(m => {
|
|
39
|
+
const match = m.match(/FR-\d+:?\*{0,2}\s*(.+)/);
|
|
40
|
+
if (match) features.push(match[1].replace(/\*+/g, '').trim());
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Fallback: look for numbered Core User Journeys
|
|
46
|
+
if (features.length === 0) {
|
|
47
|
+
const journeySection = extractSection(prdDoc, 'Core\\s+User\\s+Journeys|User\\s+Journeys', { maxLength: 3000 });
|
|
48
|
+
if (journeySection) {
|
|
49
|
+
const journeyHeaders = journeySection.match(/^#{1,4}\s+\d+\.\d+\s+Journey\s+\d+\s*[—-]\s*(.+)$/gm);
|
|
50
|
+
if (journeyHeaders) {
|
|
51
|
+
journeyHeaders.forEach(h => {
|
|
52
|
+
const match = h.match(/Journey\s+\d+\s*[—-]\s*(.+)/);
|
|
53
|
+
if (match) features.push(match[1].trim());
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Limit to top 15 features
|
|
60
|
+
return features.slice(0, 15);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Extract target users/personas from documents
|
|
65
|
+
* @param {object} docs - Document map
|
|
66
|
+
* @returns {string[]} Array of persona names
|
|
67
|
+
*/
|
|
68
|
+
function extractTargetUsers(docs) {
|
|
69
|
+
const visionDoc = docs['VISION'] || docs['vision'];
|
|
70
|
+
const prdDoc = docs['PRD'] || docs['prd'];
|
|
71
|
+
const audienceDoc = docs['AUDIENCE'] || docs['audience'];
|
|
72
|
+
|
|
73
|
+
const personas = [];
|
|
74
|
+
|
|
75
|
+
// Try AUDIENCE doc first - look for "## X.X Persona PX — Name" pattern
|
|
76
|
+
if (audienceDoc) {
|
|
77
|
+
const personaMatches = audienceDoc.match(/^#{1,4}\s+\d+\.\d+\s+Persona\s+P?\d+\s*[—-]\s*(.+)$/gm);
|
|
78
|
+
if (personaMatches) {
|
|
79
|
+
personaMatches.forEach(m => {
|
|
80
|
+
const match = m.match(/[—-]\s*(.+)/);
|
|
81
|
+
if (match && match[1]) personas.push(match[1].trim());
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
// Also look for ICP definitions like "## 3.1 ICP-A: AI Agency Operator"
|
|
85
|
+
if (personas.length === 0) {
|
|
86
|
+
const icpMatches = audienceDoc.match(/^#{1,4}\s+\d+\.\d+\s+ICP-[A-Z]:\s*(.+?)(?:\s*\(|$)/gm);
|
|
87
|
+
if (icpMatches) {
|
|
88
|
+
icpMatches.forEach(m => {
|
|
89
|
+
const match = m.match(/ICP-[A-Z]:\s*(.+?)(?:\s*\(|$)/);
|
|
90
|
+
if (match && match[1]) personas.push(match[1].trim());
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Try VISION
|
|
97
|
+
if (visionDoc && personas.length === 0) {
|
|
98
|
+
const targetSection = extractSection(visionDoc, 'Target\\s+Users|Who\\s+This\\s+Is\\s+For', { maxLength: 2000 });
|
|
99
|
+
if (targetSection) {
|
|
100
|
+
// Look for bold text patterns like "- **AI Agencies & AI Arbitrage Specialists**"
|
|
101
|
+
const boldMatches = targetSection.match(/^\s*[-*]\s*\*{2}([^*]+)\*{2}/gm);
|
|
102
|
+
if (boldMatches) {
|
|
103
|
+
boldMatches.forEach(m => {
|
|
104
|
+
const match = m.match(/\*{2}([^*]+)\*{2}/);
|
|
105
|
+
if (match && match[1]) personas.push(match[1].trim());
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Try PRD
|
|
112
|
+
if (prdDoc && personas.length === 0) {
|
|
113
|
+
const personaSection = extractSection(prdDoc, 'Personas|Who\\s+We\\s+Serve', { maxLength: 2000 });
|
|
114
|
+
if (personaSection) {
|
|
115
|
+
const personaMatches = personaSection.match(/^#{1,4}\s+\d+\.\d+\s+Persona\s+[A-Z]\s*[—-]\s*(.+)$/gm);
|
|
116
|
+
if (personaMatches) {
|
|
117
|
+
personaMatches.forEach(m => {
|
|
118
|
+
const match = m.match(/[—-]\s*(.+)/);
|
|
119
|
+
if (match && match[1]) personas.push(match[1].trim());
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return personas.slice(0, 6);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Extract database entities from technical spec
|
|
130
|
+
* @param {object} docs - Document map
|
|
131
|
+
* @returns {string[]} Array of entity/table names
|
|
132
|
+
*/
|
|
133
|
+
function extractDatabaseEntities(docs) {
|
|
134
|
+
const techDoc = docs['TECHNICAL_SPEC'] || docs['TECHNICAL-SPEC'] || docs['technical_spec'];
|
|
135
|
+
if (!techDoc) return [];
|
|
136
|
+
|
|
137
|
+
const entities = [];
|
|
138
|
+
|
|
139
|
+
// Strategy 1: Look specifically for "Key tables" or "Data Model" sections
|
|
140
|
+
// This avoids capturing run types like `interactive_chat` which are not tables
|
|
141
|
+
const tableSection = extractSection(techDoc, 'Key\\s+tables|Data\\s+Model|Database\\s+Schema', { maxLength: 3000 });
|
|
142
|
+
if (tableSection) {
|
|
143
|
+
// Extract backticked table names from bullet points in this specific section
|
|
144
|
+
const bulletTableMatches = tableSection.match(/^\s*[-*]\s*`(\w+)`/gm);
|
|
145
|
+
if (bulletTableMatches) {
|
|
146
|
+
bulletTableMatches.forEach(m => {
|
|
147
|
+
const match = m.match(/`(\w+)`/);
|
|
148
|
+
if (match && match[1] && match[1].length > 2) {
|
|
149
|
+
const word = match[1].toLowerCase();
|
|
150
|
+
// Filter out common non-entity words and known run types
|
|
151
|
+
const skipWords = [
|
|
152
|
+
'and', 'the', 'for', 'with', 'from', 'optional', 'static', 'primary', 'key', 'index', 'indexes',
|
|
153
|
+
'interactive_chat', 'widget_chat', 'lead_qualify_async', 'sequence_send_async', 'batch_eval'
|
|
154
|
+
];
|
|
155
|
+
if (!skipWords.includes(word)) {
|
|
156
|
+
entities.push(match[1]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Strategy 2: Extract model names from Prisma schema blocks
|
|
164
|
+
if (entities.length === 0) {
|
|
165
|
+
const prismaModels = techDoc.match(/model\s+(\w+)\s*\{/g);
|
|
166
|
+
if (prismaModels) {
|
|
167
|
+
prismaModels.forEach(m => {
|
|
168
|
+
const match = m.match(/model\s+(\w+)/);
|
|
169
|
+
if (match && match[1]) {
|
|
170
|
+
entities.push(match[1]);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Strategy 3: Fallback - look for backticked snake_case names in general text
|
|
177
|
+
// but only if they look like table names (contain underscore or end with 's')
|
|
178
|
+
if (entities.length === 0) {
|
|
179
|
+
const generalMatches = techDoc.match(/`([a-z][a-z_]*(?:_[a-z]+)+)`/g);
|
|
180
|
+
if (generalMatches) {
|
|
181
|
+
generalMatches.forEach(m => {
|
|
182
|
+
const match = m.match(/`([a-z_]+)`/);
|
|
183
|
+
if (match && match[1] && match[1].length > 4) {
|
|
184
|
+
const word = match[1].toLowerCase();
|
|
185
|
+
// Skip known run types
|
|
186
|
+
const runTypes = ['interactive_chat', 'widget_chat', 'lead_qualify_async', 'sequence_send_async', 'batch_eval'];
|
|
187
|
+
if (!runTypes.includes(word) && !entities.includes(match[1])) {
|
|
188
|
+
entities.push(match[1]);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Deduplicate and return
|
|
196
|
+
return [...new Set(entities)].slice(0, 25);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Extract implementation phases from ROADMAP
|
|
201
|
+
* @param {object} docs - Document map
|
|
202
|
+
* @returns {object[]} Array of phase objects with name and deliverables
|
|
203
|
+
*/
|
|
204
|
+
function extractImplementationPhases(docs) {
|
|
205
|
+
const roadmapDoc = docs['ROADMAP'] || docs['roadmap'];
|
|
206
|
+
if (!roadmapDoc) return [];
|
|
207
|
+
|
|
208
|
+
const phases = [];
|
|
209
|
+
const seenPhases = new Set();
|
|
210
|
+
|
|
211
|
+
// Prioritize full phase sections: "## X. Phase N —" (with section number)
|
|
212
|
+
// These have the actual deliverables, unlike brief summaries
|
|
213
|
+
const fullPhaseMatches = roadmapDoc.match(/^##\s+\d+\.\s+Phase\s+\d+\s*[—-]+\s*(.+)$/gm);
|
|
214
|
+
|
|
215
|
+
if (fullPhaseMatches) {
|
|
216
|
+
fullPhaseMatches.forEach(m => {
|
|
217
|
+
const match = m.match(/Phase\s+(\d+)\s*[—-]+\s*(.+)$/);
|
|
218
|
+
if (match && match[1] && match[2]) {
|
|
219
|
+
const phaseNum = match[1];
|
|
220
|
+
if (seenPhases.has(phaseNum)) return;
|
|
221
|
+
seenPhases.add(phaseNum);
|
|
222
|
+
|
|
223
|
+
// Clean up the name - remove parenthetical suffix
|
|
224
|
+
const phaseName = match[2].trim().replace(/\s*\([^)]+\)\s*$/, '').trim();
|
|
225
|
+
|
|
226
|
+
// Try to get deliverables for this phase
|
|
227
|
+
const deliverables = [];
|
|
228
|
+
|
|
229
|
+
// Find the full section for this specific phase (## X. Phase N — until next ## X. Phase)
|
|
230
|
+
const phaseRegex = new RegExp(`##\\s+\\d+\\.\\s+Phase\\s+${phaseNum}\\s*[—-][\\s\\S]*?(?=##\\s+\\d+\\.\\s+Phase\\s+\\d+|$)`, 'i');
|
|
231
|
+
const phaseSectionMatch = roadmapDoc.match(phaseRegex);
|
|
232
|
+
|
|
233
|
+
if (phaseSectionMatch) {
|
|
234
|
+
const phaseContent = phaseSectionMatch[0];
|
|
235
|
+
|
|
236
|
+
// Look for "### X.X Deliverables" subsection
|
|
237
|
+
const deliverablesRegex = /###\s+[\d.]+\s*Deliverables[\s\S]*?(?=###|##|$)/i;
|
|
238
|
+
const deliverablesMatch = phaseContent.match(deliverablesRegex);
|
|
239
|
+
|
|
240
|
+
if (deliverablesMatch) {
|
|
241
|
+
// Extract bullet points
|
|
242
|
+
const bullets = deliverablesMatch[0].match(/^[-*]\s+(.+)$/gm);
|
|
243
|
+
if (bullets) {
|
|
244
|
+
bullets.slice(0, 6).forEach(b => {
|
|
245
|
+
const text = b.replace(/^[-*]\s+/, '').trim();
|
|
246
|
+
if (text.length > 3) deliverables.push(text);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// If no deliverables found, try "MVP feature set" or "feature set"
|
|
252
|
+
if (deliverables.length === 0) {
|
|
253
|
+
// Look for #### A) B) C) patterns anywhere in phase content
|
|
254
|
+
const featureHeaders = phaseContent.match(/^####\s+[A-Z]\)\s+(.+)$/gm);
|
|
255
|
+
if (featureHeaders && featureHeaders.length > 0) {
|
|
256
|
+
featureHeaders.slice(0, 6).forEach(h => {
|
|
257
|
+
const hMatch = h.match(/####\s+[A-Z]\)\s+(.+)/);
|
|
258
|
+
if (hMatch && hMatch[1]) {
|
|
259
|
+
// Clean up the title - remove trailing parenthetical like "(prebuilt)"
|
|
260
|
+
const title = hMatch[1].trim();
|
|
261
|
+
deliverables.push(title);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Strategy 3: Look for Exit Criteria items
|
|
268
|
+
if (deliverables.length === 0) {
|
|
269
|
+
const exitCriteriaRegex = /###\s+[\d.]+\s*Exit\s+Criteria[\s\S]*?(?=###|##|$)/i;
|
|
270
|
+
const exitMatch = phaseContent.match(exitCriteriaRegex);
|
|
271
|
+
if (exitMatch) {
|
|
272
|
+
// Extract checkmark items like "- ✅ Description"
|
|
273
|
+
const checkItems = exitMatch[0].match(/^[-*]\s+[✅✓]\s*(.+)$/gm);
|
|
274
|
+
if (checkItems) {
|
|
275
|
+
checkItems.slice(0, 5).forEach(item => {
|
|
276
|
+
const text = item.replace(/^[-*]\s+[✅✓]\s*/, '').trim();
|
|
277
|
+
if (text.length > 5) deliverables.push(text);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Strategy 3: Look for "### X.X Phase goal" and extract key items
|
|
284
|
+
if (deliverables.length === 0) {
|
|
285
|
+
const goalRegex = /###\s+[\d.]+\s*(?:Phase\s+)?goal[\s\S]*?(?=###|$)/i;
|
|
286
|
+
const goalMatch = phaseContent.match(goalRegex);
|
|
287
|
+
if (goalMatch) {
|
|
288
|
+
// Extract bullet points or bold items
|
|
289
|
+
const bullets = goalMatch[0].match(/^[-*]\s+(.+)$/gm);
|
|
290
|
+
if (bullets) {
|
|
291
|
+
bullets.slice(0, 4).forEach(b => {
|
|
292
|
+
const text = b.replace(/^[-*]\s+/, '').trim();
|
|
293
|
+
if (text.length > 3) deliverables.push(text);
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
phases.push({
|
|
301
|
+
phase: phaseNum,
|
|
302
|
+
name: phaseName,
|
|
303
|
+
deliverables
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return phases.slice(0, 5);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Extract API routes from PRD/technical spec
|
|
314
|
+
* @param {object} docs - Document map
|
|
315
|
+
* @returns {string[]} Array of route paths
|
|
316
|
+
*/
|
|
317
|
+
function extractAPIRoutes(docs) {
|
|
318
|
+
const techDoc = docs['TECHNICAL_SPEC'] || docs['TECHNICAL-SPEC'] || docs['technical_spec'];
|
|
319
|
+
|
|
320
|
+
const routes = [];
|
|
321
|
+
|
|
322
|
+
if (techDoc) {
|
|
323
|
+
// Look for Application Structure section with app/ routes
|
|
324
|
+
const appStructureSection = extractSection(techDoc, 'Application\\s+Structure|Directory\\s+Structure', { maxLength: 3000 });
|
|
325
|
+
|
|
326
|
+
if (appStructureSection) {
|
|
327
|
+
// Extract page routes like "- `/dashboard`" or "- `/agents`" or "- `/agents/[id]`"
|
|
328
|
+
const pageRoutes = appStructureSection.match(/[-*]\s*`\/([\w/[\]]+)`/g);
|
|
329
|
+
if (pageRoutes) {
|
|
330
|
+
pageRoutes.forEach(r => {
|
|
331
|
+
const match = r.match(/`(\/[\w/[\]]+)`/);
|
|
332
|
+
if (match && match[1]) {
|
|
333
|
+
const route = match[1];
|
|
334
|
+
// Skip if it looks like a citation URL
|
|
335
|
+
if (!route.includes('docs/') && !route.includes('guides/')) {
|
|
336
|
+
if (!routes.includes(route)) routes.push(route);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Extract API route handler descriptions like "agents CRUD", "runs endpoints"
|
|
343
|
+
const apiHandlers = appStructureSection.match(/app\/api\/.*?handlers?:[\s\S]*?(?=###|##|$)/i);
|
|
344
|
+
if (apiHandlers) {
|
|
345
|
+
const handlerLines = apiHandlers[0].match(/[-*]\s+(\w[\w\s]+)(?:endpoints?|CRUD|callbacks?|handlers?)?/g);
|
|
346
|
+
if (handlerLines) {
|
|
347
|
+
handlerLines.forEach(line => {
|
|
348
|
+
const match = line.match(/[-*]\s+([\w\s]+)/);
|
|
349
|
+
if (match && match[1]) {
|
|
350
|
+
const name = match[1].trim().split(/\s+/)[0].toLowerCase();
|
|
351
|
+
if (name.length > 2 && !['auth', 'the', 'for', 'and'].includes(name)) {
|
|
352
|
+
const route = `/api/${name}`;
|
|
353
|
+
if (!routes.includes(route)) routes.push(route);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Also look for explicit /api/ routes in backticks
|
|
362
|
+
const explicitRoutes = techDoc.match(/`\/api\/[\w/[\]-]+`/g);
|
|
363
|
+
if (explicitRoutes) {
|
|
364
|
+
explicitRoutes.forEach(r => {
|
|
365
|
+
const route = r.replace(/`/g, '');
|
|
366
|
+
// Skip citation URLs
|
|
367
|
+
if (!route.includes('docs/') && !route.includes('guides/')) {
|
|
368
|
+
if (!routes.includes(route)) routes.push(route);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return routes.slice(0, 20);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
module.exports = {
|
|
378
|
+
extractMVPFeatures,
|
|
379
|
+
extractTargetUsers,
|
|
380
|
+
extractDatabaseEntities,
|
|
381
|
+
extractImplementationPhases,
|
|
382
|
+
extractAPIRoutes
|
|
383
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed Extractors - Re-export all extraction functions
|
|
3
|
+
* @package bootspring
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { extractSection } = require('./section-extractor');
|
|
7
|
+
const {
|
|
8
|
+
extractProjectName,
|
|
9
|
+
extractTagline,
|
|
10
|
+
extractProblem,
|
|
11
|
+
extractSolution
|
|
12
|
+
} = require('./metadata-extractors');
|
|
13
|
+
const {
|
|
14
|
+
extractTechStack,
|
|
15
|
+
extractIntegrations,
|
|
16
|
+
extractArchitecture
|
|
17
|
+
} = require('./stack-extractors');
|
|
18
|
+
const {
|
|
19
|
+
extractMVPFeatures,
|
|
20
|
+
extractTargetUsers,
|
|
21
|
+
extractDatabaseEntities,
|
|
22
|
+
extractImplementationPhases,
|
|
23
|
+
extractAPIRoutes
|
|
24
|
+
} = require('./content-extractors');
|
|
25
|
+
|
|
26
|
+
module.exports = {
|
|
27
|
+
// Section utilities
|
|
28
|
+
extractSection,
|
|
29
|
+
|
|
30
|
+
// Metadata
|
|
31
|
+
extractProjectName,
|
|
32
|
+
extractTagline,
|
|
33
|
+
extractProblem,
|
|
34
|
+
extractSolution,
|
|
35
|
+
|
|
36
|
+
// Tech stack
|
|
37
|
+
extractTechStack,
|
|
38
|
+
extractIntegrations,
|
|
39
|
+
extractArchitecture,
|
|
40
|
+
|
|
41
|
+
// Content
|
|
42
|
+
extractMVPFeatures,
|
|
43
|
+
extractTargetUsers,
|
|
44
|
+
extractDatabaseEntities,
|
|
45
|
+
extractImplementationPhases,
|
|
46
|
+
extractAPIRoutes
|
|
47
|
+
};
|