@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.
Files changed (159) hide show
  1. package/bin/bootspring.js +5 -0
  2. package/cli/org.js +474 -0
  3. package/cli/preseed/index.js +16 -0
  4. package/cli/preseed/interactive.js +143 -0
  5. package/cli/preseed/templates.js +227 -0
  6. package/cli/preseed.js +9 -301
  7. package/cli/seed/builders/ai-context-builder.js +85 -0
  8. package/cli/seed/builders/index.js +13 -0
  9. package/cli/seed/builders/seed-builder.js +272 -0
  10. package/cli/seed/extractors/content-extractors.js +383 -0
  11. package/cli/seed/extractors/index.js +47 -0
  12. package/cli/seed/extractors/metadata-extractors.js +167 -0
  13. package/cli/seed/extractors/section-extractor.js +54 -0
  14. package/cli/seed/extractors/stack-extractors.js +228 -0
  15. package/cli/seed/index.js +18 -0
  16. package/cli/seed/utils/folder-structure.js +84 -0
  17. package/cli/seed/utils/index.js +11 -0
  18. package/cli/seed.js +23 -1074
  19. package/core/api-client.js +77 -0
  20. package/core/entitlements.js +36 -0
  21. package/core/organizations.js +223 -0
  22. package/core/policies.js +51 -6
  23. package/core/policy-matrix.js +303 -0
  24. package/core/project-context.js +1 -0
  25. package/dist/cli/index.d.ts +3 -0
  26. package/dist/cli/index.js +3220 -0
  27. package/dist/cli/index.js.map +1 -0
  28. package/dist/context-McpJQa_2.d.ts +5710 -0
  29. package/dist/core/index.d.ts +635 -0
  30. package/dist/core/index.js +2593 -0
  31. package/dist/core/index.js.map +1 -0
  32. package/dist/index-QqbeEiDm.d.ts +857 -0
  33. package/dist/index-UiYCgwiH.d.ts +174 -0
  34. package/dist/index.d.ts +453 -0
  35. package/dist/index.js +44228 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/mcp/index.d.ts +1 -0
  38. package/dist/mcp/index.js +41173 -0
  39. package/dist/mcp/index.js.map +1 -0
  40. package/generators/index.ts +82 -0
  41. package/intelligence/orchestrator/config/failure-signatures.js +48 -0
  42. package/intelligence/orchestrator/config/index.js +23 -0
  43. package/intelligence/orchestrator/config/pack-lifecycle.js +262 -0
  44. package/intelligence/orchestrator/config/phases.js +111 -0
  45. package/intelligence/orchestrator/config/remediation.js +150 -0
  46. package/intelligence/orchestrator/config/workflows.js +168 -0
  47. package/intelligence/orchestrator/core/index.js +16 -0
  48. package/intelligence/orchestrator/core/state-manager.js +88 -0
  49. package/intelligence/orchestrator/core/telemetry.js +24 -0
  50. package/intelligence/orchestrator/index.js +17 -0
  51. package/intelligence/orchestrator.js +17 -512
  52. package/mcp/contracts/mcp-contract.v1.json +1 -1
  53. package/package.json +16 -3
  54. package/src/cli/agent.ts +703 -0
  55. package/src/cli/analyze.ts +640 -0
  56. package/src/cli/audit.ts +707 -0
  57. package/src/cli/auth.ts +930 -0
  58. package/src/cli/billing.ts +364 -0
  59. package/src/cli/build.ts +1089 -0
  60. package/src/cli/business.ts +508 -0
  61. package/src/cli/checkpoint-utils.ts +236 -0
  62. package/src/cli/checkpoint.ts +757 -0
  63. package/src/cli/cloud-sync.ts +534 -0
  64. package/src/cli/content.ts +273 -0
  65. package/src/cli/context.ts +667 -0
  66. package/src/cli/dashboard.ts +133 -0
  67. package/src/cli/deploy.ts +704 -0
  68. package/src/cli/doctor.ts +480 -0
  69. package/src/cli/fundraise.ts +494 -0
  70. package/src/cli/generate.ts +346 -0
  71. package/src/cli/github-cmd.ts +566 -0
  72. package/src/cli/health.ts +599 -0
  73. package/src/cli/index.ts +113 -0
  74. package/src/cli/init.ts +838 -0
  75. package/src/cli/legal.ts +495 -0
  76. package/src/cli/log.ts +316 -0
  77. package/src/cli/loop.ts +1660 -0
  78. package/src/cli/manager.ts +878 -0
  79. package/src/cli/mcp.ts +275 -0
  80. package/src/cli/memory.ts +346 -0
  81. package/src/cli/metrics.ts +590 -0
  82. package/src/cli/monitor.ts +960 -0
  83. package/src/cli/mvp.ts +662 -0
  84. package/src/cli/onboard.ts +663 -0
  85. package/src/cli/orchestrator.ts +622 -0
  86. package/src/cli/plugin.ts +483 -0
  87. package/src/cli/prd.ts +671 -0
  88. package/src/cli/preseed-start.ts +1633 -0
  89. package/src/cli/preseed.ts +2434 -0
  90. package/src/cli/project.ts +526 -0
  91. package/src/cli/quality.ts +885 -0
  92. package/src/cli/security.ts +1079 -0
  93. package/src/cli/seed.ts +1224 -0
  94. package/src/cli/skill.ts +537 -0
  95. package/src/cli/suggest.ts +1225 -0
  96. package/src/cli/switch.ts +518 -0
  97. package/src/cli/task.ts +780 -0
  98. package/src/cli/telemetry.ts +172 -0
  99. package/src/cli/todo.ts +627 -0
  100. package/src/cli/types.ts +15 -0
  101. package/src/cli/update.ts +334 -0
  102. package/src/cli/visualize.ts +609 -0
  103. package/src/cli/watch.ts +895 -0
  104. package/src/cli/workspace.ts +709 -0
  105. package/src/core/action-recorder.ts +673 -0
  106. package/src/core/analyze-workflow.ts +1453 -0
  107. package/src/core/api-client.ts +1120 -0
  108. package/src/core/audit-workflow.ts +1681 -0
  109. package/src/core/auth.ts +471 -0
  110. package/src/core/build-orchestrator.ts +509 -0
  111. package/src/core/build-state.ts +621 -0
  112. package/src/core/checkpoint-engine.ts +482 -0
  113. package/src/core/config.ts +1285 -0
  114. package/src/core/context-loader.ts +694 -0
  115. package/src/core/context.ts +410 -0
  116. package/src/core/deploy-workflow.ts +1085 -0
  117. package/src/core/entitlements.ts +322 -0
  118. package/src/core/github-sync.ts +720 -0
  119. package/src/core/index.ts +981 -0
  120. package/src/core/ingest.ts +1186 -0
  121. package/src/core/metrics-engine.ts +886 -0
  122. package/src/core/mvp.ts +847 -0
  123. package/src/core/onboard-workflow.ts +1293 -0
  124. package/src/core/policies.ts +81 -0
  125. package/src/core/preseed-workflow.ts +1163 -0
  126. package/src/core/preseed.ts +1826 -0
  127. package/src/core/project-context.ts +380 -0
  128. package/src/core/project-state.ts +699 -0
  129. package/src/core/r2-sync.ts +691 -0
  130. package/src/core/scaffold.ts +1715 -0
  131. package/src/core/session.ts +286 -0
  132. package/src/core/task-extractor.ts +799 -0
  133. package/src/core/telemetry.ts +371 -0
  134. package/src/core/tier-enforcement.ts +737 -0
  135. package/src/core/utils.ts +437 -0
  136. package/src/index.ts +29 -0
  137. package/src/intelligence/agent-collab.ts +2376 -0
  138. package/src/intelligence/auto-suggest.ts +713 -0
  139. package/src/intelligence/content-gen.ts +1351 -0
  140. package/src/intelligence/cross-project.ts +1692 -0
  141. package/src/intelligence/git-memory.ts +529 -0
  142. package/src/intelligence/index.ts +318 -0
  143. package/src/intelligence/orchestrator.ts +534 -0
  144. package/src/intelligence/prd.ts +466 -0
  145. package/src/intelligence/recommendations.ts +982 -0
  146. package/src/intelligence/workflow-composer.ts +1472 -0
  147. package/src/mcp/capabilities.ts +233 -0
  148. package/src/mcp/index.ts +37 -0
  149. package/src/mcp/registry.ts +1268 -0
  150. package/src/mcp/response-formatter.ts +797 -0
  151. package/src/mcp/server.ts +240 -0
  152. package/src/types/agent.ts +69 -0
  153. package/src/types/config.ts +86 -0
  154. package/src/types/context.ts +77 -0
  155. package/src/types/index.ts +53 -0
  156. package/src/types/mcp.ts +91 -0
  157. package/src/types/skills.ts +47 -0
  158. package/src/types/workflow.ts +155 -0
  159. 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
  */