@agile-vibe-coding/avc 0.3.3 → 0.3.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.
@@ -779,6 +779,37 @@ Return your response as JSON following the exact structure specified in your ins
779
779
  this.debug(`[INFO] Updated story doc.md after task extraction (${storyDocContent.length} bytes)`);
780
780
  }
781
781
 
782
+ // Compute ready status for tasks/subtasks with no pending dependencies
783
+ try {
784
+ const { checkDependenciesReady } = await import('./dependency-checker.js');
785
+ const lookup = {};
786
+ for (const task of hierarchy.tasks) {
787
+ lookup[task.id] = { id: task.id, name: task.name, type: 'task', status: 'planned', dependencies: task.dependencies || [] };
788
+ for (const sub of task.subtasks || []) {
789
+ lookup[sub.id] = { id: sub.id, name: sub.name, type: 'subtask', status: 'planned', dependencies: sub.dependencies || [] };
790
+ }
791
+ }
792
+
793
+ let readyCount = 0;
794
+ for (const id of Object.keys(lookup)) {
795
+ const result = checkDependenciesReady(id, lookup);
796
+ if (result.ready) {
797
+ const itemDir = path.join(this.projectPath, id);
798
+ const wjPath = path.join(itemDir, 'work.json');
799
+ if (fs.existsSync(wjPath)) {
800
+ const wj = JSON.parse(fs.readFileSync(wjPath, 'utf8'));
801
+ wj.status = 'ready';
802
+ fs.writeFileSync(wjPath, JSON.stringify(wj, null, 2), 'utf8');
803
+ lookup[id].status = 'ready';
804
+ readyCount++;
805
+ }
806
+ }
807
+ }
808
+ this.debug(`[INFO] Set ${readyCount} task/subtask items to 'ready' status (dependency-free)`);
809
+ } catch (err) {
810
+ this.debug(`[WARN] Ready status computation failed (non-critical): ${err.message}`);
811
+ }
812
+
782
813
  return { taskCount, subtaskCount, taskIds };
783
814
  }
784
815
 
@@ -895,7 +926,6 @@ Return your response as JSON following the exact structure specified in your ins
895
926
  throw error;
896
927
  }
897
928
  }
898
- }
899
929
 
900
930
  /**
901
931
  * Worker-compatible execution — uses callbacks instead of REPL output functions.
@@ -141,16 +141,21 @@ export class WorktreeRunner {
141
141
  return { success: false, error: `Tests failed: ${testResult.summary}` };
142
142
  }
143
143
 
144
- // 5. Commit and merge
145
- progressCallback?.('Committing and merging to main...');
144
+ // 5. Commit in worktree (do NOT merge — leave for human review)
145
+ progressCallback?.('Committing changes in worktree...');
146
146
  if (cancelledCheck?.()) throw new Error('CANCELLED');
147
- this.commitAndMerge();
147
+ this.commitInWorktree();
148
148
 
149
- // 6. Cleanup
150
- progressCallback?.('Cleaning up worktree...');
151
- this.cleanup();
149
+ // 6. Leave worktree in place for review — do NOT cleanup or merge
150
+ progressCallback?.(`Code ready for review in worktree: ${this.worktreePath}`);
151
+ progressCallback?.(`Branch: ${this.branchName}`);
152
152
 
153
- return { success: true };
153
+ return {
154
+ success: true,
155
+ review: true,
156
+ worktreePath: this.worktreePath,
157
+ branchName: this.branchName,
158
+ };
154
159
  } catch (err) {
155
160
  // Always cleanup on failure
156
161
  try { this.cleanup(); } catch {}
@@ -583,28 +588,35 @@ export class WorktreeRunner {
583
588
  /**
584
589
  * Commit changes in the worktree and merge to the main branch.
585
590
  */
586
- commitAndMerge() {
587
- // Stage all changes
591
+ /**
592
+ * Commit all changes in the worktree (does NOT merge to main).
593
+ * Called after tests pass — the worktree stays in place for human review.
594
+ */
595
+ commitInWorktree() {
588
596
  git(['add', '-A'], this.worktreePath);
589
597
 
590
- // Check if there's anything to commit
591
598
  const status = git(['status', '--porcelain'], this.worktreePath);
592
599
  if (!status) {
593
600
  this.debug('No changes to commit');
594
601
  return;
595
602
  }
596
603
 
597
- // Commit
598
604
  const commitMsg = `feat(${this.taskId}): implement task\n\nGenerated by AVC WorktreeRunner`;
599
605
  git(['commit', '-m', commitMsg], this.worktreePath);
600
606
  this.debug('Committed in worktree');
607
+ }
601
608
 
609
+ /**
610
+ * Merge the worktree branch into main and clean up.
611
+ * Called AFTER human review approves the code (status: implemented → completed).
612
+ * Can be invoked via API or CLI.
613
+ */
614
+ mergeAndCleanup() {
602
615
  // Determine the main branch name
603
616
  let mainBranch = 'main';
604
617
  try {
605
618
  mainBranch = git(['symbolic-ref', '--short', 'HEAD'], this.projectRoot);
606
619
  } catch {
607
- // If HEAD is detached, try common branch names
608
620
  try { git(['rev-parse', '--verify', 'main'], this.projectRoot); mainBranch = 'main'; } catch {
609
621
  try { git(['rev-parse', '--verify', 'master'], this.projectRoot); mainBranch = 'master'; } catch {}
610
622
  }
@@ -615,10 +627,12 @@ export class WorktreeRunner {
615
627
  git(['merge', '--no-ff', this.branchName, '-m', `Merge ${this.branchName}: implement ${this.taskId}`], this.projectRoot);
616
628
  this.debug('Merged to main', { mainBranch });
617
629
  } catch (err) {
618
- // Merge conflict — abort and report
619
630
  try { git(['merge', '--abort'], this.projectRoot); } catch {}
620
631
  throw new Error(`Merge conflict: ${err.message}`);
621
632
  }
633
+
634
+ // Cleanup worktree and branch
635
+ this.cleanup();
622
636
  }
623
637
 
624
638
  /**