diff --git a/auto-claude-ui/src/renderer/components/task-detail/TaskReview.tsx b/auto-claude-ui/src/renderer/components/task-detail/TaskReview.tsx index 3df18b1b..8527c2f4 100644 --- a/auto-claude-ui/src/renderer/components/task-detail/TaskReview.tsx +++ b/auto-claude-ui/src/renderer/components/task-detail/TaskReview.tsx @@ -112,9 +112,13 @@ export function TaskReview({ onMerge={onMerge} /> ) : task.stagedInMainProject ? ( - + ) : ( - + )} {/* QA Feedback Section */} diff --git a/auto-claude-ui/src/renderer/components/task-detail/task-review/WorkspaceMessages.tsx b/auto-claude-ui/src/renderer/components/task-detail/task-review/WorkspaceMessages.tsx index 67f9d2c1..cc2ee3e6 100644 --- a/auto-claude-ui/src/renderer/components/task-detail/task-review/WorkspaceMessages.tsx +++ b/auto-claude-ui/src/renderer/components/task-detail/task-review/WorkspaceMessages.tsx @@ -1,4 +1,7 @@ -import { AlertCircle, GitMerge, Loader2 } from 'lucide-react'; +import { AlertCircle, GitMerge, Loader2, Trash2, Check } from 'lucide-react'; +import { useState } from 'react'; +import { Button } from '../../ui/button'; +import { persistTaskStatus } from '../../../stores/task-store'; import type { Task } from '../../../../shared/types'; interface LoadingMessageProps { @@ -19,31 +22,103 @@ export function LoadingMessage({ message = 'Loading workspace info...' }: Loadin ); } +interface NoWorkspaceMessageProps { + task?: Task; +} + /** * Displays message when no workspace is found for the task */ -export function NoWorkspaceMessage() { +export function NoWorkspaceMessage({ task }: NoWorkspaceMessageProps) { + const [isMarkingDone, setIsMarkingDone] = useState(false); + + const handleMarkDone = async () => { + if (!task) return; + + setIsMarkingDone(true); + try { + await persistTaskStatus(task.id, 'done'); + } catch (err) { + console.error('Error marking task as done:', err); + } finally { + setIsMarkingDone(false); + } + }; + return (

No Workspace Found

-

+

No isolated workspace was found for this task. The changes may have been made directly in your project.

+ + {/* Allow marking as done */} + {task && task.status === 'human_review' && ( + + )}
); } interface StagedInProjectMessageProps { task: Task; + projectPath?: string; + hasWorktree?: boolean; } /** * Displays message when changes have already been staged in the main project */ -export function StagedInProjectMessage({ task }: StagedInProjectMessageProps) { +export function StagedInProjectMessage({ task, projectPath, hasWorktree = false }: StagedInProjectMessageProps) { + const [isDeleting, setIsDeleting] = useState(false); + const [error, setError] = useState(null); + + const handleDeleteWorktreeAndMarkDone = async () => { + setIsDeleting(true); + setError(null); + + try { + // Call the discard/delete worktree command + const result = await window.electronAPI.discardWorktree(task.id); + + if (!result.success) { + setError(result.error || 'Failed to delete worktree'); + return; + } + + // Mark task as done + await persistTaskStatus(task.id, 'done'); + + // Success - the UI will update automatically via the store + } catch (err) { + console.error('Error deleting worktree:', err); + setError(err instanceof Error ? err.message : 'Failed to delete worktree'); + } finally { + setIsDeleting(false); + } + }; + return (

@@ -53,7 +128,7 @@ export function StagedInProjectMessage({ task }: StagedInProjectMessageProps) {

This task's changes have been staged in your main project{task.stagedAt ? ` on ${new Date(task.stagedAt).toLocaleDateString()}` : ''}.

-
+

Next steps:

  1. Review staged changes with git status and git diff --staged
  2. @@ -61,6 +136,39 @@ export function StagedInProjectMessage({ task }: StagedInProjectMessageProps) {
  3. Push to remote when satisfied
+ + {/* Action buttons */} + {hasWorktree && ( +
+
+ +
+ {error && ( +

{error}

+ )} +

+ This will delete the isolated workspace and mark the task as complete. +

+
+ )}
); } diff --git a/auto-claude/core/workspace.py b/auto-claude/core/workspace.py index ebfba65d..601f16f0 100644 --- a/auto-claude/core/workspace.py +++ b/auto-claude/core/workspace.py @@ -197,22 +197,20 @@ def merge_existing_build( if had_conflicts: # Git conflicts were resolved (via AI or lock file exclusion) - changes are already staged - _print_merge_success(no_commit, stats) + _print_merge_success(no_commit, stats, spec_name=spec_name, keep_worktree=True) - # Cleanup the worktree since merge is done - try: - manager.remove_worktree(spec_name, delete_branch=True) - except Exception: - pass # Best effort cleanup + # Don't auto-delete worktree - let user test and manually cleanup + # User can delete with: python auto-claude/run.py --spec --discard + # Or via UI "Delete Worktree" button return True else: # No git conflicts, do standard git merge success_result = manager.merge_worktree( - spec_name, delete_after=True, no_commit=no_commit + spec_name, delete_after=False, no_commit=no_commit ) if success_result: - _print_merge_success(no_commit, stats) + _print_merge_success(no_commit, stats, spec_name=spec_name, keep_worktree=True) return True elif smart_result.get("git_conflicts"): # Had git conflicts that AI couldn't fully resolve @@ -247,7 +245,7 @@ def merge_existing_build( # Fall back to standard git merge success_result = manager.merge_worktree( - spec_name, delete_after=True, no_commit=no_commit + spec_name, delete_after=False, no_commit=no_commit ) if success_result: @@ -258,10 +256,13 @@ def merge_existing_build( print("Review the changes in your IDE, then commit:") print(highlight(" git commit -m 'your commit message'")) print() - print("Or discard if not satisfied:") - print(muted(" git reset --hard HEAD")) + print("When satisfied, delete the worktree:") + print(muted(f" python auto-claude/run.py --spec {spec_name} --discard")) else: print_status("Your feature has been added to your project.", "success") + print() + print("When satisfied, delete the worktree:") + print(muted(f" python auto-claude/run.py --spec {spec_name} --discard")) return True else: print() diff --git a/auto-claude/core/workspace/display.py b/auto-claude/core/workspace/display.py index 07f0fc7d..24b104ed 100644 --- a/auto-claude/core/workspace/display.py +++ b/auto-claude/core/workspace/display.py @@ -70,7 +70,12 @@ def show_changed_files(manager: WorktreeManager, spec_name: str) -> None: print(f" {status} {filepath}") -def print_merge_success(no_commit: bool, stats: dict | None = None) -> None: +def print_merge_success( + no_commit: bool, + stats: dict | None = None, + spec_name: str | None = None, + keep_worktree: bool = False +) -> None: """Print a success message after merge.""" from ui import Icons, box, icon @@ -88,6 +93,12 @@ def print_merge_success(no_commit: bool, stats: dict | None = None) -> None: lines.append("Note: Lock files kept from main.") lines.append("Regenerate: npm install / pip install / cargo update") + # Add worktree cleanup instructions + if keep_worktree and spec_name: + lines.append("") + lines.append("Worktree kept for testing. Delete when satisfied:") + lines.append(f" python auto-claude/run.py --spec {spec_name} --discard") + content = lines else: lines = [ @@ -111,12 +122,23 @@ def print_merge_success(no_commit: bool, stats: dict | None = None) -> None: ) lines.append("") - lines.extend( - [ - "Your new feature is now part of your project.", - "The separate workspace has been cleaned up.", - ] - ) + if keep_worktree: + lines.extend( + [ + "Your new feature is now part of your project.", + "", + "Worktree kept for testing. Delete when satisfied:", + ] + ) + if spec_name: + lines.append(f" python auto-claude/run.py --spec {spec_name} --discard") + else: + lines.extend( + [ + "Your new feature is now part of your project.", + "The separate workspace has been cleaned up.", + ] + ) content = lines print()