@weavelogic/knowledge-graph-agent 0.12.0 → 0.12.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.
@@ -1,5 +1,6 @@
1
- import { existsSync, mkdirSync, writeFileSync, readdirSync, statSync, readFileSync } from "fs";
2
- import { join, extname, relative } from "path";
1
+ import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync, statSync } from "fs";
2
+ import { join, basename, relative, extname } from "path";
3
+ import matter from "gray-matter";
3
4
  import { createLogger } from "../utils/logger.js";
4
5
  import { createDecisionLogManager } from "./decision-log.js";
5
6
  import { createConsensusBuilder, ConsensusBuilder } from "./consensus.js";
@@ -10,9 +11,11 @@ class SPARCPlanner {
10
11
  plan;
11
12
  decisionLog;
12
13
  consensusBuilder;
14
+ parsedDocs = [];
13
15
  constructor(options) {
14
16
  this.options = {
15
17
  outputDir: join(options.projectRoot, ".sparc"),
18
+ docsDir: "docs",
16
19
  parallelResearch: true,
17
20
  reviewPasses: 3,
18
21
  autoConsensus: true,
@@ -31,7 +34,8 @@ class SPARCPlanner {
31
34
  });
32
35
  logger.info("SPARC Planner initialized", {
33
36
  planId: this.plan.id,
34
- projectRoot: this.options.projectRoot
37
+ projectRoot: this.options.projectRoot,
38
+ docsDir: this.options.docsDir
35
39
  });
36
40
  }
37
41
  /**
@@ -112,7 +116,8 @@ class SPARCPlanner {
112
116
  logger.info("SPARC planning completed", {
113
117
  planId: this.plan.id,
114
118
  status: this.plan.status,
115
- tasks: this.plan.tasks.length
119
+ tasks: this.plan.tasks.length,
120
+ docsAnalyzed: this.parsedDocs.length
116
121
  });
117
122
  return this.plan;
118
123
  } catch (error) {
@@ -122,134 +127,251 @@ class SPARCPlanner {
122
127
  }
123
128
  }
124
129
  /**
125
- * Execute research phase
130
+ * Execute research phase - read and parse all documentation
126
131
  */
127
132
  async executeResearchPhase() {
128
133
  logger.info("Executing research phase");
129
134
  this.plan.currentPhase = "specification";
135
+ const docsPath = join(this.options.projectRoot, this.options.docsDir);
136
+ if (existsSync(docsPath)) {
137
+ this.parsedDocs = await this.readDocsDirectory(docsPath);
138
+ for (const doc of this.parsedDocs) {
139
+ const finding = this.createFindingFromDoc(doc);
140
+ this.plan.researchFindings.push(finding);
141
+ }
142
+ this.addDecision(
143
+ "Documentation Analysis",
144
+ `Analyzed ${this.parsedDocs.length} documentation files from ${this.options.docsDir}/`,
145
+ "specification",
146
+ "high",
147
+ `Found ${this.parsedDocs.filter((d) => d.type === "feature").length} feature docs, ${this.parsedDocs.filter((d) => d.type === "requirement").length} requirement docs, ${this.parsedDocs.filter((d) => d.type === "architecture").length} architecture docs`
148
+ );
149
+ } else {
150
+ logger.warn("No docs directory found", { docsPath });
151
+ }
130
152
  const srcPath = join(this.options.projectRoot, "src");
131
153
  if (existsSync(srcPath)) {
132
154
  this.plan.existingCode = await this.analyzeExistingCode(srcPath);
133
155
  this.addDecision(
134
156
  "Existing Code Integration",
135
- "Integrate with existing codebase in src/",
157
+ `Found ${this.plan.existingCode.fileCount} files in src/`,
136
158
  "specification",
137
159
  "high",
138
- "Existing code found and analyzed for patterns and integration points",
139
- ["Start from scratch", "Partial rewrite", "Full integration"]
160
+ `Languages: ${Object.entries(this.plan.existingCode.languages).map(([k, v]) => `${k}: ${v}`).join(", ")}`
140
161
  );
141
162
  }
142
- const researchTasks = this.defineResearchTasks();
143
- for (const task of researchTasks) {
144
- const finding = await this.executeResearchTask(task);
145
- this.plan.researchFindings.push(finding);
146
- }
147
163
  logger.info("Research phase completed", {
164
+ docsAnalyzed: this.parsedDocs.length,
148
165
  findings: this.plan.researchFindings.length,
149
166
  hasExistingCode: !!this.plan.existingCode
150
167
  });
151
168
  }
152
169
  /**
153
- * Analyze existing code in src directory
170
+ * Read all documentation files from a directory
154
171
  */
155
- async analyzeExistingCode(srcPath) {
156
- const analysis = {
157
- hasSrcDirectory: true,
158
- srcPath,
159
- fileCount: 0,
160
- languages: {},
161
- keyFiles: [],
162
- patterns: [],
163
- dependencies: [],
164
- entryPoints: []
165
- };
166
- const analyzeDir = (dir) => {
172
+ async readDocsDirectory(docsPath) {
173
+ const docs = [];
174
+ const readDir = (dir) => {
167
175
  const entries = readdirSync(dir);
168
176
  for (const entry of entries) {
169
177
  const fullPath = join(dir, entry);
170
178
  const stat = statSync(fullPath);
171
179
  if (stat.isDirectory()) {
172
180
  if (!entry.startsWith(".") && entry !== "node_modules") {
173
- analyzeDir(fullPath);
181
+ readDir(fullPath);
174
182
  }
175
- } else if (stat.isFile()) {
176
- analysis.fileCount++;
177
- const ext = extname(entry);
178
- analysis.languages[ext] = (analysis.languages[ext] || 0) + 1;
179
- if (entry === "index.ts" || entry === "index.js") {
180
- analysis.entryPoints.push(relative(this.options.projectRoot, fullPath));
181
- }
182
- if (entry === "package.json") {
183
- try {
184
- const pkg = JSON.parse(readFileSync(fullPath, "utf-8"));
185
- analysis.dependencies = Object.keys(pkg.dependencies || {});
186
- } catch {
187
- }
183
+ } else if (stat.isFile() && (entry.endsWith(".md") || entry.endsWith(".mdx"))) {
184
+ try {
185
+ const doc = this.parseDocFile(fullPath);
186
+ docs.push(doc);
187
+ } catch (error) {
188
+ logger.warn("Failed to parse doc file", { path: fullPath, error });
188
189
  }
189
190
  }
190
191
  }
191
192
  };
192
- analyzeDir(srcPath);
193
- if (analysis.languages[".ts"] > 0 || analysis.languages[".tsx"] > 0) {
194
- analysis.patterns.push("TypeScript");
193
+ readDir(docsPath);
194
+ return docs;
195
+ }
196
+ /**
197
+ * Parse a markdown documentation file
198
+ */
199
+ parseDocFile(filePath) {
200
+ const content = readFileSync(filePath, "utf-8");
201
+ const { data: frontmatter, content: body } = matter(content);
202
+ const filename = basename(filePath);
203
+ const headings = this.extractHeadings(body);
204
+ const sections = this.extractSections(body, headings);
205
+ const codeBlocks = this.extractCodeBlocks(body);
206
+ const links = this.extractLinks(body);
207
+ let title = frontmatter.title || "";
208
+ if (!title && headings.length > 0) {
209
+ title = headings[0].text;
195
210
  }
196
- if (existsSync(join(this.options.projectRoot, "tests")) || existsSync(join(this.options.projectRoot, "__tests__"))) {
197
- analysis.patterns.push("Test-Driven Development");
198
- analysis.testCoverage = { hasTests: true };
211
+ if (!title) {
212
+ title = filename.replace(/\.(md|mdx)$/, "");
199
213
  }
200
- return analysis;
214
+ const type = this.classifyDocument(filePath, frontmatter, title, body);
215
+ return {
216
+ path: filePath,
217
+ filename,
218
+ title,
219
+ content: body,
220
+ frontmatter,
221
+ headings,
222
+ sections,
223
+ codeBlocks,
224
+ links,
225
+ type
226
+ };
201
227
  }
202
228
  /**
203
- * Define research tasks
229
+ * Extract headings from markdown content
204
230
  */
205
- defineResearchTasks() {
206
- return [
207
- {
208
- id: "research-requirements",
209
- agentType: "researcher",
210
- description: "Research and analyze project requirements",
211
- topics: ["functional requirements", "non-functional requirements", "constraints"],
212
- context: [this.options.description]
213
- },
214
- {
215
- id: "research-patterns",
216
- agentType: "researcher",
217
- description: "Research applicable design patterns",
218
- topics: ["design patterns", "architecture patterns", "best practices"],
219
- context: this.plan.existingCode?.patterns || []
220
- },
221
- {
222
- id: "research-technology",
223
- agentType: "researcher",
224
- description: "Research technology choices and alternatives",
225
- topics: ["technology stack", "frameworks", "libraries"],
226
- context: this.plan.existingCode?.dependencies || []
231
+ extractHeadings(content) {
232
+ const headings = [];
233
+ const lines = content.split("\n");
234
+ lines.forEach((line, index) => {
235
+ const match = line.match(/^(#{1,6})\s+(.+)$/);
236
+ if (match) {
237
+ headings.push({
238
+ level: match[1].length,
239
+ text: match[2].trim(),
240
+ line: index + 1
241
+ });
227
242
  }
228
- ];
243
+ });
244
+ return headings;
245
+ }
246
+ /**
247
+ * Extract sections from markdown content based on headings
248
+ */
249
+ extractSections(content, headings) {
250
+ const sections = [];
251
+ const lines = content.split("\n");
252
+ for (let i = 0; i < headings.length; i++) {
253
+ const heading = headings[i];
254
+ const nextHeading = headings[i + 1];
255
+ const startLine = heading.line;
256
+ const endLine = nextHeading ? nextHeading.line - 1 : lines.length;
257
+ const sectionContent = lines.slice(startLine, endLine).join("\n").trim();
258
+ sections.push({
259
+ heading: heading.text,
260
+ content: sectionContent,
261
+ level: heading.level
262
+ });
263
+ }
264
+ return sections;
265
+ }
266
+ /**
267
+ * Extract code blocks from markdown
268
+ */
269
+ extractCodeBlocks(content) {
270
+ const blocks = [];
271
+ const regex = /```(\w*)\n([\s\S]*?)```/g;
272
+ let match;
273
+ while ((match = regex.exec(content)) !== null) {
274
+ blocks.push({
275
+ language: match[1] || "text",
276
+ code: match[2].trim()
277
+ });
278
+ }
279
+ return blocks;
229
280
  }
230
281
  /**
231
- * Execute a research task (stub - would spawn agent)
282
+ * Extract links from markdown
232
283
  */
233
- async executeResearchTask(task) {
284
+ extractLinks(content) {
285
+ const links = [];
286
+ const regex = /\[([^\]]+)\]\(([^)]+)\)/g;
287
+ let match;
288
+ while ((match = regex.exec(content)) !== null) {
289
+ links.push(match[2]);
290
+ }
291
+ return links;
292
+ }
293
+ /**
294
+ * Classify document type based on content and path
295
+ */
296
+ classifyDocument(filePath, frontmatter, title, content) {
297
+ const lowerPath = filePath.toLowerCase();
298
+ const lowerTitle = title.toLowerCase();
299
+ const lowerContent = content.toLowerCase();
300
+ if (frontmatter.type) {
301
+ const type = String(frontmatter.type).toLowerCase();
302
+ if (type.includes("feature")) return "feature";
303
+ if (type.includes("require")) return "requirement";
304
+ if (type.includes("arch")) return "architecture";
305
+ if (type.includes("api")) return "api";
306
+ if (type.includes("spec")) return "spec";
307
+ if (type.includes("guide")) return "guide";
308
+ }
309
+ if (lowerPath.includes("/features/") || lowerPath.includes("/feature/")) return "feature";
310
+ if (lowerPath.includes("/requirements/") || lowerPath.includes("/req/")) return "requirement";
311
+ if (lowerPath.includes("/architecture/") || lowerPath.includes("/arch/")) return "architecture";
312
+ if (lowerPath.includes("/api/") || lowerPath.includes("/apis/")) return "api";
313
+ if (lowerPath.includes("/spec/") || lowerPath.includes("/specs/")) return "spec";
314
+ if (lowerPath.includes("/guide/") || lowerPath.includes("/guides/")) return "guide";
315
+ if (lowerTitle.includes("feature") || lowerTitle.includes("epic")) return "feature";
316
+ if (lowerTitle.includes("requirement") || lowerTitle.includes("req-")) return "requirement";
317
+ if (lowerTitle.includes("architecture") || lowerTitle.includes("design")) return "architecture";
318
+ if (lowerTitle.includes("api") || lowerTitle.includes("endpoint")) return "api";
319
+ if (lowerTitle.includes("spec")) return "spec";
320
+ const featureKeywords = ["user story", "as a user", "acceptance criteria", "feature:", "epic:"];
321
+ const reqKeywords = ["shall", "must", "requirement", "req-", "functional requirement", "non-functional"];
322
+ const archKeywords = ["component", "module", "service", "architecture", "system design", "data flow"];
323
+ const apiKeywords = ["endpoint", "request", "response", "http", "rest", "graphql", "method:"];
324
+ if (featureKeywords.some((kw) => lowerContent.includes(kw))) return "feature";
325
+ if (reqKeywords.some((kw) => lowerContent.includes(kw))) return "requirement";
326
+ if (archKeywords.some((kw) => lowerContent.includes(kw))) return "architecture";
327
+ if (apiKeywords.some((kw) => lowerContent.includes(kw))) return "api";
328
+ return "unknown";
329
+ }
330
+ /**
331
+ * Create a research finding from a parsed document
332
+ */
333
+ createFindingFromDoc(doc) {
234
334
  return {
235
- id: `finding_${task.id}`,
236
- agent: task.agentType,
237
- topic: task.topics[0],
238
- summary: `Research findings for ${task.description}`,
239
- details: `Detailed research results for topics: ${task.topics.join(", ")}`,
240
- confidence: "medium",
241
- evidence: task.context,
335
+ id: `finding_${doc.filename.replace(/[^a-z0-9]/gi, "_")}`,
336
+ agent: "doc-analyzer",
337
+ topic: doc.type,
338
+ summary: doc.title,
339
+ details: this.summarizeDocContent(doc),
340
+ confidence: doc.frontmatter.status === "approved" ? "high" : "medium",
341
+ evidence: [doc.path],
242
342
  relatedFindings: [],
243
343
  kgReferences: [],
344
+ vectorResults: [],
244
345
  timestamp: /* @__PURE__ */ new Date()
245
346
  };
246
347
  }
247
348
  /**
248
- * Execute specification phase
349
+ * Summarize document content
350
+ */
351
+ summarizeDocContent(doc) {
352
+ const lines = [];
353
+ lines.push(`**Document Type:** ${doc.type}`);
354
+ lines.push(`**File:** ${relative(this.options.projectRoot, doc.path)}`);
355
+ if (doc.headings.length > 0) {
356
+ lines.push(`**Sections:** ${doc.headings.map((h) => h.text).join(", ")}`);
357
+ }
358
+ if (doc.codeBlocks.length > 0) {
359
+ lines.push(`**Code Examples:** ${doc.codeBlocks.length} blocks (${[...new Set(doc.codeBlocks.map((b) => b.language))].join(", ")})`);
360
+ }
361
+ const contentPreview = doc.content.substring(0, 500).replace(/\n/g, " ").trim();
362
+ if (contentPreview) {
363
+ lines.push(`**Preview:** ${contentPreview}...`);
364
+ }
365
+ return lines.join("\n");
366
+ }
367
+ /**
368
+ * Execute specification phase - extract requirements and features from docs
249
369
  */
250
370
  async executeSpecificationPhase() {
251
371
  logger.info("Executing specification phase");
252
372
  this.plan.currentPhase = "specification";
373
+ const requirements = this.extractRequirementsFromDocs();
374
+ const features = this.extractFeaturesFromDocs();
253
375
  const spec = {
254
376
  id: `spec_${this.plan.id}`,
255
377
  projectName: this.options.name,
@@ -259,27 +381,364 @@ class SPARCPlanner {
259
381
  summary: this.options.description,
260
382
  problemStatement: this.extractProblemStatement(),
261
383
  goals: this.extractGoals(),
262
- requirements: this.generateRequirements(),
263
- features: this.generateFeatures(),
384
+ requirements,
385
+ features,
264
386
  constraints: this.extractConstraints(),
265
- assumptions: [],
387
+ assumptions: this.extractAssumptions(),
266
388
  outOfScope: [],
267
- successMetrics: this.generateSuccessMetrics(),
389
+ successMetrics: this.extractSuccessMetrics(),
268
390
  kgReferences: []
269
391
  };
270
392
  this.plan.specification = spec;
271
393
  this.addDecision(
272
- "Requirements Definition",
273
- `Defined ${spec.requirements.length} requirements and ${spec.features.length} features`,
394
+ "Requirements Extracted",
395
+ `Extracted ${requirements.length} requirements and ${features.length} features from documentation`,
274
396
  "specification",
275
- "high",
276
- "Requirements derived from research findings and project description"
397
+ requirements.length > 0 ? "high" : "low",
398
+ `Sources: ${this.parsedDocs.filter((d) => d.type === "requirement" || d.type === "feature").map((d) => d.filename).join(", ")}`
277
399
  );
278
400
  logger.info("Specification phase completed", {
279
- requirements: spec.requirements.length,
280
- features: spec.features.length
401
+ requirements: requirements.length,
402
+ features: features.length
281
403
  });
282
404
  }
405
+ /**
406
+ * Extract requirements from documentation
407
+ */
408
+ extractRequirementsFromDocs() {
409
+ const requirements = [];
410
+ let reqCounter = 1;
411
+ const reqDocs = this.parsedDocs.filter((d) => d.type === "requirement" || d.type === "spec");
412
+ for (const doc of reqDocs) {
413
+ for (const section of doc.sections) {
414
+ const lowerHeading = section.heading.toLowerCase();
415
+ if (lowerHeading.includes("requirement") || lowerHeading.includes("shall") || lowerHeading.includes("must")) {
416
+ requirements.push(this.createRequirement(
417
+ `REQ-${String(reqCounter++).padStart(3, "0")}`,
418
+ section.heading,
419
+ section.content,
420
+ "functional",
421
+ doc.path
422
+ ));
423
+ }
424
+ if (lowerHeading.includes("non-functional") || lowerHeading.includes("performance") || lowerHeading.includes("security") || lowerHeading.includes("scalability")) {
425
+ requirements.push(this.createRequirement(
426
+ `NFR-${String(reqCounter++).padStart(3, "0")}`,
427
+ section.heading,
428
+ section.content,
429
+ "non-functional",
430
+ doc.path
431
+ ));
432
+ }
433
+ }
434
+ const bulletReqs = this.extractBulletRequirements(doc.content);
435
+ for (const req of bulletReqs) {
436
+ requirements.push(this.createRequirement(
437
+ `REQ-${String(reqCounter++).padStart(3, "0")}`,
438
+ req.title,
439
+ req.description,
440
+ "functional",
441
+ doc.path
442
+ ));
443
+ }
444
+ }
445
+ const featureDocs = this.parsedDocs.filter((d) => d.type === "feature");
446
+ for (const doc of featureDocs) {
447
+ for (const section of doc.sections) {
448
+ if (section.heading.toLowerCase().includes("requirement") || section.heading.toLowerCase().includes("acceptance")) {
449
+ requirements.push(this.createRequirement(
450
+ `REQ-${String(reqCounter++).padStart(3, "0")}`,
451
+ `${doc.title}: ${section.heading}`,
452
+ section.content,
453
+ "functional",
454
+ doc.path
455
+ ));
456
+ }
457
+ }
458
+ }
459
+ return requirements;
460
+ }
461
+ /**
462
+ * Extract bullet point requirements from content
463
+ */
464
+ extractBulletRequirements(content) {
465
+ const reqs = [];
466
+ const lines = content.split("\n");
467
+ for (const line of lines) {
468
+ const match = line.match(/^[-*]\s+(.+(?:shall|must|should).+)$/i);
469
+ if (match) {
470
+ const text = match[1].trim();
471
+ reqs.push({
472
+ title: text.substring(0, 60) + (text.length > 60 ? "..." : ""),
473
+ description: text
474
+ });
475
+ }
476
+ }
477
+ return reqs;
478
+ }
479
+ /**
480
+ * Create a requirement object
481
+ */
482
+ createRequirement(id, title, description, type, source) {
483
+ const criteria = this.extractAcceptanceCriteria(description);
484
+ return {
485
+ id,
486
+ type,
487
+ description: description.substring(0, 500),
488
+ priority: this.inferPriority(description),
489
+ source: relative(this.options.projectRoot, source),
490
+ acceptanceCriteria: criteria.length > 0 ? criteria : [`${title} is implemented and working`],
491
+ relatedRequirements: []
492
+ };
493
+ }
494
+ /**
495
+ * Extract acceptance criteria from text
496
+ */
497
+ extractAcceptanceCriteria(text) {
498
+ const criteria = [];
499
+ const lines = text.split("\n");
500
+ let inCriteriaSection = false;
501
+ for (const line of lines) {
502
+ const lowerLine = line.toLowerCase();
503
+ if (lowerLine.includes("acceptance criteria") || lowerLine.includes("criteria:")) {
504
+ inCriteriaSection = true;
505
+ continue;
506
+ }
507
+ if (inCriteriaSection) {
508
+ const match = line.match(/^[-*]\s+(.+)$/);
509
+ if (match) {
510
+ criteria.push(match[1].trim());
511
+ } else if (line.match(/^#{1,6}\s/)) {
512
+ inCriteriaSection = false;
513
+ }
514
+ }
515
+ if (line.match(/^\s*(Given|When|Then)\s+/i)) {
516
+ criteria.push(line.trim());
517
+ }
518
+ }
519
+ return criteria.slice(0, 10);
520
+ }
521
+ /**
522
+ * Infer priority from text
523
+ */
524
+ inferPriority(text) {
525
+ const lower = text.toLowerCase();
526
+ if (lower.includes("critical") || lower.includes("must have") || lower.includes("p0")) {
527
+ return "must-have";
528
+ }
529
+ if (lower.includes("high priority") || lower.includes("should have") || lower.includes("p1")) {
530
+ return "should-have";
531
+ }
532
+ if (lower.includes("nice to have") || lower.includes("could have") || lower.includes("p2")) {
533
+ return "could-have";
534
+ }
535
+ if (lower.includes("future") || lower.includes("won't") || lower.includes("p3")) {
536
+ return "won-t-have";
537
+ }
538
+ return "should-have";
539
+ }
540
+ /**
541
+ * Extract features from documentation
542
+ */
543
+ extractFeaturesFromDocs() {
544
+ const features = [];
545
+ let featCounter = 1;
546
+ const featureDocs = this.parsedDocs.filter((d) => d.type === "feature");
547
+ for (const doc of featureDocs) {
548
+ features.push(this.createFeatureFromDoc(doc, `FEAT-${String(featCounter++).padStart(3, "0")}`));
549
+ }
550
+ const specDocs = this.parsedDocs.filter((d) => d.type === "spec" || d.type === "unknown");
551
+ for (const doc of specDocs) {
552
+ for (const section of doc.sections) {
553
+ const lowerHeading = section.heading.toLowerCase();
554
+ if (lowerHeading.includes("feature") || lowerHeading.includes("capability") || lowerHeading.includes("functionality")) {
555
+ features.push({
556
+ id: `FEAT-${String(featCounter++).padStart(3, "0")}`,
557
+ name: section.heading,
558
+ description: section.content.substring(0, 500),
559
+ userStories: this.extractUserStories(section.content),
560
+ requirements: [],
561
+ complexity: this.inferComplexity(section.content),
562
+ dependencies: [],
563
+ parallelizable: true
564
+ });
565
+ }
566
+ }
567
+ }
568
+ if (features.length === 0) {
569
+ for (const doc of this.parsedDocs) {
570
+ if (doc.headings.length > 0) {
571
+ for (const heading of doc.headings.filter((h) => h.level <= 2)) {
572
+ const section = doc.sections.find((s) => s.heading === heading.text);
573
+ if (section && section.content.length > 100) {
574
+ features.push({
575
+ id: `FEAT-${String(featCounter++).padStart(3, "0")}`,
576
+ name: heading.text,
577
+ description: section.content.substring(0, 500),
578
+ userStories: this.extractUserStories(section.content),
579
+ requirements: [],
580
+ complexity: this.inferComplexity(section.content),
581
+ dependencies: [],
582
+ parallelizable: true
583
+ });
584
+ }
585
+ }
586
+ }
587
+ }
588
+ }
589
+ return features;
590
+ }
591
+ /**
592
+ * Create a feature from a document
593
+ */
594
+ createFeatureFromDoc(doc, id) {
595
+ return {
596
+ id,
597
+ name: doc.title,
598
+ description: doc.content.substring(0, 1e3),
599
+ userStories: this.extractUserStories(doc.content),
600
+ requirements: [],
601
+ complexity: this.inferComplexity(doc.content),
602
+ dependencies: this.extractDependencies(doc),
603
+ parallelizable: true
604
+ };
605
+ }
606
+ /**
607
+ * Extract user stories from text
608
+ */
609
+ extractUserStories(text) {
610
+ const stories = [];
611
+ const regex = /As a[n]?\s+([^,]+),?\s+I want\s+([^,]+),?\s+(?:so that|because)\s+([^.]+)/gi;
612
+ let match;
613
+ while ((match = regex.exec(text)) !== null) {
614
+ stories.push(match[0]);
615
+ }
616
+ const lines = text.split("\n");
617
+ for (const line of lines) {
618
+ if (line.toLowerCase().includes("user story") || line.match(/^[-*]\s+As a/i)) {
619
+ stories.push(line.replace(/^[-*]\s+/, "").trim());
620
+ }
621
+ }
622
+ return stories.slice(0, 5);
623
+ }
624
+ /**
625
+ * Infer complexity from content
626
+ */
627
+ inferComplexity(text) {
628
+ const lower = text.toLowerCase();
629
+ const codeBlocks = (text.match(/```/g) || []).length / 2;
630
+ const wordCount = text.split(/\s+/).length;
631
+ if (lower.includes("complex") || lower.includes("difficult") || codeBlocks > 5 || wordCount > 1e3) {
632
+ return "very-high";
633
+ }
634
+ if (lower.includes("moderate") || codeBlocks > 3 || wordCount > 500) {
635
+ return "high";
636
+ }
637
+ if (lower.includes("simple") || lower.includes("basic") || wordCount < 200) {
638
+ return "low";
639
+ }
640
+ return "medium";
641
+ }
642
+ /**
643
+ * Extract dependencies from document links
644
+ */
645
+ extractDependencies(doc) {
646
+ const deps = [];
647
+ for (const link of doc.links) {
648
+ if (link.startsWith("./") || link.startsWith("../") || !link.startsWith("http")) {
649
+ const linkedDoc = this.parsedDocs.find((d) => d.path.includes(link.replace(".md", "")));
650
+ if (linkedDoc && linkedDoc.type === "feature") {
651
+ deps.push(linkedDoc.title);
652
+ }
653
+ }
654
+ }
655
+ return deps;
656
+ }
657
+ /**
658
+ * Extract problem statement from docs
659
+ */
660
+ extractProblemStatement() {
661
+ for (const doc of this.parsedDocs) {
662
+ for (const section of doc.sections) {
663
+ const lower = section.heading.toLowerCase();
664
+ if (lower.includes("problem") || lower.includes("overview") || lower.includes("background")) {
665
+ return section.content.substring(0, 1e3);
666
+ }
667
+ }
668
+ }
669
+ return `Building: ${this.options.description}`;
670
+ }
671
+ /**
672
+ * Extract goals from docs
673
+ */
674
+ extractGoals() {
675
+ const goals = [];
676
+ for (const doc of this.parsedDocs) {
677
+ for (const section of doc.sections) {
678
+ const lower = section.heading.toLowerCase();
679
+ if (lower.includes("goal") || lower.includes("objective") || lower.includes("outcome")) {
680
+ const bullets = section.content.match(/^[-*]\s+(.+)$/gm);
681
+ if (bullets) {
682
+ goals.push(...bullets.map((b) => b.replace(/^[-*]\s+/, "").trim()));
683
+ }
684
+ }
685
+ }
686
+ }
687
+ return goals.length > 0 ? goals.slice(0, 10) : ["Complete implementation as documented"];
688
+ }
689
+ /**
690
+ * Extract constraints from docs
691
+ */
692
+ extractConstraints() {
693
+ const constraints = [];
694
+ for (const doc of this.parsedDocs) {
695
+ for (const section of doc.sections) {
696
+ const lower = section.heading.toLowerCase();
697
+ if (lower.includes("constraint") || lower.includes("limitation") || lower.includes("restriction")) {
698
+ const bullets = section.content.match(/^[-*]\s+(.+)$/gm);
699
+ if (bullets) {
700
+ constraints.push(...bullets.map((b) => b.replace(/^[-*]\s+/, "").trim()));
701
+ }
702
+ }
703
+ }
704
+ }
705
+ return constraints;
706
+ }
707
+ /**
708
+ * Extract assumptions from docs
709
+ */
710
+ extractAssumptions() {
711
+ const assumptions = [];
712
+ for (const doc of this.parsedDocs) {
713
+ for (const section of doc.sections) {
714
+ if (section.heading.toLowerCase().includes("assumption")) {
715
+ const bullets = section.content.match(/^[-*]\s+(.+)$/gm);
716
+ if (bullets) {
717
+ assumptions.push(...bullets.map((b) => b.replace(/^[-*]\s+/, "").trim()));
718
+ }
719
+ }
720
+ }
721
+ }
722
+ return assumptions;
723
+ }
724
+ /**
725
+ * Extract success metrics from docs
726
+ */
727
+ extractSuccessMetrics() {
728
+ const metrics = [];
729
+ for (const doc of this.parsedDocs) {
730
+ for (const section of doc.sections) {
731
+ const lower = section.heading.toLowerCase();
732
+ if (lower.includes("metric") || lower.includes("success") || lower.includes("kpi")) {
733
+ const bullets = section.content.match(/^[-*]\s+(.+)$/gm);
734
+ if (bullets) {
735
+ metrics.push(...bullets.map((b) => b.replace(/^[-*]\s+/, "").trim()));
736
+ }
737
+ }
738
+ }
739
+ }
740
+ return metrics.length > 0 ? metrics : ["All documented features implemented", "All tests passing"];
741
+ }
283
742
  /**
284
743
  * Execute pseudocode phase
285
744
  */
@@ -287,14 +746,66 @@ class SPARCPlanner {
287
746
  logger.info("Executing pseudocode phase");
288
747
  this.plan.currentPhase = "pseudocode";
289
748
  const algorithms = [];
749
+ const apiDocs = this.parsedDocs.filter((d) => d.type === "api");
750
+ for (const doc of apiDocs) {
751
+ algorithms.push(this.createAlgorithmFromDoc(doc));
752
+ }
290
753
  for (const feature of this.plan.specification?.features || []) {
291
- const algorithm = this.generateAlgorithmDesign(feature);
292
- algorithms.push(algorithm);
754
+ const featureDoc = this.parsedDocs.find((d) => d.title === feature.name);
755
+ if (featureDoc && featureDoc.codeBlocks.length > 0) {
756
+ algorithms.push(this.createAlgorithmFromDoc(featureDoc));
757
+ }
293
758
  }
294
759
  this.plan.algorithms = algorithms;
295
- logger.info("Pseudocode phase completed", {
296
- algorithms: algorithms.length
297
- });
760
+ logger.info("Pseudocode phase completed", { algorithms: algorithms.length });
761
+ }
762
+ /**
763
+ * Create algorithm design from document
764
+ */
765
+ createAlgorithmFromDoc(doc) {
766
+ const steps = [];
767
+ let stepNum = 1;
768
+ for (const section of doc.sections) {
769
+ if (section.level >= 2) {
770
+ const relevantCodeBlock = doc.codeBlocks.find(
771
+ (block) => section.content.includes(block.code.substring(0, 50))
772
+ );
773
+ steps.push({
774
+ step: stepNum++,
775
+ description: section.heading,
776
+ pseudocode: relevantCodeBlock?.code ?? `// Implement ${section.heading}`,
777
+ inputs: [],
778
+ outputs: []
779
+ });
780
+ }
781
+ }
782
+ if (steps.length === 0 && doc.codeBlocks.length > 0) {
783
+ for (const block of doc.codeBlocks) {
784
+ steps.push({
785
+ step: stepNum++,
786
+ description: `${block.language} implementation`,
787
+ pseudocode: block.code,
788
+ inputs: [],
789
+ outputs: []
790
+ });
791
+ }
792
+ }
793
+ return {
794
+ id: `algo_${doc.filename.replace(/[^a-z0-9]/gi, "_")}`,
795
+ name: doc.title,
796
+ purpose: doc.content.substring(0, 200),
797
+ steps: steps.length > 0 ? steps : [{
798
+ step: 1,
799
+ description: "Implement feature",
800
+ pseudocode: "// Implementation needed",
801
+ inputs: [],
802
+ outputs: []
803
+ }],
804
+ timeComplexity: "O(n)",
805
+ spaceComplexity: "O(1)",
806
+ edgeCases: [],
807
+ relatedFeatures: []
808
+ };
298
809
  }
299
810
  /**
300
811
  * Execute architecture phase
@@ -302,73 +813,310 @@ class SPARCPlanner {
302
813
  async executeArchitecturePhase() {
303
814
  logger.info("Executing architecture phase");
304
815
  this.plan.currentPhase = "architecture";
816
+ const components = this.extractComponentsFromDocs();
817
+ const patterns = this.extractPatternsFromDocs();
305
818
  const arch = {
306
819
  id: `arch_${this.plan.id}`,
307
820
  version: "1.0.0",
308
821
  createdAt: /* @__PURE__ */ new Date(),
309
- overview: this.generateArchitectureOverview(),
310
- patterns: this.identifyArchitecturePatterns(),
311
- components: this.generateComponents(),
822
+ overview: this.extractArchitectureOverview(),
823
+ patterns,
824
+ components,
312
825
  decisions: [],
313
- dataFlow: this.describeDataFlow(),
314
- securityConsiderations: this.identifySecurityConsiderations(),
826
+ dataFlow: this.extractDataFlow(),
827
+ securityConsiderations: this.extractSecurityConsiderations(),
315
828
  scalabilityConsiderations: [],
316
829
  diagrams: []
317
830
  };
318
831
  this.plan.architecture = arch;
319
832
  this.addDecision(
320
- "Architecture Design",
321
- `Defined ${arch.components.length} components using ${arch.patterns.join(", ")} patterns`,
833
+ "Architecture Defined",
834
+ `Identified ${components.length} components with patterns: ${patterns.join(", ")}`,
322
835
  "architecture",
323
- "medium",
324
- "Architecture designed based on requirements and existing patterns",
325
- ["Microservices", "Monolith", "Modular monolith"]
836
+ components.length > 0 ? "medium" : "low",
837
+ `Based on analysis of ${this.parsedDocs.filter((d) => d.type === "architecture").length} architecture docs`
326
838
  );
327
839
  logger.info("Architecture phase completed", {
328
- components: arch.components.length,
329
- patterns: arch.patterns.length
840
+ components: components.length,
841
+ patterns: patterns.length
330
842
  });
331
843
  }
332
844
  /**
333
- * Execute refinement phase (task breakdown)
845
+ * Extract components from documentation
846
+ */
847
+ extractComponentsFromDocs() {
848
+ const components = [];
849
+ let compCounter = 1;
850
+ const archDocs = this.parsedDocs.filter((d) => d.type === "architecture");
851
+ for (const doc of archDocs) {
852
+ for (const section of doc.sections) {
853
+ const lower = section.heading.toLowerCase();
854
+ if (lower.includes("component") || lower.includes("module") || lower.includes("service") || lower.includes("layer")) {
855
+ components.push({
856
+ id: `COMP-${String(compCounter++).padStart(3, "0")}`,
857
+ name: section.heading,
858
+ type: this.inferComponentType(section.heading, section.content),
859
+ description: section.content.substring(0, 500),
860
+ responsibilities: this.extractResponsibilities(section.content),
861
+ interfaces: [],
862
+ dependencies: [],
863
+ technologies: this.extractTechnologies(section.content),
864
+ path: doc.path
865
+ });
866
+ }
867
+ }
868
+ }
869
+ if (components.length === 0) {
870
+ for (const feature of this.plan.specification?.features || []) {
871
+ components.push({
872
+ id: `COMP-${String(compCounter++).padStart(3, "0")}`,
873
+ name: `${feature.name} Module`,
874
+ type: "module",
875
+ description: feature.description.substring(0, 300),
876
+ responsibilities: [`Implement ${feature.name}`],
877
+ interfaces: [],
878
+ dependencies: feature.dependencies,
879
+ technologies: []
880
+ });
881
+ }
882
+ }
883
+ return components;
884
+ }
885
+ /**
886
+ * Infer component type
887
+ */
888
+ inferComponentType(heading, content) {
889
+ const lower = (heading + " " + content).toLowerCase();
890
+ if (lower.includes("service") || lower.includes("microservice")) return "service";
891
+ if (lower.includes("api") || lower.includes("endpoint")) return "api";
892
+ if (lower.includes("database") || lower.includes("storage") || lower.includes("db")) return "database";
893
+ if (lower.includes("ui") || lower.includes("frontend") || lower.includes("component")) return "ui";
894
+ if (lower.includes("library") || lower.includes("util")) return "library";
895
+ if (lower.includes("infrastructure") || lower.includes("deploy")) return "infrastructure";
896
+ return "module";
897
+ }
898
+ /**
899
+ * Extract responsibilities from content
900
+ */
901
+ extractResponsibilities(content) {
902
+ const responsibilities = [];
903
+ const lines = content.split("\n");
904
+ let inResponsibilitySection = false;
905
+ for (const line of lines) {
906
+ const lower = line.toLowerCase();
907
+ if (lower.includes("responsibilit") || lower.includes("function") || lower.includes("purpose")) {
908
+ inResponsibilitySection = true;
909
+ continue;
910
+ }
911
+ if (inResponsibilitySection) {
912
+ const match = line.match(/^[-*]\s+(.+)$/);
913
+ if (match) {
914
+ responsibilities.push(match[1].trim());
915
+ } else if (line.match(/^#{1,6}\s/)) {
916
+ inResponsibilitySection = false;
917
+ }
918
+ }
919
+ }
920
+ return responsibilities.slice(0, 5);
921
+ }
922
+ /**
923
+ * Extract technologies mentioned
924
+ */
925
+ extractTechnologies(content) {
926
+ const techs = [];
927
+ const knownTechs = [
928
+ "TypeScript",
929
+ "JavaScript",
930
+ "Python",
931
+ "Go",
932
+ "Rust",
933
+ "Java",
934
+ "React",
935
+ "Vue",
936
+ "Angular",
937
+ "Node.js",
938
+ "Express",
939
+ "FastAPI",
940
+ "PostgreSQL",
941
+ "MongoDB",
942
+ "Redis",
943
+ "SQLite",
944
+ "MySQL",
945
+ "Docker",
946
+ "Kubernetes",
947
+ "AWS",
948
+ "GCP",
949
+ "Azure",
950
+ "GraphQL",
951
+ "REST",
952
+ "gRPC",
953
+ "WebSocket"
954
+ ];
955
+ for (const tech of knownTechs) {
956
+ if (content.toLowerCase().includes(tech.toLowerCase())) {
957
+ techs.push(tech);
958
+ }
959
+ }
960
+ return techs;
961
+ }
962
+ /**
963
+ * Extract patterns from documentation
964
+ */
965
+ extractPatternsFromDocs() {
966
+ const patterns = [];
967
+ const knownPatterns = [
968
+ "MVC",
969
+ "MVVM",
970
+ "MVP",
971
+ "Microservices",
972
+ "Monolith",
973
+ "Serverless",
974
+ "Event-Driven",
975
+ "CQRS",
976
+ "Event Sourcing",
977
+ "Domain-Driven Design",
978
+ "Repository Pattern",
979
+ "Factory Pattern",
980
+ "Singleton",
981
+ "Observer",
982
+ "Clean Architecture",
983
+ "Hexagonal",
984
+ "Layered",
985
+ "Modular"
986
+ ];
987
+ const allContent = this.parsedDocs.map((d) => d.content).join(" ").toLowerCase();
988
+ for (const pattern of knownPatterns) {
989
+ if (allContent.includes(pattern.toLowerCase())) {
990
+ patterns.push(pattern);
991
+ }
992
+ }
993
+ return patterns.length > 0 ? patterns : ["Modular"];
994
+ }
995
+ /**
996
+ * Extract architecture overview
997
+ */
998
+ extractArchitectureOverview() {
999
+ const archDocs = this.parsedDocs.filter((d) => d.type === "architecture");
1000
+ if (archDocs.length > 0) {
1001
+ return archDocs[0].content.substring(0, 1e3);
1002
+ }
1003
+ return `Architecture for ${this.options.name}: ${this.options.description}`;
1004
+ }
1005
+ /**
1006
+ * Extract data flow description
1007
+ */
1008
+ extractDataFlow() {
1009
+ for (const doc of this.parsedDocs) {
1010
+ for (const section of doc.sections) {
1011
+ if (section.heading.toLowerCase().includes("data flow") || section.heading.toLowerCase().includes("flow")) {
1012
+ return section.content;
1013
+ }
1014
+ }
1015
+ }
1016
+ return "Data flow documentation not found";
1017
+ }
1018
+ /**
1019
+ * Extract security considerations
1020
+ */
1021
+ extractSecurityConsiderations() {
1022
+ const considerations = [];
1023
+ for (const doc of this.parsedDocs) {
1024
+ for (const section of doc.sections) {
1025
+ if (section.heading.toLowerCase().includes("security")) {
1026
+ const bullets = section.content.match(/^[-*]\s+(.+)$/gm);
1027
+ if (bullets) {
1028
+ considerations.push(...bullets.map((b) => b.replace(/^[-*]\s+/, "").trim()));
1029
+ }
1030
+ }
1031
+ }
1032
+ }
1033
+ return considerations;
1034
+ }
1035
+ /**
1036
+ * Execute refinement phase - generate development tasks
334
1037
  */
335
1038
  async executeRefinementPhase() {
336
1039
  logger.info("Executing refinement phase");
337
1040
  this.plan.currentPhase = "refinement";
338
1041
  const tasks = [];
339
1042
  tasks.push(this.createTask(
340
- "Research and Analysis",
341
- "Comprehensive research on project requirements and patterns",
1043
+ "Documentation Analysis Complete",
1044
+ `Analyze all ${this.parsedDocs.length} documentation files`,
342
1045
  "specification",
343
1046
  "research",
344
1047
  "high",
345
- 4
1048
+ 2,
1049
+ true
346
1050
  ));
347
- for (const component of this.plan.architecture?.components || []) {
1051
+ for (const feature of this.plan.specification?.features || []) {
1052
+ tasks.push(this.createTask(
1053
+ `Design: ${feature.name}`,
1054
+ `Design the implementation approach for ${feature.name}: ${feature.description.substring(0, 200)}`,
1055
+ "pseudocode",
1056
+ "design",
1057
+ this.mapComplexityToPriority(feature.complexity),
1058
+ this.estimateDesignHours(feature.complexity),
1059
+ true
1060
+ ));
348
1061
  tasks.push(this.createTask(
349
- `Implement ${component.name}`,
350
- `Implement the ${component.name} component: ${component.description}`,
1062
+ `Implement: ${feature.name}`,
1063
+ `Implement ${feature.name}
1064
+
1065
+ User Stories:
1066
+ ${feature.userStories.map((s) => `- ${s}`).join("\n")}`,
351
1067
  "refinement",
352
1068
  "implementation",
353
- this.getComponentPriority(component),
354
- this.estimateComponentHours(component)
1069
+ this.mapComplexityToPriority(feature.complexity),
1070
+ this.estimateImplementationHours(feature.complexity),
1071
+ true
1072
+ ));
1073
+ tasks.push(this.createTask(
1074
+ `Test: ${feature.name}`,
1075
+ `Create tests for ${feature.name}`,
1076
+ "refinement",
1077
+ "testing",
1078
+ "medium",
1079
+ this.estimateTestHours(feature.complexity),
1080
+ true
355
1081
  ));
356
1082
  }
357
- tasks.push(this.createTask(
358
- "Unit Testing",
359
- "Create comprehensive unit tests for all components",
360
- "refinement",
361
- "testing",
362
- "high",
363
- 8
364
- ));
1083
+ for (const component of this.plan.architecture?.components || []) {
1084
+ if (!tasks.find((t) => t.name.includes(component.name))) {
1085
+ tasks.push(this.createTask(
1086
+ `Build: ${component.name}`,
1087
+ `Implement the ${component.name} component
1088
+
1089
+ Responsibilities:
1090
+ ${component.responsibilities.map((r) => `- ${r}`).join("\n")}`,
1091
+ "refinement",
1092
+ "implementation",
1093
+ "medium",
1094
+ 4,
1095
+ true
1096
+ ));
1097
+ }
1098
+ }
1099
+ for (const req of this.plan.specification?.requirements || []) {
1100
+ if (!tasks.find((t) => t.description.includes(req.description.substring(0, 50)))) {
1101
+ tasks.push(this.createTask(
1102
+ `Requirement: ${req.id}`,
1103
+ req.description,
1104
+ "refinement",
1105
+ "implementation",
1106
+ this.mapPriorityToTaskPriority(req.priority),
1107
+ 2,
1108
+ true
1109
+ ));
1110
+ }
1111
+ }
365
1112
  tasks.push(this.createTask(
366
1113
  "Integration Testing",
367
- "Create integration tests for component interactions",
1114
+ `Create integration tests for all ${(this.plan.specification?.features || []).length} features`,
368
1115
  "refinement",
369
1116
  "testing",
370
- "medium",
371
- 6
1117
+ "high",
1118
+ 8,
1119
+ false
372
1120
  ));
373
1121
  tasks.push(this.createTask(
374
1122
  "API Documentation",
@@ -376,7 +1124,17 @@ class SPARCPlanner {
376
1124
  "completion",
377
1125
  "documentation",
378
1126
  "medium",
379
- 4
1127
+ 4,
1128
+ true
1129
+ ));
1130
+ tasks.push(this.createTask(
1131
+ "Final Review",
1132
+ "Review all implementations against requirements and documentation",
1133
+ "completion",
1134
+ "review",
1135
+ "high",
1136
+ 4,
1137
+ false
380
1138
  ));
381
1139
  this.plan.tasks = tasks;
382
1140
  this.calculateParallelGroups();
@@ -386,51 +1144,10 @@ class SPARCPlanner {
386
1144
  parallelGroups: this.plan.parallelGroups.length
387
1145
  });
388
1146
  }
389
- /**
390
- * Execute review phase
391
- */
392
- async executeReviewPhase() {
393
- logger.info("Executing review phase");
394
- this.plan.currentPhase = "completion";
395
- this.plan.decisionLog = this.decisionLog.getLog();
396
- const reviewProcess = createReviewProcess({
397
- plan: this.plan,
398
- passes: this.options.reviewPasses,
399
- autoFix: false,
400
- strictMode: false
401
- });
402
- const result = await reviewProcess.executeReview();
403
- logger.info("Review phase completed", {
404
- status: result.overallStatus,
405
- totalFindings: result.totalFindings,
406
- criticalFindings: result.criticalFindings
407
- });
408
- return result;
409
- }
410
- /**
411
- * Add a decision to the log
412
- */
413
- addDecision(title, description, phase, confidence, rationale, alternatives) {
414
- const decision = this.decisionLog.addDecision({
415
- title,
416
- description,
417
- phase,
418
- confidence,
419
- rationale,
420
- alternatives,
421
- decidedBy: "sparc-planner"
422
- });
423
- if (this.options.autoConsensus && ConsensusBuilder.needsConsensus(confidence)) {
424
- logger.info("Building consensus for low-confidence decision", {
425
- decisionId: decision.id,
426
- confidence
427
- });
428
- }
429
- }
430
1147
  /**
431
1148
  * Create a SPARC task
432
1149
  */
433
- createTask(name, description, phase, type, priority, estimatedHours) {
1150
+ createTask(name, description, phase, type, priority, estimatedHours, parallelizable) {
434
1151
  return {
435
1152
  id: `task_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
436
1153
  name,
@@ -440,12 +1157,97 @@ class SPARCPlanner {
440
1157
  priority,
441
1158
  estimatedHours,
442
1159
  dependencies: [],
443
- parallelizable: type === "implementation" || type === "testing",
1160
+ parallelizable,
444
1161
  status: "pending",
445
1162
  contextLinks: [],
446
1163
  kgReferences: []
447
1164
  };
448
1165
  }
1166
+ /**
1167
+ * Map complexity to priority
1168
+ */
1169
+ mapComplexityToPriority(complexity) {
1170
+ switch (complexity) {
1171
+ case "very-high":
1172
+ return "critical";
1173
+ case "high":
1174
+ return "high";
1175
+ case "medium":
1176
+ return "medium";
1177
+ case "low":
1178
+ return "low";
1179
+ default:
1180
+ return "medium";
1181
+ }
1182
+ }
1183
+ /**
1184
+ * Map requirement priority to task priority
1185
+ */
1186
+ mapPriorityToTaskPriority(priority) {
1187
+ switch (priority) {
1188
+ case "must-have":
1189
+ return "critical";
1190
+ case "should-have":
1191
+ return "high";
1192
+ case "could-have":
1193
+ return "medium";
1194
+ case "won-t-have":
1195
+ return "low";
1196
+ default:
1197
+ return "medium";
1198
+ }
1199
+ }
1200
+ /**
1201
+ * Estimate design hours based on complexity
1202
+ */
1203
+ estimateDesignHours(complexity) {
1204
+ switch (complexity) {
1205
+ case "very-high":
1206
+ return 8;
1207
+ case "high":
1208
+ return 4;
1209
+ case "medium":
1210
+ return 2;
1211
+ case "low":
1212
+ return 1;
1213
+ default:
1214
+ return 2;
1215
+ }
1216
+ }
1217
+ /**
1218
+ * Estimate implementation hours based on complexity
1219
+ */
1220
+ estimateImplementationHours(complexity) {
1221
+ switch (complexity) {
1222
+ case "very-high":
1223
+ return 40;
1224
+ case "high":
1225
+ return 20;
1226
+ case "medium":
1227
+ return 8;
1228
+ case "low":
1229
+ return 4;
1230
+ default:
1231
+ return 8;
1232
+ }
1233
+ }
1234
+ /**
1235
+ * Estimate test hours based on complexity
1236
+ */
1237
+ estimateTestHours(complexity) {
1238
+ switch (complexity) {
1239
+ case "very-high":
1240
+ return 16;
1241
+ case "high":
1242
+ return 8;
1243
+ case "medium":
1244
+ return 4;
1245
+ case "low":
1246
+ return 2;
1247
+ default:
1248
+ return 4;
1249
+ }
1250
+ }
449
1251
  /**
450
1252
  * Calculate parallel task groups
451
1253
  */
@@ -472,6 +1274,94 @@ class SPARCPlanner {
472
1274
  this.plan.criticalPath = critical;
473
1275
  this.plan.executionOrder = this.plan.tasks.map((t) => t.id);
474
1276
  }
1277
+ /**
1278
+ * Analyze existing code in src directory
1279
+ */
1280
+ async analyzeExistingCode(srcPath) {
1281
+ const analysis = {
1282
+ hasSrcDirectory: true,
1283
+ srcPath,
1284
+ fileCount: 0,
1285
+ languages: {},
1286
+ keyFiles: [],
1287
+ patterns: [],
1288
+ dependencies: [],
1289
+ entryPoints: []
1290
+ };
1291
+ const analyzeDir = (dir) => {
1292
+ const entries = readdirSync(dir);
1293
+ for (const entry of entries) {
1294
+ const fullPath = join(dir, entry);
1295
+ const stat = statSync(fullPath);
1296
+ if (stat.isDirectory()) {
1297
+ if (!entry.startsWith(".") && entry !== "node_modules") {
1298
+ analyzeDir(fullPath);
1299
+ }
1300
+ } else if (stat.isFile()) {
1301
+ analysis.fileCount++;
1302
+ const ext = extname(entry);
1303
+ analysis.languages[ext] = (analysis.languages[ext] || 0) + 1;
1304
+ if (entry === "index.ts" || entry === "index.js") {
1305
+ analysis.entryPoints.push(relative(this.options.projectRoot, fullPath));
1306
+ }
1307
+ if (entry === "package.json") {
1308
+ try {
1309
+ const pkg = JSON.parse(readFileSync(fullPath, "utf-8"));
1310
+ analysis.dependencies = Object.keys(pkg.dependencies || {});
1311
+ } catch {
1312
+ }
1313
+ }
1314
+ }
1315
+ }
1316
+ };
1317
+ analyzeDir(srcPath);
1318
+ if (analysis.languages[".ts"] > 0 || analysis.languages[".tsx"] > 0) {
1319
+ analysis.patterns.push("TypeScript");
1320
+ }
1321
+ if (existsSync(join(this.options.projectRoot, "tests")) || existsSync(join(this.options.projectRoot, "__tests__"))) {
1322
+ analysis.patterns.push("Test-Driven Development");
1323
+ analysis.testCoverage = { hasTests: true };
1324
+ }
1325
+ return analysis;
1326
+ }
1327
+ /**
1328
+ * Execute review phase
1329
+ */
1330
+ async executeReviewPhase() {
1331
+ logger.info("Executing review phase");
1332
+ this.plan.currentPhase = "completion";
1333
+ this.plan.decisionLog = this.decisionLog.getLog();
1334
+ const reviewProcess = createReviewProcess({
1335
+ plan: this.plan,
1336
+ passes: this.options.reviewPasses,
1337
+ autoFix: false,
1338
+ strictMode: false
1339
+ });
1340
+ const result = await reviewProcess.executeReview();
1341
+ logger.info("Review phase completed", {
1342
+ status: result.overallStatus,
1343
+ totalFindings: result.totalFindings,
1344
+ criticalFindings: result.criticalFindings
1345
+ });
1346
+ return result;
1347
+ }
1348
+ /**
1349
+ * Add a decision to the log
1350
+ */
1351
+ addDecision(title, description, phase, confidence, rationale, alternatives) {
1352
+ this.decisionLog.addDecision({
1353
+ title,
1354
+ description,
1355
+ phase,
1356
+ confidence,
1357
+ rationale,
1358
+ alternatives,
1359
+ decidedBy: "sparc-planner"
1360
+ });
1361
+ if (this.options.autoConsensus && ConsensusBuilder.needsConsensus(confidence)) {
1362
+ logger.info("Low confidence decision - would trigger consensus", { title, confidence });
1363
+ }
1364
+ }
475
1365
  /**
476
1366
  * Update plan statistics
477
1367
  */
@@ -513,6 +1403,7 @@ class SPARCPlanner {
513
1403
  `**Status:** ${this.plan.status}`,
514
1404
  `**Version:** ${this.plan.version}`,
515
1405
  `**Created:** ${this.plan.createdAt.toISOString()}`,
1406
+ `**Docs Analyzed:** ${this.parsedDocs.length} files`,
516
1407
  "",
517
1408
  "## Summary",
518
1409
  "",
@@ -525,16 +1416,58 @@ class SPARCPlanner {
525
1416
  `| Total Tasks | ${this.plan.statistics.totalTasks} |`,
526
1417
  `| Parallelizable | ${this.plan.statistics.parallelizableTasks} |`,
527
1418
  `| Estimated Hours | ${this.plan.statistics.estimatedHours} |`,
1419
+ `| Documentation Files Analyzed | ${this.parsedDocs.length} |`,
528
1420
  `| Research Findings | ${this.plan.statistics.researchFindings} |`,
529
1421
  `| Decisions | ${this.plan.statistics.decisions} |`,
530
1422
  ""
531
1423
  ];
1424
+ if (this.parsedDocs.length > 0) {
1425
+ lines.push("## Documentation Sources");
1426
+ lines.push("");
1427
+ const byType = /* @__PURE__ */ new Map();
1428
+ for (const doc of this.parsedDocs) {
1429
+ const existing = byType.get(doc.type) || [];
1430
+ existing.push(doc);
1431
+ byType.set(doc.type, existing);
1432
+ }
1433
+ for (const [type, docs] of byType.entries()) {
1434
+ lines.push(`### ${type.charAt(0).toUpperCase() + type.slice(1)} Documents (${docs.length})`);
1435
+ lines.push("");
1436
+ for (const doc of docs) {
1437
+ lines.push(`- **${doc.title}** - \`${relative(this.options.projectRoot, doc.path)}\``);
1438
+ }
1439
+ lines.push("");
1440
+ }
1441
+ }
532
1442
  if (this.plan.specification) {
533
1443
  lines.push("## Specification");
534
1444
  lines.push("");
535
1445
  lines.push(`- **Requirements:** ${this.plan.specification.requirements.length}`);
536
1446
  lines.push(`- **Features:** ${this.plan.specification.features.length}`);
537
1447
  lines.push("");
1448
+ if (this.plan.specification.features.length > 0) {
1449
+ lines.push("### Features");
1450
+ lines.push("");
1451
+ for (const feature of this.plan.specification.features) {
1452
+ lines.push(`#### ${feature.id}: ${feature.name}`);
1453
+ lines.push("");
1454
+ lines.push(feature.description.substring(0, 300) + "...");
1455
+ lines.push("");
1456
+ lines.push(`- **Complexity:** ${feature.complexity}`);
1457
+ if (feature.userStories.length > 0) {
1458
+ lines.push(`- **User Stories:** ${feature.userStories.length}`);
1459
+ }
1460
+ lines.push("");
1461
+ }
1462
+ }
1463
+ if (this.plan.specification.requirements.length > 0) {
1464
+ lines.push("### Requirements");
1465
+ lines.push("");
1466
+ for (const req of this.plan.specification.requirements) {
1467
+ lines.push(`- **${req.id}** (${req.type}, ${req.priority}): ${req.description.substring(0, 100)}...`);
1468
+ }
1469
+ lines.push("");
1470
+ }
538
1471
  }
539
1472
  if (this.plan.architecture) {
540
1473
  lines.push("## Architecture");
@@ -542,20 +1475,43 @@ class SPARCPlanner {
542
1475
  lines.push(`- **Components:** ${this.plan.architecture.components.length}`);
543
1476
  lines.push(`- **Patterns:** ${this.plan.architecture.patterns.join(", ")}`);
544
1477
  lines.push("");
1478
+ if (this.plan.architecture.components.length > 0) {
1479
+ lines.push("### Components");
1480
+ lines.push("");
1481
+ for (const comp of this.plan.architecture.components) {
1482
+ lines.push(`#### ${comp.id}: ${comp.name}`);
1483
+ lines.push("");
1484
+ lines.push(`- **Type:** ${comp.type}`);
1485
+ lines.push(`- **Description:** ${comp.description.substring(0, 200)}`);
1486
+ if (comp.technologies.length > 0) {
1487
+ lines.push(`- **Technologies:** ${comp.technologies.join(", ")}`);
1488
+ }
1489
+ lines.push("");
1490
+ }
1491
+ }
545
1492
  }
546
- lines.push("## Tasks");
1493
+ lines.push("## Development Tasks");
547
1494
  lines.push("");
1495
+ const tasksByPhase = /* @__PURE__ */ new Map();
548
1496
  for (const task of this.plan.tasks) {
549
- lines.push(`### ${task.name}`);
550
- lines.push("");
551
- lines.push(`- **Phase:** ${task.phase}`);
552
- lines.push(`- **Type:** ${task.type}`);
553
- lines.push(`- **Priority:** ${task.priority}`);
554
- lines.push(`- **Estimated:** ${task.estimatedHours}h`);
555
- lines.push(`- **Parallelizable:** ${task.parallelizable ? "Yes" : "No"}`);
556
- lines.push("");
557
- lines.push(task.description);
1497
+ const existing = tasksByPhase.get(task.phase) || [];
1498
+ existing.push(task);
1499
+ tasksByPhase.set(task.phase, existing);
1500
+ }
1501
+ for (const [phase, tasks] of tasksByPhase.entries()) {
1502
+ lines.push(`### ${phase.charAt(0).toUpperCase() + phase.slice(1)} Phase (${tasks.length} tasks)`);
558
1503
  lines.push("");
1504
+ for (const task of tasks) {
1505
+ lines.push(`#### ${task.name}`);
1506
+ lines.push("");
1507
+ lines.push(`- **Type:** ${task.type}`);
1508
+ lines.push(`- **Priority:** ${task.priority}`);
1509
+ lines.push(`- **Estimated:** ${task.estimatedHours}h`);
1510
+ lines.push(`- **Parallelizable:** ${task.parallelizable ? "Yes" : "No"}`);
1511
+ lines.push("");
1512
+ lines.push(task.description.substring(0, 500));
1513
+ lines.push("");
1514
+ }
559
1515
  }
560
1516
  if (this.plan.reviewResult) {
561
1517
  lines.push("## Review Result");
@@ -575,165 +1531,6 @@ class SPARCPlanner {
575
1531
  }
576
1532
  return lines.join("\n");
577
1533
  }
578
- // Helper methods for content generation
579
- extractProblemStatement() {
580
- return `Problem to solve: ${this.options.description}`;
581
- }
582
- extractGoals() {
583
- return [
584
- "Implement all specified features",
585
- "Ensure code quality and maintainability",
586
- "Provide comprehensive documentation",
587
- "Enable parallel development where possible"
588
- ];
589
- }
590
- generateRequirements() {
591
- return [
592
- {
593
- id: "req-001",
594
- type: "functional",
595
- description: "Core functionality as described",
596
- priority: "must-have",
597
- source: "User request",
598
- acceptanceCriteria: ["Feature works as specified"],
599
- relatedRequirements: []
600
- },
601
- {
602
- id: "req-002",
603
- type: "non-functional",
604
- description: "Code must be well-documented",
605
- priority: "should-have",
606
- source: "Best practices",
607
- acceptanceCriteria: ["API documentation exists"],
608
- relatedRequirements: []
609
- },
610
- {
611
- id: "req-003",
612
- type: "non-functional",
613
- description: "Test coverage must be adequate",
614
- priority: "should-have",
615
- source: "Best practices",
616
- acceptanceCriteria: ["Unit tests exist for core functionality"],
617
- relatedRequirements: []
618
- }
619
- ];
620
- }
621
- generateFeatures() {
622
- return [
623
- {
624
- id: "feat-001",
625
- name: "Core Implementation",
626
- description: "Main feature implementation",
627
- userStories: [`As a user, I want ${this.options.description}`],
628
- requirements: ["req-001"],
629
- complexity: "medium",
630
- dependencies: [],
631
- parallelizable: true
632
- }
633
- ];
634
- }
635
- extractConstraints() {
636
- return [
637
- "Must follow existing code patterns if present",
638
- "Must maintain backwards compatibility"
639
- ];
640
- }
641
- generateSuccessMetrics() {
642
- return [
643
- "All tests pass",
644
- "Documentation is complete",
645
- "Code review approved"
646
- ];
647
- }
648
- generateAlgorithmDesign(feature) {
649
- return {
650
- id: `algo_${feature.id}`,
651
- name: `${feature.name} Algorithm`,
652
- purpose: feature.description,
653
- steps: [
654
- {
655
- step: 1,
656
- description: "Initialize",
657
- pseudocode: "// Initialize component",
658
- inputs: [],
659
- outputs: []
660
- },
661
- {
662
- step: 2,
663
- description: "Process",
664
- pseudocode: "// Process input",
665
- inputs: ["input"],
666
- outputs: ["result"]
667
- },
668
- {
669
- step: 3,
670
- description: "Return",
671
- pseudocode: "// Return result",
672
- inputs: ["result"],
673
- outputs: ["output"]
674
- }
675
- ],
676
- timeComplexity: "O(n)",
677
- spaceComplexity: "O(1)",
678
- edgeCases: ["Empty input", "Invalid input"],
679
- relatedFeatures: [feature.id]
680
- };
681
- }
682
- generateArchitectureOverview() {
683
- return `Architecture for ${this.options.name}: ${this.options.description}`;
684
- }
685
- identifyArchitecturePatterns() {
686
- const patterns = ["Modular"];
687
- if (this.plan.existingCode?.patterns.includes("TypeScript")) {
688
- patterns.push("Type-Safe");
689
- }
690
- if (this.plan.existingCode?.patterns.includes("Test-Driven Development")) {
691
- patterns.push("TDD");
692
- }
693
- return patterns;
694
- }
695
- generateComponents() {
696
- return [
697
- {
698
- id: "comp-core",
699
- name: "Core Module",
700
- type: "module",
701
- description: "Core functionality module",
702
- responsibilities: ["Main feature implementation"],
703
- interfaces: [],
704
- dependencies: [],
705
- technologies: ["TypeScript"]
706
- },
707
- {
708
- id: "comp-api",
709
- name: "API Layer",
710
- type: "api",
711
- description: "Public API interface",
712
- responsibilities: ["Expose public API"],
713
- interfaces: [],
714
- dependencies: ["comp-core"],
715
- technologies: ["TypeScript"]
716
- }
717
- ];
718
- }
719
- describeDataFlow() {
720
- return "Input -> API Layer -> Core Module -> Output";
721
- }
722
- identifySecurityConsiderations() {
723
- return [
724
- "Input validation",
725
- "Error handling"
726
- ];
727
- }
728
- getComponentPriority(component) {
729
- if (component.dependencies.length === 0) return "high";
730
- return "medium";
731
- }
732
- estimateComponentHours(component) {
733
- const base = 4;
734
- const depFactor = component.dependencies.length * 2;
735
- return base + depFactor;
736
- }
737
1534
  /**
738
1535
  * Get the current plan
739
1536
  */
@@ -746,6 +1543,12 @@ class SPARCPlanner {
746
1543
  getDecisionLog() {
747
1544
  return this.decisionLog;
748
1545
  }
1546
+ /**
1547
+ * Get parsed documentation
1548
+ */
1549
+ getParsedDocs() {
1550
+ return this.parsedDocs;
1551
+ }
749
1552
  }
750
1553
  function createSPARCPlanner(options) {
751
1554
  return new SPARCPlanner(options);