ai-sdlc 0.3.0 → 0.3.1-alpha.0

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 (84) hide show
  1. package/README.md +78 -6
  2. package/dist/agents/implementation.d.ts +5 -0
  3. package/dist/agents/implementation.d.ts.map +1 -1
  4. package/dist/agents/implementation.js +78 -9
  5. package/dist/agents/implementation.js.map +1 -1
  6. package/dist/agents/planning.d.ts.map +1 -1
  7. package/dist/agents/planning.js +10 -3
  8. package/dist/agents/planning.js.map +1 -1
  9. package/dist/agents/research.d.ts.map +1 -1
  10. package/dist/agents/research.js +14 -6
  11. package/dist/agents/research.js.map +1 -1
  12. package/dist/agents/review.d.ts +81 -0
  13. package/dist/agents/review.d.ts.map +1 -1
  14. package/dist/agents/review.js +405 -39
  15. package/dist/agents/review.js.map +1 -1
  16. package/dist/agents/single-task.d.ts +1 -1
  17. package/dist/agents/single-task.d.ts.map +1 -1
  18. package/dist/agents/single-task.js +1 -1
  19. package/dist/cli/batch-processor.d.ts +64 -0
  20. package/dist/cli/batch-processor.d.ts.map +1 -0
  21. package/dist/cli/batch-processor.js +85 -0
  22. package/dist/cli/batch-processor.js.map +1 -0
  23. package/dist/cli/batch-validator.d.ts +80 -0
  24. package/dist/cli/batch-validator.d.ts.map +1 -0
  25. package/dist/cli/batch-validator.js +121 -0
  26. package/dist/cli/batch-validator.js.map +1 -0
  27. package/dist/cli/commands.d.ts +7 -0
  28. package/dist/cli/commands.d.ts.map +1 -1
  29. package/dist/cli/commands.js +285 -1
  30. package/dist/cli/commands.js.map +1 -1
  31. package/dist/cli/dependency-resolver.d.ts +49 -0
  32. package/dist/cli/dependency-resolver.d.ts.map +1 -0
  33. package/dist/cli/dependency-resolver.js +133 -0
  34. package/dist/cli/dependency-resolver.js.map +1 -0
  35. package/dist/cli/epic-processor.d.ts +16 -0
  36. package/dist/cli/epic-processor.d.ts.map +1 -0
  37. package/dist/cli/epic-processor.js +489 -0
  38. package/dist/cli/epic-processor.js.map +1 -0
  39. package/dist/cli/formatting.d.ts +15 -0
  40. package/dist/cli/formatting.d.ts.map +1 -1
  41. package/dist/cli/formatting.js +19 -0
  42. package/dist/cli/formatting.js.map +1 -1
  43. package/dist/cli/progress-dashboard.d.ts +58 -0
  44. package/dist/cli/progress-dashboard.d.ts.map +1 -0
  45. package/dist/cli/progress-dashboard.js +216 -0
  46. package/dist/cli/progress-dashboard.js.map +1 -0
  47. package/dist/cli/table-renderer.d.ts.map +1 -1
  48. package/dist/cli/table-renderer.js +5 -1
  49. package/dist/cli/table-renderer.js.map +1 -1
  50. package/dist/core/agent-executor.d.ts +13 -0
  51. package/dist/core/agent-executor.d.ts.map +1 -0
  52. package/dist/core/agent-executor.js +153 -0
  53. package/dist/core/agent-executor.js.map +1 -0
  54. package/dist/core/config.d.ts +16 -1
  55. package/dist/core/config.d.ts.map +1 -1
  56. package/dist/core/config.js +113 -0
  57. package/dist/core/config.js.map +1 -1
  58. package/dist/core/git-utils.d.ts +19 -0
  59. package/dist/core/git-utils.d.ts.map +1 -1
  60. package/dist/core/git-utils.js +58 -0
  61. package/dist/core/git-utils.js.map +1 -1
  62. package/dist/core/kanban.d.ts +125 -1
  63. package/dist/core/kanban.d.ts.map +1 -1
  64. package/dist/core/kanban.js +371 -4
  65. package/dist/core/kanban.js.map +1 -1
  66. package/dist/core/orchestrator.d.ts +63 -0
  67. package/dist/core/orchestrator.d.ts.map +1 -0
  68. package/dist/core/orchestrator.js +320 -0
  69. package/dist/core/orchestrator.js.map +1 -0
  70. package/dist/core/story.d.ts +84 -0
  71. package/dist/core/story.d.ts.map +1 -1
  72. package/dist/core/story.js +159 -14
  73. package/dist/core/story.js.map +1 -1
  74. package/dist/core/worktree.d.ts +7 -0
  75. package/dist/core/worktree.d.ts.map +1 -1
  76. package/dist/core/worktree.js +44 -0
  77. package/dist/core/worktree.js.map +1 -1
  78. package/dist/index.js +53 -0
  79. package/dist/index.js.map +1 -1
  80. package/dist/types/index.d.ts +252 -0
  81. package/dist/types/index.d.ts.map +1 -1
  82. package/dist/types/index.js +23 -0
  83. package/dist/types/index.js.map +1 -1
  84. package/package.json +1 -1
@@ -61,12 +61,19 @@ export declare function run(options: {
61
61
  dryRun?: boolean;
62
62
  continue?: boolean;
63
63
  story?: string;
64
+ batch?: string;
65
+ epic?: string;
66
+ maxConcurrent?: string;
67
+ concurrent?: string;
64
68
  step?: string;
65
69
  maxIterations?: string;
66
70
  watch?: boolean;
67
71
  force?: boolean;
68
72
  worktree?: boolean;
69
73
  clean?: boolean;
74
+ keepWorktrees?: boolean;
75
+ merge?: boolean;
76
+ mergeStrategy?: string;
70
77
  }): Promise<void>;
71
78
  /**
72
79
  * Phase information for RPIV display
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,EAAU,UAAU,EAA0H,eAAe,EAAE,MAAM,mBAAmB,CAAC;AA4BvM;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAyB1C;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmF1E;AA2DD;;GAEG;AACH,wBAAsB,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAyGpF;AA6HD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,EAC/B,cAAc,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,EACpC,WAAW,EAAE,KAAK,GAAG,IAAI,GACxB,OAAO,CAKT;AA0GD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,KAAK,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAC3B,OAAO,CAAC,eAAe,CAAC,CA8H1B;AA+JD;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAytCtX;AAoXD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CAClC;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,GAAG,SAAS,GAAG,IAAI,CAiDlF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,KAAK,GAAG;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,CAgCA;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAG,MAAM,CAsBtE;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAgB3E;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAYtD;AA6DD;;GAEG;AACH,wBAAsB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkH7D;AA8GD;;GAEG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgClG;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA8G7G;AAqFD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CA8DnD;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyEhE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuElG"}
@@ -5,7 +5,7 @@ import * as readline from 'readline';
5
5
  import { spawnSync } from 'child_process';
6
6
  import { getSdlcRoot, loadConfig, initConfig, validateWorktreeBasePath, DEFAULT_WORKTREE_CONFIG } from '../core/config.js';
7
7
  import { initializeKanban, kanbanExists, assessState, getBoardStats, findStoryBySlug, findStoriesByStatus } from '../core/kanban.js';
8
- import { createStory, parseStory, resetRPIVCycle, isAtMaxRetries, unblockStory, getStory, findStoryById, updateStoryField, writeStory, sanitizeStoryId, autoCompleteStoryAfterReview, incrementImplementationRetryCount, getEffectiveMaxImplementationRetries, isAtMaxImplementationRetries, updateStoryStatus } from '../core/story.js';
8
+ import { createStory, parseStory, resetRPIVCycle, isAtMaxRetries, unblockStory, getStory, findStoryById, updateStoryField, writeStory, sanitizeStoryId, autoCompleteStoryAfterReview, incrementImplementationRetryCount, resetImplementationRetryCount, getEffectiveMaxImplementationRetries, isAtMaxImplementationRetries, updateStoryStatus } from '../core/story.js';
9
9
  import { GitWorktreeService, getLastCompletedPhase, getNextPhase } from '../core/worktree.js';
10
10
  import { ReviewDecision } from '../types/index.js';
11
11
  import { getThemedChalk } from '../core/theme.js';
@@ -287,6 +287,35 @@ function validateAutoStoryOptions(options) {
287
287
  ' - ai-sdlc run --story <id> --step <phase> (single phase)');
288
288
  }
289
289
  }
290
+ /**
291
+ * Validates flag combinations for --batch conflicts
292
+ * @throws Error if conflicting flags are detected
293
+ */
294
+ function validateBatchOptions(options) {
295
+ if (!options.batch) {
296
+ return; // No batch flag, nothing to validate
297
+ }
298
+ // --batch and --story are mutually exclusive
299
+ if (options.story) {
300
+ throw new Error('Cannot combine --batch with --story flag.\n' +
301
+ 'Use either:\n' +
302
+ ' - ai-sdlc run --batch S-001,S-002,S-003 (batch processing)\n' +
303
+ ' - ai-sdlc run --auto --story <id> (single story)');
304
+ }
305
+ // --batch and --watch are mutually exclusive
306
+ if (options.watch) {
307
+ throw new Error('Cannot combine --batch with --watch flag.\n' +
308
+ 'Use either:\n' +
309
+ ' - ai-sdlc run --batch S-001,S-002,S-003 (batch processing)\n' +
310
+ ' - ai-sdlc run --watch (daemon mode)');
311
+ }
312
+ // --batch and --continue are mutually exclusive
313
+ if (options.continue) {
314
+ throw new Error('Cannot combine --batch with --continue flag.\n' +
315
+ 'Batch mode does not support resuming from checkpoints.\n' +
316
+ 'Use: ai-sdlc run --batch S-001,S-002,S-003');
317
+ }
318
+ }
290
319
  /**
291
320
  * Determines if a specific phase should be executed based on story state
292
321
  * @param story The story to check
@@ -587,6 +616,149 @@ export async function preFlightConflictCheck(targetStory, sdlcRoot, options) {
587
616
  return { proceed: true, warnings: ['Conflict detection failed'] };
588
617
  }
589
618
  }
619
+ /**
620
+ * Process multiple stories sequentially through full SDLC
621
+ * Internal function used by batch mode
622
+ */
623
+ async function processBatchInternal(storyIds, sdlcRoot, options) {
624
+ const startTime = Date.now();
625
+ const config = loadConfig();
626
+ const c = getThemedChalk(config);
627
+ const { formatBatchProgress, formatBatchSummary, logStoryCompletion, promptContinueOnError } = await import('./batch-processor.js');
628
+ const result = {
629
+ total: storyIds.length,
630
+ succeeded: 0,
631
+ failed: 0,
632
+ skipped: 0,
633
+ errors: [],
634
+ duration: 0,
635
+ };
636
+ console.log();
637
+ console.log(c.bold('═══ Starting Batch Processing ═══'));
638
+ console.log(c.dim(` Stories: ${storyIds.join(', ')}`));
639
+ console.log(c.dim(` Dry run: ${options.dryRun ? 'yes' : 'no'}`));
640
+ console.log();
641
+ // Process each story sequentially
642
+ for (let i = 0; i < storyIds.length; i++) {
643
+ const storyId = storyIds[i];
644
+ // Get story and check status
645
+ let story;
646
+ try {
647
+ story = getStory(sdlcRoot, storyId);
648
+ }
649
+ catch (error) {
650
+ result.failed++;
651
+ result.errors.push({
652
+ storyId,
653
+ error: `Story not found: ${error instanceof Error ? error.message : String(error)}`,
654
+ });
655
+ console.log(c.error(`[${i + 1}/${storyIds.length}] ✗ Story not found: ${storyId}`));
656
+ console.log();
657
+ // Ask if user wants to continue (or abort in non-interactive)
658
+ const shouldContinue = await promptContinueOnError(storyId, c);
659
+ if (!shouldContinue) {
660
+ console.log(c.warning('Batch processing aborted.'));
661
+ break;
662
+ }
663
+ continue;
664
+ }
665
+ // Skip if already done
666
+ if (story.frontmatter.status === 'done') {
667
+ result.skipped++;
668
+ console.log(c.dim(`[${i + 1}/${storyIds.length}] ⊘ Skipping ${storyId} (already completed)`));
669
+ console.log();
670
+ continue;
671
+ }
672
+ // Show progress header
673
+ const progress = {
674
+ currentIndex: i,
675
+ total: storyIds.length,
676
+ currentStory: story,
677
+ };
678
+ console.log(c.info(formatBatchProgress(progress)));
679
+ console.log();
680
+ // Dry-run mode: just show what would be done
681
+ if (options.dryRun) {
682
+ console.log(c.dim(' Would process story through full SDLC'));
683
+ console.log(c.dim(` Status: ${story.frontmatter.status}`));
684
+ console.log();
685
+ result.succeeded++;
686
+ continue;
687
+ }
688
+ // Process story through full SDLC by recursively calling run()
689
+ // We set auto: true to ensure full SDLC execution
690
+ try {
691
+ await run({
692
+ auto: true,
693
+ story: storyId,
694
+ dryRun: false,
695
+ worktree: options.worktree,
696
+ force: options.force,
697
+ });
698
+ // Check if story completed successfully (moved to done)
699
+ const finalStory = getStory(sdlcRoot, storyId);
700
+ if (finalStory.frontmatter.status === 'done') {
701
+ result.succeeded++;
702
+ logStoryCompletion(storyId, true, c);
703
+ }
704
+ else {
705
+ // Story didn't reach done state - treat as failure
706
+ result.failed++;
707
+ result.errors.push({
708
+ storyId,
709
+ error: `Story did not complete (status: ${finalStory.frontmatter.status})`,
710
+ });
711
+ logStoryCompletion(storyId, false, c);
712
+ // Ask if user wants to continue (or abort in non-interactive)
713
+ const shouldContinue = await promptContinueOnError(storyId, c);
714
+ if (!shouldContinue) {
715
+ console.log(c.warning('Batch processing aborted.'));
716
+ break;
717
+ }
718
+ }
719
+ }
720
+ catch (error) {
721
+ result.failed++;
722
+ result.errors.push({
723
+ storyId,
724
+ error: error instanceof Error ? error.message : String(error),
725
+ });
726
+ logStoryCompletion(storyId, false, c);
727
+ // Ask if user wants to continue (or abort in non-interactive)
728
+ const shouldContinue = await promptContinueOnError(storyId, c);
729
+ if (!shouldContinue) {
730
+ console.log(c.warning('Batch processing aborted.'));
731
+ break;
732
+ }
733
+ }
734
+ console.log();
735
+ }
736
+ // Display final summary
737
+ result.duration = Date.now() - startTime;
738
+ const summaryLines = formatBatchSummary(result);
739
+ summaryLines.forEach((line) => {
740
+ if (line.includes('✓')) {
741
+ console.log(c.success(line));
742
+ }
743
+ else if (line.includes('✗')) {
744
+ console.log(c.error(line));
745
+ }
746
+ else if (line.includes('⊘')) {
747
+ console.log(c.warning(line));
748
+ }
749
+ else if (line.startsWith(' -')) {
750
+ console.log(c.dim(line));
751
+ }
752
+ else {
753
+ console.log(line);
754
+ }
755
+ });
756
+ // Return non-zero exit code if any failures occurred
757
+ if (result.failed > 0) {
758
+ process.exitCode = 1;
759
+ }
760
+ return result;
761
+ }
590
762
  /**
591
763
  * Run the workflow (process one action or all)
592
764
  */
@@ -626,6 +798,115 @@ export async function run(options) {
626
798
  await startDaemon({ maxIterations: maxIterationsOverride });
627
799
  return; // Daemon runs indefinitely
628
800
  }
801
+ // Handle concurrent mode
802
+ if (options.concurrent) {
803
+ const concurrency = parseInt(options.concurrent, 10);
804
+ // Validate concurrency value
805
+ if (isNaN(concurrency) || concurrency <= 0) {
806
+ console.log(c.warning(`Warning: Invalid --concurrent value "${options.concurrent}". Defaulting to 1 (single-story mode).`));
807
+ // Fall through to normal single-story mode
808
+ }
809
+ else if (concurrency > 1) {
810
+ // Import orchestrator and run concurrent mode
811
+ const { Orchestrator } = await import('../core/orchestrator.js');
812
+ const { findStoriesByStatus } = await import('../core/kanban.js');
813
+ // Query database for ready stories, sorted by priority
814
+ const readyStories = findStoriesByStatus(sdlcRoot, 'ready');
815
+ if (readyStories.length === 0) {
816
+ console.log(c.info('No ready stories found. Add stories to the ready column in the kanban board.'));
817
+ return;
818
+ }
819
+ // Limit to available stories
820
+ const storiesToRun = readyStories.slice(0, Math.min(concurrency, readyStories.length));
821
+ console.log(c.info(`🚀 Running ${storiesToRun.length} stories concurrently (concurrency: ${concurrency})`));
822
+ // Create orchestrator and execute
823
+ const orchestrator = new Orchestrator({
824
+ concurrency,
825
+ shutdownTimeout: 10000,
826
+ keepWorktrees: options.keepWorktrees,
827
+ });
828
+ const results = await orchestrator.execute(storiesToRun);
829
+ // Report results
830
+ const succeeded = results.filter((r) => r.success).length;
831
+ const failed = results.filter((r) => !r.success).length;
832
+ console.log('');
833
+ console.log(c.info('═══════════════════════════════════════════'));
834
+ console.log(c.info('Concurrent Execution Summary'));
835
+ console.log(c.info('═══════════════════════════════════════════'));
836
+ console.log(c.success(`✅ Succeeded: ${succeeded}`));
837
+ if (failed > 0) {
838
+ console.log(c.error(`❌ Failed: ${failed}`));
839
+ }
840
+ console.log('');
841
+ // Exit with error code if any failed
842
+ if (failed > 0) {
843
+ process.exit(1);
844
+ }
845
+ return;
846
+ }
847
+ }
848
+ // Handle epic mode
849
+ if (options.epic) {
850
+ const { processEpic } = await import('./epic-processor.js');
851
+ const maxConcurrent = options.maxConcurrent ? parseInt(options.maxConcurrent, 10) : undefined;
852
+ // Parse merge strategy if provided
853
+ const mergeStrategy = options.mergeStrategy;
854
+ const exitCode = await processEpic({
855
+ epicId: options.epic,
856
+ maxConcurrent,
857
+ dryRun: options.dryRun,
858
+ force: options.force,
859
+ keepWorktrees: options.keepWorktrees,
860
+ merge: options.merge,
861
+ mergeStrategy,
862
+ });
863
+ process.exit(exitCode);
864
+ }
865
+ // Handle batch mode
866
+ if (options.batch) {
867
+ // Validate batch options first
868
+ try {
869
+ validateBatchOptions(options);
870
+ }
871
+ catch (error) {
872
+ console.log(c.error(`Error: ${error instanceof Error ? error.message : String(error)}`));
873
+ return;
874
+ }
875
+ // Import batch validation modules
876
+ const { parseStoryIdList, deduplicateStoryIds, validateStoryIds } = await import('./batch-validator.js');
877
+ // Parse and validate story IDs
878
+ const rawStoryIds = parseStoryIdList(options.batch);
879
+ if (rawStoryIds.length === 0) {
880
+ console.log(c.error('Error: Empty batch - no story IDs provided'));
881
+ console.log(c.dim('Usage: ai-sdlc run --batch S-001,S-002,S-003'));
882
+ return;
883
+ }
884
+ // Deduplicate story IDs
885
+ const storyIds = deduplicateStoryIds(rawStoryIds);
886
+ if (storyIds.length < rawStoryIds.length) {
887
+ const duplicateCount = rawStoryIds.length - storyIds.length;
888
+ console.log(c.dim(`Note: Removed ${duplicateCount} duplicate story ID(s)`));
889
+ }
890
+ // Validate all stories exist before processing
891
+ const validation = validateStoryIds(storyIds, sdlcRoot);
892
+ if (!validation.valid) {
893
+ console.log(c.error('Error: Batch validation failed'));
894
+ console.log();
895
+ for (const error of validation.errors) {
896
+ console.log(c.error(` - ${error.message}`));
897
+ }
898
+ console.log();
899
+ console.log(c.dim('Fix the errors above and try again.'));
900
+ return;
901
+ }
902
+ // Process the batch using internal function
903
+ await processBatchInternal(storyIds, sdlcRoot, {
904
+ dryRun: options.dryRun,
905
+ worktree: options.worktree,
906
+ force: options.force,
907
+ });
908
+ return; // Batch processing complete
909
+ }
629
910
  // Valid step names for --step option
630
911
  const validSteps = ['refine', 'research', 'plan', 'implement', 'review'];
631
912
  // Validate --step option early
@@ -1693,6 +1974,9 @@ async function executeAction(action, sdlcRoot) {
1693
1974
  story = await autoCompleteStoryAfterReview(story, config, reviewResult);
1694
1975
  // Log auto-completion if it occurred
1695
1976
  if (reviewResult.decision === ReviewDecision.APPROVED && config.reviewConfig.autoCompleteOnApproval) {
1977
+ // Reset implementation retry count on successful review approval
1978
+ await resetImplementationRetryCount(story);
1979
+ storyLogger?.log('INFO', 'Implementation retry count reset after review approval');
1696
1980
  spinner.text = c.success('Review approved - auto-completing story');
1697
1981
  storyLogger?.log('INFO', `Story auto-completed after review approval: "${story.frontmatter.title}"`);
1698
1982
  // Auto-create PR in automated mode