@krr2020/taskflow-core 0.1.0-beta.4 → 0.1.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/index.js +41 -3
  3. package/dist/commands/base.d.ts +41 -0
  4. package/dist/commands/base.js +141 -0
  5. package/dist/commands/configure.d.ts +29 -0
  6. package/dist/commands/configure.js +187 -0
  7. package/dist/commands/init.js +15 -1
  8. package/dist/commands/prd/create.d.ts +1 -1
  9. package/dist/commands/prd/create.js +26 -8
  10. package/dist/commands/tasks/generate.d.ts +1 -1
  11. package/dist/commands/tasks/generate.js +81 -55
  12. package/dist/commands/upgrade.js +35 -2
  13. package/dist/commands/workflow/check.d.ts +16 -0
  14. package/dist/commands/workflow/check.js +355 -32
  15. package/dist/commands/workflow/do.js +157 -62
  16. package/dist/index.d.ts +4 -0
  17. package/dist/index.js +6 -0
  18. package/dist/lib/config-paths.js +6 -1
  19. package/dist/lib/file-validator.d.ts +119 -0
  20. package/dist/lib/file-validator.js +291 -0
  21. package/dist/lib/log-parser.d.ts +91 -0
  22. package/dist/lib/log-parser.js +178 -0
  23. package/dist/lib/retrospective.d.ts +27 -0
  24. package/dist/lib/retrospective.js +110 -0
  25. package/dist/lib/types.d.ts +8 -0
  26. package/dist/lib/types.js +12 -8
  27. package/dist/llm/base.d.ts +52 -0
  28. package/dist/llm/base.js +35 -0
  29. package/dist/llm/factory.d.ts +39 -0
  30. package/dist/llm/factory.js +102 -0
  31. package/dist/llm/index.d.ts +7 -0
  32. package/dist/llm/index.js +7 -0
  33. package/dist/llm/model-selector.d.ts +71 -0
  34. package/dist/llm/model-selector.js +139 -0
  35. package/dist/llm/providers/anthropic.d.ts +31 -0
  36. package/dist/llm/providers/anthropic.js +116 -0
  37. package/dist/llm/providers/index.d.ts +6 -0
  38. package/dist/llm/providers/index.js +6 -0
  39. package/dist/llm/providers/ollama.d.ts +28 -0
  40. package/dist/llm/providers/ollama.js +91 -0
  41. package/dist/llm/providers/openai-compatible.d.ts +30 -0
  42. package/dist/llm/providers/openai-compatible.js +93 -0
  43. package/dist/schemas/config.d.ts +82 -0
  44. package/dist/schemas/config.js +35 -0
  45. package/package.json +43 -43
  46. package/dist/lib/package-manager.d.ts +0 -17
  47. package/dist/lib/package-manager.js +0 -53
@@ -1,12 +1,19 @@
1
1
  /**
2
2
  * Check command - Validate and advance task state
3
3
  */
4
+ import { execSync } from "node:child_process";
5
+ import { readdir } from "node:fs/promises";
6
+ import path from "node:path";
4
7
  import { ConfigLoader } from "../../lib/config-loader.js";
5
8
  import { getRefFilePath, REF_FILES } from "../../lib/config-paths.js";
6
9
  import { findActiveTask, loadTasksProgress, updateTaskStatus, } from "../../lib/data-access.js";
7
10
  import { NoActiveSessionError } from "../../lib/errors.js";
8
- import { processValidationOutput } from "../../lib/retrospective.js";
11
+ import { FileValidator, } from "../../lib/file-validator.js";
12
+ import { LogParser } from "../../lib/log-parser.js";
13
+ import { extractNewPatterns, formatNewPatternForDisplay, processValidationOutput, } from "../../lib/retrospective.js";
9
14
  import { runValidations } from "../../lib/validation.js";
15
+ import { Phase } from "../../llm/base.js";
16
+ import { ProviderFactory } from "../../llm/factory.js";
10
17
  import { BaseCommand } from "../base.js";
11
18
  export class CheckCommand extends BaseCommand {
12
19
  async execute() {
@@ -248,7 +255,7 @@ export class CheckCommand extends BaseCommand {
248
255
  "",
249
256
  "LEARNINGS TRACKING:",
250
257
  "────────────────────",
251
- "Capture only general, project-wide insights: not implementation details (\"I added a function\"), not what you did but what you learned, prevent repeated mistakes in future tasks.",
258
+ "Capture only general, project-wide insights: not implementation details ('I added a function'), not what you did but what you learned, prevent repeated mistakes in future tasks.",
252
259
  "",
253
260
  "TECH DEBT REPORTING:",
254
261
  "────────────────────────",
@@ -347,51 +354,138 @@ export class CheckCommand extends BaseCommand {
347
354
  });
348
355
  }
349
356
  async advanceToCommitting(tasksDir, logsDir, refDir, tasksProgress, taskId, content, validationCommands) {
350
- // Run validations
357
+ // Check if AI validation is enabled
358
+ const configLoader = new ConfigLoader(this.context.projectRoot);
359
+ const config = configLoader.load();
360
+ // Run AI-powered validation if configured
361
+ if (config.ai?.enabled && config.ai.provider) {
362
+ const aiResult = await this.runAIValidation(logsDir, Phase.Analysis);
363
+ if (!aiResult.passed) {
364
+ return aiResult.result;
365
+ }
366
+ }
367
+ // Run traditional validations
351
368
  console.log("\n🧪 Running validations...\n");
352
369
  const summary = runValidations(logsDir, taskId, validationCommands);
353
370
  // Check for known errors in output
354
371
  const retroCheck = processValidationOutput(refDir, summary.allOutput);
355
372
  if (!summary.passed) {
373
+ // Get LLM error analysis if available
374
+ let llmGuidance = [
375
+ "Validation Failed - Fix Required",
376
+ "",
377
+ retroCheck.knownErrors.length > 0
378
+ ? "KNOWN ERRORS DETECTED:"
379
+ : "Errors found in validation.",
380
+ retroCheck.knownErrors.length > 0
381
+ ? "You have made mistakes that are already documented."
382
+ : "",
383
+ retroCheck.knownErrors.length > 0
384
+ ? "Check the output above for solutions."
385
+ : "",
386
+ retroCheck.knownErrors.length > 0
387
+ ? "STOP REPEATING THESE MISTAKES."
388
+ : "",
389
+ "",
390
+ "How to fix:",
391
+ "1. Read error messages carefully",
392
+ "2. Apply solutions from retrospective (if shown)",
393
+ "3. Fix the code",
394
+ "4. Re-run: taskflow check",
395
+ "",
396
+ "DO NOT bypass validations",
397
+ "DO NOT use 'any' types to suppress errors",
398
+ "DO fix the root cause",
399
+ ].join("\n");
400
+ // Parse logs and detect new error patterns
401
+ const logParser = new LogParser();
402
+ const logFiles = await readdir(logsDir);
403
+ const parsedErrors = [];
404
+ for (const logFile of logFiles) {
405
+ if (logFile.endsWith(".log")) {
406
+ try {
407
+ const logPath = path.join(logsDir, logFile);
408
+ const parseResult = await logParser.parseFile(logPath);
409
+ parsedErrors.push(...parseResult.errors);
410
+ }
411
+ catch {
412
+ // Ignore log parsing errors
413
+ }
414
+ }
415
+ }
416
+ // Detect NEW error patterns for retrospective
417
+ let newPatternsDetected = [];
418
+ if (parsedErrors.length > 0) {
419
+ const newPatterns = extractNewPatterns(parsedErrors, refDir);
420
+ if (newPatterns.length > 0) {
421
+ newPatternsDetected = newPatterns.map((pattern) => formatNewPatternForDisplay(pattern));
422
+ }
423
+ }
424
+ // Get AI error analysis if LLM available
425
+ if (this.isLLMAvailable() && parsedErrors.length > 0) {
426
+ try {
427
+ const errors = parsedErrors.map((error) => ({
428
+ file: error.file,
429
+ message: error.message,
430
+ line: error.line ?? undefined,
431
+ code: error.code ?? undefined,
432
+ }));
433
+ const errorAnalysis = await this.getErrorAnalysis(errors);
434
+ if (errorAnalysis) {
435
+ llmGuidance = [
436
+ "AI Error Analysis:",
437
+ "───────────────────",
438
+ errorAnalysis,
439
+ "",
440
+ "How to fix:",
441
+ "1. Review the AI analysis above",
442
+ "2. Apply suggested fixes file-by-file",
443
+ "3. Re-run: taskflow check",
444
+ "",
445
+ retroCheck.knownErrors.length > 0
446
+ ? "⚠️ Known errors also detected - see solutions above"
447
+ : "",
448
+ "",
449
+ newPatternsDetected.length > 0
450
+ ? "📝 NEW error patterns detected (see warnings below)"
451
+ : "",
452
+ ].join("\n");
453
+ }
454
+ }
455
+ catch {
456
+ // Fall back to standard guidance
457
+ }
458
+ }
459
+ // Build warnings with new patterns
460
+ const warnings = [];
461
+ if (newPatternsDetected.length > 0) {
462
+ warnings.push("📝 NEW Error Patterns Detected:");
463
+ warnings.push("");
464
+ for (const patternDisplay of newPatternsDetected) {
465
+ warnings.push(patternDisplay);
466
+ warnings.push("");
467
+ }
468
+ warnings.push("💡 Consider adding these to retrospective: taskflow retro add");
469
+ warnings.push("");
470
+ }
471
+ const failureOptions = {
472
+ aiGuidance: llmGuidance,
473
+ };
474
+ if (warnings.length > 0) {
475
+ failureOptions.warnings = warnings;
476
+ }
356
477
  return this.failure("Validation failed", summary.failedChecks.map((check) => `${check} failed`), [
357
478
  "Fix the errors shown above and re-run: taskflow check",
358
479
  "",
359
480
  retroCheck.knownErrors.length > 0
360
481
  ? "⚠️ Known errors detected - see solutions above"
361
482
  : "",
362
- retroCheck.hasNewErrors
483
+ retroCheck.hasNewErrors && newPatternsDetected.length === 0
363
484
  ? "⚠️ New error detected - consider adding to retrospective: taskflow retro add"
364
485
  : "",
365
486
  "",
366
487
  `Full logs: ${logsDir}`,
367
- ].join("\n"), {
368
- aiGuidance: [
369
- "Validation Failed - Fix Required",
370
- "",
371
- retroCheck.knownErrors.length > 0
372
- ? "KNOWN ERRORS DETECTED:"
373
- : "Errors found in validation.",
374
- retroCheck.knownErrors.length > 0
375
- ? "You have made mistakes that are already documented."
376
- : "",
377
- retroCheck.knownErrors.length > 0
378
- ? "Check the output above for solutions."
379
- : "",
380
- retroCheck.knownErrors.length > 0
381
- ? "STOP REPEATING THESE MISTAKES."
382
- : "",
383
- "",
384
- "How to fix:",
385
- "1. Read error messages carefully",
386
- "2. Apply solutions from retrospective (if shown)",
387
- "3. Fix the code",
388
- "4. Re-run: taskflow check",
389
- "",
390
- "DO NOT bypass validations",
391
- "DO NOT use 'any' types to suppress errors",
392
- "DO fix the root cause",
393
- ].join("\n"),
394
- });
488
+ ].join("\n"), failureOptions);
395
489
  }
396
490
  // All validations passed - advance to committing
397
491
  updateTaskStatus(tasksDir, tasksProgress, taskId, "committing");
@@ -460,4 +554,233 @@ export class CheckCommand extends BaseCommand {
460
554
  ],
461
555
  });
462
556
  }
557
+ /**
558
+ * Run AI-powered validation on changed files
559
+ */
560
+ async runAIValidation(logsDir, phase) {
561
+ console.log("\n🤖 Running AI-powered file validation...\n");
562
+ try {
563
+ // Get AI config to create provider with correct settings
564
+ const config = this.configLoader.load();
565
+ if (!config?.ai || !config.ai.enabled) {
566
+ throw new Error("AI configuration not enabled");
567
+ }
568
+ if (!config.ai.provider) {
569
+ throw new Error("AI provider not configured");
570
+ }
571
+ // Create provider using actual config (cast to AIConfig)
572
+ const provider = ProviderFactory.createSelector(config.ai);
573
+ const analysisProvider = provider.getProvider(phase);
574
+ // Find modified files (git status, recent changes, etc.)
575
+ const modifiedFiles = await this.findModifiedFiles();
576
+ if (modifiedFiles.length === 0) {
577
+ console.log("No modified files found for validation");
578
+ return {
579
+ passed: true,
580
+ result: this.success("No files to validate", ""),
581
+ };
582
+ }
583
+ console.log(`Validating ${modifiedFiles.length} file${modifiedFiles.length > 1 ? "s" : ""}...\n`);
584
+ // Create file validator
585
+ const validator = new FileValidator({
586
+ provider: analysisProvider,
587
+ includeContext: true,
588
+ provideFixes: true,
589
+ maxIssues: 10,
590
+ });
591
+ // Validate each file
592
+ const results = [];
593
+ for (const filePath of modifiedFiles) {
594
+ try {
595
+ const result = await validator.validate(filePath);
596
+ results.push(result);
597
+ }
598
+ catch (error) {
599
+ console.warn(`Failed to validate ${filePath}: ${error.message}`);
600
+ }
601
+ }
602
+ // Check if all files passed
603
+ const allPassed = results.every((r) => r.passed);
604
+ // Format and display results
605
+ const output = this.formatValidationResults(results);
606
+ console.log(output);
607
+ if (allPassed) {
608
+ return {
609
+ passed: true,
610
+ result: this.success("All files passed AI validation", "Ready to proceed with standard validation"),
611
+ };
612
+ }
613
+ // Parse logs for errors to provide context
614
+ const logParser = new LogParser();
615
+ const logFiles = await readdir(logsDir);
616
+ const errors = [];
617
+ for (const logFile of logFiles) {
618
+ if (logFile.endsWith(".log")) {
619
+ try {
620
+ const logPath = path.join(logsDir, logFile);
621
+ const parseResult = await logParser.parseFile(logPath);
622
+ errors.push(...parseResult.errors);
623
+ }
624
+ catch {
625
+ // Ignore log parsing errors
626
+ }
627
+ }
628
+ }
629
+ // Create AI guidance with error context
630
+ const errorSummary = this.createErrorSummary(errors);
631
+ const messages = [
632
+ "Fix issues shown above and re-run: taskflow check",
633
+ "",
634
+ errorSummary,
635
+ "",
636
+ "AI-Powered Validation Benefits:",
637
+ "- Early error detection before compilation",
638
+ "- Context-aware fix suggestions",
639
+ "- Pattern-based issue identification",
640
+ "- Code quality analysis",
641
+ ];
642
+ const guidance = [
643
+ "AI Validation Results",
644
+ "────────────────────",
645
+ `Files validated: ${results.length}`,
646
+ `Files passed: ${results.filter((r) => r.passed).length}`,
647
+ `Files failed: ${results.filter((r) => !r.passed).length}`,
648
+ "",
649
+ "Fix Instructions:",
650
+ "1. Review issues shown above for each file",
651
+ "2. Apply suggested fixes",
652
+ "3. Re-run: taskflow check",
653
+ "",
654
+ "If errors persist, check for:",
655
+ "- Incorrect imports or types",
656
+ "- Missing dependencies",
657
+ "- Logic errors or bugs",
658
+ "- Known patterns in retrospective",
659
+ ];
660
+ return {
661
+ passed: false,
662
+ result: this.failure("AI validation failed", results.flatMap((r) => r.issues.map((i) => i.message)), messages.join("\n"), {
663
+ aiGuidance: guidance.join("\n"),
664
+ }),
665
+ };
666
+ }
667
+ catch (error) {
668
+ console.warn(`AI validation failed: ${error.message}`);
669
+ console.log("Falling back to standard validation\n");
670
+ // Return passed to fall back to standard validation
671
+ return {
672
+ passed: true,
673
+ result: this.success("AI validation skipped", "Proceeding with standard validation"),
674
+ };
675
+ }
676
+ }
677
+ /**
678
+ * Find modified files for validation
679
+ */
680
+ async findModifiedFiles() {
681
+ try {
682
+ // Get modified, added, and untracked files from git
683
+ const output = execSync("git status --porcelain --untracked-files=all", {
684
+ cwd: this.context.projectRoot,
685
+ encoding: "utf-8",
686
+ }).trim();
687
+ if (!output) {
688
+ return [];
689
+ }
690
+ const files = [];
691
+ const lines = output.split("\n");
692
+ for (const line of lines) {
693
+ // Parse git status output format:
694
+ // XY FILENAME
695
+ // X = status in index, Y = status in working tree
696
+ // Status codes: M (modified), A (added), D (deleted), ?? (untracked), etc.
697
+ const match = line.match(/^(.{2})\s+(.+)$/);
698
+ if (!match)
699
+ continue;
700
+ const [, status, filePath] = match;
701
+ if (!status || !filePath)
702
+ continue;
703
+ // Skip deleted files
704
+ if (status.includes("D"))
705
+ continue;
706
+ // Only validate source files (ts, tsx, js, jsx) but not test files
707
+ const isSourceFile = /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(filePath);
708
+ const isTestFile = /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filePath);
709
+ if (isSourceFile && !isTestFile) {
710
+ // Convert to absolute path
711
+ const absolutePath = path.join(this.context.projectRoot, filePath);
712
+ files.push(absolutePath);
713
+ }
714
+ }
715
+ return files;
716
+ }
717
+ catch (error) {
718
+ console.warn(`Failed to get modified files: ${error.message}`);
719
+ return [];
720
+ }
721
+ }
722
+ /**
723
+ * Create error summary from logs and validation results
724
+ */
725
+ createErrorSummary(errors) {
726
+ if (errors.length === 0) {
727
+ return "No errors found in logs.";
728
+ }
729
+ const grouped = new Map();
730
+ for (const error of errors) {
731
+ if (error.file) {
732
+ const existing = grouped.get(error.file) || [];
733
+ existing.push(error);
734
+ grouped.set(error.file, existing);
735
+ }
736
+ }
737
+ const lines = ["Error Summary:", ""];
738
+ for (const [file, fileErrors] of grouped) {
739
+ lines.push(`${file}: ${fileErrors.length} error${fileErrors.length > 1 ? "s" : ""}`);
740
+ }
741
+ return lines.join("\n");
742
+ }
743
+ /**
744
+ * Format validation results
745
+ */
746
+ formatValidationResults(results) {
747
+ const lines = [];
748
+ lines.push("=".repeat(80));
749
+ lines.push("AI-Powered Validation Results");
750
+ lines.push("=".repeat(80));
751
+ for (const result of results) {
752
+ lines.push("");
753
+ lines.push(`File: ${result.file}`);
754
+ lines.push(`Status: ${result.passed ? "✓ PASSED" : "✗ FAILED"}`);
755
+ lines.push(`Summary: ${result.summary}`);
756
+ if (result.issues.length > 0) {
757
+ lines.push("");
758
+ lines.push("Issues:");
759
+ for (const issue of result.issues) {
760
+ const icon = issue.severity === "error"
761
+ ? "✗"
762
+ : issue.severity === "warning"
763
+ ? "⚠"
764
+ : "ℹ";
765
+ lines.push(` ${icon} ${issue.message}`);
766
+ if (issue.line) {
767
+ lines.push(` Line: ${issue.line}`);
768
+ }
769
+ if (issue.suggestedFix) {
770
+ lines.push(` Suggested: ${issue.suggestedFix}`);
771
+ }
772
+ }
773
+ }
774
+ if (result.suggestions.length > 0) {
775
+ lines.push("");
776
+ lines.push("Suggestions:");
777
+ for (const suggestion of result.suggestions) {
778
+ lines.push(` • ${suggestion}`);
779
+ }
780
+ }
781
+ }
782
+ lines.push("");
783
+ lines.push("=".repeat(80));
784
+ return lines.join("\n");
785
+ }
463
786
  }