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:
- Review staged changes with
git status and git diff --staged
@@ -61,6 +136,39 @@ export function StagedInProjectMessage({ task }: StagedInProjectMessageProps) {
- 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()