@girardmedia/bootspring 1.2.0 → 2.0.3
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/README.md +107 -14
- package/bin/bootspring.js +166 -27
- package/cli/agent.js +189 -17
- package/cli/analyze.js +499 -0
- package/cli/audit.js +557 -0
- package/cli/auth.js +495 -38
- package/cli/billing.js +302 -0
- package/cli/build.js +695 -0
- package/cli/business.js +109 -26
- package/cli/checkpoint-utils.js +168 -0
- package/cli/checkpoint.js +639 -0
- package/cli/cloud-sync.js +447 -0
- package/cli/content.js +198 -0
- package/cli/context.js +1 -1
- package/cli/deploy.js +543 -0
- package/cli/fundraise.js +112 -50
- package/cli/github-cmd.js +435 -0
- package/cli/health.js +477 -0
- package/cli/init.js +84 -13
- package/cli/legal.js +107 -95
- package/cli/log.js +2 -2
- package/cli/loop.js +976 -73
- package/cli/manager.js +711 -0
- package/cli/metrics.js +480 -0
- package/cli/monitor.js +812 -0
- package/cli/onboard.js +521 -0
- package/cli/orchestrator.js +12 -24
- package/cli/prd.js +594 -0
- package/cli/preseed-start.js +1483 -0
- package/cli/preseed.js +2302 -0
- package/cli/project.js +436 -0
- package/cli/quality.js +233 -0
- package/cli/security.js +913 -0
- package/cli/seed.js +1441 -5
- package/cli/skill.js +273 -211
- package/cli/suggest.js +989 -0
- package/cli/switch.js +453 -0
- package/cli/visualize.js +527 -0
- package/cli/watch.js +769 -0
- package/cli/workspace.js +607 -0
- package/core/analyze-workflow.js +1134 -0
- package/core/api-client.js +535 -22
- package/core/audit-workflow.js +1350 -0
- package/core/build-orchestrator.js +480 -0
- package/core/build-state.js +577 -0
- package/core/checkpoint-engine.js +408 -0
- package/core/config.js +1109 -26
- package/core/context-loader.js +21 -1
- package/core/deploy-workflow.js +836 -0
- package/core/entitlements.js +93 -22
- package/core/github-sync.js +610 -0
- package/core/index.js +8 -1
- package/core/ingest.js +1111 -0
- package/core/metrics-engine.js +768 -0
- package/core/onboard-workflow.js +1007 -0
- package/core/preseed-workflow.js +934 -0
- package/core/preseed.js +1617 -0
- package/core/project-context.js +325 -0
- package/core/project-state.js +694 -0
- package/core/r2-sync.js +583 -0
- package/core/scaffold.js +525 -7
- package/core/session.js +258 -0
- package/core/task-extractor.js +758 -0
- package/core/telemetry.js +28 -6
- package/core/tier-enforcement.js +737 -0
- package/core/utils.js +38 -14
- package/generators/questionnaire.js +15 -12
- package/generators/sections/ai.js +7 -7
- package/generators/sections/content.js +300 -0
- package/generators/sections/index.js +3 -0
- package/generators/sections/plugins.js +7 -6
- package/generators/templates/build-planning.template.js +596 -0
- package/generators/templates/content.template.js +819 -0
- package/generators/templates/index.js +2 -1
- package/hooks/git-autopilot.js +1250 -0
- package/hooks/index.js +9 -0
- package/intelligence/agent-collab.js +2057 -0
- package/intelligence/auto-suggest.js +634 -0
- package/intelligence/content-gen.js +1589 -0
- package/intelligence/cross-project.js +1647 -0
- package/intelligence/index.js +184 -0
- package/intelligence/learning/insights.json +517 -7
- package/intelligence/learning/pattern-learner.js +1008 -14
- package/intelligence/memory/decision-tracker.js +1431 -31
- package/intelligence/memory/decisions.jsonl +0 -0
- package/intelligence/orchestrator.js +2896 -1
- package/intelligence/prd.js +92 -1
- package/intelligence/recommendation-weights.json +14 -2
- package/intelligence/recommendations.js +463 -9
- package/intelligence/workflow-composer.js +1451 -0
- package/marketplace/index.d.ts +324 -0
- package/marketplace/index.js +1921 -0
- package/mcp/contracts/mcp-contract.v1.json +342 -4
- package/mcp/registry.js +680 -3
- package/mcp/response-formatter.js +23 -0
- package/mcp/tools/assist-tool.js +78 -4
- package/mcp/tools/autopilot-tool.js +408 -0
- package/mcp/tools/content-tool.js +571 -0
- package/mcp/tools/dashboard-tool.js +251 -5
- package/mcp/tools/mvp-tool.js +344 -0
- package/mcp/tools/plugin-tool.js +23 -1
- package/mcp/tools/prd-tool.js +579 -0
- package/mcp/tools/seed-tool.js +447 -0
- package/mcp/tools/skill-tool.js +43 -14
- package/mcp/tools/suggest-tool.js +147 -0
- package/package.json +15 -6
- package/agents/README.md +0 -93
- package/agents/ai-integration-expert/context.md +0 -386
- package/agents/api-expert/context.md +0 -416
- package/agents/architecture-expert/context.md +0 -454
- package/agents/auth-expert/context.md +0 -399
- package/agents/backend-expert/context.md +0 -483
- package/agents/business-strategy-expert/context.md +0 -180
- package/agents/code-review-expert/context.md +0 -365
- package/agents/competitive-analysis-expert/context.md +0 -239
- package/agents/data-modeling-expert/context.md +0 -352
- package/agents/database-expert/context.md +0 -250
- package/agents/devops-expert/context.md +0 -446
- package/agents/email-expert/context.md +0 -379
- package/agents/financial-expert/context.md +0 -213
- package/agents/frontend-expert/context.md +0 -364
- package/agents/fundraising-expert/context.md +0 -257
- package/agents/growth-expert/context.md +0 -249
- package/agents/index.js +0 -140
- package/agents/investor-relations-expert/context.md +0 -266
- package/agents/legal-expert/context.md +0 -284
- package/agents/marketing-expert/context.md +0 -236
- package/agents/monitoring-expert/context.md +0 -362
- package/agents/operations-expert/context.md +0 -279
- package/agents/partnerships-expert/context.md +0 -286
- package/agents/payment-expert/context.md +0 -340
- package/agents/performance-expert/context.md +0 -377
- package/agents/private-equity-expert/context.md +0 -246
- package/agents/railway-expert/context.md +0 -284
- package/agents/research-expert/context.md +0 -245
- package/agents/sales-expert/context.md +0 -241
- package/agents/security-expert/context.md +0 -343
- package/agents/testing-expert/context.md +0 -414
- package/agents/ui-ux-expert/context.md +0 -448
- package/agents/vercel-expert/context.md +0 -426
- package/skills/index.js +0 -787
- package/skills/patterns/README.md +0 -163
- package/skills/patterns/ai/agents.md +0 -281
- package/skills/patterns/ai/claude.md +0 -138
- package/skills/patterns/ai/embeddings.md +0 -150
- package/skills/patterns/ai/rag.md +0 -266
- package/skills/patterns/ai/streaming.md +0 -170
- package/skills/patterns/ai/structured-output.md +0 -162
- package/skills/patterns/ai/tools.md +0 -154
- package/skills/patterns/analytics/tracking.md +0 -220
- package/skills/patterns/api/errors.md +0 -296
- package/skills/patterns/api/graphql.md +0 -440
- package/skills/patterns/api/middleware.md +0 -279
- package/skills/patterns/api/openapi.md +0 -285
- package/skills/patterns/api/rate-limiting.md +0 -231
- package/skills/patterns/api/route-handler.md +0 -217
- package/skills/patterns/api/server-action.md +0 -249
- package/skills/patterns/api/versioning.md +0 -443
- package/skills/patterns/api/webhooks.md +0 -247
- package/skills/patterns/auth/clerk.md +0 -132
- package/skills/patterns/auth/mfa.md +0 -313
- package/skills/patterns/auth/nextauth.md +0 -140
- package/skills/patterns/auth/oauth.md +0 -237
- package/skills/patterns/auth/rbac.md +0 -152
- package/skills/patterns/auth/session-management.md +0 -367
- package/skills/patterns/auth/session.md +0 -120
- package/skills/patterns/database/audit.md +0 -177
- package/skills/patterns/database/migrations.md +0 -177
- package/skills/patterns/database/pagination.md +0 -230
- package/skills/patterns/database/pooling.md +0 -357
- package/skills/patterns/database/prisma.md +0 -180
- package/skills/patterns/database/relations.md +0 -187
- package/skills/patterns/database/seeding.md +0 -246
- package/skills/patterns/database/soft-delete.md +0 -153
- package/skills/patterns/database/transactions.md +0 -162
- package/skills/patterns/deployment/ci-cd.md +0 -231
- package/skills/patterns/deployment/docker.md +0 -188
- package/skills/patterns/deployment/monitoring.md +0 -387
- package/skills/patterns/deployment/vercel.md +0 -160
- package/skills/patterns/email/resend.md +0 -143
- package/skills/patterns/email/templates.md +0 -245
- package/skills/patterns/email/transactional.md +0 -503
- package/skills/patterns/email/verification.md +0 -176
- package/skills/patterns/files/download.md +0 -243
- package/skills/patterns/files/upload.md +0 -239
- package/skills/patterns/i18n/nextintl.md +0 -188
- package/skills/patterns/logging/structured.md +0 -292
- package/skills/patterns/notifications/email-queue.md +0 -248
- package/skills/patterns/notifications/push.md +0 -279
- package/skills/patterns/payments/checkout.md +0 -303
- package/skills/patterns/payments/invoices.md +0 -287
- package/skills/patterns/payments/portal.md +0 -245
- package/skills/patterns/payments/stripe.md +0 -272
- package/skills/patterns/payments/subscriptions.md +0 -300
- package/skills/patterns/payments/usage.md +0 -279
- package/skills/patterns/performance/caching.md +0 -276
- package/skills/patterns/performance/code-splitting.md +0 -233
- package/skills/patterns/performance/edge.md +0 -254
- package/skills/patterns/performance/isr.md +0 -266
- package/skills/patterns/performance/lazy-loading.md +0 -281
- package/skills/patterns/realtime/sse.md +0 -327
- package/skills/patterns/realtime/websockets.md +0 -336
- package/skills/patterns/search/filtering.md +0 -329
- package/skills/patterns/search/fulltext.md +0 -260
- package/skills/patterns/security/audit-logging.md +0 -444
- package/skills/patterns/security/csrf.md +0 -234
- package/skills/patterns/security/headers.md +0 -252
- package/skills/patterns/security/sanitization.md +0 -258
- package/skills/patterns/security/secrets.md +0 -261
- package/skills/patterns/security/validation.md +0 -268
- package/skills/patterns/security/xss.md +0 -229
- package/skills/patterns/seo/metadata.md +0 -252
- package/skills/patterns/state/context.md +0 -349
- package/skills/patterns/state/react-query.md +0 -313
- package/skills/patterns/state/url-state.md +0 -482
- package/skills/patterns/state/zustand.md +0 -262
- package/skills/patterns/testing/api.md +0 -259
- package/skills/patterns/testing/component.md +0 -233
- package/skills/patterns/testing/coverage.md +0 -207
- package/skills/patterns/testing/fixtures.md +0 -225
- package/skills/patterns/testing/integration.md +0 -436
- package/skills/patterns/testing/mocking.md +0 -177
- package/skills/patterns/testing/playwright.md +0 -162
- package/skills/patterns/testing/snapshot.md +0 -175
- package/skills/patterns/testing/vitest.md +0 -307
- package/skills/patterns/ui/accordions.md +0 -395
- package/skills/patterns/ui/cards.md +0 -299
- package/skills/patterns/ui/dropdowns.md +0 -476
- package/skills/patterns/ui/empty-states.md +0 -320
- package/skills/patterns/ui/forms.md +0 -405
- package/skills/patterns/ui/inputs.md +0 -319
- package/skills/patterns/ui/layouts.md +0 -282
- package/skills/patterns/ui/loading.md +0 -291
- package/skills/patterns/ui/modals.md +0 -338
- package/skills/patterns/ui/navigation.md +0 -374
- package/skills/patterns/ui/tables.md +0 -407
- package/skills/patterns/ui/toasts.md +0 -300
- package/skills/patterns/ui/tooltips.md +0 -396
- package/skills/patterns/utils/dates.md +0 -435
- package/skills/patterns/utils/errors.md +0 -451
- package/skills/patterns/utils/formatting.md +0 -345
- package/skills/patterns/utils/validation.md +0 -434
- package/templates/bootspring.config.js +0 -83
- package/templates/business/business-model-canvas.md +0 -246
- package/templates/business/business-plan.md +0 -266
- package/templates/business/competitive-analysis.md +0 -312
- package/templates/fundraising/data-room-checklist.md +0 -300
- package/templates/fundraising/investor-research.md +0 -243
- package/templates/fundraising/pitch-deck-outline.md +0 -253
- package/templates/legal/gdpr-checklist.md +0 -339
- package/templates/legal/privacy-policy.md +0 -285
- package/templates/legal/terms-of-service.md +0 -222
- package/templates/mcp.json +0 -9
|
@@ -0,0 +1,758 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring Task Extractor
|
|
3
|
+
*
|
|
4
|
+
* Extracts implementation tasks from seed documents (SEED.md, PRD.md, ROADMAP.md, etc.)
|
|
5
|
+
* and organizes them into an ordered implementation queue.
|
|
6
|
+
*
|
|
7
|
+
* @package bootspring
|
|
8
|
+
* @module core/task-extractor
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Main extraction function - parse all seed documents
|
|
16
|
+
* @param {object} docs - Document contents { SEED: string, PRD: string, ROADMAP: string, ... }
|
|
17
|
+
* @param {object} options - Extraction options
|
|
18
|
+
* @returns {object} Extracted tasks and metadata
|
|
19
|
+
*/
|
|
20
|
+
function extractFromDocs(docs, options = {}) {
|
|
21
|
+
const result = {
|
|
22
|
+
tasks: [],
|
|
23
|
+
phases: [],
|
|
24
|
+
mvpCriteria: [],
|
|
25
|
+
metadata: {
|
|
26
|
+
extractedAt: new Date().toISOString(),
|
|
27
|
+
sources: Object.keys(docs)
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Extract from PRD (primary source for features)
|
|
32
|
+
if (docs.PRD || docs.prd) {
|
|
33
|
+
const prdContent = docs.PRD || docs.prd;
|
|
34
|
+
const prdTasks = extractFromPrd(prdContent);
|
|
35
|
+
result.tasks.push(...prdTasks);
|
|
36
|
+
result.mvpCriteria.push(...extractMvpCriteria(prdContent));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Extract from ROADMAP (phases and milestones)
|
|
40
|
+
if (docs.ROADMAP || docs.roadmap) {
|
|
41
|
+
const roadmapContent = docs.ROADMAP || docs.roadmap;
|
|
42
|
+
const { phases, phaseTasks } = extractFromRoadmap(roadmapContent);
|
|
43
|
+
result.phases = phases;
|
|
44
|
+
result.tasks.push(...phaseTasks);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Extract from TECHNICAL_SPEC (architecture tasks)
|
|
48
|
+
if (docs.TECHNICAL_SPEC || docs.technical_spec) {
|
|
49
|
+
const techContent = docs.TECHNICAL_SPEC || docs.technical_spec;
|
|
50
|
+
const techTasks = extractFromTechnicalSpec(techContent);
|
|
51
|
+
result.tasks.push(...techTasks);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Extract from SEED.md (may have additional features)
|
|
55
|
+
if (docs.SEED || docs.seed) {
|
|
56
|
+
const seedContent = docs.SEED || docs.seed;
|
|
57
|
+
const seedTasks = extractFromSeed(seedContent);
|
|
58
|
+
result.tasks.push(...seedTasks);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Deduplicate tasks
|
|
62
|
+
result.tasks = deduplicateTasks(result.tasks);
|
|
63
|
+
|
|
64
|
+
// Order by dependencies
|
|
65
|
+
result.tasks = orderByDependencies(result.tasks);
|
|
66
|
+
|
|
67
|
+
// Assign IDs if not present
|
|
68
|
+
result.tasks = result.tasks.map((task, index) => ({
|
|
69
|
+
...task,
|
|
70
|
+
id: task.id || `task-${(index + 1).toString().padStart(3, '0')}`
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Extract tasks from PRD.md
|
|
78
|
+
* @param {string} content - PRD content
|
|
79
|
+
* @returns {array} Tasks
|
|
80
|
+
*/
|
|
81
|
+
function extractFromPrd(content) {
|
|
82
|
+
const tasks = [];
|
|
83
|
+
|
|
84
|
+
// Extract from MVP Features section (#### F-XXX: Feature Name pattern)
|
|
85
|
+
const mvpSection = extractSection(content, 'MVP\\s+Features|P1\\s+.*Features|Must\\s+Ship', { maxLength: 10000 });
|
|
86
|
+
if (mvpSection) {
|
|
87
|
+
// Look for feature headers like "#### F-001: Feature Name"
|
|
88
|
+
const featureMatches = mvpSection.matchAll(/^#{1,4}\s+(F-\d+):\s*(.+)$/gm);
|
|
89
|
+
for (const match of featureMatches) {
|
|
90
|
+
const featureId = match[1];
|
|
91
|
+
const featureName = match[2].trim();
|
|
92
|
+
|
|
93
|
+
// Try to find acceptance criteria for this feature
|
|
94
|
+
const acceptanceCriteria = extractAcceptanceCriteria(mvpSection, featureId);
|
|
95
|
+
|
|
96
|
+
tasks.push({
|
|
97
|
+
title: `Implement ${featureName}`,
|
|
98
|
+
source: 'PRD.md',
|
|
99
|
+
sourceSection: `${featureId}: ${featureName}`,
|
|
100
|
+
phase: 'mvp',
|
|
101
|
+
status: 'pending',
|
|
102
|
+
acceptanceCriteria,
|
|
103
|
+
estimatedComplexity: estimateComplexity(featureName, acceptanceCriteria)
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Extract from Feature Requirements section (FR-XX patterns)
|
|
109
|
+
const frSection = extractSection(content, 'Feature\\s+Requirements', { maxLength: 10000 });
|
|
110
|
+
if (frSection) {
|
|
111
|
+
const frMatches = frSection.matchAll(/\*{0,2}(FR-\d+):?\*{0,2}\s*(.+)/g);
|
|
112
|
+
for (const match of frMatches) {
|
|
113
|
+
const frId = match[1];
|
|
114
|
+
const frName = match[2].replace(/\*+/g, '').trim();
|
|
115
|
+
|
|
116
|
+
// Skip if we already have a similar task
|
|
117
|
+
if (!tasks.some(t => t.title.toLowerCase().includes(frName.toLowerCase().slice(0, 20)))) {
|
|
118
|
+
tasks.push({
|
|
119
|
+
title: frName,
|
|
120
|
+
source: 'PRD.md',
|
|
121
|
+
sourceSection: frId,
|
|
122
|
+
phase: 'mvp',
|
|
123
|
+
status: 'pending',
|
|
124
|
+
acceptanceCriteria: [],
|
|
125
|
+
estimatedComplexity: 'medium'
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Extract from User Stories section
|
|
132
|
+
const storiesSection = extractSection(content, 'User\\s+Stories|Stories', { maxLength: 10000 });
|
|
133
|
+
if (storiesSection) {
|
|
134
|
+
// Look for "As a... I want... so that..." patterns
|
|
135
|
+
const storyMatches = storiesSection.matchAll(/As\s+a[n]?\s+([^,]+),\s*I\s+want\s+([^,]+?)(?:,\s*so\s+that|$)/gi);
|
|
136
|
+
for (const match of storyMatches) {
|
|
137
|
+
const role = match[1].trim();
|
|
138
|
+
const want = match[2].trim();
|
|
139
|
+
|
|
140
|
+
const title = `${want.charAt(0).toUpperCase() + want.slice(1)}`;
|
|
141
|
+
|
|
142
|
+
if (!tasks.some(t => t.title.toLowerCase().includes(want.toLowerCase().slice(0, 15)))) {
|
|
143
|
+
tasks.push({
|
|
144
|
+
title,
|
|
145
|
+
description: `As a ${role}, I want ${want}`,
|
|
146
|
+
source: 'PRD.md',
|
|
147
|
+
sourceSection: 'User Stories',
|
|
148
|
+
phase: 'mvp',
|
|
149
|
+
status: 'pending',
|
|
150
|
+
acceptanceCriteria: [],
|
|
151
|
+
estimatedComplexity: 'medium'
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Extract from Core User Journeys
|
|
158
|
+
const journeySection = extractSection(content, 'Core\\s+User\\s+Journeys|User\\s+Journeys', { maxLength: 5000 });
|
|
159
|
+
if (journeySection) {
|
|
160
|
+
const journeyMatches = journeySection.matchAll(/^#{1,4}\s+\d+\.\d+\s+Journey\s+\d+\s*[—\-]\s*(.+)$/gm);
|
|
161
|
+
for (const match of journeyMatches) {
|
|
162
|
+
const journeyName = match[1].trim();
|
|
163
|
+
|
|
164
|
+
if (!tasks.some(t => t.title.toLowerCase().includes(journeyName.toLowerCase().slice(0, 15)))) {
|
|
165
|
+
tasks.push({
|
|
166
|
+
title: `Implement ${journeyName} journey`,
|
|
167
|
+
source: 'PRD.md',
|
|
168
|
+
sourceSection: 'User Journeys',
|
|
169
|
+
phase: 'mvp',
|
|
170
|
+
status: 'pending',
|
|
171
|
+
acceptanceCriteria: [],
|
|
172
|
+
estimatedComplexity: 'high'
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return tasks;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Extract tasks and phases from ROADMAP.md
|
|
183
|
+
* @param {string} content - ROADMAP content
|
|
184
|
+
* @returns {object} { phases: array, phaseTasks: array }
|
|
185
|
+
*/
|
|
186
|
+
function extractFromRoadmap(content) {
|
|
187
|
+
const phases = [];
|
|
188
|
+
const phaseTasks = [];
|
|
189
|
+
const seenPhases = new Set();
|
|
190
|
+
|
|
191
|
+
// Extract phase headers: "## X. Phase N — Name"
|
|
192
|
+
const phaseMatches = content.matchAll(/^##\s+\d+\.\s+Phase\s+(\d+)\s*[—\-]+\s*(.+)$/gm);
|
|
193
|
+
|
|
194
|
+
for (const match of phaseMatches) {
|
|
195
|
+
const phaseNum = match[1];
|
|
196
|
+
const phaseName = match[2].trim().replace(/\s*\([^)]+\)\s*$/, '');
|
|
197
|
+
|
|
198
|
+
if (seenPhases.has(phaseNum)) continue;
|
|
199
|
+
seenPhases.add(phaseNum);
|
|
200
|
+
|
|
201
|
+
// Determine phase type
|
|
202
|
+
const phaseType = phaseNum === '1' ? 'foundation' :
|
|
203
|
+
phaseNum === '2' ? 'mvp' :
|
|
204
|
+
phaseNum === '3' ? 'launch' : `phase${phaseNum}`;
|
|
205
|
+
|
|
206
|
+
// Extract deliverables for this phase
|
|
207
|
+
const phaseRegex = new RegExp(
|
|
208
|
+
`##\\s+\\d+\\.\\s+Phase\\s+${phaseNum}\\s*[—\\-][\\s\\S]*?(?=##\\s+\\d+\\.\\s+Phase\\s+\\d+|$)`,
|
|
209
|
+
'i'
|
|
210
|
+
);
|
|
211
|
+
const phaseSectionMatch = content.match(phaseRegex);
|
|
212
|
+
|
|
213
|
+
const deliverables = [];
|
|
214
|
+
if (phaseSectionMatch) {
|
|
215
|
+
const phaseContent = phaseSectionMatch[0];
|
|
216
|
+
|
|
217
|
+
// Look for deliverables subsection
|
|
218
|
+
const deliverablesRegex = /###\s+[\d.]+\s*Deliverables[\s\S]*?(?=###|##|$)/i;
|
|
219
|
+
const deliverablesMatch = phaseContent.match(deliverablesRegex);
|
|
220
|
+
|
|
221
|
+
if (deliverablesMatch) {
|
|
222
|
+
const bullets = deliverablesMatch[0].match(/^[\-\*]\s+(.+)$/gm);
|
|
223
|
+
if (bullets) {
|
|
224
|
+
bullets.slice(0, 8).forEach(b => {
|
|
225
|
+
const text = b.replace(/^[\-\*]\s+/, '').trim();
|
|
226
|
+
if (text.length > 3) {
|
|
227
|
+
deliverables.push(text);
|
|
228
|
+
|
|
229
|
+
// Create task for each deliverable
|
|
230
|
+
phaseTasks.push({
|
|
231
|
+
title: text,
|
|
232
|
+
source: 'ROADMAP.md',
|
|
233
|
+
sourceSection: `Phase ${phaseNum}: ${phaseName}`,
|
|
234
|
+
phase: phaseType,
|
|
235
|
+
status: 'pending',
|
|
236
|
+
acceptanceCriteria: [],
|
|
237
|
+
estimatedComplexity: 'medium'
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// If no deliverables, look for feature headers (#### A) B) C) patterns)
|
|
245
|
+
if (deliverables.length === 0) {
|
|
246
|
+
const featureHeaders = phaseContent.match(/^####\s+[A-Z]\)\s+(.+)$/gm);
|
|
247
|
+
if (featureHeaders) {
|
|
248
|
+
featureHeaders.slice(0, 6).forEach(h => {
|
|
249
|
+
const hMatch = h.match(/####\s+[A-Z]\)\s+(.+)/);
|
|
250
|
+
if (hMatch && hMatch[1]) {
|
|
251
|
+
const title = hMatch[1].trim();
|
|
252
|
+
deliverables.push(title);
|
|
253
|
+
|
|
254
|
+
phaseTasks.push({
|
|
255
|
+
title: `Implement ${title}`,
|
|
256
|
+
source: 'ROADMAP.md',
|
|
257
|
+
sourceSection: `Phase ${phaseNum}: ${phaseName}`,
|
|
258
|
+
phase: phaseType,
|
|
259
|
+
status: 'pending',
|
|
260
|
+
acceptanceCriteria: [],
|
|
261
|
+
estimatedComplexity: 'high'
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Look for exit criteria
|
|
269
|
+
const exitCriteriaRegex = /###\s+[\d.]+\s*Exit\s+Criteria[\s\S]*?(?=###|##|$)/i;
|
|
270
|
+
const exitMatch = phaseContent.match(exitCriteriaRegex);
|
|
271
|
+
if (exitMatch) {
|
|
272
|
+
const checkItems = exitMatch[0].match(/^[\-\*]\s+[✅✓⬜]\s*(.+)$/gm);
|
|
273
|
+
if (checkItems) {
|
|
274
|
+
checkItems.forEach(item => {
|
|
275
|
+
const text = item.replace(/^[\-\*]\s+[✅✓⬜]\s*/, '').trim();
|
|
276
|
+
if (text.length > 5 && !deliverables.includes(text)) {
|
|
277
|
+
deliverables.push(text);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
phases.push({
|
|
285
|
+
phase: phaseNum,
|
|
286
|
+
name: phaseName,
|
|
287
|
+
type: phaseType,
|
|
288
|
+
deliverables
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return { phases, phaseTasks };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Extract tasks from TECHNICAL_SPEC.md
|
|
297
|
+
* @param {string} content - Technical spec content
|
|
298
|
+
* @returns {array} Tasks
|
|
299
|
+
*/
|
|
300
|
+
function extractFromTechnicalSpec(content) {
|
|
301
|
+
const tasks = [];
|
|
302
|
+
|
|
303
|
+
// Extract database setup tasks
|
|
304
|
+
const dbSection = extractSection(content, 'Data\\s+Model|Database|Key\\s+tables', { maxLength: 3000 });
|
|
305
|
+
if (dbSection) {
|
|
306
|
+
// Look for table names
|
|
307
|
+
const tableMatches = dbSection.match(/^\s*[\-\*]\s*`(\w+)`/gm);
|
|
308
|
+
if (tableMatches && tableMatches.length > 0) {
|
|
309
|
+
tasks.push({
|
|
310
|
+
title: 'Set up database schema with Prisma',
|
|
311
|
+
description: `Create database models for: ${tableMatches.slice(0, 5).map(m => m.match(/`(\w+)`/)?.[1]).filter(Boolean).join(', ')}`,
|
|
312
|
+
source: 'TECHNICAL_SPEC.md',
|
|
313
|
+
sourceSection: 'Database Schema',
|
|
314
|
+
phase: 'foundation',
|
|
315
|
+
status: 'pending',
|
|
316
|
+
acceptanceCriteria: [
|
|
317
|
+
'Prisma schema defined with all models',
|
|
318
|
+
'Database migrations generated',
|
|
319
|
+
'Seed data scripts created'
|
|
320
|
+
],
|
|
321
|
+
estimatedComplexity: 'high'
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Extract API route tasks
|
|
327
|
+
const apiSection = extractSection(content, 'API\\s+Routes|Application\\s+Structure', { maxLength: 3000 });
|
|
328
|
+
if (apiSection) {
|
|
329
|
+
// Group related API routes
|
|
330
|
+
const apiGroups = new Map();
|
|
331
|
+
const apiMatches = apiSection.matchAll(/`\/api\/([^`\/]+)/g);
|
|
332
|
+
|
|
333
|
+
for (const match of apiMatches) {
|
|
334
|
+
const group = match[1];
|
|
335
|
+
if (!apiGroups.has(group)) {
|
|
336
|
+
apiGroups.set(group, []);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (apiGroups.size > 0) {
|
|
341
|
+
tasks.push({
|
|
342
|
+
title: 'Implement core API endpoints',
|
|
343
|
+
description: `Set up API routes for: ${[...apiGroups.keys()].slice(0, 5).join(', ')}`,
|
|
344
|
+
source: 'TECHNICAL_SPEC.md',
|
|
345
|
+
sourceSection: 'API Routes',
|
|
346
|
+
phase: 'mvp',
|
|
347
|
+
status: 'pending',
|
|
348
|
+
acceptanceCriteria: [
|
|
349
|
+
'All CRUD endpoints implemented',
|
|
350
|
+
'Input validation with Zod',
|
|
351
|
+
'Error handling middleware',
|
|
352
|
+
'API documentation updated'
|
|
353
|
+
],
|
|
354
|
+
estimatedComplexity: 'high'
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Extract architecture setup tasks
|
|
360
|
+
const archSection = extractSection(content, 'Core\\s+system\\s+components|Architecture', { maxLength: 2000 });
|
|
361
|
+
if (archSection) {
|
|
362
|
+
const compMatches = archSection.match(/^\s*\d+\)\s*(.+)$/gm);
|
|
363
|
+
if (compMatches && compMatches.length > 0) {
|
|
364
|
+
tasks.push({
|
|
365
|
+
title: 'Set up project architecture and scaffolding',
|
|
366
|
+
description: 'Configure project structure, linting, testing, and CI/CD',
|
|
367
|
+
source: 'TECHNICAL_SPEC.md',
|
|
368
|
+
sourceSection: 'Architecture',
|
|
369
|
+
phase: 'foundation',
|
|
370
|
+
status: 'pending',
|
|
371
|
+
acceptanceCriteria: [
|
|
372
|
+
'Project structure follows conventions',
|
|
373
|
+
'ESLint and Prettier configured',
|
|
374
|
+
'Testing framework set up',
|
|
375
|
+
'CI/CD pipeline configured'
|
|
376
|
+
],
|
|
377
|
+
estimatedComplexity: 'medium',
|
|
378
|
+
dependencies: []
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Extract third-party integration tasks
|
|
384
|
+
const integrations = extractIntegrations(content);
|
|
385
|
+
const intCategories = Object.entries(integrations).filter(([_, v]) => v.length > 0);
|
|
386
|
+
|
|
387
|
+
if (intCategories.length > 0) {
|
|
388
|
+
for (const [category, services] of intCategories) {
|
|
389
|
+
if (services.length > 0) {
|
|
390
|
+
tasks.push({
|
|
391
|
+
title: `Integrate ${category} services (${services.join(', ')})`,
|
|
392
|
+
source: 'TECHNICAL_SPEC.md',
|
|
393
|
+
sourceSection: 'Integrations',
|
|
394
|
+
phase: 'mvp',
|
|
395
|
+
status: 'pending',
|
|
396
|
+
acceptanceCriteria: services.map(s => `${s} integration working`),
|
|
397
|
+
estimatedComplexity: services.length > 2 ? 'high' : 'medium'
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return tasks;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Extract tasks from SEED.md
|
|
408
|
+
* @param {string} content - SEED content
|
|
409
|
+
* @returns {array} Tasks
|
|
410
|
+
*/
|
|
411
|
+
function extractFromSeed(content) {
|
|
412
|
+
const tasks = [];
|
|
413
|
+
|
|
414
|
+
// Extract MVP Features section
|
|
415
|
+
const mvpSection = extractSection(content, 'MVP\\s+Features', { maxLength: 5000 });
|
|
416
|
+
if (mvpSection) {
|
|
417
|
+
// Look for numbered list items
|
|
418
|
+
const featureMatches = mvpSection.matchAll(/^\d+\.\s+(.+)$/gm);
|
|
419
|
+
for (const match of featureMatches) {
|
|
420
|
+
const featureName = match[1].trim();
|
|
421
|
+
|
|
422
|
+
tasks.push({
|
|
423
|
+
title: `Implement ${featureName}`,
|
|
424
|
+
source: 'SEED.md',
|
|
425
|
+
sourceSection: 'MVP Features',
|
|
426
|
+
phase: 'mvp',
|
|
427
|
+
status: 'pending',
|
|
428
|
+
acceptanceCriteria: [],
|
|
429
|
+
estimatedComplexity: 'medium'
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return tasks;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Extract MVP completion criteria
|
|
439
|
+
* @param {string} content - Document content
|
|
440
|
+
* @returns {array} MVP criteria
|
|
441
|
+
*/
|
|
442
|
+
function extractMvpCriteria(content) {
|
|
443
|
+
const criteria = [];
|
|
444
|
+
|
|
445
|
+
// Look for MVP Success Criteria or similar sections
|
|
446
|
+
const successSection = extractSection(content, 'MVP\\s+Success|Success\\s+Criteria|Definition\\s+of\\s+Done', { maxLength: 3000 });
|
|
447
|
+
|
|
448
|
+
if (successSection) {
|
|
449
|
+
const bulletMatches = successSection.match(/^[\-\*]\s+(.+)$/gm);
|
|
450
|
+
if (bulletMatches) {
|
|
451
|
+
bulletMatches.forEach(b => {
|
|
452
|
+
const text = b.replace(/^[\-\*]\s+/, '').replace(/\*+/g, '').trim();
|
|
453
|
+
if (text.length > 5) {
|
|
454
|
+
criteria.push({
|
|
455
|
+
name: text,
|
|
456
|
+
status: 'pending'
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Also extract from MVP Features as criteria
|
|
464
|
+
const mvpSection = extractSection(content, 'MVP\\s+Features|P1\\s+.*Features', { maxLength: 5000 });
|
|
465
|
+
if (mvpSection) {
|
|
466
|
+
const featureHeaders = mvpSection.match(/^#{1,4}\s+F-\d+:\s*(.+)$/gm);
|
|
467
|
+
if (featureHeaders) {
|
|
468
|
+
featureHeaders.forEach(h => {
|
|
469
|
+
const match = h.match(/F-\d+:\s*(.+)/);
|
|
470
|
+
if (match && match[1]) {
|
|
471
|
+
criteria.push({
|
|
472
|
+
name: match[1].trim(),
|
|
473
|
+
status: 'pending'
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return criteria;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Order tasks by dependencies (topological sort)
|
|
485
|
+
* @param {array} tasks - Tasks to order
|
|
486
|
+
* @returns {array} Ordered tasks
|
|
487
|
+
*/
|
|
488
|
+
function orderByDependencies(tasks) {
|
|
489
|
+
// Build dependency graph
|
|
490
|
+
const taskMap = new Map(tasks.map((t, i) => [t.id || `task-${i}`, t]));
|
|
491
|
+
const visited = new Set();
|
|
492
|
+
const result = [];
|
|
493
|
+
|
|
494
|
+
// Add foundation tasks first
|
|
495
|
+
const foundationTasks = tasks.filter(t => t.phase === 'foundation');
|
|
496
|
+
const mvpTasks = tasks.filter(t => t.phase === 'mvp');
|
|
497
|
+
const launchTasks = tasks.filter(t => t.phase === 'launch');
|
|
498
|
+
const otherTasks = tasks.filter(t => !['foundation', 'mvp', 'launch'].includes(t.phase));
|
|
499
|
+
|
|
500
|
+
// Order by priority within phases
|
|
501
|
+
const priorityOrder = [
|
|
502
|
+
// Foundation tasks first
|
|
503
|
+
...foundationTasks.filter(t => t.title.toLowerCase().includes('setup') || t.title.toLowerCase().includes('scaffold')),
|
|
504
|
+
...foundationTasks.filter(t => t.title.toLowerCase().includes('database') || t.title.toLowerCase().includes('schema')),
|
|
505
|
+
...foundationTasks.filter(t => !t.title.toLowerCase().includes('setup') && !t.title.toLowerCase().includes('database')),
|
|
506
|
+
|
|
507
|
+
// MVP tasks
|
|
508
|
+
...mvpTasks.filter(t => t.title.toLowerCase().includes('auth')),
|
|
509
|
+
...mvpTasks.filter(t => t.title.toLowerCase().includes('api')),
|
|
510
|
+
...mvpTasks.filter(t => !t.title.toLowerCase().includes('auth') && !t.title.toLowerCase().includes('api')),
|
|
511
|
+
|
|
512
|
+
// Launch tasks
|
|
513
|
+
...launchTasks,
|
|
514
|
+
|
|
515
|
+
// Other tasks
|
|
516
|
+
...otherTasks
|
|
517
|
+
];
|
|
518
|
+
|
|
519
|
+
return priorityOrder;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Deduplicate tasks based on title similarity
|
|
524
|
+
* @param {array} tasks - Tasks to deduplicate
|
|
525
|
+
* @returns {array} Deduplicated tasks
|
|
526
|
+
*/
|
|
527
|
+
function deduplicateTasks(tasks) {
|
|
528
|
+
const seen = new Map();
|
|
529
|
+
|
|
530
|
+
return tasks.filter(task => {
|
|
531
|
+
// Normalize title for comparison
|
|
532
|
+
const normalizedTitle = task.title
|
|
533
|
+
.toLowerCase()
|
|
534
|
+
.replace(/^implement\s+/i, '')
|
|
535
|
+
.replace(/\s+/g, ' ')
|
|
536
|
+
.trim();
|
|
537
|
+
|
|
538
|
+
// Check first 30 chars for similarity
|
|
539
|
+
const key = normalizedTitle.slice(0, 30);
|
|
540
|
+
|
|
541
|
+
if (seen.has(key)) {
|
|
542
|
+
// Merge acceptance criteria
|
|
543
|
+
const existing = seen.get(key);
|
|
544
|
+
if (task.acceptanceCriteria?.length > existing.acceptanceCriteria?.length) {
|
|
545
|
+
existing.acceptanceCriteria = task.acceptanceCriteria;
|
|
546
|
+
}
|
|
547
|
+
return false;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
seen.set(key, task);
|
|
551
|
+
return true;
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Extract acceptance criteria for a feature
|
|
557
|
+
* @param {string} content - Section content
|
|
558
|
+
* @param {string} featureId - Feature ID
|
|
559
|
+
* @returns {array} Acceptance criteria
|
|
560
|
+
*/
|
|
561
|
+
function extractAcceptanceCriteria(content, featureId) {
|
|
562
|
+
const criteria = [];
|
|
563
|
+
|
|
564
|
+
// Look for acceptance criteria section after feature header
|
|
565
|
+
const featureRegex = new RegExp(
|
|
566
|
+
`#{1,4}\\s+${featureId}:[\\s\\S]*?(?=#{1,4}\\s+F-\\d+:|$)`,
|
|
567
|
+
'i'
|
|
568
|
+
);
|
|
569
|
+
const featureMatch = content.match(featureRegex);
|
|
570
|
+
|
|
571
|
+
if (featureMatch) {
|
|
572
|
+
const featureContent = featureMatch[0];
|
|
573
|
+
|
|
574
|
+
// Look for bullet points
|
|
575
|
+
const bulletMatches = featureContent.match(/^[\-\*]\s+(.+)$/gm);
|
|
576
|
+
if (bulletMatches) {
|
|
577
|
+
bulletMatches.slice(0, 5).forEach(b => {
|
|
578
|
+
const text = b.replace(/^[\-\*]\s+/, '').replace(/\*+/g, '').trim();
|
|
579
|
+
if (text.length > 5 && text.length < 200) {
|
|
580
|
+
criteria.push(text);
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
return criteria;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Estimate task complexity
|
|
591
|
+
* @param {string} title - Task title
|
|
592
|
+
* @param {array} acceptanceCriteria - Acceptance criteria
|
|
593
|
+
* @returns {string} Complexity: low, medium, high
|
|
594
|
+
*/
|
|
595
|
+
function estimateComplexity(title, acceptanceCriteria = []) {
|
|
596
|
+
const titleLower = title.toLowerCase();
|
|
597
|
+
|
|
598
|
+
// High complexity indicators
|
|
599
|
+
const highIndicators = [
|
|
600
|
+
'authentication', 'payment', 'integration', 'real-time',
|
|
601
|
+
'voice', 'agent', 'workflow', 'pipeline', 'migration',
|
|
602
|
+
'security', 'encryption', 'multi-tenant'
|
|
603
|
+
];
|
|
604
|
+
|
|
605
|
+
// Low complexity indicators
|
|
606
|
+
const lowIndicators = [
|
|
607
|
+
'ui', 'style', 'button', 'form', 'display', 'show',
|
|
608
|
+
'list', 'view', 'page', 'component', 'typo', 'fix'
|
|
609
|
+
];
|
|
610
|
+
|
|
611
|
+
if (highIndicators.some(i => titleLower.includes(i))) {
|
|
612
|
+
return 'high';
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (lowIndicators.some(i => titleLower.includes(i))) {
|
|
616
|
+
return 'low';
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Also consider acceptance criteria count
|
|
620
|
+
if (acceptanceCriteria.length > 5) {
|
|
621
|
+
return 'high';
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
return 'medium';
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Extract a section from markdown content
|
|
629
|
+
* @param {string} content - Markdown content
|
|
630
|
+
* @param {string} headingPattern - Heading pattern to match
|
|
631
|
+
* @param {object} options - Options
|
|
632
|
+
* @returns {string|null} Section content or null
|
|
633
|
+
*/
|
|
634
|
+
function extractSection(content, headingPattern, options = {}) {
|
|
635
|
+
const { maxLength = 2000 } = options;
|
|
636
|
+
|
|
637
|
+
const headingRegex = new RegExp(`^(#{1,4})\\s*(?:\\d+\\.\\s*)?(?:${headingPattern})[^\\n]*\\n`, 'im');
|
|
638
|
+
const match = content.match(headingRegex);
|
|
639
|
+
if (!match || !match[1]) return null;
|
|
640
|
+
|
|
641
|
+
const startIndex = match.index + match[0].length;
|
|
642
|
+
const headingLevel = match[1].length;
|
|
643
|
+
|
|
644
|
+
const restContent = content.slice(startIndex);
|
|
645
|
+
const nextHeadingRegex = new RegExp(`^#{1,${headingLevel}}\\s+`, 'm');
|
|
646
|
+
const nextMatch = restContent.match(nextHeadingRegex);
|
|
647
|
+
|
|
648
|
+
let sectionContent = nextMatch
|
|
649
|
+
? restContent.slice(0, nextMatch.index)
|
|
650
|
+
: restContent;
|
|
651
|
+
|
|
652
|
+
sectionContent = sectionContent.trim();
|
|
653
|
+
if (sectionContent.length > maxLength) {
|
|
654
|
+
sectionContent = sectionContent.slice(0, maxLength);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
return sectionContent || null;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Extract third-party integrations from content
|
|
662
|
+
* @param {string} content - Document content
|
|
663
|
+
* @returns {object} Integrations by category
|
|
664
|
+
*/
|
|
665
|
+
function extractIntegrations(content) {
|
|
666
|
+
const integrations = {
|
|
667
|
+
auth: [],
|
|
668
|
+
payments: [],
|
|
669
|
+
email: [],
|
|
670
|
+
ai: []
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
// Auth
|
|
674
|
+
if (content.match(/\bClerk\b/i)) integrations.auth.push('Clerk');
|
|
675
|
+
if (content.match(/\bNextAuth|Auth\.js/i)) integrations.auth.push('NextAuth');
|
|
676
|
+
|
|
677
|
+
// Payments
|
|
678
|
+
if (content.match(/\bStripe\b/i)) integrations.payments.push('Stripe');
|
|
679
|
+
if (content.match(/\bPaddle\b/i)) integrations.payments.push('Paddle');
|
|
680
|
+
|
|
681
|
+
// Email
|
|
682
|
+
if (content.match(/\bSendGrid\b/i)) integrations.email.push('SendGrid');
|
|
683
|
+
if (content.match(/\bResend\b/i)) integrations.email.push('Resend');
|
|
684
|
+
|
|
685
|
+
// AI
|
|
686
|
+
if (content.match(/\bOpenAI\b|GPT-4/i)) integrations.ai.push('OpenAI');
|
|
687
|
+
if (content.match(/\bAnthropic\b|Claude/i)) integrations.ai.push('Anthropic');
|
|
688
|
+
|
|
689
|
+
return integrations;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Load and extract from preseed directory
|
|
694
|
+
* @param {string} projectRoot - Project root path
|
|
695
|
+
* @returns {object} Extracted tasks and metadata
|
|
696
|
+
*/
|
|
697
|
+
function extractFromPreseedDir(projectRoot) {
|
|
698
|
+
const preseedDir = path.join(projectRoot, '.bootspring', 'preseed');
|
|
699
|
+
|
|
700
|
+
if (!fs.existsSync(preseedDir)) {
|
|
701
|
+
return { tasks: [], phases: [], mvpCriteria: [], error: 'No preseed directory found' };
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const validDocs = [
|
|
705
|
+
'VISION.md', 'AUDIENCE.md', 'MARKET.md', 'COMPETITORS.md',
|
|
706
|
+
'BUSINESS_MODEL.md', 'PRD.md', 'TECHNICAL_SPEC.md', 'ROADMAP.md'
|
|
707
|
+
];
|
|
708
|
+
|
|
709
|
+
const docs = {};
|
|
710
|
+
|
|
711
|
+
for (const file of validDocs) {
|
|
712
|
+
const filePath = path.join(preseedDir, file);
|
|
713
|
+
if (fs.existsSync(filePath)) {
|
|
714
|
+
const docName = file.replace('.md', '');
|
|
715
|
+
docs[docName] = fs.readFileSync(filePath, 'utf-8');
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
if (Object.keys(docs).length === 0) {
|
|
720
|
+
return { tasks: [], phases: [], mvpCriteria: [], error: 'No preseed documents found' };
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
return extractFromDocs(docs);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Load and extract from SEED.md
|
|
728
|
+
* @param {string} projectRoot - Project root path
|
|
729
|
+
* @returns {object} Extracted tasks and metadata
|
|
730
|
+
*/
|
|
731
|
+
function extractFromSeedFile(projectRoot) {
|
|
732
|
+
const seedPath = path.join(projectRoot, 'SEED.md');
|
|
733
|
+
|
|
734
|
+
if (!fs.existsSync(seedPath)) {
|
|
735
|
+
return { tasks: [], phases: [], mvpCriteria: [], error: 'No SEED.md found' };
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
const seedContent = fs.readFileSync(seedPath, 'utf-8');
|
|
739
|
+
|
|
740
|
+
return extractFromDocs({ SEED: seedContent });
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
module.exports = {
|
|
744
|
+
extractFromDocs,
|
|
745
|
+
extractFromPrd,
|
|
746
|
+
extractFromRoadmap,
|
|
747
|
+
extractFromTechnicalSpec,
|
|
748
|
+
extractFromSeed,
|
|
749
|
+
extractMvpCriteria,
|
|
750
|
+
orderByDependencies,
|
|
751
|
+
deduplicateTasks,
|
|
752
|
+
extractAcceptanceCriteria,
|
|
753
|
+
estimateComplexity,
|
|
754
|
+
extractSection,
|
|
755
|
+
extractIntegrations,
|
|
756
|
+
extractFromPreseedDir,
|
|
757
|
+
extractFromSeedFile
|
|
758
|
+
};
|