@girardmedia/bootspring 3.3.0 → 3.3.1
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/dist/cli/index.js +754 -1215
- package/dist/core/index.d.ts +1 -1
- package/dist/core.js +3 -3
- package/dist/mcp-server.js +2 -2
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -3386,7 +3386,7 @@ var init_release = __esm({
|
|
|
3386
3386
|
"../../packages/shared/src/release.ts"() {
|
|
3387
3387
|
"use strict";
|
|
3388
3388
|
init_cjs_shims();
|
|
3389
|
-
BOOTSPRING_VERSION = "3.
|
|
3389
|
+
BOOTSPRING_VERSION = "3.3.1";
|
|
3390
3390
|
BOOTSPRING_PACKAGE_NAME = "@girardmedia/bootspring";
|
|
3391
3391
|
}
|
|
3392
3392
|
});
|
|
@@ -52001,1123 +52001,6 @@ var init_git_insight = __esm({
|
|
|
52001
52001
|
}
|
|
52002
52002
|
});
|
|
52003
52003
|
|
|
52004
|
-
// ../../packages/core/compat-src/task-extractor.ts
|
|
52005
|
-
var task_extractor_exports = {};
|
|
52006
|
-
__export(task_extractor_exports, {
|
|
52007
|
-
deduplicateTasks: () => deduplicateTasks,
|
|
52008
|
-
estimateComplexity: () => estimateComplexity,
|
|
52009
|
-
extractAcceptanceCriteria: () => extractAcceptanceCriteria,
|
|
52010
|
-
extractFromDocs: () => extractFromDocs,
|
|
52011
|
-
extractFromPlanningFiles: () => extractFromPlanningFiles,
|
|
52012
|
-
extractFromPrd: () => extractFromPrd,
|
|
52013
|
-
extractFromPreseedDir: () => extractFromPreseedDir,
|
|
52014
|
-
extractFromRoadmap: () => extractFromRoadmap,
|
|
52015
|
-
extractFromSeed: () => extractFromSeed,
|
|
52016
|
-
extractFromSeedFile: () => extractFromSeedFile,
|
|
52017
|
-
extractFromTaskQueue: () => extractFromTaskQueue,
|
|
52018
|
-
extractFromTaskQueueFile: () => extractFromTaskQueueFile,
|
|
52019
|
-
extractFromTechnicalSpec: () => extractFromTechnicalSpec,
|
|
52020
|
-
extractFromTodo: () => extractFromTodo,
|
|
52021
|
-
extractFromTodoFile: () => extractFromTodoFile,
|
|
52022
|
-
extractIntegrations: () => extractIntegrations,
|
|
52023
|
-
extractMvpCriteria: () => extractMvpCriteria,
|
|
52024
|
-
extractSection: () => extractSection,
|
|
52025
|
-
orderByDependencies: () => orderByDependencies,
|
|
52026
|
-
syncFromPlanningFiles: () => syncFromPlanningFiles,
|
|
52027
|
-
syncFromTaskQueue: () => syncFromTaskQueue,
|
|
52028
|
-
syncFromTodo: () => syncFromTodo
|
|
52029
|
-
});
|
|
52030
|
-
function normalizeTaskStatus(value) {
|
|
52031
|
-
const normalized = String(value || "pending").trim().toLowerCase().replace(/[\s-]+/g, "_");
|
|
52032
|
-
if (normalized === "done" || normalized === "complete" || normalized === "completed") {
|
|
52033
|
-
return "completed";
|
|
52034
|
-
}
|
|
52035
|
-
if (normalized === "inprogress" || normalized === "in_progress") {
|
|
52036
|
-
return "in_progress";
|
|
52037
|
-
}
|
|
52038
|
-
if (normalized === "blocked" || normalized === "skipped") {
|
|
52039
|
-
return normalized;
|
|
52040
|
-
}
|
|
52041
|
-
return normalized === "pending" ? "pending" : "pending";
|
|
52042
|
-
}
|
|
52043
|
-
function normalizeTaskPhase(value) {
|
|
52044
|
-
const normalized = String(value || "mvp").trim().toLowerCase().replace(/[\s-]+/g, "_");
|
|
52045
|
-
return ["foundation", "mvp", "launch"].includes(normalized) ? normalized : "mvp";
|
|
52046
|
-
}
|
|
52047
|
-
function normalizeComplexity(value) {
|
|
52048
|
-
const normalized = String(value || "medium").trim().toLowerCase();
|
|
52049
|
-
if (normalized === "low" || normalized === "high" || normalized === "medium") {
|
|
52050
|
-
return normalized;
|
|
52051
|
-
}
|
|
52052
|
-
return "medium";
|
|
52053
|
-
}
|
|
52054
|
-
function extractFromDocs(docs, _options = {}) {
|
|
52055
|
-
const result = {
|
|
52056
|
-
tasks: [],
|
|
52057
|
-
phases: [],
|
|
52058
|
-
mvpCriteria: [],
|
|
52059
|
-
metadata: {
|
|
52060
|
-
extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
52061
|
-
sources: Object.keys(docs)
|
|
52062
|
-
}
|
|
52063
|
-
};
|
|
52064
|
-
if (docs.PRD || docs.prd) {
|
|
52065
|
-
const prdContent = docs.PRD || docs.prd;
|
|
52066
|
-
if (prdContent) {
|
|
52067
|
-
const prdTasks = extractFromPrd(prdContent);
|
|
52068
|
-
result.tasks.push(...prdTasks);
|
|
52069
|
-
result.mvpCriteria.push(...extractMvpCriteria(prdContent));
|
|
52070
|
-
}
|
|
52071
|
-
}
|
|
52072
|
-
if (docs.ROADMAP || docs.roadmap) {
|
|
52073
|
-
const roadmapContent = docs.ROADMAP || docs.roadmap;
|
|
52074
|
-
if (roadmapContent) {
|
|
52075
|
-
const { phases, phaseTasks } = extractFromRoadmap(roadmapContent);
|
|
52076
|
-
result.phases = phases;
|
|
52077
|
-
result.tasks.push(...phaseTasks);
|
|
52078
|
-
}
|
|
52079
|
-
}
|
|
52080
|
-
if (docs.TECHNICAL_SPEC || docs.technical_spec) {
|
|
52081
|
-
const techContent = docs.TECHNICAL_SPEC || docs.technical_spec;
|
|
52082
|
-
if (techContent) {
|
|
52083
|
-
const techTasks = extractFromTechnicalSpec(techContent);
|
|
52084
|
-
result.tasks.push(...techTasks);
|
|
52085
|
-
}
|
|
52086
|
-
}
|
|
52087
|
-
if (docs.SEED || docs.seed) {
|
|
52088
|
-
const seedContent = docs.SEED || docs.seed;
|
|
52089
|
-
if (seedContent) {
|
|
52090
|
-
const seedTasks = extractFromSeed(seedContent);
|
|
52091
|
-
result.tasks.push(...seedTasks);
|
|
52092
|
-
}
|
|
52093
|
-
}
|
|
52094
|
-
result.tasks = deduplicateTasks(result.tasks);
|
|
52095
|
-
result.tasks = orderByDependencies(result.tasks);
|
|
52096
|
-
result.tasks = result.tasks.map((task, index) => ({
|
|
52097
|
-
...task,
|
|
52098
|
-
id: task.id || `task-${(index + 1).toString().padStart(3, "0")}`
|
|
52099
|
-
}));
|
|
52100
|
-
return result;
|
|
52101
|
-
}
|
|
52102
|
-
function extractFromPrd(content) {
|
|
52103
|
-
const tasks = [];
|
|
52104
|
-
const mvpSection = extractSection(content, "MVP\\s+Features|P1\\s+.*Features|Must\\s+Ship", { maxLength: 1e4 });
|
|
52105
|
-
if (mvpSection) {
|
|
52106
|
-
const featureMatches = mvpSection.matchAll(/^#{1,4}\s+(F-\d+):\s*(.+)$/gm);
|
|
52107
|
-
for (const match of featureMatches) {
|
|
52108
|
-
const featureId = match[1];
|
|
52109
|
-
const featureName = match[2]?.trim() ?? "";
|
|
52110
|
-
const acceptanceCriteria = extractAcceptanceCriteria(mvpSection, featureId ?? "");
|
|
52111
|
-
tasks.push({
|
|
52112
|
-
title: `Implement ${featureName}`,
|
|
52113
|
-
source: "PRD.md",
|
|
52114
|
-
sourceSection: `${featureId}: ${featureName}`,
|
|
52115
|
-
phase: "mvp",
|
|
52116
|
-
status: "pending",
|
|
52117
|
-
acceptanceCriteria,
|
|
52118
|
-
estimatedComplexity: estimateComplexity(featureName, acceptanceCriteria)
|
|
52119
|
-
});
|
|
52120
|
-
}
|
|
52121
|
-
}
|
|
52122
|
-
const frSection = extractSection(content, "Feature\\s+Requirements", { maxLength: 1e4 });
|
|
52123
|
-
if (frSection) {
|
|
52124
|
-
const frMatches = frSection.matchAll(/\*{0,2}(FR-\d+):?\*{0,2}\s*(.+)/g);
|
|
52125
|
-
for (const match of frMatches) {
|
|
52126
|
-
const frId = match[1];
|
|
52127
|
-
const frName = match[2]?.replace(/\*+/g, "").trim() ?? "";
|
|
52128
|
-
if (!tasks.some((t) => t.title.toLowerCase().includes(frName.toLowerCase().slice(0, 20)))) {
|
|
52129
|
-
tasks.push({
|
|
52130
|
-
title: frName,
|
|
52131
|
-
source: "PRD.md",
|
|
52132
|
-
sourceSection: frId ?? "",
|
|
52133
|
-
phase: "mvp",
|
|
52134
|
-
status: "pending",
|
|
52135
|
-
acceptanceCriteria: [],
|
|
52136
|
-
estimatedComplexity: "medium"
|
|
52137
|
-
});
|
|
52138
|
-
}
|
|
52139
|
-
}
|
|
52140
|
-
}
|
|
52141
|
-
const storiesSection = extractSection(content, "User\\s+Stories|Stories", { maxLength: 1e4 });
|
|
52142
|
-
if (storiesSection) {
|
|
52143
|
-
const storyMatches = storiesSection.matchAll(/As\s+a[n]?\s+([^,]+),\s*I\s+want\s+([^,]+?)(?:,\s*so\s+that|$)/gi);
|
|
52144
|
-
for (const match of storyMatches) {
|
|
52145
|
-
const role = match[1]?.trim() ?? "";
|
|
52146
|
-
const want = match[2]?.trim() ?? "";
|
|
52147
|
-
const title = `${want.charAt(0).toUpperCase() + want.slice(1)}`;
|
|
52148
|
-
if (!tasks.some((t) => t.title.toLowerCase().includes(want.toLowerCase().slice(0, 15)))) {
|
|
52149
|
-
tasks.push({
|
|
52150
|
-
title,
|
|
52151
|
-
description: `As a ${role}, I want ${want}`,
|
|
52152
|
-
source: "PRD.md",
|
|
52153
|
-
sourceSection: "User Stories",
|
|
52154
|
-
phase: "mvp",
|
|
52155
|
-
status: "pending",
|
|
52156
|
-
acceptanceCriteria: [],
|
|
52157
|
-
estimatedComplexity: "medium"
|
|
52158
|
-
});
|
|
52159
|
-
}
|
|
52160
|
-
}
|
|
52161
|
-
}
|
|
52162
|
-
const journeySection = extractSection(content, "Core\\s+User\\s+Journeys|User\\s+Journeys", { maxLength: 5e3 });
|
|
52163
|
-
if (journeySection) {
|
|
52164
|
-
const journeyMatches = journeySection.matchAll(/^#{1,4}\s+\d+\.\d+\s+Journey\s+\d+\s*[—-]\s*(.+)$/gm);
|
|
52165
|
-
for (const match of journeyMatches) {
|
|
52166
|
-
const journeyName = match[1]?.trim() ?? "";
|
|
52167
|
-
if (!tasks.some((t) => t.title.toLowerCase().includes(journeyName.toLowerCase().slice(0, 15)))) {
|
|
52168
|
-
tasks.push({
|
|
52169
|
-
title: `Implement ${journeyName} journey`,
|
|
52170
|
-
source: "PRD.md",
|
|
52171
|
-
sourceSection: "User Journeys",
|
|
52172
|
-
phase: "mvp",
|
|
52173
|
-
status: "pending",
|
|
52174
|
-
acceptanceCriteria: [],
|
|
52175
|
-
estimatedComplexity: "high"
|
|
52176
|
-
});
|
|
52177
|
-
}
|
|
52178
|
-
}
|
|
52179
|
-
}
|
|
52180
|
-
return tasks;
|
|
52181
|
-
}
|
|
52182
|
-
function extractFromRoadmap(content) {
|
|
52183
|
-
const phases = [];
|
|
52184
|
-
const phaseTasks = [];
|
|
52185
|
-
const seenPhases = /* @__PURE__ */ new Set();
|
|
52186
|
-
const phaseMatches = content.matchAll(/^##\s+\d+\.\s+Phase\s+(\d+)\s*[—-]+\s*(.+)$/gm);
|
|
52187
|
-
for (const match of phaseMatches) {
|
|
52188
|
-
const phaseNum = match[1] ?? "";
|
|
52189
|
-
const phaseName = match[2]?.trim().replace(/\s*\([^)]+\)\s*$/, "") ?? "";
|
|
52190
|
-
if (seenPhases.has(phaseNum)) continue;
|
|
52191
|
-
seenPhases.add(phaseNum);
|
|
52192
|
-
const phaseType = phaseNum === "1" ? "foundation" : phaseNum === "2" ? "mvp" : phaseNum === "3" ? "launch" : `phase${phaseNum}`;
|
|
52193
|
-
const phaseRegex = new RegExp(
|
|
52194
|
-
`##\\s+\\d+\\.\\s+Phase\\s+${phaseNum}\\s*[\u2014-][\\s\\S]*?(?=##\\s+\\d+\\.\\s+Phase\\s+\\d+|$)`,
|
|
52195
|
-
"i"
|
|
52196
|
-
);
|
|
52197
|
-
const phaseSectionMatch = content.match(phaseRegex);
|
|
52198
|
-
const deliverables = [];
|
|
52199
|
-
if (phaseSectionMatch) {
|
|
52200
|
-
const phaseContent = phaseSectionMatch[0] ?? "";
|
|
52201
|
-
const deliverablesRegex = /###\s+[\d.]+\s*Deliverables[\s\S]*?(?=###|##|$)/i;
|
|
52202
|
-
const deliverablesMatch = phaseContent.match(deliverablesRegex);
|
|
52203
|
-
if (deliverablesMatch) {
|
|
52204
|
-
const bullets = deliverablesMatch[0]?.match(/^[-*]\s+(.+)$/gm);
|
|
52205
|
-
if (bullets) {
|
|
52206
|
-
bullets.slice(0, 8).forEach((b) => {
|
|
52207
|
-
const text = b.replace(/^[-*]\s+/, "").trim();
|
|
52208
|
-
if (text.length > 3) {
|
|
52209
|
-
deliverables.push(text);
|
|
52210
|
-
phaseTasks.push({
|
|
52211
|
-
title: text,
|
|
52212
|
-
source: "ROADMAP.md",
|
|
52213
|
-
sourceSection: `Phase ${phaseNum}: ${phaseName}`,
|
|
52214
|
-
phase: phaseType,
|
|
52215
|
-
status: "pending",
|
|
52216
|
-
acceptanceCriteria: [],
|
|
52217
|
-
estimatedComplexity: "medium"
|
|
52218
|
-
});
|
|
52219
|
-
}
|
|
52220
|
-
});
|
|
52221
|
-
}
|
|
52222
|
-
}
|
|
52223
|
-
if (deliverables.length === 0) {
|
|
52224
|
-
const featureHeaders = phaseContent.match(/^####\s+[A-Z]\)\s+(.+)$/gm);
|
|
52225
|
-
if (featureHeaders) {
|
|
52226
|
-
featureHeaders.slice(0, 6).forEach((h) => {
|
|
52227
|
-
const hMatch = h.match(/####\s+[A-Z]\)\s+(.+)/);
|
|
52228
|
-
if (hMatch && hMatch[1]) {
|
|
52229
|
-
const title = hMatch[1].trim();
|
|
52230
|
-
deliverables.push(title);
|
|
52231
|
-
phaseTasks.push({
|
|
52232
|
-
title: `Implement ${title}`,
|
|
52233
|
-
source: "ROADMAP.md",
|
|
52234
|
-
sourceSection: `Phase ${phaseNum}: ${phaseName}`,
|
|
52235
|
-
phase: phaseType,
|
|
52236
|
-
status: "pending",
|
|
52237
|
-
acceptanceCriteria: [],
|
|
52238
|
-
estimatedComplexity: "high"
|
|
52239
|
-
});
|
|
52240
|
-
}
|
|
52241
|
-
});
|
|
52242
|
-
}
|
|
52243
|
-
}
|
|
52244
|
-
const exitCriteriaRegex = /###\s+[\d.]+\s*Exit\s+Criteria[\s\S]*?(?=###|##|$)/i;
|
|
52245
|
-
const exitMatch = phaseContent.match(exitCriteriaRegex);
|
|
52246
|
-
if (exitMatch) {
|
|
52247
|
-
const checkItems = exitMatch[0]?.match(/^[-*]\s+[✅✓⬜]\s*(.+)$/gm);
|
|
52248
|
-
if (checkItems) {
|
|
52249
|
-
checkItems.forEach((item) => {
|
|
52250
|
-
const text = item.replace(/^[-*]\s+[✅✓⬜]\s*/, "").trim();
|
|
52251
|
-
if (text.length > 5 && !deliverables.includes(text)) {
|
|
52252
|
-
deliverables.push(text);
|
|
52253
|
-
}
|
|
52254
|
-
});
|
|
52255
|
-
}
|
|
52256
|
-
}
|
|
52257
|
-
}
|
|
52258
|
-
phases.push({
|
|
52259
|
-
phase: phaseNum,
|
|
52260
|
-
name: phaseName,
|
|
52261
|
-
type: phaseType,
|
|
52262
|
-
deliverables
|
|
52263
|
-
});
|
|
52264
|
-
}
|
|
52265
|
-
return { phases, phaseTasks };
|
|
52266
|
-
}
|
|
52267
|
-
function extractFromTechnicalSpec(content) {
|
|
52268
|
-
const tasks = [];
|
|
52269
|
-
const dbSection = extractSection(content, "Data\\s+Model|Database|Key\\s+tables", { maxLength: 3e3 });
|
|
52270
|
-
if (dbSection) {
|
|
52271
|
-
const tableMatches = dbSection.match(/^\s*[-*]\s*`(\w+)`/gm);
|
|
52272
|
-
if (tableMatches && tableMatches.length > 0) {
|
|
52273
|
-
const tableNames = tableMatches.slice(0, 5).map((m) => m.match(/`(\w+)`/)?.[1]).filter((name) => Boolean(name)).join(", ");
|
|
52274
|
-
tasks.push({
|
|
52275
|
-
title: "Set up database schema with Prisma",
|
|
52276
|
-
description: `Create database models for: ${tableNames}`,
|
|
52277
|
-
source: "TECHNICAL_SPEC.md",
|
|
52278
|
-
sourceSection: "Database Schema",
|
|
52279
|
-
phase: "foundation",
|
|
52280
|
-
status: "pending",
|
|
52281
|
-
acceptanceCriteria: [
|
|
52282
|
-
"Prisma schema defined with all models",
|
|
52283
|
-
"Database migrations generated",
|
|
52284
|
-
"Seed data scripts created"
|
|
52285
|
-
],
|
|
52286
|
-
estimatedComplexity: "high"
|
|
52287
|
-
});
|
|
52288
|
-
}
|
|
52289
|
-
}
|
|
52290
|
-
const apiSection = extractSection(content, "API\\s+Routes|Application\\s+Structure", { maxLength: 3e3 });
|
|
52291
|
-
if (apiSection) {
|
|
52292
|
-
const apiGroups = /* @__PURE__ */ new Map();
|
|
52293
|
-
const apiMatches = apiSection.matchAll(/`\/api\/([^`/]+)/g);
|
|
52294
|
-
for (const match of apiMatches) {
|
|
52295
|
-
const group = match[1];
|
|
52296
|
-
if (group && !apiGroups.has(group)) {
|
|
52297
|
-
apiGroups.set(group, []);
|
|
52298
|
-
}
|
|
52299
|
-
}
|
|
52300
|
-
if (apiGroups.size > 0) {
|
|
52301
|
-
tasks.push({
|
|
52302
|
-
title: "Implement core API endpoints",
|
|
52303
|
-
description: `Set up API routes for: ${[...apiGroups.keys()].slice(0, 5).join(", ")}`,
|
|
52304
|
-
source: "TECHNICAL_SPEC.md",
|
|
52305
|
-
sourceSection: "API Routes",
|
|
52306
|
-
phase: "mvp",
|
|
52307
|
-
status: "pending",
|
|
52308
|
-
acceptanceCriteria: [
|
|
52309
|
-
"All CRUD endpoints implemented",
|
|
52310
|
-
"Input validation with Zod",
|
|
52311
|
-
"Error handling middleware",
|
|
52312
|
-
"API documentation updated"
|
|
52313
|
-
],
|
|
52314
|
-
estimatedComplexity: "high"
|
|
52315
|
-
});
|
|
52316
|
-
}
|
|
52317
|
-
}
|
|
52318
|
-
const archSection = extractSection(content, "Core\\s+system\\s+components|Architecture", { maxLength: 2e3 });
|
|
52319
|
-
if (archSection) {
|
|
52320
|
-
const compMatches = archSection.match(/^\s*\d+\)\s*(.+)$/gm);
|
|
52321
|
-
if (compMatches && compMatches.length > 0) {
|
|
52322
|
-
tasks.push({
|
|
52323
|
-
title: "Set up project architecture and scaffolding",
|
|
52324
|
-
description: "Configure project structure, linting, testing, and CI/CD",
|
|
52325
|
-
source: "TECHNICAL_SPEC.md",
|
|
52326
|
-
sourceSection: "Architecture",
|
|
52327
|
-
phase: "foundation",
|
|
52328
|
-
status: "pending",
|
|
52329
|
-
acceptanceCriteria: [
|
|
52330
|
-
"Project structure follows conventions",
|
|
52331
|
-
"ESLint and Prettier configured",
|
|
52332
|
-
"Testing framework set up",
|
|
52333
|
-
"CI/CD pipeline configured"
|
|
52334
|
-
],
|
|
52335
|
-
estimatedComplexity: "medium",
|
|
52336
|
-
dependencies: []
|
|
52337
|
-
});
|
|
52338
|
-
}
|
|
52339
|
-
}
|
|
52340
|
-
const integrations = extractIntegrations(content);
|
|
52341
|
-
const intCategories = Object.entries(integrations).filter(([, v]) => v.length > 0);
|
|
52342
|
-
if (intCategories.length > 0) {
|
|
52343
|
-
for (const [category, services] of intCategories) {
|
|
52344
|
-
if (services.length > 0) {
|
|
52345
|
-
tasks.push({
|
|
52346
|
-
title: `Integrate ${category} services (${services.join(", ")})`,
|
|
52347
|
-
source: "TECHNICAL_SPEC.md",
|
|
52348
|
-
sourceSection: "Integrations",
|
|
52349
|
-
phase: "mvp",
|
|
52350
|
-
status: "pending",
|
|
52351
|
-
acceptanceCriteria: services.map((s) => `${s} integration working`),
|
|
52352
|
-
estimatedComplexity: services.length > 2 ? "high" : "medium"
|
|
52353
|
-
});
|
|
52354
|
-
}
|
|
52355
|
-
}
|
|
52356
|
-
}
|
|
52357
|
-
return tasks;
|
|
52358
|
-
}
|
|
52359
|
-
function extractFromSeed(content) {
|
|
52360
|
-
const tasks = [];
|
|
52361
|
-
let taskIdCounter = 0;
|
|
52362
|
-
const nextId = () => {
|
|
52363
|
-
taskIdCounter++;
|
|
52364
|
-
return `task-${String(taskIdCounter).padStart(4, "0")}`;
|
|
52365
|
-
};
|
|
52366
|
-
const userStoryPattern = /^###\s+US-\d+\s*[—–-]\s*(.+)$/gm;
|
|
52367
|
-
const userStoryMatches = [...content.matchAll(userStoryPattern)];
|
|
52368
|
-
for (const usMatch of userStoryMatches) {
|
|
52369
|
-
const title = usMatch[1]?.trim() ?? "";
|
|
52370
|
-
if (!title) continue;
|
|
52371
|
-
const storyStart = (usMatch.index ?? 0) + usMatch[0].length;
|
|
52372
|
-
const nextHeading = content.slice(storyStart).search(/^###?\s+/m);
|
|
52373
|
-
const storyBlock = nextHeading === -1 ? content.slice(storyStart) : content.slice(storyStart, storyStart + nextHeading);
|
|
52374
|
-
const criteria = [];
|
|
52375
|
-
for (const acMatch of storyBlock.matchAll(/^-\s+\[[ xX]\]\s+(.+)$/gm)) {
|
|
52376
|
-
if (acMatch[1]) criteria.push(acMatch[1].trim());
|
|
52377
|
-
}
|
|
52378
|
-
tasks.push({
|
|
52379
|
-
id: nextId(),
|
|
52380
|
-
title,
|
|
52381
|
-
source: "SEED.md",
|
|
52382
|
-
sourceSection: "User Stories",
|
|
52383
|
-
phase: "mvp",
|
|
52384
|
-
status: "pending",
|
|
52385
|
-
acceptanceCriteria: criteria,
|
|
52386
|
-
estimatedComplexity: criteria.length > 5 ? "high" : criteria.length > 2 ? "medium" : "low"
|
|
52387
|
-
});
|
|
52388
|
-
}
|
|
52389
|
-
let buildSection = extractSection(content, "Build\\s+(?:Order|Priorities)", { maxLength: 1e4 });
|
|
52390
|
-
if (!buildSection) {
|
|
52391
|
-
const boldBuildMatch = content.match(/\*{2}Build\s+order:?\*{2}/i);
|
|
52392
|
-
if (boldBuildMatch) {
|
|
52393
|
-
const start = (boldBuildMatch.index ?? 0) + boldBuildMatch[0].length;
|
|
52394
|
-
const nextHeading = content.slice(start).search(/^#{1,4}\s+/m);
|
|
52395
|
-
buildSection = nextHeading === -1 ? content.slice(start, start + 1e4) : content.slice(start, start + nextHeading);
|
|
52396
|
-
buildSection = buildSection.trim();
|
|
52397
|
-
}
|
|
52398
|
-
}
|
|
52399
|
-
if (buildSection) {
|
|
52400
|
-
const buildSteps = [...buildSection.matchAll(/^\d+\.\s+\*{0,2}(.+?)\*{0,2}\s*(?:\(.*?\))?\s*$/gm)];
|
|
52401
|
-
for (const step of buildSteps) {
|
|
52402
|
-
const stepTitle = step[1]?.replace(/\*+/g, "").trim() ?? "";
|
|
52403
|
-
if (!stepTitle || stepTitle.length < 3) continue;
|
|
52404
|
-
const stepStart = (step.index ?? 0) + step[0].length;
|
|
52405
|
-
const nextStep = buildSection.slice(stepStart).search(/^\d+\.\s+/m);
|
|
52406
|
-
const stepBlock = nextStep === -1 ? buildSection.slice(stepStart) : buildSection.slice(stepStart, stepStart + nextStep);
|
|
52407
|
-
const criteria = [];
|
|
52408
|
-
for (const subMatch of stepBlock.matchAll(/^\s+-\s+(.+)$/gm)) {
|
|
52409
|
-
if (subMatch[1]) criteria.push(subMatch[1].trim());
|
|
52410
|
-
}
|
|
52411
|
-
const lowerTitle = stepTitle.toLowerCase();
|
|
52412
|
-
let phase = "mvp";
|
|
52413
|
-
if (lowerTitle.includes("setup") || lowerTitle.includes("scaffold") || lowerTitle.includes("config")) {
|
|
52414
|
-
phase = "foundation";
|
|
52415
|
-
} else if (lowerTitle.includes("auth") || lowerTitle.includes("database") || lowerTitle.includes("schema")) {
|
|
52416
|
-
phase = "foundation";
|
|
52417
|
-
}
|
|
52418
|
-
tasks.push({
|
|
52419
|
-
id: nextId(),
|
|
52420
|
-
title: stepTitle,
|
|
52421
|
-
source: "SEED.md",
|
|
52422
|
-
sourceSection: "Build Order",
|
|
52423
|
-
phase,
|
|
52424
|
-
status: "pending",
|
|
52425
|
-
acceptanceCriteria: criteria,
|
|
52426
|
-
estimatedComplexity: criteria.length > 4 ? "high" : "medium"
|
|
52427
|
-
});
|
|
52428
|
-
}
|
|
52429
|
-
}
|
|
52430
|
-
const roadmapPhasePattern = /^#{2,3}\s+(?:\d+\.\s+)?Phase\s+(\d+)\s*[:\-—–]+\s*(.+)$/gm;
|
|
52431
|
-
const roadmapMatches = [...content.matchAll(roadmapPhasePattern)];
|
|
52432
|
-
for (const rmMatch of roadmapMatches) {
|
|
52433
|
-
const phaseNum = parseInt(rmMatch[1] ?? "0", 10);
|
|
52434
|
-
const phaseName = rmMatch[2]?.trim() ?? "";
|
|
52435
|
-
if (!phaseName) continue;
|
|
52436
|
-
const phaseStart = (rmMatch.index ?? 0) + rmMatch[0].length;
|
|
52437
|
-
const nextPhase = content.slice(phaseStart).search(/^#{2,3}\s+(?:\d+\.\s+)?Phase\s+\d+/m);
|
|
52438
|
-
const phaseBlock = nextPhase === -1 ? content.slice(phaseStart, phaseStart + 5e3) : content.slice(phaseStart, phaseStart + nextPhase);
|
|
52439
|
-
const deliverablesMatch = phaseBlock.match(/\*{0,2}Deliverables?\*{0,2}\s*[:\n]/i);
|
|
52440
|
-
if (deliverablesMatch) {
|
|
52441
|
-
const delStart = (deliverablesMatch.index ?? 0) + deliverablesMatch[0].length;
|
|
52442
|
-
const delBlock = phaseBlock.slice(delStart, delStart + 3e3);
|
|
52443
|
-
const deliverables = [];
|
|
52444
|
-
for (const dMatch of delBlock.matchAll(/^[-*]\s+(.+)$/gm)) {
|
|
52445
|
-
if (dMatch[1] && !dMatch[1].startsWith("**")) deliverables.push(dMatch[1].trim());
|
|
52446
|
-
if (deliverables.length >= 15) break;
|
|
52447
|
-
}
|
|
52448
|
-
if (deliverables.length > 0) {
|
|
52449
|
-
const phase = phaseNum <= 1 ? "foundation" : phaseNum <= 6 ? "mvp" : "launch";
|
|
52450
|
-
tasks.push({
|
|
52451
|
-
id: nextId(),
|
|
52452
|
-
title: `Phase ${phaseNum}: ${phaseName}`,
|
|
52453
|
-
description: deliverables.slice(0, 5).join("; "),
|
|
52454
|
-
source: "SEED.md",
|
|
52455
|
-
sourceSection: "Roadmap",
|
|
52456
|
-
phase,
|
|
52457
|
-
status: "pending",
|
|
52458
|
-
acceptanceCriteria: deliverables,
|
|
52459
|
-
estimatedComplexity: deliverables.length > 5 ? "high" : "medium"
|
|
52460
|
-
});
|
|
52461
|
-
}
|
|
52462
|
-
}
|
|
52463
|
-
}
|
|
52464
|
-
const mvpTableSection = extractSection(content, "MVP.*Must.Have|MVP.*P1|MVP.*Features", { maxLength: 8e3 });
|
|
52465
|
-
if (mvpTableSection) {
|
|
52466
|
-
const tableRows = [...mvpTableSection.matchAll(/^\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|/gm)];
|
|
52467
|
-
for (const row of tableRows) {
|
|
52468
|
-
const feature = row[1]?.trim() ?? "";
|
|
52469
|
-
const desc = row[2]?.trim() ?? "";
|
|
52470
|
-
if (!feature || feature.includes("---") || feature.toLowerCase() === "feature") continue;
|
|
52471
|
-
tasks.push({
|
|
52472
|
-
id: nextId(),
|
|
52473
|
-
title: feature,
|
|
52474
|
-
description: desc,
|
|
52475
|
-
source: "SEED.md",
|
|
52476
|
-
sourceSection: "MVP Features",
|
|
52477
|
-
phase: "mvp",
|
|
52478
|
-
status: "pending",
|
|
52479
|
-
acceptanceCriteria: [],
|
|
52480
|
-
estimatedComplexity: "medium"
|
|
52481
|
-
});
|
|
52482
|
-
}
|
|
52483
|
-
}
|
|
52484
|
-
if (tasks.length === 0) {
|
|
52485
|
-
const mvpSection = extractSection(content, "MVP\\s+Features", { maxLength: 5e3 });
|
|
52486
|
-
if (mvpSection) {
|
|
52487
|
-
const featureMatches = mvpSection.matchAll(/^\d+\.\s+(.+)$/gm);
|
|
52488
|
-
for (const match of featureMatches) {
|
|
52489
|
-
const featureName = match[1]?.trim() ?? "";
|
|
52490
|
-
tasks.push({
|
|
52491
|
-
id: nextId(),
|
|
52492
|
-
title: `Implement ${featureName}`,
|
|
52493
|
-
source: "SEED.md",
|
|
52494
|
-
sourceSection: "MVP Features",
|
|
52495
|
-
phase: "mvp",
|
|
52496
|
-
status: "pending",
|
|
52497
|
-
acceptanceCriteria: [],
|
|
52498
|
-
estimatedComplexity: "medium"
|
|
52499
|
-
});
|
|
52500
|
-
}
|
|
52501
|
-
}
|
|
52502
|
-
}
|
|
52503
|
-
const seen = /* @__PURE__ */ new Set();
|
|
52504
|
-
const deduped = tasks.filter((t) => {
|
|
52505
|
-
const key = t.title.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
52506
|
-
if (seen.has(key)) return false;
|
|
52507
|
-
seen.add(key);
|
|
52508
|
-
return true;
|
|
52509
|
-
});
|
|
52510
|
-
return deduped;
|
|
52511
|
-
}
|
|
52512
|
-
function extractMvpCriteria(content) {
|
|
52513
|
-
const criteria = [];
|
|
52514
|
-
const successSection = extractSection(content, "MVP\\s+Success|Success\\s+Criteria|Definition\\s+of\\s+Done", { maxLength: 3e3 });
|
|
52515
|
-
if (successSection) {
|
|
52516
|
-
const bulletMatches = successSection.match(/^[-*]\s+(.+)$/gm);
|
|
52517
|
-
if (bulletMatches) {
|
|
52518
|
-
bulletMatches.forEach((b) => {
|
|
52519
|
-
const text = b.replace(/^[-*]\s+/, "").replace(/\*+/g, "").trim();
|
|
52520
|
-
if (text.length > 5) {
|
|
52521
|
-
criteria.push({
|
|
52522
|
-
name: text,
|
|
52523
|
-
status: "pending"
|
|
52524
|
-
});
|
|
52525
|
-
}
|
|
52526
|
-
});
|
|
52527
|
-
}
|
|
52528
|
-
}
|
|
52529
|
-
const mvpSection = extractSection(content, "MVP\\s+Features|P1\\s+.*Features", { maxLength: 5e3 });
|
|
52530
|
-
if (mvpSection) {
|
|
52531
|
-
const featureHeaders = mvpSection.match(/^#{1,4}\s+F-\d+:\s*(.+)$/gm);
|
|
52532
|
-
if (featureHeaders) {
|
|
52533
|
-
featureHeaders.forEach((h) => {
|
|
52534
|
-
const match = h.match(/F-\d+:\s*(.+)/);
|
|
52535
|
-
if (match && match[1]) {
|
|
52536
|
-
criteria.push({
|
|
52537
|
-
name: match[1].trim(),
|
|
52538
|
-
status: "pending"
|
|
52539
|
-
});
|
|
52540
|
-
}
|
|
52541
|
-
});
|
|
52542
|
-
}
|
|
52543
|
-
}
|
|
52544
|
-
return criteria;
|
|
52545
|
-
}
|
|
52546
|
-
function orderByDependencies(tasks) {
|
|
52547
|
-
const foundationTasks = tasks.filter((t) => t.phase === "foundation");
|
|
52548
|
-
const mvpTasks = tasks.filter((t) => t.phase === "mvp");
|
|
52549
|
-
const launchTasks = tasks.filter((t) => t.phase === "launch");
|
|
52550
|
-
const otherTasks = tasks.filter((t) => !["foundation", "mvp", "launch"].includes(t.phase));
|
|
52551
|
-
const priorityOrder = [
|
|
52552
|
-
// Foundation tasks first
|
|
52553
|
-
...foundationTasks.filter((t) => t.title.toLowerCase().includes("setup") || t.title.toLowerCase().includes("scaffold")),
|
|
52554
|
-
...foundationTasks.filter((t) => t.title.toLowerCase().includes("database") || t.title.toLowerCase().includes("schema")),
|
|
52555
|
-
...foundationTasks.filter((t) => !t.title.toLowerCase().includes("setup") && !t.title.toLowerCase().includes("database")),
|
|
52556
|
-
// MVP tasks
|
|
52557
|
-
...mvpTasks.filter((t) => t.title.toLowerCase().includes("auth")),
|
|
52558
|
-
...mvpTasks.filter((t) => t.title.toLowerCase().includes("api")),
|
|
52559
|
-
...mvpTasks.filter((t) => !t.title.toLowerCase().includes("auth") && !t.title.toLowerCase().includes("api")),
|
|
52560
|
-
// Launch tasks
|
|
52561
|
-
...launchTasks,
|
|
52562
|
-
// Other tasks
|
|
52563
|
-
...otherTasks
|
|
52564
|
-
];
|
|
52565
|
-
return priorityOrder;
|
|
52566
|
-
}
|
|
52567
|
-
function deduplicateTasks(tasks) {
|
|
52568
|
-
const seen = /* @__PURE__ */ new Map();
|
|
52569
|
-
return tasks.filter((task) => {
|
|
52570
|
-
const normalizedTitle = task.title.toLowerCase().replace(/^implement\s+/i, "").replace(/\s+/g, " ").trim();
|
|
52571
|
-
const key = normalizedTitle.slice(0, 30);
|
|
52572
|
-
const existing = seen.get(key);
|
|
52573
|
-
if (existing) {
|
|
52574
|
-
if ((task.acceptanceCriteria?.length ?? 0) > (existing.acceptanceCriteria?.length ?? 0)) {
|
|
52575
|
-
existing.acceptanceCriteria = task.acceptanceCriteria;
|
|
52576
|
-
}
|
|
52577
|
-
return false;
|
|
52578
|
-
}
|
|
52579
|
-
seen.set(key, task);
|
|
52580
|
-
return true;
|
|
52581
|
-
});
|
|
52582
|
-
}
|
|
52583
|
-
function extractAcceptanceCriteria(content, featureId) {
|
|
52584
|
-
const criteria = [];
|
|
52585
|
-
const featureRegex = new RegExp(
|
|
52586
|
-
`#{1,4}\\s+${featureId}:[\\s\\S]*?(?=#{1,4}\\s+F-\\d+:|$)`,
|
|
52587
|
-
"i"
|
|
52588
|
-
);
|
|
52589
|
-
const featureMatch = content.match(featureRegex);
|
|
52590
|
-
if (featureMatch) {
|
|
52591
|
-
const featureContent = featureMatch[0] ?? "";
|
|
52592
|
-
const bulletMatches = featureContent.match(/^[-*]\s+(.+)$/gm);
|
|
52593
|
-
if (bulletMatches) {
|
|
52594
|
-
bulletMatches.slice(0, 5).forEach((b) => {
|
|
52595
|
-
const text = b.replace(/^[-*]\s+/, "").replace(/\*+/g, "").trim();
|
|
52596
|
-
if (text.length > 5 && text.length < 200) {
|
|
52597
|
-
criteria.push(text);
|
|
52598
|
-
}
|
|
52599
|
-
});
|
|
52600
|
-
}
|
|
52601
|
-
}
|
|
52602
|
-
return criteria;
|
|
52603
|
-
}
|
|
52604
|
-
function estimateComplexity(title, acceptanceCriteria = []) {
|
|
52605
|
-
const titleLower = title.toLowerCase();
|
|
52606
|
-
const highIndicators = [
|
|
52607
|
-
"authentication",
|
|
52608
|
-
"payment",
|
|
52609
|
-
"integration",
|
|
52610
|
-
"real-time",
|
|
52611
|
-
"voice",
|
|
52612
|
-
"agent",
|
|
52613
|
-
"workflow",
|
|
52614
|
-
"pipeline",
|
|
52615
|
-
"migration",
|
|
52616
|
-
"security",
|
|
52617
|
-
"encryption",
|
|
52618
|
-
"multi-tenant"
|
|
52619
|
-
];
|
|
52620
|
-
const lowIndicators = [
|
|
52621
|
-
"ui",
|
|
52622
|
-
"style",
|
|
52623
|
-
"button",
|
|
52624
|
-
"form",
|
|
52625
|
-
"display",
|
|
52626
|
-
"show",
|
|
52627
|
-
"list",
|
|
52628
|
-
"view",
|
|
52629
|
-
"page",
|
|
52630
|
-
"component",
|
|
52631
|
-
"typo",
|
|
52632
|
-
"fix"
|
|
52633
|
-
];
|
|
52634
|
-
if (highIndicators.some((i) => titleLower.includes(i))) {
|
|
52635
|
-
return "high";
|
|
52636
|
-
}
|
|
52637
|
-
if (lowIndicators.some((i) => titleLower.includes(i))) {
|
|
52638
|
-
return "low";
|
|
52639
|
-
}
|
|
52640
|
-
if (acceptanceCriteria.length > 5) {
|
|
52641
|
-
return "high";
|
|
52642
|
-
}
|
|
52643
|
-
return "medium";
|
|
52644
|
-
}
|
|
52645
|
-
function extractSection(content, headingPattern, options = {}) {
|
|
52646
|
-
const { maxLength = 2e3 } = options;
|
|
52647
|
-
const headingRegex = new RegExp(`^(#{1,4})\\s*(?:\\d+\\.\\s*)?(?:${headingPattern})[^\\n]*\\n`, "im");
|
|
52648
|
-
const match = content.match(headingRegex);
|
|
52649
|
-
if (!match || !match[1]) return null;
|
|
52650
|
-
const startIndex = (match.index ?? 0) + match[0].length;
|
|
52651
|
-
const headingLevel = match[1].length;
|
|
52652
|
-
const restContent = content.slice(startIndex);
|
|
52653
|
-
const nextHeadingRegex = new RegExp(`^#{1,${headingLevel}}\\s+`, "m");
|
|
52654
|
-
const nextMatch = restContent.match(nextHeadingRegex);
|
|
52655
|
-
let sectionContent = nextMatch ? restContent.slice(0, nextMatch.index) : restContent;
|
|
52656
|
-
sectionContent = sectionContent.trim();
|
|
52657
|
-
if (sectionContent.length > maxLength) {
|
|
52658
|
-
sectionContent = sectionContent.slice(0, maxLength);
|
|
52659
|
-
}
|
|
52660
|
-
return sectionContent || null;
|
|
52661
|
-
}
|
|
52662
|
-
function extractIntegrations(content) {
|
|
52663
|
-
const integrations = {
|
|
52664
|
-
auth: [],
|
|
52665
|
-
payments: [],
|
|
52666
|
-
email: [],
|
|
52667
|
-
ai: []
|
|
52668
|
-
};
|
|
52669
|
-
if (/\bClerk\b/i.test(content)) integrations.auth.push("Clerk");
|
|
52670
|
-
if (/\bNextAuth|Auth\.js/i.test(content)) integrations.auth.push("NextAuth");
|
|
52671
|
-
if (/\bStripe\b/i.test(content)) integrations.payments.push("Stripe");
|
|
52672
|
-
if (/\bPaddle\b/i.test(content)) integrations.payments.push("Paddle");
|
|
52673
|
-
if (/\bSendGrid\b/i.test(content)) integrations.email.push("SendGrid");
|
|
52674
|
-
if (/\bResend\b/i.test(content)) integrations.email.push("Resend");
|
|
52675
|
-
if (/\bOpenAI\b|GPT-4/i.test(content)) integrations.ai.push("OpenAI");
|
|
52676
|
-
if (/\bAnthropic\b|Claude/i.test(content)) integrations.ai.push("Anthropic");
|
|
52677
|
-
return integrations;
|
|
52678
|
-
}
|
|
52679
|
-
function extractFromPreseedDir(projectRoot) {
|
|
52680
|
-
const preseedDir = path57.join(projectRoot, ".bootspring", "preseed");
|
|
52681
|
-
if (!fs56.existsSync(preseedDir)) {
|
|
52682
|
-
return { tasks: [], phases: [], mvpCriteria: [], metadata: { extractedAt: (/* @__PURE__ */ new Date()).toISOString(), sources: [] }, error: "No preseed directory found" };
|
|
52683
|
-
}
|
|
52684
|
-
const validDocs = [
|
|
52685
|
-
"VISION.md",
|
|
52686
|
-
"AUDIENCE.md",
|
|
52687
|
-
"MARKET.md",
|
|
52688
|
-
"COMPETITORS.md",
|
|
52689
|
-
"BUSINESS_MODEL.md",
|
|
52690
|
-
"PRD.md",
|
|
52691
|
-
"TECHNICAL_SPEC.md",
|
|
52692
|
-
"ROADMAP.md"
|
|
52693
|
-
];
|
|
52694
|
-
const docs = {};
|
|
52695
|
-
for (const file2 of validDocs) {
|
|
52696
|
-
const filePath = path57.join(preseedDir, file2);
|
|
52697
|
-
if (fs56.existsSync(filePath)) {
|
|
52698
|
-
const docName = file2.replace(".md", "");
|
|
52699
|
-
docs[docName] = fs56.readFileSync(filePath, "utf-8");
|
|
52700
|
-
}
|
|
52701
|
-
}
|
|
52702
|
-
if (Object.keys(docs).length === 0) {
|
|
52703
|
-
return { tasks: [], phases: [], mvpCriteria: [], metadata: { extractedAt: (/* @__PURE__ */ new Date()).toISOString(), sources: [] }, error: "No preseed documents found" };
|
|
52704
|
-
}
|
|
52705
|
-
return extractFromDocs(docs);
|
|
52706
|
-
}
|
|
52707
|
-
function extractFromSeedFile(projectRoot) {
|
|
52708
|
-
const planningSeedPath = path57.join(projectRoot, "planning", "SEED.md");
|
|
52709
|
-
const rootSeedPath = path57.join(projectRoot, "SEED.md");
|
|
52710
|
-
const seedPath = fs56.existsSync(planningSeedPath) ? planningSeedPath : rootSeedPath;
|
|
52711
|
-
if (!fs56.existsSync(seedPath)) {
|
|
52712
|
-
return { tasks: [], phases: [], mvpCriteria: [], metadata: { extractedAt: (/* @__PURE__ */ new Date()).toISOString(), sources: [] }, error: "No planning/SEED.md found" };
|
|
52713
|
-
}
|
|
52714
|
-
const seedContent = fs56.readFileSync(seedPath, "utf-8");
|
|
52715
|
-
return extractFromDocs({ SEED: seedContent });
|
|
52716
|
-
}
|
|
52717
|
-
function parseDependencyIds2(rawText) {
|
|
52718
|
-
const ids = [];
|
|
52719
|
-
const seen = /* @__PURE__ */ new Set();
|
|
52720
|
-
const tokenMatches = rawText.match(/[a-z0-9][a-z0-9_-]*/gi) || [];
|
|
52721
|
-
for (const rawToken of tokenMatches) {
|
|
52722
|
-
const token = rawToken.trim().replace(/[.,;:]+$/, "");
|
|
52723
|
-
if (!token || !token.includes("-") || !/\d/.test(token)) continue;
|
|
52724
|
-
if (seen.has(token)) continue;
|
|
52725
|
-
seen.add(token);
|
|
52726
|
-
ids.push(token);
|
|
52727
|
-
}
|
|
52728
|
-
return ids;
|
|
52729
|
-
}
|
|
52730
|
-
function extractDependenciesFromSection(sectionContent) {
|
|
52731
|
-
const dependencies = [];
|
|
52732
|
-
const seen = /* @__PURE__ */ new Set();
|
|
52733
|
-
const inlineMatch = sectionContent.match(/\*{0,2}(?:Dependencies|Depends on):\*{0,2}\s*([^\n]+)/i);
|
|
52734
|
-
if (inlineMatch?.[1]) {
|
|
52735
|
-
for (const depId of parseDependencyIds2(inlineMatch[1])) {
|
|
52736
|
-
if (!seen.has(depId)) {
|
|
52737
|
-
seen.add(depId);
|
|
52738
|
-
dependencies.push(depId);
|
|
52739
|
-
}
|
|
52740
|
-
}
|
|
52741
|
-
}
|
|
52742
|
-
const blockMatch = sectionContent.match(/\*{0,2}(?:Dependencies|Depends on):\*{0,2}\s*\n([\s\S]*?)(?=\n\*{0,2}[A-Z]|\n---|\n###|$)/i);
|
|
52743
|
-
if (blockMatch?.[1]) {
|
|
52744
|
-
const bullets = blockMatch[1].matchAll(/^\s*[-*]\s*(?:\[[ xX]\]\s*)?(.+)$/gm);
|
|
52745
|
-
for (const bullet of bullets) {
|
|
52746
|
-
for (const depId of parseDependencyIds2(bullet[1] ?? "")) {
|
|
52747
|
-
if (!seen.has(depId)) {
|
|
52748
|
-
seen.add(depId);
|
|
52749
|
-
dependencies.push(depId);
|
|
52750
|
-
}
|
|
52751
|
-
}
|
|
52752
|
-
}
|
|
52753
|
-
if (dependencies.length === 0) {
|
|
52754
|
-
for (const depId of parseDependencyIds2(blockMatch[1])) {
|
|
52755
|
-
if (!seen.has(depId)) {
|
|
52756
|
-
seen.add(depId);
|
|
52757
|
-
dependencies.push(depId);
|
|
52758
|
-
}
|
|
52759
|
-
}
|
|
52760
|
-
}
|
|
52761
|
-
}
|
|
52762
|
-
return dependencies;
|
|
52763
|
-
}
|
|
52764
|
-
function extractSourceMetadata(rawSource) {
|
|
52765
|
-
const trimmed = rawSource.trim();
|
|
52766
|
-
if (!trimmed) {
|
|
52767
|
-
return {
|
|
52768
|
-
source: "TODO.md",
|
|
52769
|
-
sourceSection: "Checklist"
|
|
52770
|
-
};
|
|
52771
|
-
}
|
|
52772
|
-
const sourceMatch = trimmed.match(/^(.+?)\s+\((.+)\)$/);
|
|
52773
|
-
if (sourceMatch?.[1] && sourceMatch?.[2]) {
|
|
52774
|
-
return {
|
|
52775
|
-
source: sourceMatch[1].trim(),
|
|
52776
|
-
sourceSection: sourceMatch[2].trim()
|
|
52777
|
-
};
|
|
52778
|
-
}
|
|
52779
|
-
return {
|
|
52780
|
-
source: trimmed,
|
|
52781
|
-
sourceSection: "Checklist"
|
|
52782
|
-
};
|
|
52783
|
-
}
|
|
52784
|
-
function sortTasksWithDependencyChecks(tasks) {
|
|
52785
|
-
const taskById = /* @__PURE__ */ new Map();
|
|
52786
|
-
const orderById = /* @__PURE__ */ new Map();
|
|
52787
|
-
const orderedByInput = [];
|
|
52788
|
-
tasks.forEach((task, index) => {
|
|
52789
|
-
orderedByInput.push(task);
|
|
52790
|
-
if (task.id) {
|
|
52791
|
-
taskById.set(task.id, task);
|
|
52792
|
-
orderById.set(task.id, index);
|
|
52793
|
-
}
|
|
52794
|
-
});
|
|
52795
|
-
const visited = /* @__PURE__ */ new Set();
|
|
52796
|
-
const visiting = /* @__PURE__ */ new Set();
|
|
52797
|
-
const ordered = [];
|
|
52798
|
-
function visit(task) {
|
|
52799
|
-
const taskId = task.id || "";
|
|
52800
|
-
if (visited.has(taskId)) return;
|
|
52801
|
-
if (visiting.has(taskId)) return;
|
|
52802
|
-
visiting.add(taskId);
|
|
52803
|
-
const deps = (task.dependencies || []).filter((depId) => taskById.has(depId)).sort((a, b) => (orderById.get(a) ?? Number.MAX_SAFE_INTEGER) - (orderById.get(b) ?? Number.MAX_SAFE_INTEGER));
|
|
52804
|
-
for (const depId of deps) {
|
|
52805
|
-
const depTask = taskById.get(depId);
|
|
52806
|
-
if (depTask) {
|
|
52807
|
-
visit(depTask);
|
|
52808
|
-
}
|
|
52809
|
-
}
|
|
52810
|
-
visiting.delete(taskId);
|
|
52811
|
-
visited.add(taskId);
|
|
52812
|
-
ordered.push(task);
|
|
52813
|
-
}
|
|
52814
|
-
for (const task of orderedByInput) {
|
|
52815
|
-
visit(task);
|
|
52816
|
-
}
|
|
52817
|
-
for (const task of orderedByInput) {
|
|
52818
|
-
if (!ordered.includes(task)) {
|
|
52819
|
-
ordered.push(task);
|
|
52820
|
-
}
|
|
52821
|
-
}
|
|
52822
|
-
return ordered;
|
|
52823
|
-
}
|
|
52824
|
-
function extractFromTaskQueue(content) {
|
|
52825
|
-
const tasks = [];
|
|
52826
|
-
const tableMatches = content.matchAll(/^\|\s*([^|]+)\s*\|\s*([a-z0-9][a-z0-9_-]*)\s*\|\s*([^|]+)\s*\|\s*([^|]+)\s*\|\s*([^|]+)\s*\|\s*([^|]+)\s*\|$/gmi);
|
|
52827
|
-
for (const match of tableMatches) {
|
|
52828
|
-
const id = match[2]?.trim() ?? "";
|
|
52829
|
-
const title = match[3]?.trim() ?? "";
|
|
52830
|
-
const phase = normalizeTaskPhase(match[4]?.trim());
|
|
52831
|
-
const complexity = normalizeComplexity(match[5]?.trim());
|
|
52832
|
-
const status = normalizeTaskStatus(match[6]?.trim());
|
|
52833
|
-
if (id.toLowerCase() === "id" || title.toLowerCase() === "task" || id.includes("---") || title.includes("---")) continue;
|
|
52834
|
-
tasks.push({
|
|
52835
|
-
id,
|
|
52836
|
-
title,
|
|
52837
|
-
source: "TASK_QUEUE.md",
|
|
52838
|
-
sourceSection: "Queue Status",
|
|
52839
|
-
phase,
|
|
52840
|
-
status,
|
|
52841
|
-
acceptanceCriteria: [],
|
|
52842
|
-
estimatedComplexity: complexity
|
|
52843
|
-
});
|
|
52844
|
-
}
|
|
52845
|
-
const detailMatches = content.matchAll(/^###\s+(task-\d+|[\w-]+):\s*(.+)$/gm);
|
|
52846
|
-
for (const match of detailMatches) {
|
|
52847
|
-
const id = match[1]?.trim() ?? "";
|
|
52848
|
-
const title = match[2]?.trim() ?? "";
|
|
52849
|
-
const sectionStart = (match.index ?? 0) + match[0].length;
|
|
52850
|
-
const nextSection = content.slice(sectionStart).search(/^###\s+/m);
|
|
52851
|
-
const sectionContent = nextSection === -1 ? content.slice(sectionStart) : content.slice(sectionStart, sectionStart + nextSection);
|
|
52852
|
-
const phaseMatch = sectionContent.match(/\*{0,2}Phase:\*{0,2}\s*(\w+(?:\s*\d+)?)/i);
|
|
52853
|
-
const phase = normalizeTaskPhase(phaseMatch?.[1]?.trim());
|
|
52854
|
-
const complexityMatch = sectionContent.match(/\*{0,2}Complexity:\*{0,2}\s*(\w+)/i);
|
|
52855
|
-
const complexity = normalizeComplexity(complexityMatch?.[1]?.trim());
|
|
52856
|
-
const statusMatch = sectionContent.match(/\*{0,2}Status:\*{0,2}\s*([^\n]+)/i);
|
|
52857
|
-
const status = normalizeTaskStatus(statusMatch?.[1]?.trim());
|
|
52858
|
-
const criteriaSection = sectionContent.match(/\*{0,2}Acceptance\s+Criteria:\*{0,2}\s*([\s\S]*?)(?=\n\*{0,2}[A-Z]|\n---|\n###|$)/i);
|
|
52859
|
-
const acceptanceCriteria = [];
|
|
52860
|
-
if (criteriaSection && criteriaSection[1]) {
|
|
52861
|
-
const criteriaMatches = criteriaSection[1].matchAll(/[-*]\s*\[[ x]\]\s*(.+)/g);
|
|
52862
|
-
for (const cm of criteriaMatches) {
|
|
52863
|
-
acceptanceCriteria.push(cm[1]?.trim() ?? "");
|
|
52864
|
-
}
|
|
52865
|
-
if (acceptanceCriteria.length === 0) {
|
|
52866
|
-
const bulletMatches = criteriaSection[1].matchAll(/^\s*[-*]\s+(.+)$/gm);
|
|
52867
|
-
for (const bm of bulletMatches) {
|
|
52868
|
-
const criterion = bm[1]?.trim() ?? "";
|
|
52869
|
-
if (criterion) {
|
|
52870
|
-
acceptanceCriteria.push(criterion);
|
|
52871
|
-
}
|
|
52872
|
-
}
|
|
52873
|
-
}
|
|
52874
|
-
}
|
|
52875
|
-
const dependencies = extractDependenciesFromSection(sectionContent);
|
|
52876
|
-
const existingIndex = tasks.findIndex((t) => t.id === id);
|
|
52877
|
-
const existingTask = existingIndex >= 0 ? tasks[existingIndex] : null;
|
|
52878
|
-
if (existingTask) {
|
|
52879
|
-
tasks[existingIndex] = {
|
|
52880
|
-
...existingTask,
|
|
52881
|
-
acceptanceCriteria: acceptanceCriteria.length > 0 ? acceptanceCriteria : existingTask.acceptanceCriteria,
|
|
52882
|
-
dependencies: dependencies.length > 0 ? dependencies : existingTask.dependencies
|
|
52883
|
-
};
|
|
52884
|
-
} else {
|
|
52885
|
-
tasks.push({
|
|
52886
|
-
id,
|
|
52887
|
-
title,
|
|
52888
|
-
source: "TASK_QUEUE.md",
|
|
52889
|
-
sourceSection: "Task Details",
|
|
52890
|
-
phase,
|
|
52891
|
-
status,
|
|
52892
|
-
acceptanceCriteria,
|
|
52893
|
-
estimatedComplexity: complexity,
|
|
52894
|
-
dependencies
|
|
52895
|
-
});
|
|
52896
|
-
}
|
|
52897
|
-
}
|
|
52898
|
-
return sortTasksWithDependencyChecks(tasks);
|
|
52899
|
-
}
|
|
52900
|
-
function extractFromTodo(content) {
|
|
52901
|
-
const tasks = [];
|
|
52902
|
-
const lines = content.split("\n");
|
|
52903
|
-
let currentPhase = "mvp";
|
|
52904
|
-
let currentTask = null;
|
|
52905
|
-
const pushCurrentTask = () => {
|
|
52906
|
-
if (!currentTask) return;
|
|
52907
|
-
tasks.push(currentTask);
|
|
52908
|
-
currentTask = null;
|
|
52909
|
-
};
|
|
52910
|
-
for (const line of lines) {
|
|
52911
|
-
const phaseMatch = line.match(/^##\s+(Foundation|MVP|Launch)\b/i);
|
|
52912
|
-
if (phaseMatch?.[1]) {
|
|
52913
|
-
pushCurrentTask();
|
|
52914
|
-
currentPhase = normalizeTaskPhase(phaseMatch[1]);
|
|
52915
|
-
continue;
|
|
52916
|
-
}
|
|
52917
|
-
const sectionMatch = line.match(/^##\s+(.+)\s*$/);
|
|
52918
|
-
if (sectionMatch?.[1]) {
|
|
52919
|
-
pushCurrentTask();
|
|
52920
|
-
const sectionName = sectionMatch[1].trim().toLowerCase();
|
|
52921
|
-
if (["foundation", "mvp", "launch"].includes(sectionName)) {
|
|
52922
|
-
currentPhase = normalizeTaskPhase(sectionName);
|
|
52923
|
-
}
|
|
52924
|
-
continue;
|
|
52925
|
-
}
|
|
52926
|
-
const taskMatch = line.match(/^-\s+\[([ xX])\]\s+`([^`]+)`\s+(.+?)(?:\s+\(`?([\w-]+)`?\))?\s*$/);
|
|
52927
|
-
if (taskMatch?.[2] && taskMatch?.[3]) {
|
|
52928
|
-
pushCurrentTask();
|
|
52929
|
-
const checked = taskMatch[1]?.toLowerCase() === "x";
|
|
52930
|
-
const explicitStatus = taskMatch[4];
|
|
52931
|
-
currentTask = {
|
|
52932
|
-
id: taskMatch[2].trim(),
|
|
52933
|
-
title: taskMatch[3].replace(/\*+/g, "").trim(),
|
|
52934
|
-
source: "TODO.md",
|
|
52935
|
-
sourceSection: "Checklist",
|
|
52936
|
-
phase: currentPhase,
|
|
52937
|
-
status: normalizeTaskStatus(explicitStatus || (checked ? "completed" : "pending")),
|
|
52938
|
-
acceptanceCriteria: [],
|
|
52939
|
-
estimatedComplexity: "medium",
|
|
52940
|
-
dependencies: []
|
|
52941
|
-
};
|
|
52942
|
-
continue;
|
|
52943
|
-
}
|
|
52944
|
-
if (!currentTask) {
|
|
52945
|
-
continue;
|
|
52946
|
-
}
|
|
52947
|
-
const sourceMatch = line.match(/^\s{2,}-\s+\*\*Source:\*\*\s+(.+)\s*$/);
|
|
52948
|
-
if (sourceMatch?.[1]) {
|
|
52949
|
-
const sourceMetadata = extractSourceMetadata(sourceMatch[1]);
|
|
52950
|
-
currentTask.source = sourceMetadata.source;
|
|
52951
|
-
currentTask.sourceSection = sourceMetadata.sourceSection;
|
|
52952
|
-
continue;
|
|
52953
|
-
}
|
|
52954
|
-
const descriptionMatch = line.match(/^\s{2,}-\s+\*\*Description:\*\*\s+(.+)\s*$/);
|
|
52955
|
-
if (descriptionMatch?.[1]) {
|
|
52956
|
-
currentTask.description = descriptionMatch[1].trim();
|
|
52957
|
-
continue;
|
|
52958
|
-
}
|
|
52959
|
-
const complexityMatch = line.match(/^\s{2,}-\s+\*\*Complexity:\*\*\s+(.+)\s*$/);
|
|
52960
|
-
if (complexityMatch?.[1]) {
|
|
52961
|
-
currentTask.estimatedComplexity = normalizeComplexity(complexityMatch[1]);
|
|
52962
|
-
continue;
|
|
52963
|
-
}
|
|
52964
|
-
const dependenciesMatch = line.match(/^\s{2,}-\s+\*\*Dependencies:\*\*\s+(.+)\s*$/);
|
|
52965
|
-
if (dependenciesMatch?.[1]) {
|
|
52966
|
-
currentTask.dependencies = parseDependencyIds2(dependenciesMatch[1]);
|
|
52967
|
-
continue;
|
|
52968
|
-
}
|
|
52969
|
-
const criteriaMatch = line.match(/^\s{2,}-\s+\[[ xX]\]\s+(.+)\s*$/);
|
|
52970
|
-
if (criteriaMatch?.[1]) {
|
|
52971
|
-
currentTask.acceptanceCriteria.push(criteriaMatch[1].trim());
|
|
52972
|
-
continue;
|
|
52973
|
-
}
|
|
52974
|
-
const plainCriteriaMatch = line.match(/^\s{2,}-\s+(.+)\s*$/);
|
|
52975
|
-
if (plainCriteriaMatch?.[1] && !plainCriteriaMatch[1].includes("**")) {
|
|
52976
|
-
currentTask.acceptanceCriteria.push(plainCriteriaMatch[1].trim());
|
|
52977
|
-
}
|
|
52978
|
-
}
|
|
52979
|
-
pushCurrentTask();
|
|
52980
|
-
return sortTasksWithDependencyChecks(tasks);
|
|
52981
|
-
}
|
|
52982
|
-
function extractFromTaskQueueFile(projectRoot) {
|
|
52983
|
-
const queuePath = path57.join(projectRoot, "planning", "TASK_QUEUE.md");
|
|
52984
|
-
if (!fs56.existsSync(queuePath)) {
|
|
52985
|
-
return {
|
|
52986
|
-
tasks: [],
|
|
52987
|
-
phases: [],
|
|
52988
|
-
mvpCriteria: [],
|
|
52989
|
-
metadata: { extractedAt: (/* @__PURE__ */ new Date()).toISOString(), sources: [] },
|
|
52990
|
-
error: "No TASK_QUEUE.md found"
|
|
52991
|
-
};
|
|
52992
|
-
}
|
|
52993
|
-
const queueContent = fs56.readFileSync(queuePath, "utf-8");
|
|
52994
|
-
const tasks = extractFromTaskQueue(queueContent);
|
|
52995
|
-
return {
|
|
52996
|
-
tasks,
|
|
52997
|
-
phases: [],
|
|
52998
|
-
mvpCriteria: [],
|
|
52999
|
-
metadata: {
|
|
53000
|
-
extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
53001
|
-
sources: ["TASK_QUEUE.md"]
|
|
53002
|
-
}
|
|
53003
|
-
};
|
|
53004
|
-
}
|
|
53005
|
-
function extractFromTodoFile(projectRoot) {
|
|
53006
|
-
const todoPath = path57.join(projectRoot, "planning", "TODO.md");
|
|
53007
|
-
if (!fs56.existsSync(todoPath)) {
|
|
53008
|
-
return {
|
|
53009
|
-
tasks: [],
|
|
53010
|
-
phases: [],
|
|
53011
|
-
mvpCriteria: [],
|
|
53012
|
-
metadata: { extractedAt: (/* @__PURE__ */ new Date()).toISOString(), sources: [] },
|
|
53013
|
-
error: "No TODO.md found"
|
|
53014
|
-
};
|
|
53015
|
-
}
|
|
53016
|
-
const todoContent = fs56.readFileSync(todoPath, "utf-8");
|
|
53017
|
-
const tasks = extractFromTodo(todoContent);
|
|
53018
|
-
return {
|
|
53019
|
-
tasks,
|
|
53020
|
-
phases: [],
|
|
53021
|
-
mvpCriteria: [],
|
|
53022
|
-
metadata: {
|
|
53023
|
-
extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
53024
|
-
sources: ["TODO.md"]
|
|
53025
|
-
}
|
|
53026
|
-
};
|
|
53027
|
-
}
|
|
53028
|
-
function extractFromPlanningFiles(projectRoot, options = {}) {
|
|
53029
|
-
const preferTodo = options.preferTodo !== false;
|
|
53030
|
-
const allowLegacyQueue = options.allowLegacyQueue !== false;
|
|
53031
|
-
const sources = preferTodo ? [
|
|
53032
|
-
{ type: "todo", enabled: true },
|
|
53033
|
-
{ type: "queue", enabled: allowLegacyQueue }
|
|
53034
|
-
] : [
|
|
53035
|
-
{ type: "queue", enabled: allowLegacyQueue },
|
|
53036
|
-
{ type: "todo", enabled: true }
|
|
53037
|
-
];
|
|
53038
|
-
for (const source of sources) {
|
|
53039
|
-
if (!source.enabled) continue;
|
|
53040
|
-
const result = source.type === "todo" ? extractFromTodoFile(projectRoot) : extractFromTaskQueueFile(projectRoot);
|
|
53041
|
-
if (!result.error && result.tasks.length > 0) {
|
|
53042
|
-
return result;
|
|
53043
|
-
}
|
|
53044
|
-
}
|
|
53045
|
-
return {
|
|
53046
|
-
tasks: [],
|
|
53047
|
-
phases: [],
|
|
53048
|
-
mvpCriteria: [],
|
|
53049
|
-
metadata: { extractedAt: (/* @__PURE__ */ new Date()).toISOString(), sources: [] },
|
|
53050
|
-
error: allowLegacyQueue ? "No TODO.md or TASK_QUEUE.md found" : "No TODO.md found"
|
|
53051
|
-
};
|
|
53052
|
-
}
|
|
53053
|
-
function syncExtractedTasks(projectRoot, result, options = {}) {
|
|
53054
|
-
if (result.error || result.tasks.length === 0) {
|
|
53055
|
-
return { added: 0, updated: 0, total: 0 };
|
|
53056
|
-
}
|
|
53057
|
-
const statePath = path57.join(projectRoot, "planning", "BUILD_STATE.json");
|
|
53058
|
-
let state = {};
|
|
53059
|
-
if (fs56.existsSync(statePath)) {
|
|
53060
|
-
try {
|
|
53061
|
-
state = JSON.parse(fs56.readFileSync(statePath, "utf-8"));
|
|
53062
|
-
} catch {
|
|
53063
|
-
state = {};
|
|
53064
|
-
}
|
|
53065
|
-
}
|
|
53066
|
-
const replace = options.replace === true;
|
|
53067
|
-
const existingQueue = replace ? [] : state.implementationQueue || [];
|
|
53068
|
-
const existingIds = new Set(existingQueue.map((t) => t.id));
|
|
53069
|
-
let added = 0;
|
|
53070
|
-
let updated = 0;
|
|
53071
|
-
for (const task of result.tasks) {
|
|
53072
|
-
if (existingIds.has(task.id)) {
|
|
53073
|
-
const index = existingQueue.findIndex((t) => t.id === task.id);
|
|
53074
|
-
const existing = index >= 0 ? existingQueue[index] : null;
|
|
53075
|
-
if (existing) {
|
|
53076
|
-
const existingDeps = (existing.dependencies || []).join(",");
|
|
53077
|
-
const nextDeps = (task.dependencies || []).join(",");
|
|
53078
|
-
const existingCriteria = (existing.acceptanceCriteria || []).join("\n");
|
|
53079
|
-
const nextCriteria = (task.acceptanceCriteria || []).join("\n");
|
|
53080
|
-
const metadataChanged = existing.title !== task.title || existing.phase !== task.phase || existing.estimatedComplexity !== task.estimatedComplexity || existing.description !== task.description || existing.source !== task.source || existing.sourceSection !== task.sourceSection;
|
|
53081
|
-
if (existing.status !== task.status || metadataChanged || existingDeps !== nextDeps || existingCriteria !== nextCriteria) {
|
|
53082
|
-
existingQueue[index] = { ...existing, ...task };
|
|
53083
|
-
updated++;
|
|
53084
|
-
}
|
|
53085
|
-
}
|
|
53086
|
-
} else {
|
|
53087
|
-
existingQueue.push(task);
|
|
53088
|
-
added++;
|
|
53089
|
-
}
|
|
53090
|
-
}
|
|
53091
|
-
const orderedQueue = sortTasksWithDependencyChecks(existingQueue);
|
|
53092
|
-
state.implementationQueue = orderedQueue;
|
|
53093
|
-
const activeTask = orderedQueue.find((task) => task.status === "in_progress") || orderedQueue.find((task) => task.status === "pending") || orderedQueue[0] || null;
|
|
53094
|
-
state.currentPhase = activeTask?.phase || null;
|
|
53095
|
-
fs56.writeFileSync(statePath, JSON.stringify(state, null, 2));
|
|
53096
|
-
return { added, updated, total: orderedQueue.length };
|
|
53097
|
-
}
|
|
53098
|
-
function syncFromTaskQueue(projectRoot, options = {}) {
|
|
53099
|
-
return syncExtractedTasks(projectRoot, extractFromTaskQueueFile(projectRoot), options);
|
|
53100
|
-
}
|
|
53101
|
-
function syncFromTodo(projectRoot, options = {}) {
|
|
53102
|
-
return syncExtractedTasks(projectRoot, extractFromTodoFile(projectRoot), options);
|
|
53103
|
-
}
|
|
53104
|
-
function syncFromPlanningFiles(projectRoot, options = {}) {
|
|
53105
|
-
return syncExtractedTasks(
|
|
53106
|
-
projectRoot,
|
|
53107
|
-
extractFromPlanningFiles(projectRoot, options),
|
|
53108
|
-
options
|
|
53109
|
-
);
|
|
53110
|
-
}
|
|
53111
|
-
var fs56, path57;
|
|
53112
|
-
var init_task_extractor = __esm({
|
|
53113
|
-
"../../packages/core/compat-src/task-extractor.ts"() {
|
|
53114
|
-
"use strict";
|
|
53115
|
-
init_cjs_shims();
|
|
53116
|
-
fs56 = __toESM(require("fs"));
|
|
53117
|
-
path57 = __toESM(require("path"));
|
|
53118
|
-
}
|
|
53119
|
-
});
|
|
53120
|
-
|
|
53121
52004
|
// ../../packages/session-intelligence/src/features/stats/efficiency-scorer.ts
|
|
53122
52005
|
var efficiency_scorer_exports = {};
|
|
53123
52006
|
__export(efficiency_scorer_exports, {
|
|
@@ -62381,80 +61264,712 @@ function registerHealthCommand(program3) {
|
|
|
62381
61264
|
message: `${f.source}: ${f.message}`
|
|
62382
61265
|
});
|
|
62383
61266
|
}
|
|
62384
|
-
}
|
|
61267
|
+
}
|
|
61268
|
+
}
|
|
61269
|
+
}
|
|
61270
|
+
}
|
|
61271
|
+
if (!opts.local) {
|
|
61272
|
+
const apiFailLevel = auth_exports.isAuthenticated() ? "fail" : "warn";
|
|
61273
|
+
try {
|
|
61274
|
+
const status = await api_client_exports.healthCheck();
|
|
61275
|
+
if (status.connected) {
|
|
61276
|
+
checks.push({ name: "API Connection", status: "pass", message: `Connected (v${status.version || "unknown"})` });
|
|
61277
|
+
} else {
|
|
61278
|
+
checks.push({ name: "API Connection", status: apiFailLevel, message: "Cannot connect" });
|
|
61279
|
+
}
|
|
61280
|
+
} catch {
|
|
61281
|
+
checks.push({ name: "API Connection", status: apiFailLevel, message: "Connection failed" });
|
|
61282
|
+
}
|
|
61283
|
+
}
|
|
61284
|
+
let passCount = 0;
|
|
61285
|
+
let failCount = 0;
|
|
61286
|
+
let warnCount = 0;
|
|
61287
|
+
for (const check3 of checks) {
|
|
61288
|
+
if (check3.status === "pass") passCount++;
|
|
61289
|
+
else if (check3.status === "fail") failCount++;
|
|
61290
|
+
else warnCount++;
|
|
61291
|
+
}
|
|
61292
|
+
const score = checks.length > 0 ? Math.round(passCount / checks.length * 100) : 0;
|
|
61293
|
+
const grade2 = failCount === 0 ? warnCount === 0 ? "A" : "B" : "F";
|
|
61294
|
+
if (opts.json) {
|
|
61295
|
+
console.log(JSON.stringify({
|
|
61296
|
+
score,
|
|
61297
|
+
grade: grade2,
|
|
61298
|
+
passed: passCount,
|
|
61299
|
+
warnings: warnCount,
|
|
61300
|
+
failed: failCount,
|
|
61301
|
+
checks
|
|
61302
|
+
}));
|
|
61303
|
+
} else {
|
|
61304
|
+
for (const check3 of checks) {
|
|
61305
|
+
const icon = check3.status === "pass" ? "+" : check3.status === "fail" ? "x" : "!";
|
|
61306
|
+
if (check3.status === "pass") {
|
|
61307
|
+
print.success(`[${icon}] ${check3.name}: ${check3.message}`);
|
|
61308
|
+
} else if (check3.status === "fail") {
|
|
61309
|
+
print.error(`[${icon}] ${check3.name}: ${check3.message}`);
|
|
61310
|
+
} else {
|
|
61311
|
+
print.warning(`[${icon}] ${check3.name}: ${check3.message}`);
|
|
61312
|
+
}
|
|
61313
|
+
}
|
|
61314
|
+
console.log(`
|
|
61315
|
+
${passCount} passed, ${warnCount} warnings, ${failCount} failed`);
|
|
61316
|
+
if (failCount > 0 || warnCount > 0) {
|
|
61317
|
+
print.dim(" For machine/install diagnostics, run `bootspring doctor`.");
|
|
61318
|
+
}
|
|
61319
|
+
}
|
|
61320
|
+
if (!opts.local) {
|
|
61321
|
+
try {
|
|
61322
|
+
await presence_exports.sendHealthReport({
|
|
61323
|
+
score,
|
|
61324
|
+
grade: grade2,
|
|
61325
|
+
data: { checks, passCount, warnCount, failCount }
|
|
61326
|
+
});
|
|
61327
|
+
} catch {
|
|
61328
|
+
}
|
|
61329
|
+
}
|
|
61330
|
+
if (failCount > 0) {
|
|
61331
|
+
process.exitCode = 1;
|
|
61332
|
+
}
|
|
61333
|
+
});
|
|
61334
|
+
}
|
|
61335
|
+
|
|
61336
|
+
// src/commands/generate.ts
|
|
61337
|
+
init_cjs_shims();
|
|
61338
|
+
var fs57 = __toESM(require("fs"));
|
|
61339
|
+
var path58 = __toESM(require("path"));
|
|
61340
|
+
init_src();
|
|
61341
|
+
|
|
61342
|
+
// ../../packages/core/compat-src/task-extractor.ts
|
|
61343
|
+
init_cjs_shims();
|
|
61344
|
+
var fs56 = __toESM(require("fs"));
|
|
61345
|
+
var path57 = __toESM(require("path"));
|
|
61346
|
+
function extractFromDocs(docs, _options = {}) {
|
|
61347
|
+
const result = {
|
|
61348
|
+
tasks: [],
|
|
61349
|
+
phases: [],
|
|
61350
|
+
mvpCriteria: [],
|
|
61351
|
+
metadata: {
|
|
61352
|
+
extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
61353
|
+
sources: Object.keys(docs)
|
|
61354
|
+
}
|
|
61355
|
+
};
|
|
61356
|
+
if (docs.PRD || docs.prd) {
|
|
61357
|
+
const prdContent = docs.PRD || docs.prd;
|
|
61358
|
+
if (prdContent) {
|
|
61359
|
+
const prdTasks = extractFromPrd(prdContent);
|
|
61360
|
+
result.tasks.push(...prdTasks);
|
|
61361
|
+
result.mvpCriteria.push(...extractMvpCriteria(prdContent));
|
|
61362
|
+
}
|
|
61363
|
+
}
|
|
61364
|
+
if (docs.ROADMAP || docs.roadmap) {
|
|
61365
|
+
const roadmapContent = docs.ROADMAP || docs.roadmap;
|
|
61366
|
+
if (roadmapContent) {
|
|
61367
|
+
const { phases, phaseTasks } = extractFromRoadmap(roadmapContent);
|
|
61368
|
+
result.phases = phases;
|
|
61369
|
+
result.tasks.push(...phaseTasks);
|
|
61370
|
+
}
|
|
61371
|
+
}
|
|
61372
|
+
if (docs.TECHNICAL_SPEC || docs.technical_spec) {
|
|
61373
|
+
const techContent = docs.TECHNICAL_SPEC || docs.technical_spec;
|
|
61374
|
+
if (techContent) {
|
|
61375
|
+
const techTasks = extractFromTechnicalSpec(techContent);
|
|
61376
|
+
result.tasks.push(...techTasks);
|
|
61377
|
+
}
|
|
61378
|
+
}
|
|
61379
|
+
if (docs.SEED || docs.seed) {
|
|
61380
|
+
const seedContent = docs.SEED || docs.seed;
|
|
61381
|
+
if (seedContent) {
|
|
61382
|
+
const seedTasks = extractFromSeed(seedContent);
|
|
61383
|
+
result.tasks.push(...seedTasks);
|
|
61384
|
+
}
|
|
61385
|
+
}
|
|
61386
|
+
result.tasks = deduplicateTasks(result.tasks);
|
|
61387
|
+
result.tasks = orderByDependencies(result.tasks);
|
|
61388
|
+
result.tasks = result.tasks.map((task, index) => ({
|
|
61389
|
+
...task,
|
|
61390
|
+
id: task.id || `task-${(index + 1).toString().padStart(3, "0")}`
|
|
61391
|
+
}));
|
|
61392
|
+
return result;
|
|
61393
|
+
}
|
|
61394
|
+
function extractFromPrd(content) {
|
|
61395
|
+
const tasks = [];
|
|
61396
|
+
const mvpSection = extractSection(content, "MVP\\s+Features|P1\\s+.*Features|Must\\s+Ship", { maxLength: 1e4 });
|
|
61397
|
+
if (mvpSection) {
|
|
61398
|
+
const featureMatches = mvpSection.matchAll(/^#{1,4}\s+(F-\d+):\s*(.+)$/gm);
|
|
61399
|
+
for (const match of featureMatches) {
|
|
61400
|
+
const featureId = match[1];
|
|
61401
|
+
const featureName = match[2]?.trim() ?? "";
|
|
61402
|
+
const acceptanceCriteria = extractAcceptanceCriteria(mvpSection, featureId ?? "");
|
|
61403
|
+
tasks.push({
|
|
61404
|
+
title: `Implement ${featureName}`,
|
|
61405
|
+
source: "PRD.md",
|
|
61406
|
+
sourceSection: `${featureId}: ${featureName}`,
|
|
61407
|
+
phase: "mvp",
|
|
61408
|
+
status: "pending",
|
|
61409
|
+
acceptanceCriteria,
|
|
61410
|
+
estimatedComplexity: estimateComplexity(featureName, acceptanceCriteria)
|
|
61411
|
+
});
|
|
61412
|
+
}
|
|
61413
|
+
}
|
|
61414
|
+
const frSection = extractSection(content, "Feature\\s+Requirements", { maxLength: 1e4 });
|
|
61415
|
+
if (frSection) {
|
|
61416
|
+
const frMatches = frSection.matchAll(/\*{0,2}(FR-\d+):?\*{0,2}\s*(.+)/g);
|
|
61417
|
+
for (const match of frMatches) {
|
|
61418
|
+
const frId = match[1];
|
|
61419
|
+
const frName = match[2]?.replace(/\*+/g, "").trim() ?? "";
|
|
61420
|
+
if (!tasks.some((t) => t.title.toLowerCase().includes(frName.toLowerCase().slice(0, 20)))) {
|
|
61421
|
+
tasks.push({
|
|
61422
|
+
title: frName,
|
|
61423
|
+
source: "PRD.md",
|
|
61424
|
+
sourceSection: frId ?? "",
|
|
61425
|
+
phase: "mvp",
|
|
61426
|
+
status: "pending",
|
|
61427
|
+
acceptanceCriteria: [],
|
|
61428
|
+
estimatedComplexity: "medium"
|
|
61429
|
+
});
|
|
61430
|
+
}
|
|
61431
|
+
}
|
|
61432
|
+
}
|
|
61433
|
+
const storiesSection = extractSection(content, "User\\s+Stories|Stories", { maxLength: 1e4 });
|
|
61434
|
+
if (storiesSection) {
|
|
61435
|
+
const storyMatches = storiesSection.matchAll(/As\s+a[n]?\s+([^,]+),\s*I\s+want\s+([^,]+?)(?:,\s*so\s+that|$)/gi);
|
|
61436
|
+
for (const match of storyMatches) {
|
|
61437
|
+
const role = match[1]?.trim() ?? "";
|
|
61438
|
+
const want = match[2]?.trim() ?? "";
|
|
61439
|
+
const title = `${want.charAt(0).toUpperCase() + want.slice(1)}`;
|
|
61440
|
+
if (!tasks.some((t) => t.title.toLowerCase().includes(want.toLowerCase().slice(0, 15)))) {
|
|
61441
|
+
tasks.push({
|
|
61442
|
+
title,
|
|
61443
|
+
description: `As a ${role}, I want ${want}`,
|
|
61444
|
+
source: "PRD.md",
|
|
61445
|
+
sourceSection: "User Stories",
|
|
61446
|
+
phase: "mvp",
|
|
61447
|
+
status: "pending",
|
|
61448
|
+
acceptanceCriteria: [],
|
|
61449
|
+
estimatedComplexity: "medium"
|
|
61450
|
+
});
|
|
61451
|
+
}
|
|
61452
|
+
}
|
|
61453
|
+
}
|
|
61454
|
+
const journeySection = extractSection(content, "Core\\s+User\\s+Journeys|User\\s+Journeys", { maxLength: 5e3 });
|
|
61455
|
+
if (journeySection) {
|
|
61456
|
+
const journeyMatches = journeySection.matchAll(/^#{1,4}\s+\d+\.\d+\s+Journey\s+\d+\s*[—-]\s*(.+)$/gm);
|
|
61457
|
+
for (const match of journeyMatches) {
|
|
61458
|
+
const journeyName = match[1]?.trim() ?? "";
|
|
61459
|
+
if (!tasks.some((t) => t.title.toLowerCase().includes(journeyName.toLowerCase().slice(0, 15)))) {
|
|
61460
|
+
tasks.push({
|
|
61461
|
+
title: `Implement ${journeyName} journey`,
|
|
61462
|
+
source: "PRD.md",
|
|
61463
|
+
sourceSection: "User Journeys",
|
|
61464
|
+
phase: "mvp",
|
|
61465
|
+
status: "pending",
|
|
61466
|
+
acceptanceCriteria: [],
|
|
61467
|
+
estimatedComplexity: "high"
|
|
61468
|
+
});
|
|
61469
|
+
}
|
|
61470
|
+
}
|
|
61471
|
+
}
|
|
61472
|
+
return tasks;
|
|
61473
|
+
}
|
|
61474
|
+
function extractFromRoadmap(content) {
|
|
61475
|
+
const phases = [];
|
|
61476
|
+
const phaseTasks = [];
|
|
61477
|
+
const seenPhases = /* @__PURE__ */ new Set();
|
|
61478
|
+
const phaseMatches = content.matchAll(/^##\s+\d+\.\s+Phase\s+(\d+)\s*[—-]+\s*(.+)$/gm);
|
|
61479
|
+
for (const match of phaseMatches) {
|
|
61480
|
+
const phaseNum = match[1] ?? "";
|
|
61481
|
+
const phaseName = match[2]?.trim().replace(/\s*\([^)]+\)\s*$/, "") ?? "";
|
|
61482
|
+
if (seenPhases.has(phaseNum)) continue;
|
|
61483
|
+
seenPhases.add(phaseNum);
|
|
61484
|
+
const phaseType = phaseNum === "1" ? "foundation" : phaseNum === "2" ? "mvp" : phaseNum === "3" ? "launch" : `phase${phaseNum}`;
|
|
61485
|
+
const phaseRegex = new RegExp(
|
|
61486
|
+
`##\\s+\\d+\\.\\s+Phase\\s+${phaseNum}\\s*[\u2014-][\\s\\S]*?(?=##\\s+\\d+\\.\\s+Phase\\s+\\d+|$)`,
|
|
61487
|
+
"i"
|
|
61488
|
+
);
|
|
61489
|
+
const phaseSectionMatch = content.match(phaseRegex);
|
|
61490
|
+
const deliverables = [];
|
|
61491
|
+
if (phaseSectionMatch) {
|
|
61492
|
+
const phaseContent = phaseSectionMatch[0] ?? "";
|
|
61493
|
+
const deliverablesRegex = /###\s+[\d.]+\s*Deliverables[\s\S]*?(?=###|##|$)/i;
|
|
61494
|
+
const deliverablesMatch = phaseContent.match(deliverablesRegex);
|
|
61495
|
+
if (deliverablesMatch) {
|
|
61496
|
+
const bullets = deliverablesMatch[0]?.match(/^[-*]\s+(.+)$/gm);
|
|
61497
|
+
if (bullets) {
|
|
61498
|
+
bullets.slice(0, 8).forEach((b) => {
|
|
61499
|
+
const text = b.replace(/^[-*]\s+/, "").trim();
|
|
61500
|
+
if (text.length > 3) {
|
|
61501
|
+
deliverables.push(text);
|
|
61502
|
+
phaseTasks.push({
|
|
61503
|
+
title: text,
|
|
61504
|
+
source: "ROADMAP.md",
|
|
61505
|
+
sourceSection: `Phase ${phaseNum}: ${phaseName}`,
|
|
61506
|
+
phase: phaseType,
|
|
61507
|
+
status: "pending",
|
|
61508
|
+
acceptanceCriteria: [],
|
|
61509
|
+
estimatedComplexity: "medium"
|
|
61510
|
+
});
|
|
61511
|
+
}
|
|
61512
|
+
});
|
|
61513
|
+
}
|
|
61514
|
+
}
|
|
61515
|
+
if (deliverables.length === 0) {
|
|
61516
|
+
const featureHeaders = phaseContent.match(/^####\s+[A-Z]\)\s+(.+)$/gm);
|
|
61517
|
+
if (featureHeaders) {
|
|
61518
|
+
featureHeaders.slice(0, 6).forEach((h) => {
|
|
61519
|
+
const hMatch = h.match(/####\s+[A-Z]\)\s+(.+)/);
|
|
61520
|
+
if (hMatch && hMatch[1]) {
|
|
61521
|
+
const title = hMatch[1].trim();
|
|
61522
|
+
deliverables.push(title);
|
|
61523
|
+
phaseTasks.push({
|
|
61524
|
+
title: `Implement ${title}`,
|
|
61525
|
+
source: "ROADMAP.md",
|
|
61526
|
+
sourceSection: `Phase ${phaseNum}: ${phaseName}`,
|
|
61527
|
+
phase: phaseType,
|
|
61528
|
+
status: "pending",
|
|
61529
|
+
acceptanceCriteria: [],
|
|
61530
|
+
estimatedComplexity: "high"
|
|
61531
|
+
});
|
|
61532
|
+
}
|
|
61533
|
+
});
|
|
62385
61534
|
}
|
|
62386
61535
|
}
|
|
62387
|
-
|
|
62388
|
-
|
|
62389
|
-
|
|
62390
|
-
|
|
62391
|
-
|
|
62392
|
-
|
|
62393
|
-
|
|
62394
|
-
|
|
62395
|
-
|
|
61536
|
+
const exitCriteriaRegex = /###\s+[\d.]+\s*Exit\s+Criteria[\s\S]*?(?=###|##|$)/i;
|
|
61537
|
+
const exitMatch = phaseContent.match(exitCriteriaRegex);
|
|
61538
|
+
if (exitMatch) {
|
|
61539
|
+
const checkItems = exitMatch[0]?.match(/^[-*]\s+[✅✓⬜]\s*(.+)$/gm);
|
|
61540
|
+
if (checkItems) {
|
|
61541
|
+
checkItems.forEach((item) => {
|
|
61542
|
+
const text = item.replace(/^[-*]\s+[✅✓⬜]\s*/, "").trim();
|
|
61543
|
+
if (text.length > 5 && !deliverables.includes(text)) {
|
|
61544
|
+
deliverables.push(text);
|
|
61545
|
+
}
|
|
61546
|
+
});
|
|
62396
61547
|
}
|
|
62397
|
-
} catch {
|
|
62398
|
-
checks.push({ name: "API Connection", status: apiFailLevel, message: "Connection failed" });
|
|
62399
61548
|
}
|
|
62400
61549
|
}
|
|
62401
|
-
|
|
62402
|
-
|
|
62403
|
-
|
|
62404
|
-
|
|
62405
|
-
|
|
62406
|
-
|
|
62407
|
-
|
|
61550
|
+
phases.push({
|
|
61551
|
+
phase: phaseNum,
|
|
61552
|
+
name: phaseName,
|
|
61553
|
+
type: phaseType,
|
|
61554
|
+
deliverables
|
|
61555
|
+
});
|
|
61556
|
+
}
|
|
61557
|
+
return { phases, phaseTasks };
|
|
61558
|
+
}
|
|
61559
|
+
function extractFromTechnicalSpec(content) {
|
|
61560
|
+
const tasks = [];
|
|
61561
|
+
const dbSection = extractSection(content, "Data\\s+Model|Database|Key\\s+tables", { maxLength: 3e3 });
|
|
61562
|
+
if (dbSection) {
|
|
61563
|
+
const tableMatches = dbSection.match(/^\s*[-*]\s*`(\w+)`/gm);
|
|
61564
|
+
if (tableMatches && tableMatches.length > 0) {
|
|
61565
|
+
const tableNames = tableMatches.slice(0, 5).map((m) => m.match(/`(\w+)`/)?.[1]).filter((name) => Boolean(name)).join(", ");
|
|
61566
|
+
tasks.push({
|
|
61567
|
+
title: "Set up database schema with Prisma",
|
|
61568
|
+
description: `Create database models for: ${tableNames}`,
|
|
61569
|
+
source: "TECHNICAL_SPEC.md",
|
|
61570
|
+
sourceSection: "Database Schema",
|
|
61571
|
+
phase: "foundation",
|
|
61572
|
+
status: "pending",
|
|
61573
|
+
acceptanceCriteria: [
|
|
61574
|
+
"Prisma schema defined with all models",
|
|
61575
|
+
"Database migrations generated",
|
|
61576
|
+
"Seed data scripts created"
|
|
61577
|
+
],
|
|
61578
|
+
estimatedComplexity: "high"
|
|
61579
|
+
});
|
|
62408
61580
|
}
|
|
62409
|
-
|
|
62410
|
-
|
|
62411
|
-
|
|
62412
|
-
|
|
62413
|
-
|
|
62414
|
-
|
|
62415
|
-
|
|
62416
|
-
|
|
62417
|
-
|
|
62418
|
-
checks
|
|
62419
|
-
}));
|
|
62420
|
-
} else {
|
|
62421
|
-
for (const check3 of checks) {
|
|
62422
|
-
const icon = check3.status === "pass" ? "+" : check3.status === "fail" ? "x" : "!";
|
|
62423
|
-
if (check3.status === "pass") {
|
|
62424
|
-
print.success(`[${icon}] ${check3.name}: ${check3.message}`);
|
|
62425
|
-
} else if (check3.status === "fail") {
|
|
62426
|
-
print.error(`[${icon}] ${check3.name}: ${check3.message}`);
|
|
62427
|
-
} else {
|
|
62428
|
-
print.warning(`[${icon}] ${check3.name}: ${check3.message}`);
|
|
62429
|
-
}
|
|
61581
|
+
}
|
|
61582
|
+
const apiSection = extractSection(content, "API\\s+Routes|Application\\s+Structure", { maxLength: 3e3 });
|
|
61583
|
+
if (apiSection) {
|
|
61584
|
+
const apiGroups = /* @__PURE__ */ new Map();
|
|
61585
|
+
const apiMatches = apiSection.matchAll(/`\/api\/([^`/]+)/g);
|
|
61586
|
+
for (const match of apiMatches) {
|
|
61587
|
+
const group = match[1];
|
|
61588
|
+
if (group && !apiGroups.has(group)) {
|
|
61589
|
+
apiGroups.set(group, []);
|
|
62430
61590
|
}
|
|
62431
|
-
|
|
62432
|
-
|
|
62433
|
-
|
|
62434
|
-
|
|
61591
|
+
}
|
|
61592
|
+
if (apiGroups.size > 0) {
|
|
61593
|
+
tasks.push({
|
|
61594
|
+
title: "Implement core API endpoints",
|
|
61595
|
+
description: `Set up API routes for: ${[...apiGroups.keys()].slice(0, 5).join(", ")}`,
|
|
61596
|
+
source: "TECHNICAL_SPEC.md",
|
|
61597
|
+
sourceSection: "API Routes",
|
|
61598
|
+
phase: "mvp",
|
|
61599
|
+
status: "pending",
|
|
61600
|
+
acceptanceCriteria: [
|
|
61601
|
+
"All CRUD endpoints implemented",
|
|
61602
|
+
"Input validation with Zod",
|
|
61603
|
+
"Error handling middleware",
|
|
61604
|
+
"API documentation updated"
|
|
61605
|
+
],
|
|
61606
|
+
estimatedComplexity: "high"
|
|
61607
|
+
});
|
|
61608
|
+
}
|
|
61609
|
+
}
|
|
61610
|
+
const archSection = extractSection(content, "Core\\s+system\\s+components|Architecture", { maxLength: 2e3 });
|
|
61611
|
+
if (archSection) {
|
|
61612
|
+
const compMatches = archSection.match(/^\s*\d+\)\s*(.+)$/gm);
|
|
61613
|
+
if (compMatches && compMatches.length > 0) {
|
|
61614
|
+
tasks.push({
|
|
61615
|
+
title: "Set up project architecture and scaffolding",
|
|
61616
|
+
description: "Configure project structure, linting, testing, and CI/CD",
|
|
61617
|
+
source: "TECHNICAL_SPEC.md",
|
|
61618
|
+
sourceSection: "Architecture",
|
|
61619
|
+
phase: "foundation",
|
|
61620
|
+
status: "pending",
|
|
61621
|
+
acceptanceCriteria: [
|
|
61622
|
+
"Project structure follows conventions",
|
|
61623
|
+
"ESLint and Prettier configured",
|
|
61624
|
+
"Testing framework set up",
|
|
61625
|
+
"CI/CD pipeline configured"
|
|
61626
|
+
],
|
|
61627
|
+
estimatedComplexity: "medium",
|
|
61628
|
+
dependencies: []
|
|
61629
|
+
});
|
|
61630
|
+
}
|
|
61631
|
+
}
|
|
61632
|
+
const integrations = extractIntegrations(content);
|
|
61633
|
+
const intCategories = Object.entries(integrations).filter(([, v]) => v.length > 0);
|
|
61634
|
+
if (intCategories.length > 0) {
|
|
61635
|
+
for (const [category, services] of intCategories) {
|
|
61636
|
+
if (services.length > 0) {
|
|
61637
|
+
tasks.push({
|
|
61638
|
+
title: `Integrate ${category} services (${services.join(", ")})`,
|
|
61639
|
+
source: "TECHNICAL_SPEC.md",
|
|
61640
|
+
sourceSection: "Integrations",
|
|
61641
|
+
phase: "mvp",
|
|
61642
|
+
status: "pending",
|
|
61643
|
+
acceptanceCriteria: services.map((s) => `${s} integration working`),
|
|
61644
|
+
estimatedComplexity: services.length > 2 ? "high" : "medium"
|
|
61645
|
+
});
|
|
62435
61646
|
}
|
|
62436
61647
|
}
|
|
62437
|
-
|
|
62438
|
-
|
|
62439
|
-
|
|
62440
|
-
|
|
62441
|
-
|
|
62442
|
-
|
|
61648
|
+
}
|
|
61649
|
+
return tasks;
|
|
61650
|
+
}
|
|
61651
|
+
function extractFromSeed(content) {
|
|
61652
|
+
const tasks = [];
|
|
61653
|
+
let taskIdCounter = 0;
|
|
61654
|
+
const nextId = () => {
|
|
61655
|
+
taskIdCounter++;
|
|
61656
|
+
return `task-${String(taskIdCounter).padStart(4, "0")}`;
|
|
61657
|
+
};
|
|
61658
|
+
const userStoryPattern = /^###\s+US-\d+\s*[—–-]\s*(.+)$/gm;
|
|
61659
|
+
const userStoryMatches = [...content.matchAll(userStoryPattern)];
|
|
61660
|
+
for (const usMatch of userStoryMatches) {
|
|
61661
|
+
const title = usMatch[1]?.trim() ?? "";
|
|
61662
|
+
if (!title) continue;
|
|
61663
|
+
const storyStart = (usMatch.index ?? 0) + usMatch[0].length;
|
|
61664
|
+
const nextHeading = content.slice(storyStart).search(/^###?\s+/m);
|
|
61665
|
+
const storyBlock = nextHeading === -1 ? content.slice(storyStart) : content.slice(storyStart, storyStart + nextHeading);
|
|
61666
|
+
const criteria = [];
|
|
61667
|
+
for (const acMatch of storyBlock.matchAll(/^-\s+\[[ xX]\]\s+(.+)$/gm)) {
|
|
61668
|
+
if (acMatch[1]) criteria.push(acMatch[1].trim());
|
|
61669
|
+
}
|
|
61670
|
+
tasks.push({
|
|
61671
|
+
id: nextId(),
|
|
61672
|
+
title,
|
|
61673
|
+
source: "SEED.md",
|
|
61674
|
+
sourceSection: "User Stories",
|
|
61675
|
+
phase: "mvp",
|
|
61676
|
+
status: "pending",
|
|
61677
|
+
acceptanceCriteria: criteria,
|
|
61678
|
+
estimatedComplexity: criteria.length > 5 ? "high" : criteria.length > 2 ? "medium" : "low"
|
|
61679
|
+
});
|
|
61680
|
+
}
|
|
61681
|
+
let buildSection = extractSection(content, "Build\\s+(?:Order|Priorities)", { maxLength: 1e4 });
|
|
61682
|
+
if (!buildSection) {
|
|
61683
|
+
const boldBuildMatch = content.match(/\*{2}Build\s+order:?\*{2}/i);
|
|
61684
|
+
if (boldBuildMatch) {
|
|
61685
|
+
const start = (boldBuildMatch.index ?? 0) + boldBuildMatch[0].length;
|
|
61686
|
+
const nextHeading = content.slice(start).search(/^#{1,4}\s+/m);
|
|
61687
|
+
buildSection = nextHeading === -1 ? content.slice(start, start + 1e4) : content.slice(start, start + nextHeading);
|
|
61688
|
+
buildSection = buildSection.trim();
|
|
61689
|
+
}
|
|
61690
|
+
}
|
|
61691
|
+
if (buildSection) {
|
|
61692
|
+
const buildSteps = [...buildSection.matchAll(/^\d+\.\s+\*{0,2}(.+?)\*{0,2}\s*(?:\(.*?\))?\s*$/gm)];
|
|
61693
|
+
for (const step of buildSteps) {
|
|
61694
|
+
const stepTitle = step[1]?.replace(/\*+/g, "").trim() ?? "";
|
|
61695
|
+
if (!stepTitle || stepTitle.length < 3) continue;
|
|
61696
|
+
const stepStart = (step.index ?? 0) + step[0].length;
|
|
61697
|
+
const nextStep = buildSection.slice(stepStart).search(/^\d+\.\s+/m);
|
|
61698
|
+
const stepBlock = nextStep === -1 ? buildSection.slice(stepStart) : buildSection.slice(stepStart, stepStart + nextStep);
|
|
61699
|
+
const criteria = [];
|
|
61700
|
+
for (const subMatch of stepBlock.matchAll(/^\s+-\s+(.+)$/gm)) {
|
|
61701
|
+
if (subMatch[1]) criteria.push(subMatch[1].trim());
|
|
61702
|
+
}
|
|
61703
|
+
const lowerTitle = stepTitle.toLowerCase();
|
|
61704
|
+
let phase = "mvp";
|
|
61705
|
+
if (lowerTitle.includes("setup") || lowerTitle.includes("scaffold") || lowerTitle.includes("config")) {
|
|
61706
|
+
phase = "foundation";
|
|
61707
|
+
} else if (lowerTitle.includes("auth") || lowerTitle.includes("database") || lowerTitle.includes("schema")) {
|
|
61708
|
+
phase = "foundation";
|
|
61709
|
+
}
|
|
61710
|
+
tasks.push({
|
|
61711
|
+
id: nextId(),
|
|
61712
|
+
title: stepTitle,
|
|
61713
|
+
source: "SEED.md",
|
|
61714
|
+
sourceSection: "Build Order",
|
|
61715
|
+
phase,
|
|
61716
|
+
status: "pending",
|
|
61717
|
+
acceptanceCriteria: criteria,
|
|
61718
|
+
estimatedComplexity: criteria.length > 4 ? "high" : "medium"
|
|
61719
|
+
});
|
|
61720
|
+
}
|
|
61721
|
+
}
|
|
61722
|
+
const roadmapPhasePattern = /^#{2,3}\s+(?:\d+\.\s+)?Phase\s+(\d+)\s*[:\-—–]+\s*(.+)$/gm;
|
|
61723
|
+
const roadmapMatches = [...content.matchAll(roadmapPhasePattern)];
|
|
61724
|
+
for (const rmMatch of roadmapMatches) {
|
|
61725
|
+
const phaseNum = parseInt(rmMatch[1] ?? "0", 10);
|
|
61726
|
+
const phaseName = rmMatch[2]?.trim() ?? "";
|
|
61727
|
+
if (!phaseName) continue;
|
|
61728
|
+
const phaseStart = (rmMatch.index ?? 0) + rmMatch[0].length;
|
|
61729
|
+
const nextPhase = content.slice(phaseStart).search(/^#{2,3}\s+(?:\d+\.\s+)?Phase\s+\d+/m);
|
|
61730
|
+
const phaseBlock = nextPhase === -1 ? content.slice(phaseStart, phaseStart + 5e3) : content.slice(phaseStart, phaseStart + nextPhase);
|
|
61731
|
+
const deliverablesMatch = phaseBlock.match(/\*{0,2}Deliverables?\*{0,2}\s*[:\n]/i);
|
|
61732
|
+
if (deliverablesMatch) {
|
|
61733
|
+
const delStart = (deliverablesMatch.index ?? 0) + deliverablesMatch[0].length;
|
|
61734
|
+
const delBlock = phaseBlock.slice(delStart, delStart + 3e3);
|
|
61735
|
+
const deliverables = [];
|
|
61736
|
+
for (const dMatch of delBlock.matchAll(/^[-*]\s+(.+)$/gm)) {
|
|
61737
|
+
if (dMatch[1] && !dMatch[1].startsWith("**")) deliverables.push(dMatch[1].trim());
|
|
61738
|
+
if (deliverables.length >= 15) break;
|
|
61739
|
+
}
|
|
61740
|
+
if (deliverables.length > 0) {
|
|
61741
|
+
const phase = phaseNum <= 1 ? "foundation" : phaseNum <= 6 ? "mvp" : "launch";
|
|
61742
|
+
tasks.push({
|
|
61743
|
+
id: nextId(),
|
|
61744
|
+
title: `Phase ${phaseNum}: ${phaseName}`,
|
|
61745
|
+
description: deliverables.slice(0, 5).join("; "),
|
|
61746
|
+
source: "SEED.md",
|
|
61747
|
+
sourceSection: "Roadmap",
|
|
61748
|
+
phase,
|
|
61749
|
+
status: "pending",
|
|
61750
|
+
acceptanceCriteria: deliverables,
|
|
61751
|
+
estimatedComplexity: deliverables.length > 5 ? "high" : "medium"
|
|
62443
61752
|
});
|
|
62444
|
-
} catch {
|
|
62445
61753
|
}
|
|
62446
61754
|
}
|
|
62447
|
-
|
|
62448
|
-
|
|
61755
|
+
}
|
|
61756
|
+
const mvpTableSection = extractSection(content, "MVP.*Must.Have|MVP.*P1|MVP.*Features", { maxLength: 8e3 });
|
|
61757
|
+
if (mvpTableSection) {
|
|
61758
|
+
const tableRows = [...mvpTableSection.matchAll(/^\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|/gm)];
|
|
61759
|
+
for (const row of tableRows) {
|
|
61760
|
+
const feature = row[1]?.trim() ?? "";
|
|
61761
|
+
const desc = row[2]?.trim() ?? "";
|
|
61762
|
+
if (!feature || feature.includes("---") || feature.toLowerCase() === "feature") continue;
|
|
61763
|
+
tasks.push({
|
|
61764
|
+
id: nextId(),
|
|
61765
|
+
title: feature,
|
|
61766
|
+
description: desc,
|
|
61767
|
+
source: "SEED.md",
|
|
61768
|
+
sourceSection: "MVP Features",
|
|
61769
|
+
phase: "mvp",
|
|
61770
|
+
status: "pending",
|
|
61771
|
+
acceptanceCriteria: [],
|
|
61772
|
+
estimatedComplexity: "medium"
|
|
61773
|
+
});
|
|
61774
|
+
}
|
|
61775
|
+
}
|
|
61776
|
+
if (tasks.length === 0) {
|
|
61777
|
+
const mvpSection = extractSection(content, "MVP\\s+Features", { maxLength: 5e3 });
|
|
61778
|
+
if (mvpSection) {
|
|
61779
|
+
const featureMatches = mvpSection.matchAll(/^\d+\.\s+(.+)$/gm);
|
|
61780
|
+
for (const match of featureMatches) {
|
|
61781
|
+
const featureName = match[1]?.trim() ?? "";
|
|
61782
|
+
tasks.push({
|
|
61783
|
+
id: nextId(),
|
|
61784
|
+
title: `Implement ${featureName}`,
|
|
61785
|
+
source: "SEED.md",
|
|
61786
|
+
sourceSection: "MVP Features",
|
|
61787
|
+
phase: "mvp",
|
|
61788
|
+
status: "pending",
|
|
61789
|
+
acceptanceCriteria: [],
|
|
61790
|
+
estimatedComplexity: "medium"
|
|
61791
|
+
});
|
|
61792
|
+
}
|
|
61793
|
+
}
|
|
61794
|
+
}
|
|
61795
|
+
const seen = /* @__PURE__ */ new Set();
|
|
61796
|
+
const deduped = tasks.filter((t) => {
|
|
61797
|
+
const key = t.title.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
61798
|
+
if (seen.has(key)) return false;
|
|
61799
|
+
seen.add(key);
|
|
61800
|
+
return true;
|
|
61801
|
+
});
|
|
61802
|
+
return deduped;
|
|
61803
|
+
}
|
|
61804
|
+
function extractMvpCriteria(content) {
|
|
61805
|
+
const criteria = [];
|
|
61806
|
+
const successSection = extractSection(content, "MVP\\s+Success|Success\\s+Criteria|Definition\\s+of\\s+Done", { maxLength: 3e3 });
|
|
61807
|
+
if (successSection) {
|
|
61808
|
+
const bulletMatches = successSection.match(/^[-*]\s+(.+)$/gm);
|
|
61809
|
+
if (bulletMatches) {
|
|
61810
|
+
bulletMatches.forEach((b) => {
|
|
61811
|
+
const text = b.replace(/^[-*]\s+/, "").replace(/\*+/g, "").trim();
|
|
61812
|
+
if (text.length > 5) {
|
|
61813
|
+
criteria.push({
|
|
61814
|
+
name: text,
|
|
61815
|
+
status: "pending"
|
|
61816
|
+
});
|
|
61817
|
+
}
|
|
61818
|
+
});
|
|
61819
|
+
}
|
|
61820
|
+
}
|
|
61821
|
+
const mvpSection = extractSection(content, "MVP\\s+Features|P1\\s+.*Features", { maxLength: 5e3 });
|
|
61822
|
+
if (mvpSection) {
|
|
61823
|
+
const featureHeaders = mvpSection.match(/^#{1,4}\s+F-\d+:\s*(.+)$/gm);
|
|
61824
|
+
if (featureHeaders) {
|
|
61825
|
+
featureHeaders.forEach((h) => {
|
|
61826
|
+
const match = h.match(/F-\d+:\s*(.+)/);
|
|
61827
|
+
if (match && match[1]) {
|
|
61828
|
+
criteria.push({
|
|
61829
|
+
name: match[1].trim(),
|
|
61830
|
+
status: "pending"
|
|
61831
|
+
});
|
|
61832
|
+
}
|
|
61833
|
+
});
|
|
61834
|
+
}
|
|
61835
|
+
}
|
|
61836
|
+
return criteria;
|
|
61837
|
+
}
|
|
61838
|
+
function orderByDependencies(tasks) {
|
|
61839
|
+
const foundationTasks = tasks.filter((t) => t.phase === "foundation");
|
|
61840
|
+
const mvpTasks = tasks.filter((t) => t.phase === "mvp");
|
|
61841
|
+
const launchTasks = tasks.filter((t) => t.phase === "launch");
|
|
61842
|
+
const otherTasks = tasks.filter((t) => !["foundation", "mvp", "launch"].includes(t.phase));
|
|
61843
|
+
const priorityOrder = [
|
|
61844
|
+
// Foundation tasks first
|
|
61845
|
+
...foundationTasks.filter((t) => t.title.toLowerCase().includes("setup") || t.title.toLowerCase().includes("scaffold")),
|
|
61846
|
+
...foundationTasks.filter((t) => t.title.toLowerCase().includes("database") || t.title.toLowerCase().includes("schema")),
|
|
61847
|
+
...foundationTasks.filter((t) => !t.title.toLowerCase().includes("setup") && !t.title.toLowerCase().includes("database")),
|
|
61848
|
+
// MVP tasks
|
|
61849
|
+
...mvpTasks.filter((t) => t.title.toLowerCase().includes("auth")),
|
|
61850
|
+
...mvpTasks.filter((t) => t.title.toLowerCase().includes("api")),
|
|
61851
|
+
...mvpTasks.filter((t) => !t.title.toLowerCase().includes("auth") && !t.title.toLowerCase().includes("api")),
|
|
61852
|
+
// Launch tasks
|
|
61853
|
+
...launchTasks,
|
|
61854
|
+
// Other tasks
|
|
61855
|
+
...otherTasks
|
|
61856
|
+
];
|
|
61857
|
+
return priorityOrder;
|
|
61858
|
+
}
|
|
61859
|
+
function deduplicateTasks(tasks) {
|
|
61860
|
+
const seen = /* @__PURE__ */ new Map();
|
|
61861
|
+
return tasks.filter((task) => {
|
|
61862
|
+
const normalizedTitle = task.title.toLowerCase().replace(/^implement\s+/i, "").replace(/\s+/g, " ").trim();
|
|
61863
|
+
const key = normalizedTitle.slice(0, 30);
|
|
61864
|
+
const existing = seen.get(key);
|
|
61865
|
+
if (existing) {
|
|
61866
|
+
if ((task.acceptanceCriteria?.length ?? 0) > (existing.acceptanceCriteria?.length ?? 0)) {
|
|
61867
|
+
existing.acceptanceCriteria = task.acceptanceCriteria;
|
|
61868
|
+
}
|
|
61869
|
+
return false;
|
|
62449
61870
|
}
|
|
61871
|
+
seen.set(key, task);
|
|
61872
|
+
return true;
|
|
62450
61873
|
});
|
|
62451
61874
|
}
|
|
61875
|
+
function extractAcceptanceCriteria(content, featureId) {
|
|
61876
|
+
const criteria = [];
|
|
61877
|
+
const featureRegex = new RegExp(
|
|
61878
|
+
`#{1,4}\\s+${featureId}:[\\s\\S]*?(?=#{1,4}\\s+F-\\d+:|$)`,
|
|
61879
|
+
"i"
|
|
61880
|
+
);
|
|
61881
|
+
const featureMatch = content.match(featureRegex);
|
|
61882
|
+
if (featureMatch) {
|
|
61883
|
+
const featureContent = featureMatch[0] ?? "";
|
|
61884
|
+
const bulletMatches = featureContent.match(/^[-*]\s+(.+)$/gm);
|
|
61885
|
+
if (bulletMatches) {
|
|
61886
|
+
bulletMatches.slice(0, 5).forEach((b) => {
|
|
61887
|
+
const text = b.replace(/^[-*]\s+/, "").replace(/\*+/g, "").trim();
|
|
61888
|
+
if (text.length > 5 && text.length < 200) {
|
|
61889
|
+
criteria.push(text);
|
|
61890
|
+
}
|
|
61891
|
+
});
|
|
61892
|
+
}
|
|
61893
|
+
}
|
|
61894
|
+
return criteria;
|
|
61895
|
+
}
|
|
61896
|
+
function estimateComplexity(title, acceptanceCriteria = []) {
|
|
61897
|
+
const titleLower = title.toLowerCase();
|
|
61898
|
+
const highIndicators = [
|
|
61899
|
+
"authentication",
|
|
61900
|
+
"payment",
|
|
61901
|
+
"integration",
|
|
61902
|
+
"real-time",
|
|
61903
|
+
"voice",
|
|
61904
|
+
"agent",
|
|
61905
|
+
"workflow",
|
|
61906
|
+
"pipeline",
|
|
61907
|
+
"migration",
|
|
61908
|
+
"security",
|
|
61909
|
+
"encryption",
|
|
61910
|
+
"multi-tenant"
|
|
61911
|
+
];
|
|
61912
|
+
const lowIndicators = [
|
|
61913
|
+
"ui",
|
|
61914
|
+
"style",
|
|
61915
|
+
"button",
|
|
61916
|
+
"form",
|
|
61917
|
+
"display",
|
|
61918
|
+
"show",
|
|
61919
|
+
"list",
|
|
61920
|
+
"view",
|
|
61921
|
+
"page",
|
|
61922
|
+
"component",
|
|
61923
|
+
"typo",
|
|
61924
|
+
"fix"
|
|
61925
|
+
];
|
|
61926
|
+
if (highIndicators.some((i) => titleLower.includes(i))) {
|
|
61927
|
+
return "high";
|
|
61928
|
+
}
|
|
61929
|
+
if (lowIndicators.some((i) => titleLower.includes(i))) {
|
|
61930
|
+
return "low";
|
|
61931
|
+
}
|
|
61932
|
+
if (acceptanceCriteria.length > 5) {
|
|
61933
|
+
return "high";
|
|
61934
|
+
}
|
|
61935
|
+
return "medium";
|
|
61936
|
+
}
|
|
61937
|
+
function extractSection(content, headingPattern, options = {}) {
|
|
61938
|
+
const { maxLength = 2e3 } = options;
|
|
61939
|
+
const headingRegex = new RegExp(`^(#{1,4})\\s*(?:\\d+\\.\\s*)?(?:${headingPattern})[^\\n]*\\n`, "im");
|
|
61940
|
+
const match = content.match(headingRegex);
|
|
61941
|
+
if (!match || !match[1]) return null;
|
|
61942
|
+
const startIndex = (match.index ?? 0) + match[0].length;
|
|
61943
|
+
const headingLevel = match[1].length;
|
|
61944
|
+
const restContent = content.slice(startIndex);
|
|
61945
|
+
const nextHeadingRegex = new RegExp(`^#{1,${headingLevel}}\\s+`, "m");
|
|
61946
|
+
const nextMatch = restContent.match(nextHeadingRegex);
|
|
61947
|
+
let sectionContent = nextMatch ? restContent.slice(0, nextMatch.index) : restContent;
|
|
61948
|
+
sectionContent = sectionContent.trim();
|
|
61949
|
+
if (sectionContent.length > maxLength) {
|
|
61950
|
+
sectionContent = sectionContent.slice(0, maxLength);
|
|
61951
|
+
}
|
|
61952
|
+
return sectionContent || null;
|
|
61953
|
+
}
|
|
61954
|
+
function extractIntegrations(content) {
|
|
61955
|
+
const integrations = {
|
|
61956
|
+
auth: [],
|
|
61957
|
+
payments: [],
|
|
61958
|
+
email: [],
|
|
61959
|
+
ai: []
|
|
61960
|
+
};
|
|
61961
|
+
if (/\bClerk\b/i.test(content)) integrations.auth.push("Clerk");
|
|
61962
|
+
if (/\bNextAuth|Auth\.js/i.test(content)) integrations.auth.push("NextAuth");
|
|
61963
|
+
if (/\bStripe\b/i.test(content)) integrations.payments.push("Stripe");
|
|
61964
|
+
if (/\bPaddle\b/i.test(content)) integrations.payments.push("Paddle");
|
|
61965
|
+
if (/\bSendGrid\b/i.test(content)) integrations.email.push("SendGrid");
|
|
61966
|
+
if (/\bResend\b/i.test(content)) integrations.email.push("Resend");
|
|
61967
|
+
if (/\bOpenAI\b|GPT-4/i.test(content)) integrations.ai.push("OpenAI");
|
|
61968
|
+
if (/\bAnthropic\b|Claude/i.test(content)) integrations.ai.push("Anthropic");
|
|
61969
|
+
return integrations;
|
|
61970
|
+
}
|
|
62452
61971
|
|
|
62453
61972
|
// src/commands/generate.ts
|
|
62454
|
-
init_cjs_shims();
|
|
62455
|
-
var fs57 = __toESM(require("fs"));
|
|
62456
|
-
var path58 = __toESM(require("path"));
|
|
62457
|
-
init_src();
|
|
62458
61973
|
function detectProjectConfig(cwd) {
|
|
62459
61974
|
const config3 = {
|
|
62460
61975
|
name: path58.basename(cwd),
|
|
@@ -63010,21 +62525,8 @@ function extractTasksFromContext(cwd, config3) {
|
|
|
63010
62525
|
}
|
|
63011
62526
|
let allTasks = [];
|
|
63012
62527
|
try {
|
|
63013
|
-
|
|
63014
|
-
|
|
63015
|
-
try {
|
|
63016
|
-
const extractor = (init_src2(), __toCommonJS(src_exports2));
|
|
63017
|
-
extractFromDocs2 = extractor.taskExtractor?.extractFromDocs || extractor.extractFromDocs;
|
|
63018
|
-
extractFromSeed2 = extractor.taskExtractor?.extractFromSeed || extractor.extractFromSeed;
|
|
63019
|
-
} catch {
|
|
63020
|
-
try {
|
|
63021
|
-
const extractor = (init_task_extractor(), __toCommonJS(task_extractor_exports));
|
|
63022
|
-
extractFromDocs2 = extractor.extractFromDocs;
|
|
63023
|
-
extractFromSeed2 = extractor.extractFromSeed;
|
|
63024
|
-
} catch {
|
|
63025
|
-
return null;
|
|
63026
|
-
}
|
|
63027
|
-
}
|
|
62528
|
+
const extractFromDocs2 = extractFromDocs;
|
|
62529
|
+
const extractFromSeed2 = extractFromSeed;
|
|
63028
62530
|
if (extractFromDocs2) {
|
|
63029
62531
|
const result = extractFromDocs2(docs);
|
|
63030
62532
|
if (result?.tasks) allTasks.push(...result.tasks);
|
|
@@ -66864,33 +66366,70 @@ ${COLORS.bold}Detected Features:${COLORS.reset}`);
|
|
|
66864
66366
|
try {
|
|
66865
66367
|
let extractFromSeed2;
|
|
66866
66368
|
try {
|
|
66867
|
-
|
|
66868
|
-
extractFromSeed2 = extractor.taskExtractor?.extractFromSeed || extractor.extractFromSeed;
|
|
66369
|
+
extractFromSeed2 = extractFromSeed;
|
|
66869
66370
|
} catch {
|
|
66870
|
-
|
|
66871
|
-
const
|
|
66872
|
-
|
|
66873
|
-
|
|
66874
|
-
|
|
66875
|
-
|
|
66876
|
-
|
|
66877
|
-
|
|
66878
|
-
|
|
66879
|
-
counter
|
|
66880
|
-
|
|
66881
|
-
|
|
66882
|
-
|
|
66883
|
-
|
|
66884
|
-
|
|
66885
|
-
|
|
66886
|
-
|
|
66887
|
-
|
|
66888
|
-
estimatedComplexity: "medium"
|
|
66889
|
-
});
|
|
66890
|
-
}
|
|
66891
|
-
return tasks;
|
|
66371
|
+
extractFromSeed2 = (content) => {
|
|
66372
|
+
const tasks = [];
|
|
66373
|
+
let counter = 0;
|
|
66374
|
+
const addTask = (title, section, phase = "mvp") => {
|
|
66375
|
+
const clean = title.replace(/\*+/g, "").replace(/\s+/g, " ").trim();
|
|
66376
|
+
if (!clean || clean.length < 5) return;
|
|
66377
|
+
if (tasks.some((t) => t.title.toLowerCase().startsWith(clean.toLowerCase().slice(0, 20)))) return;
|
|
66378
|
+
counter++;
|
|
66379
|
+
tasks.push({
|
|
66380
|
+
id: `task-${String(counter).padStart(4, "0")}`,
|
|
66381
|
+
title: clean,
|
|
66382
|
+
source: "context",
|
|
66383
|
+
sourceSection: section,
|
|
66384
|
+
phase,
|
|
66385
|
+
status: "pending",
|
|
66386
|
+
acceptanceCriteria: [],
|
|
66387
|
+
estimatedComplexity: "medium"
|
|
66388
|
+
});
|
|
66892
66389
|
};
|
|
66893
|
-
|
|
66390
|
+
for (const m of content.matchAll(/^###\s+US-\d+\s*[—–-]\s*(.+)$/gm)) {
|
|
66391
|
+
addTask(m[1]?.trim() ?? "", "User Stories");
|
|
66392
|
+
}
|
|
66393
|
+
for (const m of content.matchAll(/^#{1,4}\s+(?:F-\d+|Feature)\s*:\s*(.+)$/gm)) {
|
|
66394
|
+
addTask(`Implement ${m[1]?.trim() ?? ""}`, "Features");
|
|
66395
|
+
}
|
|
66396
|
+
for (const m of content.matchAll(/\*{0,2}(FR-\d+):?\*{0,2}\s+(.+)/g)) {
|
|
66397
|
+
addTask(m[2]?.replace(/\*+/g, "").trim() ?? "", "Feature Requirements");
|
|
66398
|
+
}
|
|
66399
|
+
for (const m of content.matchAll(/^##\s+(?:\d+\.\s+)?Phase\s+(\d+)\s*[—–-]+\s*(.+)$/gm)) {
|
|
66400
|
+
const phaseNum = m[1] ?? "";
|
|
66401
|
+
const phaseName = m[2]?.trim().replace(/\s*\([^)]+\)\s*$/, "") ?? "";
|
|
66402
|
+
const phaseType = phaseNum === "1" ? "foundation" : phaseNum === "2" ? "mvp" : "launch";
|
|
66403
|
+
addTask(`${phaseName}`, `Phase ${phaseNum}`, phaseType);
|
|
66404
|
+
}
|
|
66405
|
+
const buildMatch = content.match(/(?:^##\s+.*(?:Build\s+Order|Implementation|Sprint).*$|^\*\*Build\s+order:\*\*)/im);
|
|
66406
|
+
if (buildMatch) {
|
|
66407
|
+
const startIdx = content.indexOf(buildMatch[0]);
|
|
66408
|
+
const section = content.slice(startIdx, startIdx + 3e3);
|
|
66409
|
+
for (const m of section.matchAll(/^\d+\.\s+(.+)$/gm)) {
|
|
66410
|
+
addTask(m[1]?.trim() ?? "", "Build Order", "foundation");
|
|
66411
|
+
}
|
|
66412
|
+
}
|
|
66413
|
+
for (const m of content.matchAll(/^\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|/gm)) {
|
|
66414
|
+
const name = m[1]?.trim() ?? "";
|
|
66415
|
+
if (name && !name.match(/^[-]+$/) && name !== "Feature" && name !== "Name" && name.length > 3) {
|
|
66416
|
+
addTask(`Implement ${name}`, "MVP Features");
|
|
66417
|
+
}
|
|
66418
|
+
}
|
|
66419
|
+
for (const m of content.matchAll(/^##\s+(.+)$/gm)) {
|
|
66420
|
+
const sectionTitle = m[1]?.trim() ?? "";
|
|
66421
|
+
const sectionStart = content.indexOf(m[0]);
|
|
66422
|
+
const nextSection = content.indexOf("\n## ", sectionStart + 1);
|
|
66423
|
+
const sectionEnd = nextSection > -1 ? nextSection : sectionStart + 5e3;
|
|
66424
|
+
const sectionContent = content.slice(sectionStart, sectionEnd);
|
|
66425
|
+
if (/implementation|tasks|deliverables|features|requirements|sprint/i.test(sectionTitle)) {
|
|
66426
|
+
for (const item of sectionContent.matchAll(/^[-*]\s+(.{10,80})$/gm)) {
|
|
66427
|
+
addTask(item[1]?.trim() ?? "", sectionTitle);
|
|
66428
|
+
}
|
|
66429
|
+
}
|
|
66430
|
+
}
|
|
66431
|
+
return tasks;
|
|
66432
|
+
};
|
|
66894
66433
|
}
|
|
66895
66434
|
for (const content of contextContent) {
|
|
66896
66435
|
const extracted = extractFromSeed2(content);
|