@girardmedia/bootspring 2.0.21 → 2.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/bootspring.js +5 -0
- package/cli/org.js +474 -0
- package/cli/preseed/index.js +16 -0
- package/cli/preseed/interactive.js +143 -0
- package/cli/preseed/templates.js +227 -0
- package/cli/preseed.js +9 -301
- package/cli/seed/builders/ai-context-builder.js +85 -0
- package/cli/seed/builders/index.js +13 -0
- package/cli/seed/builders/seed-builder.js +272 -0
- package/cli/seed/extractors/content-extractors.js +383 -0
- package/cli/seed/extractors/index.js +47 -0
- package/cli/seed/extractors/metadata-extractors.js +167 -0
- package/cli/seed/extractors/section-extractor.js +54 -0
- package/cli/seed/extractors/stack-extractors.js +228 -0
- package/cli/seed/index.js +18 -0
- package/cli/seed/utils/folder-structure.js +84 -0
- package/cli/seed/utils/index.js +11 -0
- package/cli/seed.js +23 -1074
- package/core/api-client.js +77 -0
- package/core/entitlements.js +36 -0
- package/core/organizations.js +223 -0
- package/core/policies.js +51 -6
- package/core/policy-matrix.js +303 -0
- package/core/project-context.js +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +3220 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/context-McpJQa_2.d.ts +5710 -0
- package/dist/core/index.d.ts +635 -0
- package/dist/core/index.js +2593 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index-QqbeEiDm.d.ts +857 -0
- package/dist/index-UiYCgwiH.d.ts +174 -0
- package/dist/index.d.ts +453 -0
- package/dist/index.js +44228 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.js +41173 -0
- package/dist/mcp/index.js.map +1 -0
- package/generators/index.ts +82 -0
- package/intelligence/orchestrator/config/failure-signatures.js +48 -0
- package/intelligence/orchestrator/config/index.js +23 -0
- package/intelligence/orchestrator/config/pack-lifecycle.js +262 -0
- package/intelligence/orchestrator/config/phases.js +111 -0
- package/intelligence/orchestrator/config/remediation.js +150 -0
- package/intelligence/orchestrator/config/workflows.js +168 -0
- package/intelligence/orchestrator/core/index.js +16 -0
- package/intelligence/orchestrator/core/state-manager.js +88 -0
- package/intelligence/orchestrator/core/telemetry.js +24 -0
- package/intelligence/orchestrator/index.js +17 -0
- package/intelligence/orchestrator.js +17 -512
- package/mcp/contracts/mcp-contract.v1.json +1 -1
- package/package.json +16 -3
- package/src/cli/agent.ts +703 -0
- package/src/cli/analyze.ts +640 -0
- package/src/cli/audit.ts +707 -0
- package/src/cli/auth.ts +930 -0
- package/src/cli/billing.ts +364 -0
- package/src/cli/build.ts +1089 -0
- package/src/cli/business.ts +508 -0
- package/src/cli/checkpoint-utils.ts +236 -0
- package/src/cli/checkpoint.ts +757 -0
- package/src/cli/cloud-sync.ts +534 -0
- package/src/cli/content.ts +273 -0
- package/src/cli/context.ts +667 -0
- package/src/cli/dashboard.ts +133 -0
- package/src/cli/deploy.ts +704 -0
- package/src/cli/doctor.ts +480 -0
- package/src/cli/fundraise.ts +494 -0
- package/src/cli/generate.ts +346 -0
- package/src/cli/github-cmd.ts +566 -0
- package/src/cli/health.ts +599 -0
- package/src/cli/index.ts +113 -0
- package/src/cli/init.ts +838 -0
- package/src/cli/legal.ts +495 -0
- package/src/cli/log.ts +316 -0
- package/src/cli/loop.ts +1660 -0
- package/src/cli/manager.ts +878 -0
- package/src/cli/mcp.ts +275 -0
- package/src/cli/memory.ts +346 -0
- package/src/cli/metrics.ts +590 -0
- package/src/cli/monitor.ts +960 -0
- package/src/cli/mvp.ts +662 -0
- package/src/cli/onboard.ts +663 -0
- package/src/cli/orchestrator.ts +622 -0
- package/src/cli/plugin.ts +483 -0
- package/src/cli/prd.ts +671 -0
- package/src/cli/preseed-start.ts +1633 -0
- package/src/cli/preseed.ts +2434 -0
- package/src/cli/project.ts +526 -0
- package/src/cli/quality.ts +885 -0
- package/src/cli/security.ts +1079 -0
- package/src/cli/seed.ts +1224 -0
- package/src/cli/skill.ts +537 -0
- package/src/cli/suggest.ts +1225 -0
- package/src/cli/switch.ts +518 -0
- package/src/cli/task.ts +780 -0
- package/src/cli/telemetry.ts +172 -0
- package/src/cli/todo.ts +627 -0
- package/src/cli/types.ts +15 -0
- package/src/cli/update.ts +334 -0
- package/src/cli/visualize.ts +609 -0
- package/src/cli/watch.ts +895 -0
- package/src/cli/workspace.ts +709 -0
- package/src/core/action-recorder.ts +673 -0
- package/src/core/analyze-workflow.ts +1453 -0
- package/src/core/api-client.ts +1120 -0
- package/src/core/audit-workflow.ts +1681 -0
- package/src/core/auth.ts +471 -0
- package/src/core/build-orchestrator.ts +509 -0
- package/src/core/build-state.ts +621 -0
- package/src/core/checkpoint-engine.ts +482 -0
- package/src/core/config.ts +1285 -0
- package/src/core/context-loader.ts +694 -0
- package/src/core/context.ts +410 -0
- package/src/core/deploy-workflow.ts +1085 -0
- package/src/core/entitlements.ts +322 -0
- package/src/core/github-sync.ts +720 -0
- package/src/core/index.ts +981 -0
- package/src/core/ingest.ts +1186 -0
- package/src/core/metrics-engine.ts +886 -0
- package/src/core/mvp.ts +847 -0
- package/src/core/onboard-workflow.ts +1293 -0
- package/src/core/policies.ts +81 -0
- package/src/core/preseed-workflow.ts +1163 -0
- package/src/core/preseed.ts +1826 -0
- package/src/core/project-context.ts +380 -0
- package/src/core/project-state.ts +699 -0
- package/src/core/r2-sync.ts +691 -0
- package/src/core/scaffold.ts +1715 -0
- package/src/core/session.ts +286 -0
- package/src/core/task-extractor.ts +799 -0
- package/src/core/telemetry.ts +371 -0
- package/src/core/tier-enforcement.ts +737 -0
- package/src/core/utils.ts +437 -0
- package/src/index.ts +29 -0
- package/src/intelligence/agent-collab.ts +2376 -0
- package/src/intelligence/auto-suggest.ts +713 -0
- package/src/intelligence/content-gen.ts +1351 -0
- package/src/intelligence/cross-project.ts +1692 -0
- package/src/intelligence/git-memory.ts +529 -0
- package/src/intelligence/index.ts +318 -0
- package/src/intelligence/orchestrator.ts +534 -0
- package/src/intelligence/prd.ts +466 -0
- package/src/intelligence/recommendations.ts +982 -0
- package/src/intelligence/workflow-composer.ts +1472 -0
- package/src/mcp/capabilities.ts +233 -0
- package/src/mcp/index.ts +37 -0
- package/src/mcp/registry.ts +1268 -0
- package/src/mcp/response-formatter.ts +797 -0
- package/src/mcp/server.ts +240 -0
- package/src/types/agent.ts +69 -0
- package/src/types/config.ts +86 -0
- package/src/types/context.ts +77 -0
- package/src/types/index.ts +53 -0
- package/src/types/mcp.ts +91 -0
- package/src/types/skills.ts +47 -0
- package/src/types/workflow.ts +155 -0
- package/generators/index.js +0 -18
package/cli/seed.js
CHANGED
|
@@ -25,13 +25,34 @@ 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');
|
|
30
28
|
const scaffold = require('../core/scaffold');
|
|
31
29
|
const projectState = require('../core/project-state');
|
|
32
30
|
const checkpointEngine = require('../core/checkpoint-engine');
|
|
33
31
|
const tierEnforcement = require('../core/tier-enforcement');
|
|
34
32
|
|
|
33
|
+
// Import from extracted modules
|
|
34
|
+
const {
|
|
35
|
+
extractSection,
|
|
36
|
+
extractProjectName,
|
|
37
|
+
extractTagline,
|
|
38
|
+
extractProblem,
|
|
39
|
+
extractSolution,
|
|
40
|
+
extractTechStack,
|
|
41
|
+
extractIntegrations,
|
|
42
|
+
extractArchitecture,
|
|
43
|
+
extractMVPFeatures,
|
|
44
|
+
extractTargetUsers,
|
|
45
|
+
extractDatabaseEntities,
|
|
46
|
+
extractImplementationPhases,
|
|
47
|
+
extractAPIRoutes
|
|
48
|
+
} = require('./seed/extractors');
|
|
49
|
+
|
|
50
|
+
const {
|
|
51
|
+
generateSeedFromPreseed,
|
|
52
|
+
generateEnhancementPrompt,
|
|
53
|
+
generateAIContextFiles
|
|
54
|
+
} = require('./seed/builders');
|
|
55
|
+
|
|
35
56
|
// Lazy load ingest module
|
|
36
57
|
let ingest = null;
|
|
37
58
|
function getIngest() {
|
|
@@ -41,79 +62,6 @@ function getIngest() {
|
|
|
41
62
|
return ingest;
|
|
42
63
|
}
|
|
43
64
|
|
|
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
|
-
|
|
117
65
|
/**
|
|
118
66
|
* Seed folder structure
|
|
119
67
|
*/
|
|
@@ -993,1005 +941,6 @@ ${utils.COLORS.bold}Next steps:${utils.COLORS.reset}
|
|
|
993
941
|
}
|
|
994
942
|
}
|
|
995
943
|
|
|
996
|
-
/**
|
|
997
|
-
* Extract a section from markdown content by heading
|
|
998
|
-
*/
|
|
999
|
-
function extractSection(content, headingPattern, options = {}) {
|
|
1000
|
-
const { maxLength = 2000, stopAt = null } = options;
|
|
1001
|
-
|
|
1002
|
-
// Match the heading (## or ### or ####)
|
|
1003
|
-
// Wrap headingPattern in non-capturing group to ensure | alternation works correctly
|
|
1004
|
-
// Handle optional section numbers like "## 10. Application Structure"
|
|
1005
|
-
const headingRegex = new RegExp(`^(#{1,4})\\s*(?:\\d+\\.\\s*)?(?:${headingPattern})[^\\n]*\\n`, 'im');
|
|
1006
|
-
const match = content.match(headingRegex);
|
|
1007
|
-
if (!match || !match[1]) return null;
|
|
1008
|
-
|
|
1009
|
-
const startIndex = match.index + match[0].length;
|
|
1010
|
-
const headingLevel = match[1].length;
|
|
1011
|
-
|
|
1012
|
-
// Find the next heading of same or higher level
|
|
1013
|
-
const restContent = content.slice(startIndex);
|
|
1014
|
-
const nextHeadingRegex = new RegExp(`^#{1,${headingLevel}}\\s+`, 'm');
|
|
1015
|
-
const nextMatch = restContent.match(nextHeadingRegex);
|
|
1016
|
-
|
|
1017
|
-
let sectionContent = nextMatch
|
|
1018
|
-
? restContent.slice(0, nextMatch.index)
|
|
1019
|
-
: restContent;
|
|
1020
|
-
|
|
1021
|
-
// Stop at a specific pattern if provided
|
|
1022
|
-
if (stopAt) {
|
|
1023
|
-
const stopMatch = sectionContent.match(new RegExp(stopAt, 'i'));
|
|
1024
|
-
if (stopMatch) {
|
|
1025
|
-
sectionContent = sectionContent.slice(0, stopMatch.index);
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
// Clean and truncate
|
|
1030
|
-
sectionContent = sectionContent.trim();
|
|
1031
|
-
if (sectionContent.length > maxLength) {
|
|
1032
|
-
sectionContent = sectionContent.slice(0, maxLength) + '...';
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
return sectionContent || null;
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
/**
|
|
1039
|
-
* Extract project name from preseed documents
|
|
1040
|
-
*/
|
|
1041
|
-
function extractProjectName(docs) {
|
|
1042
|
-
// Try VISION first
|
|
1043
|
-
const visionDoc = docs['VISION'] || docs['vision'];
|
|
1044
|
-
if (visionDoc) {
|
|
1045
|
-
// Look for "Application Name:" or "**Application Name:**"
|
|
1046
|
-
const nameMatch = visionDoc.match(/\*{0,2}Application\s+Name:?\*{0,2}\s*(.+)/i);
|
|
1047
|
-
if (nameMatch) return nameMatch[1].trim();
|
|
1048
|
-
|
|
1049
|
-
// Look for "# ProjectName —" pattern
|
|
1050
|
-
const titleMatch = visionDoc.match(/^#\s+([^—\-\n]+)/m);
|
|
1051
|
-
if (titleMatch) return titleMatch[1].trim();
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
// Try PRD
|
|
1055
|
-
const prdDoc = docs['PRD'] || docs['prd'];
|
|
1056
|
-
if (prdDoc) {
|
|
1057
|
-
const nameMatch = prdDoc.match(/\*{0,2}Application\s+Name:?\*{0,2}\s*(.+)/i);
|
|
1058
|
-
if (nameMatch) return nameMatch[1].trim();
|
|
1059
|
-
|
|
1060
|
-
const titleMatch = prdDoc.match(/^#\s+([^—\-\n]+)/m);
|
|
1061
|
-
if (titleMatch) return titleMatch[1].trim();
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
return 'My Project';
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
/**
|
|
1068
|
-
* Extract tagline/description from documents
|
|
1069
|
-
*/
|
|
1070
|
-
function extractTagline(docs) {
|
|
1071
|
-
const visionDoc = docs['VISION'] || docs['vision'];
|
|
1072
|
-
if (visionDoc) {
|
|
1073
|
-
// Look for "is a..." or "is an..." sentence - capture the whole description
|
|
1074
|
-
// Pattern: "is an **agentic AI platform** that makes it simple..."
|
|
1075
|
-
const isMatch = visionDoc.match(/is\s+an?\s+\*{0,2}([^*]+?)\*{0,2}\s+that\s+([^.—\n]+)/i);
|
|
1076
|
-
if (isMatch) {
|
|
1077
|
-
let tagline = `${isMatch[1].trim()} that ${isMatch[2].trim()}`;
|
|
1078
|
-
tagline = tagline.replace(/\*+/g, '');
|
|
1079
|
-
if (tagline.length > 200) tagline = tagline.slice(0, 200) + '...';
|
|
1080
|
-
if (tagline.length > 20) return tagline;
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
// Fallback: just get "is an X" pattern without "that" clause
|
|
1084
|
-
const simpleMatch = visionDoc.match(/is\s+an?\s+\*{0,2}([^*\n—:]+?)\*{0,2}(?:\s|—|:|$)/i);
|
|
1085
|
-
if (simpleMatch) {
|
|
1086
|
-
let tagline = simpleMatch[1].replace(/\*+/g, '').trim();
|
|
1087
|
-
if (tagline.length > 200) tagline = tagline.slice(0, 200) + '...';
|
|
1088
|
-
if (tagline.length > 10) return tagline;
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
// Look for executive summary first paragraph
|
|
1092
|
-
const summarySection = extractSection(visionDoc, 'Executive\\s+Vision|Vision\\s+Summary|Summary', { maxLength: 800 });
|
|
1093
|
-
if (summarySection) {
|
|
1094
|
-
const firstPara = summarySection.split(/\n\n/)[0].replace(/\*+/g, '').trim();
|
|
1095
|
-
if (firstPara.length > 20 && firstPara.length < 250) return firstPara;
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
return '';
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
/**
|
|
1102
|
-
* Extract problem statement from documents
|
|
1103
|
-
*/
|
|
1104
|
-
function extractProblem(docs) {
|
|
1105
|
-
const visionDoc = docs['VISION'] || docs['vision'];
|
|
1106
|
-
const prdDoc = docs['PRD'] || docs['prd'];
|
|
1107
|
-
|
|
1108
|
-
// Try VISION first
|
|
1109
|
-
if (visionDoc) {
|
|
1110
|
-
const problemSection = extractSection(visionDoc, '(The\\s+)?Problem|Pain\\s+Points|Market\\s+Reality', { maxLength: 800 });
|
|
1111
|
-
if (problemSection) {
|
|
1112
|
-
// Clean up and get first substantial paragraph
|
|
1113
|
-
const cleaned = problemSection
|
|
1114
|
-
// Remove subsection headers like "### 2.1 The market reality"
|
|
1115
|
-
.replace(/^#{1,4}\s+[\d.]+\s+[^\n]+\n/gm, '')
|
|
1116
|
-
// Remove section number prefixes like "2.1 " at start of content
|
|
1117
|
-
.replace(/^[\d.]+\s+[A-Z][a-z]+\s+[a-z]+\n/m, '')
|
|
1118
|
-
// Convert bullet points to flowing text
|
|
1119
|
-
.replace(/^[-*]\s+\*{0,2}([^*\n:]+):?\*{0,2}\s*/gm, '$1: ')
|
|
1120
|
-
// Remove remaining markdown
|
|
1121
|
-
.replace(/\*+/g, '')
|
|
1122
|
-
.trim();
|
|
1123
|
-
|
|
1124
|
-
// Get first substantial paragraph or combine bullet points
|
|
1125
|
-
const paragraphs = cleaned.split(/\n\n/).filter(p => p.trim().length > 30);
|
|
1126
|
-
if (paragraphs.length > 0) {
|
|
1127
|
-
let result = paragraphs[0].replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
|
|
1128
|
-
// Limit length
|
|
1129
|
-
if (result.length > 500) result = result.slice(0, 500) + '...';
|
|
1130
|
-
return result;
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
// Try PRD
|
|
1136
|
-
if (prdDoc) {
|
|
1137
|
-
const problemSection = extractSection(prdDoc, '(The\\s+)?Problem|Pain\\s+Points', { maxLength: 800 });
|
|
1138
|
-
if (problemSection) {
|
|
1139
|
-
const cleaned = problemSection
|
|
1140
|
-
.replace(/^#{1,4}\s+[\d.]+\s+[^\n]+\n/gm, '')
|
|
1141
|
-
.replace(/^[\d.]+\s+[A-Z][a-z]+\s+[a-z]+\n/m, '')
|
|
1142
|
-
.replace(/^[-*]\s+\*{0,2}([^*\n:]+):?\*{0,2}\s*/gm, '$1: ')
|
|
1143
|
-
.replace(/\*+/g, '')
|
|
1144
|
-
.trim();
|
|
1145
|
-
|
|
1146
|
-
const paragraphs = cleaned.split(/\n\n/).filter(p => p.trim().length > 30);
|
|
1147
|
-
if (paragraphs.length > 0) {
|
|
1148
|
-
let result = paragraphs[0].replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
|
|
1149
|
-
if (result.length > 500) result = result.slice(0, 500) + '...';
|
|
1150
|
-
return result;
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
return '';
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
/**
|
|
1159
|
-
* Extract solution description from documents
|
|
1160
|
-
*/
|
|
1161
|
-
function extractSolution(docs) {
|
|
1162
|
-
const visionDoc = docs['VISION'] || docs['vision'];
|
|
1163
|
-
|
|
1164
|
-
if (visionDoc) {
|
|
1165
|
-
// Look for "Our Solution" or "The Solution" or "What We Build"
|
|
1166
|
-
const solutionSection = extractSection(visionDoc, '(Our\\s+)?Solution|What\\s+We\\s+Build|Core\\s+Product', { maxLength: 1000 });
|
|
1167
|
-
if (solutionSection) {
|
|
1168
|
-
const paragraphs = solutionSection.split(/\n\n/).filter(p => p.trim().length > 50);
|
|
1169
|
-
if (paragraphs.length > 0) {
|
|
1170
|
-
return paragraphs[0].replace(/^[#*\d.-]+\s*/gm, '').replace(/\*+/g, '').trim();
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
// Look for "in one sentence" pattern
|
|
1175
|
-
const oneLineMatch = visionDoc.match(/in\s+one\s+sentence[:\s]*\*{0,2}([^*\n]+(?:\*{0,2}[^*\n]+)?)/i);
|
|
1176
|
-
if (oneLineMatch) {
|
|
1177
|
-
return oneLineMatch[1].replace(/\*+/g, '').trim();
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
return '';
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
/**
|
|
1185
|
-
* Extract tech stack from technical spec
|
|
1186
|
-
*/
|
|
1187
|
-
function extractTechStack(docs) {
|
|
1188
|
-
const techDoc = docs['TECHNICAL_SPEC'] || docs['TECHNICAL-SPEC'] || docs['technical_spec'];
|
|
1189
|
-
|
|
1190
|
-
const stack = {
|
|
1191
|
-
framework: 'nextjs',
|
|
1192
|
-
language: 'typescript',
|
|
1193
|
-
database: 'postgresql',
|
|
1194
|
-
styling: 'tailwind',
|
|
1195
|
-
uiLibrary: 'shadcn',
|
|
1196
|
-
orm: 'prisma',
|
|
1197
|
-
auth: 'clerk',
|
|
1198
|
-
payments: 'stripe',
|
|
1199
|
-
hosting: 'vercel',
|
|
1200
|
-
vector: null
|
|
1201
|
-
};
|
|
1202
|
-
|
|
1203
|
-
if (!techDoc) return stack;
|
|
1204
|
-
|
|
1205
|
-
// Framework detection
|
|
1206
|
-
if (techDoc.match(/Next\.?js/i)) stack.framework = 'nextjs';
|
|
1207
|
-
else if (techDoc.match(/Nuxt/i)) stack.framework = 'nuxt';
|
|
1208
|
-
else if (techDoc.match(/Vue/i)) stack.framework = 'vue';
|
|
1209
|
-
else if (techDoc.match(/Remix/i)) stack.framework = 'remix';
|
|
1210
|
-
else if (techDoc.match(/\bReact\b/) && !techDoc.match(/Next\.?js/i)) stack.framework = 'react';
|
|
1211
|
-
else if (techDoc.match(/Express|Fastify|Hono/i)) stack.framework = 'node';
|
|
1212
|
-
|
|
1213
|
-
// Language
|
|
1214
|
-
if (techDoc.match(/TypeScript/i)) stack.language = 'typescript';
|
|
1215
|
-
else if (techDoc.match(/JavaScript/i) && !techDoc.match(/TypeScript/i)) stack.language = 'javascript';
|
|
1216
|
-
|
|
1217
|
-
// Database
|
|
1218
|
-
if (techDoc.match(/Postgres|PostgreSQL/i)) stack.database = 'postgresql';
|
|
1219
|
-
else if (techDoc.match(/MongoDB/i)) stack.database = 'mongodb';
|
|
1220
|
-
else if (techDoc.match(/MySQL/i)) stack.database = 'mysql';
|
|
1221
|
-
else if (techDoc.match(/Supabase/i)) stack.database = 'supabase';
|
|
1222
|
-
else if (techDoc.match(/PlanetScale/i)) stack.database = 'planetscale';
|
|
1223
|
-
else if (techDoc.match(/Firebase/i)) stack.database = 'firebase';
|
|
1224
|
-
|
|
1225
|
-
// Vector store
|
|
1226
|
-
if (techDoc.match(/pgvector/i)) stack.vector = 'pgvector';
|
|
1227
|
-
else if (techDoc.match(/Pinecone/i)) stack.vector = 'pinecone';
|
|
1228
|
-
else if (techDoc.match(/Weaviate/i)) stack.vector = 'weaviate';
|
|
1229
|
-
|
|
1230
|
-
// Styling
|
|
1231
|
-
if (techDoc.match(/Tailwind/i)) stack.styling = 'tailwind';
|
|
1232
|
-
else if (techDoc.match(/styled-components/i)) stack.styling = 'styled-components';
|
|
1233
|
-
else if (techDoc.match(/CSS\s+Modules/i)) stack.styling = 'css-modules';
|
|
1234
|
-
|
|
1235
|
-
// UI Library
|
|
1236
|
-
if (techDoc.match(/Shadcn|shadcn\/ui/i)) stack.uiLibrary = 'shadcn';
|
|
1237
|
-
else if (techDoc.match(/Chakra/i)) stack.uiLibrary = 'chakra';
|
|
1238
|
-
else if (techDoc.match(/MUI|Material[- ]UI/i)) stack.uiLibrary = 'mui';
|
|
1239
|
-
else if (techDoc.match(/Radix/i)) stack.uiLibrary = 'radix';
|
|
1240
|
-
|
|
1241
|
-
// ORM
|
|
1242
|
-
if (techDoc.match(/Prisma/i)) stack.orm = 'prisma';
|
|
1243
|
-
else if (techDoc.match(/Drizzle/i)) stack.orm = 'drizzle';
|
|
1244
|
-
else if (techDoc.match(/TypeORM/i)) stack.orm = 'typeorm';
|
|
1245
|
-
|
|
1246
|
-
// Auth
|
|
1247
|
-
if (techDoc.match(/Clerk/i)) stack.auth = 'clerk';
|
|
1248
|
-
else if (techDoc.match(/NextAuth|Auth\.js/i)) stack.auth = 'nextauth';
|
|
1249
|
-
else if (techDoc.match(/Supabase\s+Auth/i)) stack.auth = 'supabase';
|
|
1250
|
-
else if (techDoc.match(/Firebase\s+Auth/i)) stack.auth = 'firebase';
|
|
1251
|
-
|
|
1252
|
-
// Payments
|
|
1253
|
-
if (techDoc.match(/Stripe/i)) stack.payments = 'stripe';
|
|
1254
|
-
else if (techDoc.match(/Paddle/i)) stack.payments = 'paddle';
|
|
1255
|
-
else if (techDoc.match(/Lemon\s*Squeezy/i)) stack.payments = 'lemonsqueezy';
|
|
1256
|
-
|
|
1257
|
-
// Hosting
|
|
1258
|
-
if (techDoc.match(/Vercel/i)) stack.hosting = 'vercel';
|
|
1259
|
-
else if (techDoc.match(/Netlify/i)) stack.hosting = 'netlify';
|
|
1260
|
-
else if (techDoc.match(/Railway/i)) stack.hosting = 'railway';
|
|
1261
|
-
else if (techDoc.match(/Fly\.io/i)) stack.hosting = 'fly';
|
|
1262
|
-
else if (techDoc.match(/AWS/i)) stack.hosting = 'aws';
|
|
1263
|
-
|
|
1264
|
-
return stack;
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
/**
|
|
1268
|
-
* Extract MVP features from PRD
|
|
1269
|
-
*/
|
|
1270
|
-
function extractMVPFeatures(docs) {
|
|
1271
|
-
const prdDoc = docs['PRD'] || docs['prd'];
|
|
1272
|
-
if (!prdDoc) return [];
|
|
1273
|
-
|
|
1274
|
-
const features = [];
|
|
1275
|
-
|
|
1276
|
-
// Look for MVP Features section
|
|
1277
|
-
const mvpSection = extractSection(prdDoc, 'MVP\\s+Features|P1\\s+.*Features|Must\\s+Ship', { maxLength: 5000 });
|
|
1278
|
-
if (mvpSection) {
|
|
1279
|
-
// Extract feature names from headers like "#### F-001: Feature Name"
|
|
1280
|
-
const featureHeaders = mvpSection.match(/^#{1,4}\s+F-\d+:\s*(.+)$/gm);
|
|
1281
|
-
if (featureHeaders && featureHeaders.length > 0) {
|
|
1282
|
-
featureHeaders.forEach(h => {
|
|
1283
|
-
const match = h.match(/F-\d+:\s*(.+)/);
|
|
1284
|
-
if (match) features.push(match[1].trim());
|
|
1285
|
-
});
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
|
-
|
|
1289
|
-
// Also look for Feature Requirements section
|
|
1290
|
-
const featureReqSection = extractSection(prdDoc, 'Feature\\s+Requirements', { maxLength: 5000 });
|
|
1291
|
-
if (featureReqSection && features.length === 0) {
|
|
1292
|
-
// Look for **FR-XX:** patterns
|
|
1293
|
-
const frMatches = featureReqSection.match(/\*{0,2}FR-\d+:?\*{0,2}\s*(.+)/g);
|
|
1294
|
-
if (frMatches) {
|
|
1295
|
-
frMatches.forEach(m => {
|
|
1296
|
-
const match = m.match(/FR-\d+:?\*{0,2}\s*(.+)/);
|
|
1297
|
-
if (match) features.push(match[1].replace(/\*+/g, '').trim());
|
|
1298
|
-
});
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
// Fallback: look for numbered Core User Journeys
|
|
1303
|
-
if (features.length === 0) {
|
|
1304
|
-
const journeySection = extractSection(prdDoc, 'Core\\s+User\\s+Journeys|User\\s+Journeys', { maxLength: 3000 });
|
|
1305
|
-
if (journeySection) {
|
|
1306
|
-
const journeyHeaders = journeySection.match(/^#{1,4}\s+\d+\.\d+\s+Journey\s+\d+\s*[—-]\s*(.+)$/gm);
|
|
1307
|
-
if (journeyHeaders) {
|
|
1308
|
-
journeyHeaders.forEach(h => {
|
|
1309
|
-
const match = h.match(/Journey\s+\d+\s*[—-]\s*(.+)/);
|
|
1310
|
-
if (match) features.push(match[1].trim());
|
|
1311
|
-
});
|
|
1312
|
-
}
|
|
1313
|
-
}
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
|
-
// Limit to top 15 features
|
|
1317
|
-
return features.slice(0, 15);
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
/**
|
|
1321
|
-
* Extract target users/personas from documents
|
|
1322
|
-
*/
|
|
1323
|
-
function extractTargetUsers(docs) {
|
|
1324
|
-
const visionDoc = docs['VISION'] || docs['vision'];
|
|
1325
|
-
const prdDoc = docs['PRD'] || docs['prd'];
|
|
1326
|
-
const audienceDoc = docs['AUDIENCE'] || docs['audience'];
|
|
1327
|
-
|
|
1328
|
-
const personas = [];
|
|
1329
|
-
|
|
1330
|
-
// Try AUDIENCE doc first - look for "## X.X Persona PX — Name" pattern
|
|
1331
|
-
if (audienceDoc) {
|
|
1332
|
-
const personaMatches = audienceDoc.match(/^#{1,4}\s+\d+\.\d+\s+Persona\s+P?\d+\s*[—-]\s*(.+)$/gm);
|
|
1333
|
-
if (personaMatches) {
|
|
1334
|
-
personaMatches.forEach(m => {
|
|
1335
|
-
const match = m.match(/[—-]\s*(.+)/);
|
|
1336
|
-
if (match && match[1]) personas.push(match[1].trim());
|
|
1337
|
-
});
|
|
1338
|
-
}
|
|
1339
|
-
// Also look for ICP definitions like "## 3.1 ICP-A: AI Agency Operator"
|
|
1340
|
-
if (personas.length === 0) {
|
|
1341
|
-
const icpMatches = audienceDoc.match(/^#{1,4}\s+\d+\.\d+\s+ICP-[A-Z]:\s*(.+?)(?:\s*\(|$)/gm);
|
|
1342
|
-
if (icpMatches) {
|
|
1343
|
-
icpMatches.forEach(m => {
|
|
1344
|
-
const match = m.match(/ICP-[A-Z]:\s*(.+?)(?:\s*\(|$)/);
|
|
1345
|
-
if (match && match[1]) personas.push(match[1].trim());
|
|
1346
|
-
});
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
// Try VISION
|
|
1352
|
-
if (visionDoc && personas.length === 0) {
|
|
1353
|
-
const targetSection = extractSection(visionDoc, 'Target\\s+Users|Who\\s+This\\s+Is\\s+For', { maxLength: 2000 });
|
|
1354
|
-
if (targetSection) {
|
|
1355
|
-
// Look for bold text patterns like "- **AI Agencies & AI Arbitrage Specialists**"
|
|
1356
|
-
const boldMatches = targetSection.match(/^\s*[-*]\s*\*{2}([^*]+)\*{2}/gm);
|
|
1357
|
-
if (boldMatches) {
|
|
1358
|
-
boldMatches.forEach(m => {
|
|
1359
|
-
const match = m.match(/\*{2}([^*]+)\*{2}/);
|
|
1360
|
-
if (match && match[1]) personas.push(match[1].trim());
|
|
1361
|
-
});
|
|
1362
|
-
}
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
|
-
// Try PRD
|
|
1367
|
-
if (prdDoc && personas.length === 0) {
|
|
1368
|
-
const personaSection = extractSection(prdDoc, 'Personas|Who\\s+We\\s+Serve', { maxLength: 2000 });
|
|
1369
|
-
if (personaSection) {
|
|
1370
|
-
const personaMatches = personaSection.match(/^#{1,4}\s+\d+\.\d+\s+Persona\s+[A-Z]\s*[—-]\s*(.+)$/gm);
|
|
1371
|
-
if (personaMatches) {
|
|
1372
|
-
personaMatches.forEach(m => {
|
|
1373
|
-
const match = m.match(/[—-]\s*(.+)/);
|
|
1374
|
-
if (match && match[1]) personas.push(match[1].trim());
|
|
1375
|
-
});
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
return personas.slice(0, 6);
|
|
1381
|
-
}
|
|
1382
|
-
|
|
1383
|
-
/**
|
|
1384
|
-
* Extract database entities from technical spec
|
|
1385
|
-
*/
|
|
1386
|
-
function extractDatabaseEntities(docs) {
|
|
1387
|
-
const techDoc = docs['TECHNICAL_SPEC'] || docs['TECHNICAL-SPEC'] || docs['technical_spec'];
|
|
1388
|
-
if (!techDoc) return [];
|
|
1389
|
-
|
|
1390
|
-
const entities = [];
|
|
1391
|
-
|
|
1392
|
-
// Strategy 1: Look specifically for "Key tables" or "Data Model" sections
|
|
1393
|
-
// This avoids capturing run types like `interactive_chat` which are not tables
|
|
1394
|
-
const tableSection = extractSection(techDoc, 'Key\\s+tables|Data\\s+Model|Database\\s+Schema', { maxLength: 3000 });
|
|
1395
|
-
if (tableSection) {
|
|
1396
|
-
// Extract backticked table names from bullet points in this specific section
|
|
1397
|
-
const bulletTableMatches = tableSection.match(/^\s*[-*]\s*`(\w+)`/gm);
|
|
1398
|
-
if (bulletTableMatches) {
|
|
1399
|
-
bulletTableMatches.forEach(m => {
|
|
1400
|
-
const match = m.match(/`(\w+)`/);
|
|
1401
|
-
if (match && match[1] && match[1].length > 2) {
|
|
1402
|
-
const word = match[1].toLowerCase();
|
|
1403
|
-
// Filter out common non-entity words and known run types
|
|
1404
|
-
const skipWords = [
|
|
1405
|
-
'and', 'the', 'for', 'with', 'from', 'optional', 'static', 'primary', 'key', 'index', 'indexes',
|
|
1406
|
-
'interactive_chat', 'widget_chat', 'lead_qualify_async', 'sequence_send_async', 'batch_eval'
|
|
1407
|
-
];
|
|
1408
|
-
if (!skipWords.includes(word)) {
|
|
1409
|
-
entities.push(match[1]);
|
|
1410
|
-
}
|
|
1411
|
-
}
|
|
1412
|
-
});
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
|
|
1416
|
-
// Strategy 2: Extract model names from Prisma schema blocks
|
|
1417
|
-
if (entities.length === 0) {
|
|
1418
|
-
const prismaModels = techDoc.match(/model\s+(\w+)\s*\{/g);
|
|
1419
|
-
if (prismaModels) {
|
|
1420
|
-
prismaModels.forEach(m => {
|
|
1421
|
-
const match = m.match(/model\s+(\w+)/);
|
|
1422
|
-
if (match && match[1]) {
|
|
1423
|
-
entities.push(match[1]);
|
|
1424
|
-
}
|
|
1425
|
-
});
|
|
1426
|
-
}
|
|
1427
|
-
}
|
|
1428
|
-
|
|
1429
|
-
// Strategy 3: Fallback - look for backticked snake_case names in general text
|
|
1430
|
-
// but only if they look like table names (contain underscore or end with 's')
|
|
1431
|
-
if (entities.length === 0) {
|
|
1432
|
-
const generalMatches = techDoc.match(/`([a-z][a-z_]*(?:_[a-z]+)+)`/g);
|
|
1433
|
-
if (generalMatches) {
|
|
1434
|
-
generalMatches.forEach(m => {
|
|
1435
|
-
const match = m.match(/`([a-z_]+)`/);
|
|
1436
|
-
if (match && match[1] && match[1].length > 4) {
|
|
1437
|
-
const word = match[1].toLowerCase();
|
|
1438
|
-
// Skip known run types
|
|
1439
|
-
const runTypes = ['interactive_chat', 'widget_chat', 'lead_qualify_async', 'sequence_send_async', 'batch_eval'];
|
|
1440
|
-
if (!runTypes.includes(word) && !entities.includes(match[1])) {
|
|
1441
|
-
entities.push(match[1]);
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
});
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
// Deduplicate and return
|
|
1449
|
-
return [...new Set(entities)].slice(0, 25);
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
/**
|
|
1453
|
-
* Extract implementation phases from ROADMAP
|
|
1454
|
-
*/
|
|
1455
|
-
function extractImplementationPhases(docs) {
|
|
1456
|
-
const roadmapDoc = docs['ROADMAP'] || docs['roadmap'];
|
|
1457
|
-
if (!roadmapDoc) return [];
|
|
1458
|
-
|
|
1459
|
-
const phases = [];
|
|
1460
|
-
const seenPhases = new Set();
|
|
1461
|
-
|
|
1462
|
-
// Prioritize full phase sections: "## X. Phase N —" (with section number)
|
|
1463
|
-
// These have the actual deliverables, unlike brief summaries
|
|
1464
|
-
const fullPhaseMatches = roadmapDoc.match(/^##\s+\d+\.\s+Phase\s+\d+\s*[—-]+\s*(.+)$/gm);
|
|
1465
|
-
|
|
1466
|
-
if (fullPhaseMatches) {
|
|
1467
|
-
fullPhaseMatches.forEach(m => {
|
|
1468
|
-
const match = m.match(/Phase\s+(\d+)\s*[—-]+\s*(.+)$/);
|
|
1469
|
-
if (match && match[1] && match[2]) {
|
|
1470
|
-
const phaseNum = match[1];
|
|
1471
|
-
if (seenPhases.has(phaseNum)) return;
|
|
1472
|
-
seenPhases.add(phaseNum);
|
|
1473
|
-
|
|
1474
|
-
// Clean up the name - remove parenthetical suffix
|
|
1475
|
-
const phaseName = match[2].trim().replace(/\s*\([^)]+\)\s*$/, '').trim();
|
|
1476
|
-
|
|
1477
|
-
// Try to get deliverables for this phase
|
|
1478
|
-
const deliverables = [];
|
|
1479
|
-
|
|
1480
|
-
// Find the full section for this specific phase (## X. Phase N — until next ## X. Phase)
|
|
1481
|
-
const phaseRegex = new RegExp(`##\\s+\\d+\\.\\s+Phase\\s+${phaseNum}\\s*[—-][\\s\\S]*?(?=##\\s+\\d+\\.\\s+Phase\\s+\\d+|$)`, 'i');
|
|
1482
|
-
const phaseSectionMatch = roadmapDoc.match(phaseRegex);
|
|
1483
|
-
|
|
1484
|
-
if (phaseSectionMatch) {
|
|
1485
|
-
const phaseContent = phaseSectionMatch[0];
|
|
1486
|
-
|
|
1487
|
-
// Look for "### X.X Deliverables" subsection
|
|
1488
|
-
const deliverablesRegex = /###\s+[\d.]+\s*Deliverables[\s\S]*?(?=###|##|$)/i;
|
|
1489
|
-
const deliverablesMatch = phaseContent.match(deliverablesRegex);
|
|
1490
|
-
|
|
1491
|
-
if (deliverablesMatch) {
|
|
1492
|
-
// Extract bullet points
|
|
1493
|
-
const bullets = deliverablesMatch[0].match(/^[-*]\s+(.+)$/gm);
|
|
1494
|
-
if (bullets) {
|
|
1495
|
-
bullets.slice(0, 6).forEach(b => {
|
|
1496
|
-
const text = b.replace(/^[-*]\s+/, '').trim();
|
|
1497
|
-
if (text.length > 3) deliverables.push(text);
|
|
1498
|
-
});
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
|
|
1502
|
-
// If no deliverables found, try "MVP feature set" or "feature set"
|
|
1503
|
-
if (deliverables.length === 0) {
|
|
1504
|
-
// Look for #### A) B) C) patterns anywhere in phase content
|
|
1505
|
-
const featureHeaders = phaseContent.match(/^####\s+[A-Z]\)\s+(.+)$/gm);
|
|
1506
|
-
if (featureHeaders && featureHeaders.length > 0) {
|
|
1507
|
-
featureHeaders.slice(0, 6).forEach(h => {
|
|
1508
|
-
const hMatch = h.match(/####\s+[A-Z]\)\s+(.+)/);
|
|
1509
|
-
if (hMatch && hMatch[1]) {
|
|
1510
|
-
// Clean up the title - remove trailing parenthetical like "(prebuilt)"
|
|
1511
|
-
const title = hMatch[1].trim();
|
|
1512
|
-
deliverables.push(title);
|
|
1513
|
-
}
|
|
1514
|
-
});
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
// Strategy 3: Look for Exit Criteria items
|
|
1519
|
-
if (deliverables.length === 0) {
|
|
1520
|
-
const exitCriteriaRegex = /###\s+[\d.]+\s*Exit\s+Criteria[\s\S]*?(?=###|##|$)/i;
|
|
1521
|
-
const exitMatch = phaseContent.match(exitCriteriaRegex);
|
|
1522
|
-
if (exitMatch) {
|
|
1523
|
-
// Extract checkmark items like "- ✅ Description"
|
|
1524
|
-
const checkItems = exitMatch[0].match(/^[-*]\s+[✅✓]\s*(.+)$/gm);
|
|
1525
|
-
if (checkItems) {
|
|
1526
|
-
checkItems.slice(0, 5).forEach(item => {
|
|
1527
|
-
const text = item.replace(/^[-*]\s+[✅✓]\s*/, '').trim();
|
|
1528
|
-
if (text.length > 5) deliverables.push(text);
|
|
1529
|
-
});
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
|
-
// Strategy 3: Look for "### X.X Phase goal" and extract key items
|
|
1535
|
-
if (deliverables.length === 0) {
|
|
1536
|
-
const goalRegex = /###\s+[\d.]+\s*(?:Phase\s+)?goal[\s\S]*?(?=###|$)/i;
|
|
1537
|
-
const goalMatch = phaseContent.match(goalRegex);
|
|
1538
|
-
if (goalMatch) {
|
|
1539
|
-
// Extract bullet points or bold items
|
|
1540
|
-
const bullets = goalMatch[0].match(/^[-*]\s+(.+)$/gm);
|
|
1541
|
-
if (bullets) {
|
|
1542
|
-
bullets.slice(0, 4).forEach(b => {
|
|
1543
|
-
const text = b.replace(/^[-*]\s+/, '').trim();
|
|
1544
|
-
if (text.length > 3) deliverables.push(text);
|
|
1545
|
-
});
|
|
1546
|
-
}
|
|
1547
|
-
}
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
phases.push({
|
|
1552
|
-
phase: phaseNum,
|
|
1553
|
-
name: phaseName,
|
|
1554
|
-
deliverables
|
|
1555
|
-
});
|
|
1556
|
-
}
|
|
1557
|
-
});
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
return phases.slice(0, 5);
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
|
-
/**
|
|
1564
|
-
* Extract third-party integrations from technical spec
|
|
1565
|
-
*/
|
|
1566
|
-
function extractIntegrations(docs) {
|
|
1567
|
-
const techDoc = docs['TECHNICAL_SPEC'] || docs['TECHNICAL-SPEC'] || docs['technical_spec'];
|
|
1568
|
-
const prdDoc = docs['PRD'] || docs['prd'];
|
|
1569
|
-
const allDocs = (techDoc || '') + '\n' + (prdDoc || '');
|
|
1570
|
-
|
|
1571
|
-
const integrations = {
|
|
1572
|
-
voice: [],
|
|
1573
|
-
email: [],
|
|
1574
|
-
calendar: [],
|
|
1575
|
-
crm: [],
|
|
1576
|
-
payments: [],
|
|
1577
|
-
auth: [],
|
|
1578
|
-
ai: [],
|
|
1579
|
-
other: []
|
|
1580
|
-
};
|
|
1581
|
-
|
|
1582
|
-
// Voice/Telephony
|
|
1583
|
-
if (allDocs.match(/\bTwilio\b/i)) integrations.voice.push('Twilio');
|
|
1584
|
-
if (allDocs.match(/\bRetell\b/i)) integrations.voice.push('Retell');
|
|
1585
|
-
if (allDocs.match(/\bVapi\b/i)) integrations.voice.push('Vapi');
|
|
1586
|
-
if (allDocs.match(/\bElevenLabs\b/i)) integrations.voice.push('ElevenLabs');
|
|
1587
|
-
if (allDocs.match(/\bDeepgram\b/i)) integrations.voice.push('Deepgram');
|
|
1588
|
-
|
|
1589
|
-
// Email
|
|
1590
|
-
if (allDocs.match(/\bGmail\b/i)) integrations.email.push('Gmail');
|
|
1591
|
-
if (allDocs.match(/\bSendGrid\b/i)) integrations.email.push('SendGrid');
|
|
1592
|
-
if (allDocs.match(/\bResend\b/i)) integrations.email.push('Resend');
|
|
1593
|
-
if (allDocs.match(/\bPostmark\b/i)) integrations.email.push('Postmark');
|
|
1594
|
-
|
|
1595
|
-
// Calendar
|
|
1596
|
-
if (allDocs.match(/Google\s*Calendar/i)) integrations.calendar.push('Google Calendar');
|
|
1597
|
-
if (allDocs.match(/\bCalendly\b/i)) integrations.calendar.push('Calendly');
|
|
1598
|
-
if (allDocs.match(/\bCal\.com\b/i)) integrations.calendar.push('Cal.com');
|
|
1599
|
-
|
|
1600
|
-
// CRM
|
|
1601
|
-
if (allDocs.match(/\bHubSpot\b/i)) integrations.crm.push('HubSpot');
|
|
1602
|
-
if (allDocs.match(/\bSalesforce\b/i)) integrations.crm.push('Salesforce');
|
|
1603
|
-
if (allDocs.match(/\bAttio\b/i)) integrations.crm.push('Attio');
|
|
1604
|
-
if (allDocs.match(/\bPipedrive\b/i)) integrations.crm.push('Pipedrive');
|
|
1605
|
-
|
|
1606
|
-
// Payments
|
|
1607
|
-
if (allDocs.match(/\bStripe\b/i)) integrations.payments.push('Stripe');
|
|
1608
|
-
if (allDocs.match(/\bPaddle\b/i)) integrations.payments.push('Paddle');
|
|
1609
|
-
|
|
1610
|
-
// Auth
|
|
1611
|
-
if (allDocs.match(/\bClerk\b/i)) integrations.auth.push('Clerk');
|
|
1612
|
-
if (allDocs.match(/\bNextAuth\b|Auth\.js/i)) integrations.auth.push('NextAuth');
|
|
1613
|
-
if (allDocs.match(/\bSupabase\s*Auth/i)) integrations.auth.push('Supabase Auth');
|
|
1614
|
-
|
|
1615
|
-
// AI/LLM
|
|
1616
|
-
if (allDocs.match(/\bOpenAI\b|GPT-4/i)) integrations.ai.push('OpenAI');
|
|
1617
|
-
if (allDocs.match(/\bAnthropic\b|Claude/i)) integrations.ai.push('Anthropic');
|
|
1618
|
-
if (allDocs.match(/\bGemini\b/i)) integrations.ai.push('Google Gemini');
|
|
1619
|
-
|
|
1620
|
-
// Other
|
|
1621
|
-
if (allDocs.match(/\bZapier\b/i)) integrations.other.push('Zapier');
|
|
1622
|
-
if (allDocs.match(/\bSlack\b/i)) integrations.other.push('Slack');
|
|
1623
|
-
if (allDocs.match(/\bWhatsApp\b/i)) integrations.other.push('WhatsApp');
|
|
1624
|
-
|
|
1625
|
-
return integrations;
|
|
1626
|
-
}
|
|
1627
|
-
|
|
1628
|
-
/**
|
|
1629
|
-
* Extract API routes from PRD/technical spec
|
|
1630
|
-
*/
|
|
1631
|
-
function extractAPIRoutes(docs) {
|
|
1632
|
-
const techDoc = docs['TECHNICAL_SPEC'] || docs['TECHNICAL-SPEC'] || docs['technical_spec'];
|
|
1633
|
-
|
|
1634
|
-
const routes = [];
|
|
1635
|
-
|
|
1636
|
-
if (techDoc) {
|
|
1637
|
-
// Look for Application Structure section with app/ routes
|
|
1638
|
-
const appStructureSection = extractSection(techDoc, 'Application\\s+Structure|Directory\\s+Structure', { maxLength: 3000 });
|
|
1639
|
-
|
|
1640
|
-
if (appStructureSection) {
|
|
1641
|
-
// Extract page routes like "- `/dashboard`" or "- `/agents`" or "- `/agents/[id]`"
|
|
1642
|
-
const pageRoutes = appStructureSection.match(/[-*]\s*`\/([\w/[\]]+)`/g);
|
|
1643
|
-
if (pageRoutes) {
|
|
1644
|
-
pageRoutes.forEach(r => {
|
|
1645
|
-
const match = r.match(/`(\/[\w/[\]]+)`/);
|
|
1646
|
-
if (match && match[1]) {
|
|
1647
|
-
const route = match[1];
|
|
1648
|
-
// Skip if it looks like a citation URL
|
|
1649
|
-
if (!route.includes('docs/') && !route.includes('guides/')) {
|
|
1650
|
-
if (!routes.includes(route)) routes.push(route);
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
});
|
|
1654
|
-
}
|
|
1655
|
-
|
|
1656
|
-
// Extract API route handler descriptions like "agents CRUD", "runs endpoints"
|
|
1657
|
-
const apiHandlers = appStructureSection.match(/app\/api\/.*?handlers?:[\s\S]*?(?=###|##|$)/i);
|
|
1658
|
-
if (apiHandlers) {
|
|
1659
|
-
const handlerLines = apiHandlers[0].match(/[-*]\s+(\w[\w\s]+)(?:endpoints?|CRUD|callbacks?|handlers?)?/g);
|
|
1660
|
-
if (handlerLines) {
|
|
1661
|
-
handlerLines.forEach(line => {
|
|
1662
|
-
const match = line.match(/[-*]\s+([\w\s]+)/);
|
|
1663
|
-
if (match && match[1]) {
|
|
1664
|
-
const name = match[1].trim().split(/\s+/)[0].toLowerCase();
|
|
1665
|
-
if (name.length > 2 && !['auth', 'the', 'for', 'and'].includes(name)) {
|
|
1666
|
-
const route = `/api/${name}`;
|
|
1667
|
-
if (!routes.includes(route)) routes.push(route);
|
|
1668
|
-
}
|
|
1669
|
-
}
|
|
1670
|
-
});
|
|
1671
|
-
}
|
|
1672
|
-
}
|
|
1673
|
-
}
|
|
1674
|
-
|
|
1675
|
-
// Also look for explicit /api/ routes in backticks
|
|
1676
|
-
const explicitRoutes = techDoc.match(/`\/api\/[\w/[\]-]+`/g);
|
|
1677
|
-
if (explicitRoutes) {
|
|
1678
|
-
explicitRoutes.forEach(r => {
|
|
1679
|
-
const route = r.replace(/`/g, '');
|
|
1680
|
-
// Skip citation URLs
|
|
1681
|
-
if (!route.includes('docs/') && !route.includes('guides/')) {
|
|
1682
|
-
if (!routes.includes(route)) routes.push(route);
|
|
1683
|
-
}
|
|
1684
|
-
});
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1687
|
-
|
|
1688
|
-
return routes.slice(0, 20);
|
|
1689
|
-
}
|
|
1690
|
-
|
|
1691
|
-
/**
|
|
1692
|
-
* Extract core architecture components from technical spec
|
|
1693
|
-
*/
|
|
1694
|
-
function extractArchitecture(docs) {
|
|
1695
|
-
const techDoc = docs['TECHNICAL_SPEC'] || docs['TECHNICAL-SPEC'] || docs['technical_spec'];
|
|
1696
|
-
if (!techDoc) return [];
|
|
1697
|
-
|
|
1698
|
-
const components = [];
|
|
1699
|
-
|
|
1700
|
-
// Strategy 1: Look specifically for "Core system components" section first
|
|
1701
|
-
// This is more specific than "Architecture" which may be a high-level overview
|
|
1702
|
-
const coreSection = extractSection(techDoc, 'Core\\s+system\\s+components', { maxLength: 1500 });
|
|
1703
|
-
if (coreSection) {
|
|
1704
|
-
// Extract numbered items like "1) Web App (Next.js)"
|
|
1705
|
-
const compMatches = coreSection.match(/^\s*\d+\)\s*(.+)$/gm);
|
|
1706
|
-
if (compMatches) {
|
|
1707
|
-
compMatches.forEach(m => {
|
|
1708
|
-
const match = m.match(/\d+\)\s*(.+)/);
|
|
1709
|
-
if (match && match[1]) {
|
|
1710
|
-
components.push(match[1].trim());
|
|
1711
|
-
}
|
|
1712
|
-
});
|
|
1713
|
-
}
|
|
1714
|
-
}
|
|
1715
|
-
|
|
1716
|
-
// Strategy 2: If no components found, look within "Architectural Overview" for subsections
|
|
1717
|
-
if (components.length === 0) {
|
|
1718
|
-
// Search the entire doc for numbered component patterns within Architecture context
|
|
1719
|
-
const archMatch = techDoc.match(/(?:Architectural|Architecture)\s+Overview[\s\S]*?Core\s+system\s+components[\s\S]*?(?=##\s+\d+\.|$)/i);
|
|
1720
|
-
if (archMatch) {
|
|
1721
|
-
const compMatches = archMatch[0].match(/^\s*\d+\)\s*(.+)$/gm);
|
|
1722
|
-
if (compMatches) {
|
|
1723
|
-
compMatches.forEach(m => {
|
|
1724
|
-
const match = m.match(/\d+\)\s*(.+)/);
|
|
1725
|
-
if (match && match[1]) {
|
|
1726
|
-
components.push(match[1].trim());
|
|
1727
|
-
}
|
|
1728
|
-
});
|
|
1729
|
-
}
|
|
1730
|
-
}
|
|
1731
|
-
}
|
|
1732
|
-
|
|
1733
|
-
// Strategy 3: Look for bullet-point architecture components
|
|
1734
|
-
if (components.length === 0) {
|
|
1735
|
-
const archSection = extractSection(techDoc, 'Architecture|System\\s+Components', { maxLength: 2000 });
|
|
1736
|
-
if (archSection) {
|
|
1737
|
-
// Look for bold component names like "- **Component Name**"
|
|
1738
|
-
const boldMatches = archSection.match(/^\s*[-*]\s+\*{2}([^*]+)\*{2}/gm);
|
|
1739
|
-
if (boldMatches) {
|
|
1740
|
-
boldMatches.forEach(m => {
|
|
1741
|
-
const match = m.match(/\*{2}([^*]+)\*{2}/);
|
|
1742
|
-
if (match && match[1] && match[1].length > 3) {
|
|
1743
|
-
components.push(match[1].trim());
|
|
1744
|
-
}
|
|
1745
|
-
});
|
|
1746
|
-
}
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
return components.slice(0, 10);
|
|
1751
|
-
}
|
|
1752
|
-
|
|
1753
|
-
/**
|
|
1754
|
-
* Generate SEED.md content from preseed documents
|
|
1755
|
-
*/
|
|
1756
|
-
function generateSeedFromPreseed(docs, preseedConfig) {
|
|
1757
|
-
// Extract from documents (preferred) with config fallback
|
|
1758
|
-
const projectName = extractProjectName(docs) || preseedConfig.identity?.name || 'My Project';
|
|
1759
|
-
const tagline = extractTagline(docs) || preseedConfig.identity?.tagline || '';
|
|
1760
|
-
const problem = extractProblem(docs) || preseedConfig.problem?.statement || '';
|
|
1761
|
-
const solution = extractSolution(docs) || preseedConfig.solution?.description || '';
|
|
1762
|
-
const techStack = extractTechStack(docs);
|
|
1763
|
-
const mvpFeatures = extractMVPFeatures(docs);
|
|
1764
|
-
const targetUsers = extractTargetUsers(docs);
|
|
1765
|
-
const dbEntities = extractDatabaseEntities(docs);
|
|
1766
|
-
const phases = extractImplementationPhases(docs);
|
|
1767
|
-
const integrations = extractIntegrations(docs);
|
|
1768
|
-
const apiRoutes = extractAPIRoutes(docs);
|
|
1769
|
-
const architecture = extractArchitecture(docs);
|
|
1770
|
-
|
|
1771
|
-
// Build tech stack YAML with detected values
|
|
1772
|
-
let stackYaml = `stack:
|
|
1773
|
-
framework: ${techStack.framework}
|
|
1774
|
-
language: ${techStack.language}
|
|
1775
|
-
database: ${techStack.database}
|
|
1776
|
-
hosting: ${techStack.hosting}
|
|
1777
|
-
|
|
1778
|
-
frontend:
|
|
1779
|
-
uiLibrary: ${techStack.uiLibrary}
|
|
1780
|
-
styling: ${techStack.styling}
|
|
1781
|
-
|
|
1782
|
-
backend:
|
|
1783
|
-
orm: ${techStack.orm}
|
|
1784
|
-
auth: ${techStack.auth}
|
|
1785
|
-
payments: ${techStack.payments}`;
|
|
1786
|
-
|
|
1787
|
-
if (techStack.vector) {
|
|
1788
|
-
stackYaml += `
|
|
1789
|
-
vector: ${techStack.vector}`;
|
|
1790
|
-
}
|
|
1791
|
-
|
|
1792
|
-
// Build features section
|
|
1793
|
-
let featuresSection = '';
|
|
1794
|
-
if (mvpFeatures.length > 0) {
|
|
1795
|
-
featuresSection = mvpFeatures.map((f, i) => `${i + 1}. ${f}`).join('\n');
|
|
1796
|
-
} else {
|
|
1797
|
-
featuresSection = `1. User authentication and workspace management
|
|
1798
|
-
2. Core dashboard with analytics
|
|
1799
|
-
3. Data management and CRUD operations
|
|
1800
|
-
4. API endpoints for integrations
|
|
1801
|
-
5. Admin interface and settings`;
|
|
1802
|
-
}
|
|
1803
|
-
|
|
1804
|
-
// Build target users section
|
|
1805
|
-
let usersSection = '';
|
|
1806
|
-
if (targetUsers.length > 0) {
|
|
1807
|
-
usersSection = targetUsers.map(u => `- ${u}`).join('\n');
|
|
1808
|
-
}
|
|
1809
|
-
|
|
1810
|
-
// Build database entities section
|
|
1811
|
-
let entitiesSection = '';
|
|
1812
|
-
if (dbEntities.length > 0) {
|
|
1813
|
-
entitiesSection = dbEntities.map(e => `- \`${e}\``).join('\n');
|
|
1814
|
-
}
|
|
1815
|
-
|
|
1816
|
-
// Build architecture section
|
|
1817
|
-
let architectureSection = '';
|
|
1818
|
-
if (architecture.length > 0) {
|
|
1819
|
-
architectureSection = architecture.map((c, i) => `${i + 1}. ${c}`).join('\n');
|
|
1820
|
-
}
|
|
1821
|
-
|
|
1822
|
-
// Build integrations section
|
|
1823
|
-
let integrationsSection = '';
|
|
1824
|
-
const intCats = Object.entries(integrations).filter(([_, v]) => v.length > 0);
|
|
1825
|
-
if (intCats.length > 0) {
|
|
1826
|
-
integrationsSection = intCats.map(([cat, services]) => {
|
|
1827
|
-
const catName = cat.charAt(0).toUpperCase() + cat.slice(1);
|
|
1828
|
-
return `**${catName}:** ${services.join(', ')}`;
|
|
1829
|
-
}).join('\n');
|
|
1830
|
-
}
|
|
1831
|
-
|
|
1832
|
-
// Build phases section
|
|
1833
|
-
let phasesSection = '';
|
|
1834
|
-
if (phases.length > 0) {
|
|
1835
|
-
phasesSection = phases.map(p => {
|
|
1836
|
-
let section = `### Phase ${p.phase}: ${p.name}`;
|
|
1837
|
-
if (p.deliverables.length > 0) {
|
|
1838
|
-
section += '\n' + p.deliverables.map(d => `- ${d}`).join('\n');
|
|
1839
|
-
}
|
|
1840
|
-
return section;
|
|
1841
|
-
}).join('\n\n');
|
|
1842
|
-
}
|
|
1843
|
-
|
|
1844
|
-
// Build API routes section
|
|
1845
|
-
let apiRoutesSection = '';
|
|
1846
|
-
if (apiRoutes.length > 0) {
|
|
1847
|
-
apiRoutesSection = apiRoutes.map(r => `- \`${r}\``).join('\n');
|
|
1848
|
-
}
|
|
1849
|
-
|
|
1850
|
-
const content = `# ${projectName}
|
|
1851
|
-
|
|
1852
|
-
${tagline ? `> ${tagline}` : ''}
|
|
1853
|
-
|
|
1854
|
-
## Overview
|
|
1855
|
-
|
|
1856
|
-
${problem ? `**The Problem:** ${problem}\n\n` : ''}${solution ? `**Our Solution:** ${solution}` : ''}
|
|
1857
|
-
|
|
1858
|
-
---
|
|
1859
|
-
|
|
1860
|
-
## Tech Stack
|
|
1861
|
-
|
|
1862
|
-
\`\`\`yaml
|
|
1863
|
-
${stackYaml}
|
|
1864
|
-
\`\`\`
|
|
1865
|
-
|
|
1866
|
-
---
|
|
1867
|
-
${architecture.length > 0 ? `
|
|
1868
|
-
## Architecture
|
|
1869
|
-
|
|
1870
|
-
${architectureSection}
|
|
1871
|
-
|
|
1872
|
-
---
|
|
1873
|
-
` : ''}
|
|
1874
|
-
${intCats.length > 0 ? `
|
|
1875
|
-
## Integrations
|
|
1876
|
-
|
|
1877
|
-
${integrationsSection}
|
|
1878
|
-
|
|
1879
|
-
---
|
|
1880
|
-
` : ''}
|
|
1881
|
-
## MVP Features
|
|
1882
|
-
|
|
1883
|
-
${featuresSection}
|
|
1884
|
-
|
|
1885
|
-
---
|
|
1886
|
-
${phases.length > 0 ? `
|
|
1887
|
-
## Implementation Phases
|
|
1888
|
-
|
|
1889
|
-
${phasesSection}
|
|
1890
|
-
|
|
1891
|
-
---
|
|
1892
|
-
` : ''}
|
|
1893
|
-
${targetUsers.length > 0 ? `
|
|
1894
|
-
## Target Users
|
|
1895
|
-
|
|
1896
|
-
${usersSection}
|
|
1897
|
-
|
|
1898
|
-
---
|
|
1899
|
-
` : ''}
|
|
1900
|
-
${dbEntities.length > 0 ? `
|
|
1901
|
-
## Core Data Entities
|
|
1902
|
-
|
|
1903
|
-
${entitiesSection}
|
|
1904
|
-
|
|
1905
|
-
---
|
|
1906
|
-
` : ''}
|
|
1907
|
-
${apiRoutes.length > 0 ? `
|
|
1908
|
-
## API Routes
|
|
1909
|
-
|
|
1910
|
-
${apiRoutesSection}
|
|
1911
|
-
|
|
1912
|
-
---
|
|
1913
|
-
` : ''}
|
|
1914
|
-
## Development Standards
|
|
1915
|
-
|
|
1916
|
-
### Code Style
|
|
1917
|
-
- Use ${techStack.language === 'typescript' ? 'TypeScript' : 'JavaScript'} for all new code
|
|
1918
|
-
- Follow existing naming conventions in the codebase
|
|
1919
|
-
- Keep files focused and under 300 lines when possible
|
|
1920
|
-
|
|
1921
|
-
### Best Practices
|
|
1922
|
-
${techStack.framework === 'nextjs' ? `- Use Server Components by default
|
|
1923
|
-
- Prefer Server Actions over API routes for mutations` : `- Follow ${techStack.framework} best practices`}
|
|
1924
|
-
- Use Zod for all input validation
|
|
1925
|
-
- Never expose API keys to client-side code
|
|
1926
|
-
- Write tests for new features
|
|
1927
|
-
|
|
1928
|
-
### Git Commits
|
|
1929
|
-
- Use conventional format: \`feat:\`, \`fix:\`, \`docs:\`, \`refactor:\`
|
|
1930
|
-
- Keep commits focused and atomic
|
|
1931
|
-
- Never commit sensitive data or API keys
|
|
1932
|
-
|
|
1933
|
-
---
|
|
1934
|
-
|
|
1935
|
-
## Project Structure
|
|
1936
|
-
|
|
1937
|
-
\`\`\`
|
|
1938
|
-
${projectName.toLowerCase().replace(/\s+/g, '-')}/
|
|
1939
|
-
├── app/ # Next.js App Router
|
|
1940
|
-
│ ├── (auth)/ # Auth pages${techStack.auth === 'clerk' ? ' (Clerk)' : ''}
|
|
1941
|
-
│ ├── (dashboard)/ # Main dashboard
|
|
1942
|
-
│ ├── (marketing)/ # Landing pages
|
|
1943
|
-
│ └── api/ # API routes
|
|
1944
|
-
├── components/
|
|
1945
|
-
│ ├── ui/ # UI components${techStack.uiLibrary === 'shadcn' ? ' (shadcn/ui)' : ''}
|
|
1946
|
-
│ └── [feature]/ # Feature components
|
|
1947
|
-
├── lib/
|
|
1948
|
-
│ ├── db.ts # Database client${techStack.orm === 'prisma' ? ' (Prisma)' : ''}
|
|
1949
|
-
│ ├── auth.ts # Auth utilities
|
|
1950
|
-
│ └── utils.ts # Helpers
|
|
1951
|
-
├── prisma/
|
|
1952
|
-
│ └── schema.prisma # Database schema
|
|
1953
|
-
└── public/ # Static assets
|
|
1954
|
-
\`\`\`
|
|
1955
|
-
|
|
1956
|
-
---
|
|
1957
|
-
|
|
1958
|
-
## Source Documents
|
|
1959
|
-
|
|
1960
|
-
This SEED.md was synthesized from:
|
|
1961
|
-
${Object.keys(docs).map(name => `- \`.bootspring/preseed/${name}.md\``).join('\n')}
|
|
1962
|
-
|
|
1963
|
-
---
|
|
1964
|
-
|
|
1965
|
-
*Generated with [Bootspring](https://bootspring.com)*
|
|
1966
|
-
`;
|
|
1967
|
-
|
|
1968
|
-
return content;
|
|
1969
|
-
}
|
|
1970
|
-
|
|
1971
|
-
/**
|
|
1972
|
-
* Generate AI enhancement prompt
|
|
1973
|
-
*/
|
|
1974
|
-
function generateEnhancementPrompt(docs, preseedConfig) {
|
|
1975
|
-
return `# Enhance SEED.md
|
|
1976
|
-
|
|
1977
|
-
I've created a basic SEED.md from my preseed documents. Please review and enhance it with:
|
|
1978
|
-
|
|
1979
|
-
1. **More specific tech stack recommendations** based on my requirements
|
|
1980
|
-
2. **Detailed file structure** for the project
|
|
1981
|
-
3. **Implementation order** - which features to build first
|
|
1982
|
-
4. **API endpoints** we'll need
|
|
1983
|
-
5. **Database schema** suggestions
|
|
1984
|
-
6. **Third-party services** recommendations
|
|
1985
|
-
|
|
1986
|
-
Here are my preseed documents for context:
|
|
1987
|
-
|
|
1988
|
-
${Object.entries(docs).map(([name, content]) => `## ${name}\n\n${content}`).join('\n\n---\n\n')}
|
|
1989
|
-
|
|
1990
|
-
---
|
|
1991
|
-
|
|
1992
|
-
Please provide an enhanced SEED.md that a developer (or AI assistant) can follow to build this project from scratch.`;
|
|
1993
|
-
}
|
|
1994
|
-
|
|
1995
944
|
/**
|
|
1996
945
|
* Parse SEED.md file
|
|
1997
946
|
*/
|