@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,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata Extractors - Extract project name, tagline, problem, solution
|
|
3
|
+
* @package bootspring
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { extractSection } = require('./section-extractor');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Extract project name from preseed documents
|
|
10
|
+
* @param {object} docs - Document map (key: doc name, value: content)
|
|
11
|
+
* @returns {string} Project name or 'My Project' if not found
|
|
12
|
+
*/
|
|
13
|
+
function extractProjectName(docs) {
|
|
14
|
+
// Try VISION first
|
|
15
|
+
const visionDoc = docs['VISION'] || docs['vision'];
|
|
16
|
+
if (visionDoc) {
|
|
17
|
+
// Look for "Application Name:" or "**Application Name:**"
|
|
18
|
+
const nameMatch = visionDoc.match(/\*{0,2}Application\s+Name:?\*{0,2}\s*(.+)/i);
|
|
19
|
+
if (nameMatch) return nameMatch[1].trim();
|
|
20
|
+
|
|
21
|
+
// Look for "# ProjectName —" pattern
|
|
22
|
+
const titleMatch = visionDoc.match(/^#\s+([^—\-\n]+)/m);
|
|
23
|
+
if (titleMatch) return titleMatch[1].trim();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Try PRD
|
|
27
|
+
const prdDoc = docs['PRD'] || docs['prd'];
|
|
28
|
+
if (prdDoc) {
|
|
29
|
+
const nameMatch = prdDoc.match(/\*{0,2}Application\s+Name:?\*{0,2}\s*(.+)/i);
|
|
30
|
+
if (nameMatch) return nameMatch[1].trim();
|
|
31
|
+
|
|
32
|
+
const titleMatch = prdDoc.match(/^#\s+([^—\-\n]+)/m);
|
|
33
|
+
if (titleMatch) return titleMatch[1].trim();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return 'My Project';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Extract tagline/description from documents
|
|
41
|
+
* @param {object} docs - Document map
|
|
42
|
+
* @returns {string} Tagline or empty string
|
|
43
|
+
*/
|
|
44
|
+
function extractTagline(docs) {
|
|
45
|
+
const visionDoc = docs['VISION'] || docs['vision'];
|
|
46
|
+
if (visionDoc) {
|
|
47
|
+
// Look for "is a..." or "is an..." sentence - capture the whole description
|
|
48
|
+
// Pattern: "is an **agentic AI platform** that makes it simple..."
|
|
49
|
+
const isMatch = visionDoc.match(/is\s+an?\s+\*{0,2}([^*]+?)\*{0,2}\s+that\s+([^.—\n]+)/i);
|
|
50
|
+
if (isMatch) {
|
|
51
|
+
let tagline = `${isMatch[1].trim()} that ${isMatch[2].trim()}`;
|
|
52
|
+
tagline = tagline.replace(/\*+/g, '');
|
|
53
|
+
if (tagline.length > 200) tagline = tagline.slice(0, 200) + '...';
|
|
54
|
+
if (tagline.length > 20) return tagline;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Fallback: just get "is an X" pattern without "that" clause
|
|
58
|
+
const simpleMatch = visionDoc.match(/is\s+an?\s+\*{0,2}([^*\n—:]+?)\*{0,2}(?:\s|—|:|$)/i);
|
|
59
|
+
if (simpleMatch) {
|
|
60
|
+
let tagline = simpleMatch[1].replace(/\*+/g, '').trim();
|
|
61
|
+
if (tagline.length > 200) tagline = tagline.slice(0, 200) + '...';
|
|
62
|
+
if (tagline.length > 10) return tagline;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Look for executive summary first paragraph
|
|
66
|
+
const summarySection = extractSection(visionDoc, 'Executive\\s+Vision|Vision\\s+Summary|Summary', { maxLength: 800 });
|
|
67
|
+
if (summarySection) {
|
|
68
|
+
const firstPara = summarySection.split(/\n\n/)[0].replace(/\*+/g, '').trim();
|
|
69
|
+
if (firstPara.length > 20 && firstPara.length < 250) return firstPara;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return '';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Extract problem statement from documents
|
|
77
|
+
* @param {object} docs - Document map
|
|
78
|
+
* @returns {string} Problem statement or empty string
|
|
79
|
+
*/
|
|
80
|
+
function extractProblem(docs) {
|
|
81
|
+
const visionDoc = docs['VISION'] || docs['vision'];
|
|
82
|
+
const prdDoc = docs['PRD'] || docs['prd'];
|
|
83
|
+
|
|
84
|
+
// Try VISION first
|
|
85
|
+
if (visionDoc) {
|
|
86
|
+
const problemSection = extractSection(visionDoc, '(The\\s+)?Problem|Pain\\s+Points|Market\\s+Reality', { maxLength: 800 });
|
|
87
|
+
if (problemSection) {
|
|
88
|
+
// Clean up and get first substantial paragraph
|
|
89
|
+
const cleaned = problemSection
|
|
90
|
+
// Remove subsection headers like "### 2.1 The market reality"
|
|
91
|
+
.replace(/^#{1,4}\s+[\d.]+\s+[^\n]+\n/gm, '')
|
|
92
|
+
// Remove section number prefixes like "2.1 " at start of content
|
|
93
|
+
.replace(/^[\d.]+\s+[A-Z][a-z]+\s+[a-z]+\n/m, '')
|
|
94
|
+
// Convert bullet points to flowing text
|
|
95
|
+
.replace(/^[-*]\s+\*{0,2}([^*\n:]+):?\*{0,2}\s*/gm, '$1: ')
|
|
96
|
+
// Remove remaining markdown
|
|
97
|
+
.replace(/\*+/g, '')
|
|
98
|
+
.trim();
|
|
99
|
+
|
|
100
|
+
// Get first substantial paragraph or combine bullet points
|
|
101
|
+
const paragraphs = cleaned.split(/\n\n/).filter(p => p.trim().length > 30);
|
|
102
|
+
if (paragraphs.length > 0) {
|
|
103
|
+
let result = paragraphs[0].replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
|
|
104
|
+
// Limit length
|
|
105
|
+
if (result.length > 500) result = result.slice(0, 500) + '...';
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Try PRD
|
|
112
|
+
if (prdDoc) {
|
|
113
|
+
const problemSection = extractSection(prdDoc, '(The\\s+)?Problem|Pain\\s+Points', { maxLength: 800 });
|
|
114
|
+
if (problemSection) {
|
|
115
|
+
const cleaned = problemSection
|
|
116
|
+
.replace(/^#{1,4}\s+[\d.]+\s+[^\n]+\n/gm, '')
|
|
117
|
+
.replace(/^[\d.]+\s+[A-Z][a-z]+\s+[a-z]+\n/m, '')
|
|
118
|
+
.replace(/^[-*]\s+\*{0,2}([^*\n:]+):?\*{0,2}\s*/gm, '$1: ')
|
|
119
|
+
.replace(/\*+/g, '')
|
|
120
|
+
.trim();
|
|
121
|
+
|
|
122
|
+
const paragraphs = cleaned.split(/\n\n/).filter(p => p.trim().length > 30);
|
|
123
|
+
if (paragraphs.length > 0) {
|
|
124
|
+
let result = paragraphs[0].replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
|
|
125
|
+
if (result.length > 500) result = result.slice(0, 500) + '...';
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return '';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Extract solution description from documents
|
|
136
|
+
* @param {object} docs - Document map
|
|
137
|
+
* @returns {string} Solution description or empty string
|
|
138
|
+
*/
|
|
139
|
+
function extractSolution(docs) {
|
|
140
|
+
const visionDoc = docs['VISION'] || docs['vision'];
|
|
141
|
+
|
|
142
|
+
if (visionDoc) {
|
|
143
|
+
// Look for "Our Solution" or "The Solution" or "What We Build"
|
|
144
|
+
const solutionSection = extractSection(visionDoc, '(Our\\s+)?Solution|What\\s+We\\s+Build|Core\\s+Product', { maxLength: 1000 });
|
|
145
|
+
if (solutionSection) {
|
|
146
|
+
const paragraphs = solutionSection.split(/\n\n/).filter(p => p.trim().length > 50);
|
|
147
|
+
if (paragraphs.length > 0) {
|
|
148
|
+
return paragraphs[0].replace(/^[#*\d.-]+\s*/gm, '').replace(/\*+/g, '').trim();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Look for "in one sentence" pattern
|
|
153
|
+
const oneLineMatch = visionDoc.match(/in\s+one\s+sentence[:\s]*\*{0,2}([^*\n]+(?:\*{0,2}[^*\n]+)?)/i);
|
|
154
|
+
if (oneLineMatch) {
|
|
155
|
+
return oneLineMatch[1].replace(/\*+/g, '').trim();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return '';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = {
|
|
163
|
+
extractProjectName,
|
|
164
|
+
extractTagline,
|
|
165
|
+
extractProblem,
|
|
166
|
+
extractSolution
|
|
167
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section Extractor - Base extraction utility for markdown content
|
|
3
|
+
* @package bootspring
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Extract a section from markdown content by heading
|
|
8
|
+
* @param {string} content - Markdown content to search
|
|
9
|
+
* @param {string} headingPattern - Regex pattern for heading to match
|
|
10
|
+
* @param {object} options - Extraction options
|
|
11
|
+
* @param {number} options.maxLength - Maximum length of extracted content (default: 2000)
|
|
12
|
+
* @param {string} options.stopAt - Pattern to stop extraction at
|
|
13
|
+
* @returns {string|null} Extracted section content or null if not found
|
|
14
|
+
*/
|
|
15
|
+
function extractSection(content, headingPattern, options = {}) {
|
|
16
|
+
const { maxLength = 2000, stopAt = null } = options;
|
|
17
|
+
|
|
18
|
+
// Match the heading (## or ### or ####)
|
|
19
|
+
// Wrap headingPattern in non-capturing group to ensure | alternation works correctly
|
|
20
|
+
// Handle optional section numbers like "## 10. Application Structure"
|
|
21
|
+
const headingRegex = new RegExp(`^(#{1,4})\\s*(?:\\d+\\.\\s*)?(?:${headingPattern})[^\\n]*\\n`, 'im');
|
|
22
|
+
const match = content.match(headingRegex);
|
|
23
|
+
if (!match || !match[1]) return null;
|
|
24
|
+
|
|
25
|
+
const startIndex = match.index + match[0].length;
|
|
26
|
+
const headingLevel = match[1].length;
|
|
27
|
+
|
|
28
|
+
// Find the next heading of same or higher level
|
|
29
|
+
const restContent = content.slice(startIndex);
|
|
30
|
+
const nextHeadingRegex = new RegExp(`^#{1,${headingLevel}}\\s+`, 'm');
|
|
31
|
+
const nextMatch = restContent.match(nextHeadingRegex);
|
|
32
|
+
|
|
33
|
+
let sectionContent = nextMatch
|
|
34
|
+
? restContent.slice(0, nextMatch.index)
|
|
35
|
+
: restContent;
|
|
36
|
+
|
|
37
|
+
// Stop at a specific pattern if provided
|
|
38
|
+
if (stopAt) {
|
|
39
|
+
const stopMatch = sectionContent.match(new RegExp(stopAt, 'i'));
|
|
40
|
+
if (stopMatch) {
|
|
41
|
+
sectionContent = sectionContent.slice(0, stopMatch.index);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Clean and truncate
|
|
46
|
+
sectionContent = sectionContent.trim();
|
|
47
|
+
if (sectionContent.length > maxLength) {
|
|
48
|
+
sectionContent = sectionContent.slice(0, maxLength) + '...';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return sectionContent || null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = { extractSection };
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stack Extractors - Extract tech stack, integrations, architecture
|
|
3
|
+
* @package bootspring
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { extractSection } = require('./section-extractor');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Extract tech stack from technical spec
|
|
10
|
+
* @param {object} docs - Document map
|
|
11
|
+
* @returns {object} Tech stack configuration
|
|
12
|
+
*/
|
|
13
|
+
function extractTechStack(docs) {
|
|
14
|
+
const techDoc = docs['TECHNICAL_SPEC'] || docs['TECHNICAL-SPEC'] || docs['technical_spec'];
|
|
15
|
+
|
|
16
|
+
const stack = {
|
|
17
|
+
framework: 'nextjs',
|
|
18
|
+
language: 'typescript',
|
|
19
|
+
database: 'postgresql',
|
|
20
|
+
styling: 'tailwind',
|
|
21
|
+
uiLibrary: 'shadcn',
|
|
22
|
+
orm: 'prisma',
|
|
23
|
+
auth: 'clerk',
|
|
24
|
+
payments: 'stripe',
|
|
25
|
+
hosting: 'vercel',
|
|
26
|
+
vector: null
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
if (!techDoc) return stack;
|
|
30
|
+
|
|
31
|
+
// Framework detection
|
|
32
|
+
if (techDoc.match(/Next\.?js/i)) stack.framework = 'nextjs';
|
|
33
|
+
else if (techDoc.match(/Nuxt/i)) stack.framework = 'nuxt';
|
|
34
|
+
else if (techDoc.match(/Vue/i)) stack.framework = 'vue';
|
|
35
|
+
else if (techDoc.match(/Remix/i)) stack.framework = 'remix';
|
|
36
|
+
else if (techDoc.match(/\bReact\b/) && !techDoc.match(/Next\.?js/i)) stack.framework = 'react';
|
|
37
|
+
else if (techDoc.match(/Express|Fastify|Hono/i)) stack.framework = 'node';
|
|
38
|
+
|
|
39
|
+
// Language
|
|
40
|
+
if (techDoc.match(/TypeScript/i)) stack.language = 'typescript';
|
|
41
|
+
else if (techDoc.match(/JavaScript/i) && !techDoc.match(/TypeScript/i)) stack.language = 'javascript';
|
|
42
|
+
|
|
43
|
+
// Database
|
|
44
|
+
if (techDoc.match(/Postgres|PostgreSQL/i)) stack.database = 'postgresql';
|
|
45
|
+
else if (techDoc.match(/MongoDB/i)) stack.database = 'mongodb';
|
|
46
|
+
else if (techDoc.match(/MySQL/i)) stack.database = 'mysql';
|
|
47
|
+
else if (techDoc.match(/Supabase/i)) stack.database = 'supabase';
|
|
48
|
+
else if (techDoc.match(/PlanetScale/i)) stack.database = 'planetscale';
|
|
49
|
+
else if (techDoc.match(/Firebase/i)) stack.database = 'firebase';
|
|
50
|
+
|
|
51
|
+
// Vector store
|
|
52
|
+
if (techDoc.match(/pgvector/i)) stack.vector = 'pgvector';
|
|
53
|
+
else if (techDoc.match(/Pinecone/i)) stack.vector = 'pinecone';
|
|
54
|
+
else if (techDoc.match(/Weaviate/i)) stack.vector = 'weaviate';
|
|
55
|
+
|
|
56
|
+
// Styling
|
|
57
|
+
if (techDoc.match(/Tailwind/i)) stack.styling = 'tailwind';
|
|
58
|
+
else if (techDoc.match(/styled-components/i)) stack.styling = 'styled-components';
|
|
59
|
+
else if (techDoc.match(/CSS\s+Modules/i)) stack.styling = 'css-modules';
|
|
60
|
+
|
|
61
|
+
// UI Library
|
|
62
|
+
if (techDoc.match(/Shadcn|shadcn\/ui/i)) stack.uiLibrary = 'shadcn';
|
|
63
|
+
else if (techDoc.match(/Chakra/i)) stack.uiLibrary = 'chakra';
|
|
64
|
+
else if (techDoc.match(/MUI|Material[- ]UI/i)) stack.uiLibrary = 'mui';
|
|
65
|
+
else if (techDoc.match(/Radix/i)) stack.uiLibrary = 'radix';
|
|
66
|
+
|
|
67
|
+
// ORM
|
|
68
|
+
if (techDoc.match(/Prisma/i)) stack.orm = 'prisma';
|
|
69
|
+
else if (techDoc.match(/Drizzle/i)) stack.orm = 'drizzle';
|
|
70
|
+
else if (techDoc.match(/TypeORM/i)) stack.orm = 'typeorm';
|
|
71
|
+
|
|
72
|
+
// Auth
|
|
73
|
+
if (techDoc.match(/Clerk/i)) stack.auth = 'clerk';
|
|
74
|
+
else if (techDoc.match(/NextAuth|Auth\.js/i)) stack.auth = 'nextauth';
|
|
75
|
+
else if (techDoc.match(/Supabase\s+Auth/i)) stack.auth = 'supabase';
|
|
76
|
+
else if (techDoc.match(/Firebase\s+Auth/i)) stack.auth = 'firebase';
|
|
77
|
+
|
|
78
|
+
// Payments
|
|
79
|
+
if (techDoc.match(/Stripe/i)) stack.payments = 'stripe';
|
|
80
|
+
else if (techDoc.match(/Paddle/i)) stack.payments = 'paddle';
|
|
81
|
+
else if (techDoc.match(/Lemon\s*Squeezy/i)) stack.payments = 'lemonsqueezy';
|
|
82
|
+
|
|
83
|
+
// Hosting
|
|
84
|
+
if (techDoc.match(/Vercel/i)) stack.hosting = 'vercel';
|
|
85
|
+
else if (techDoc.match(/Netlify/i)) stack.hosting = 'netlify';
|
|
86
|
+
else if (techDoc.match(/Railway/i)) stack.hosting = 'railway';
|
|
87
|
+
else if (techDoc.match(/Fly\.io/i)) stack.hosting = 'fly';
|
|
88
|
+
else if (techDoc.match(/AWS/i)) stack.hosting = 'aws';
|
|
89
|
+
|
|
90
|
+
return stack;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Extract third-party integrations from technical spec
|
|
95
|
+
* @param {object} docs - Document map
|
|
96
|
+
* @returns {object} Integrations by category
|
|
97
|
+
*/
|
|
98
|
+
function extractIntegrations(docs) {
|
|
99
|
+
const techDoc = docs['TECHNICAL_SPEC'] || docs['TECHNICAL-SPEC'] || docs['technical_spec'];
|
|
100
|
+
const prdDoc = docs['PRD'] || docs['prd'];
|
|
101
|
+
const allDocs = (techDoc || '') + '\n' + (prdDoc || '');
|
|
102
|
+
|
|
103
|
+
const integrations = {
|
|
104
|
+
voice: [],
|
|
105
|
+
email: [],
|
|
106
|
+
calendar: [],
|
|
107
|
+
crm: [],
|
|
108
|
+
payments: [],
|
|
109
|
+
auth: [],
|
|
110
|
+
ai: [],
|
|
111
|
+
other: []
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Voice/Telephony
|
|
115
|
+
if (allDocs.match(/\bTwilio\b/i)) integrations.voice.push('Twilio');
|
|
116
|
+
if (allDocs.match(/\bRetell\b/i)) integrations.voice.push('Retell');
|
|
117
|
+
if (allDocs.match(/\bVapi\b/i)) integrations.voice.push('Vapi');
|
|
118
|
+
if (allDocs.match(/\bElevenLabs\b/i)) integrations.voice.push('ElevenLabs');
|
|
119
|
+
if (allDocs.match(/\bDeepgram\b/i)) integrations.voice.push('Deepgram');
|
|
120
|
+
|
|
121
|
+
// Email
|
|
122
|
+
if (allDocs.match(/\bGmail\b/i)) integrations.email.push('Gmail');
|
|
123
|
+
if (allDocs.match(/\bSendGrid\b/i)) integrations.email.push('SendGrid');
|
|
124
|
+
if (allDocs.match(/\bResend\b/i)) integrations.email.push('Resend');
|
|
125
|
+
if (allDocs.match(/\bPostmark\b/i)) integrations.email.push('Postmark');
|
|
126
|
+
|
|
127
|
+
// Calendar
|
|
128
|
+
if (allDocs.match(/Google\s*Calendar/i)) integrations.calendar.push('Google Calendar');
|
|
129
|
+
if (allDocs.match(/\bCalendly\b/i)) integrations.calendar.push('Calendly');
|
|
130
|
+
if (allDocs.match(/\bCal\.com\b/i)) integrations.calendar.push('Cal.com');
|
|
131
|
+
|
|
132
|
+
// CRM
|
|
133
|
+
if (allDocs.match(/\bHubSpot\b/i)) integrations.crm.push('HubSpot');
|
|
134
|
+
if (allDocs.match(/\bSalesforce\b/i)) integrations.crm.push('Salesforce');
|
|
135
|
+
if (allDocs.match(/\bAttio\b/i)) integrations.crm.push('Attio');
|
|
136
|
+
if (allDocs.match(/\bPipedrive\b/i)) integrations.crm.push('Pipedrive');
|
|
137
|
+
|
|
138
|
+
// Payments
|
|
139
|
+
if (allDocs.match(/\bStripe\b/i)) integrations.payments.push('Stripe');
|
|
140
|
+
if (allDocs.match(/\bPaddle\b/i)) integrations.payments.push('Paddle');
|
|
141
|
+
|
|
142
|
+
// Auth
|
|
143
|
+
if (allDocs.match(/\bClerk\b/i)) integrations.auth.push('Clerk');
|
|
144
|
+
if (allDocs.match(/\bNextAuth\b|Auth\.js/i)) integrations.auth.push('NextAuth');
|
|
145
|
+
if (allDocs.match(/\bSupabase\s*Auth/i)) integrations.auth.push('Supabase Auth');
|
|
146
|
+
|
|
147
|
+
// AI/LLM
|
|
148
|
+
if (allDocs.match(/\bOpenAI\b|GPT-4/i)) integrations.ai.push('OpenAI');
|
|
149
|
+
if (allDocs.match(/\bAnthropic\b|Claude/i)) integrations.ai.push('Anthropic');
|
|
150
|
+
if (allDocs.match(/\bGemini\b/i)) integrations.ai.push('Google Gemini');
|
|
151
|
+
|
|
152
|
+
// Other
|
|
153
|
+
if (allDocs.match(/\bZapier\b/i)) integrations.other.push('Zapier');
|
|
154
|
+
if (allDocs.match(/\bSlack\b/i)) integrations.other.push('Slack');
|
|
155
|
+
if (allDocs.match(/\bWhatsApp\b/i)) integrations.other.push('WhatsApp');
|
|
156
|
+
|
|
157
|
+
return integrations;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Extract core architecture components from technical spec
|
|
162
|
+
* @param {object} docs - Document map
|
|
163
|
+
* @returns {string[]} Array of architecture component descriptions
|
|
164
|
+
*/
|
|
165
|
+
function extractArchitecture(docs) {
|
|
166
|
+
const techDoc = docs['TECHNICAL_SPEC'] || docs['TECHNICAL-SPEC'] || docs['technical_spec'];
|
|
167
|
+
if (!techDoc) return [];
|
|
168
|
+
|
|
169
|
+
const components = [];
|
|
170
|
+
|
|
171
|
+
// Strategy 1: Look specifically for "Core system components" section first
|
|
172
|
+
// This is more specific than "Architecture" which may be a high-level overview
|
|
173
|
+
const coreSection = extractSection(techDoc, 'Core\\s+system\\s+components', { maxLength: 1500 });
|
|
174
|
+
if (coreSection) {
|
|
175
|
+
// Extract numbered items like "1) Web App (Next.js)"
|
|
176
|
+
const compMatches = coreSection.match(/^\s*\d+\)\s*(.+)$/gm);
|
|
177
|
+
if (compMatches) {
|
|
178
|
+
compMatches.forEach(m => {
|
|
179
|
+
const match = m.match(/\d+\)\s*(.+)/);
|
|
180
|
+
if (match && match[1]) {
|
|
181
|
+
components.push(match[1].trim());
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Strategy 2: If no components found, look within "Architectural Overview" for subsections
|
|
188
|
+
if (components.length === 0) {
|
|
189
|
+
// Search the entire doc for numbered component patterns within Architecture context
|
|
190
|
+
const archMatch = techDoc.match(/(?:Architectural|Architecture)\s+Overview[\s\S]*?Core\s+system\s+components[\s\S]*?(?=##\s+\d+\.|$)/i);
|
|
191
|
+
if (archMatch) {
|
|
192
|
+
const compMatches = archMatch[0].match(/^\s*\d+\)\s*(.+)$/gm);
|
|
193
|
+
if (compMatches) {
|
|
194
|
+
compMatches.forEach(m => {
|
|
195
|
+
const match = m.match(/\d+\)\s*(.+)/);
|
|
196
|
+
if (match && match[1]) {
|
|
197
|
+
components.push(match[1].trim());
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Strategy 3: Look for bullet-point architecture components
|
|
205
|
+
if (components.length === 0) {
|
|
206
|
+
const archSection = extractSection(techDoc, 'Architecture|System\\s+Components', { maxLength: 2000 });
|
|
207
|
+
if (archSection) {
|
|
208
|
+
// Look for bold component names like "- **Component Name**"
|
|
209
|
+
const boldMatches = archSection.match(/^\s*[-*]\s+\*{2}([^*]+)\*{2}/gm);
|
|
210
|
+
if (boldMatches) {
|
|
211
|
+
boldMatches.forEach(m => {
|
|
212
|
+
const match = m.match(/\*{2}([^*]+)\*{2}/);
|
|
213
|
+
if (match && match[1] && match[1].length > 3) {
|
|
214
|
+
components.push(match[1].trim());
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return components.slice(0, 10);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
module.exports = {
|
|
225
|
+
extractTechStack,
|
|
226
|
+
extractIntegrations,
|
|
227
|
+
extractArchitecture
|
|
228
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed Module Index - Re-export all seed submodules
|
|
3
|
+
* @package bootspring
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const extractors = require('./extractors');
|
|
7
|
+
const builders = require('./builders');
|
|
8
|
+
const utils = require('./utils');
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
extractors,
|
|
12
|
+
builders,
|
|
13
|
+
utils,
|
|
14
|
+
// Re-export commonly used functions at top level
|
|
15
|
+
...extractors,
|
|
16
|
+
...builders,
|
|
17
|
+
...utils
|
|
18
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Folder Structure - Seed folder constants and creation utilities
|
|
3
|
+
* @package bootspring
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Seed folder structure
|
|
11
|
+
*/
|
|
12
|
+
const SEED_FOLDERS = [
|
|
13
|
+
'.bootspring',
|
|
14
|
+
'.bootspring/inputs',
|
|
15
|
+
'.bootspring/inputs/mvp',
|
|
16
|
+
'.bootspring/inputs/mvp/source',
|
|
17
|
+
'.bootspring/inputs/business',
|
|
18
|
+
'.bootspring/inputs/prd',
|
|
19
|
+
'.bootspring/inputs/prd/features',
|
|
20
|
+
'.bootspring/inputs/designs',
|
|
21
|
+
'.bootspring/inputs/designs/figma-exports',
|
|
22
|
+
'.bootspring/inputs/designs/wireframes',
|
|
23
|
+
'.bootspring/inputs/legal',
|
|
24
|
+
'.bootspring/inputs/api',
|
|
25
|
+
'.bootspring/inputs/data',
|
|
26
|
+
'.bootspring/generated',
|
|
27
|
+
'.bootspring/generated/prd',
|
|
28
|
+
'.bootspring/generated/master-plan',
|
|
29
|
+
'.bootspring/generated/business',
|
|
30
|
+
'.bootspring/generated/architecture',
|
|
31
|
+
'.bootspring/generated/legal',
|
|
32
|
+
'.bootspring/context',
|
|
33
|
+
'.bootspring/logs',
|
|
34
|
+
'.bootspring/logs/agents',
|
|
35
|
+
'.bootspring/logs/decisions',
|
|
36
|
+
'.bootspring/logs/code',
|
|
37
|
+
'.bootspring/config'
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create seed folder structure (used by both setup and init)
|
|
42
|
+
* @param {string} projectRoot - Project root path
|
|
43
|
+
* @returns {number} Number of folders created
|
|
44
|
+
*/
|
|
45
|
+
function createSeedFolders(projectRoot) {
|
|
46
|
+
let dirsCreated = 0;
|
|
47
|
+
|
|
48
|
+
for (const folder of SEED_FOLDERS) {
|
|
49
|
+
const folderPath = path.join(projectRoot, folder);
|
|
50
|
+
if (!fs.existsSync(folderPath)) {
|
|
51
|
+
fs.mkdirSync(folderPath, { recursive: true });
|
|
52
|
+
dirsCreated++;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Create README for inputs
|
|
57
|
+
const inputsReadmePath = path.join(projectRoot, '.bootspring/inputs/README.md');
|
|
58
|
+
if (!fs.existsSync(inputsReadmePath)) {
|
|
59
|
+
const readme = `# Input Files
|
|
60
|
+
|
|
61
|
+
Drop your existing files here before running \`bootspring seed generate\`.
|
|
62
|
+
|
|
63
|
+
## Folders
|
|
64
|
+
|
|
65
|
+
| Folder | What to put here |
|
|
66
|
+
|--------|------------------|
|
|
67
|
+
| \`mvp/source/\` | AI-generated MVP code (Lovable, Bolt, V0, etc.) |
|
|
68
|
+
| \`business/\` | Business plans, pitch decks, market research |
|
|
69
|
+
| \`prd/\` | Product requirements documents |
|
|
70
|
+
| \`designs/\` | Figma exports, wireframes, mockups |
|
|
71
|
+
| \`legal/\` | Existing terms, privacy policies |
|
|
72
|
+
| \`api/\` | OpenAPI specs, API documentation |
|
|
73
|
+
| \`data/\` | Sample data, database schemas |
|
|
74
|
+
`;
|
|
75
|
+
fs.writeFileSync(inputsReadmePath, readme);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return dirsCreated;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = {
|
|
82
|
+
SEED_FOLDERS,
|
|
83
|
+
createSeedFolders
|
|
84
|
+
};
|