Add force stop feature for running agents on kanban cards

This commit is contained in:
Cody Seibert
2025-12-09 01:22:53 -05:00
parent f4df08f9b4
commit ca646f2acb
4 changed files with 73 additions and 17 deletions

View File

@@ -44,7 +44,7 @@
"category": "Kanban",
"description": "Add a way to force stop an agent on a card which is currently running",
"steps": [],
"status": "in_progress"
"status": "verified"
},
{
"id": "feature-1765260864296-98yunv0vj",

View File

@@ -12,7 +12,7 @@ import {
} from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Feature } from "@/store/app-store";
import { GripVertical, Edit, CheckCircle2, Circle, Loader2, Trash2, Eye, PlayCircle, RotateCcw } from "lucide-react";
import { GripVertical, Edit, CheckCircle2, Circle, Loader2, Trash2, Eye, PlayCircle, RotateCcw, StopCircle } from "lucide-react";
interface KanbanCardProps {
feature: Feature;
@@ -21,11 +21,12 @@ interface KanbanCardProps {
onViewOutput?: () => void;
onVerify?: () => void;
onResume?: () => void;
onForceStop?: () => void;
hasContext?: boolean;
isCurrentAutoTask?: boolean;
}
export function KanbanCard({ feature, onEdit, onDelete, onViewOutput, onVerify, onResume, hasContext, isCurrentAutoTask }: KanbanCardProps) {
export function KanbanCard({ feature, onEdit, onDelete, onViewOutput, onVerify, onResume, onForceStop, hasContext, isCurrentAutoTask }: KanbanCardProps) {
// Disable dragging if the feature is in progress or verified
const isDraggable = feature.status === "backlog";
const {
@@ -112,20 +113,39 @@ export function KanbanCard({ feature, onEdit, onDelete, onViewOutput, onVerify,
{/* Actions */}
<div className="flex gap-2">
{isCurrentAutoTask && onViewOutput && (
<Button
variant="default"
size="sm"
className="flex-1 h-7 text-xs bg-purple-600 hover:bg-purple-700"
onClick={(e) => {
e.stopPropagation();
onViewOutput();
}}
data-testid={`view-output-${feature.id}`}
>
<Eye className="w-3 h-3 mr-1" />
View Output
</Button>
{isCurrentAutoTask && (
<>
{onViewOutput && (
<Button
variant="default"
size="sm"
className="flex-1 h-7 text-xs bg-purple-600 hover:bg-purple-700"
onClick={(e) => {
e.stopPropagation();
onViewOutput();
}}
data-testid={`view-output-${feature.id}`}
>
<Eye className="w-3 h-3 mr-1" />
View Output
</Button>
)}
{onForceStop && (
<Button
variant="destructive"
size="sm"
className="h-7 text-xs"
onClick={(e) => {
e.stopPropagation();
onForceStop();
}}
data-testid={`force-stop-${feature.id}`}
>
<StopCircle className="w-3 h-3 mr-1" />
Stop
</Button>
)}
</>
)}
{!isCurrentAutoTask && feature.status === "in_progress" && (
<>

View File

@@ -180,6 +180,35 @@ export function useAutoMode() {
}
}, [setAutoModeRunning, clearRunningTasks]);
// Stop a specific feature
const stopFeature = useCallback(async (featureId: string) => {
try {
const api = getElectronAPI();
if (!api?.autoMode?.stopFeature) {
throw new Error("Stop feature API not available");
}
const result = await api.autoMode.stopFeature(featureId);
if (result.success) {
removeRunningTask(featureId);
console.log("[AutoMode] Feature stopped successfully:", featureId);
addAutoModeActivity({
featureId,
type: "complete",
message: "Feature stopped by user",
passes: false,
});
} else {
console.error("[AutoMode] Failed to stop feature:", result.error);
throw new Error(result.error || "Failed to stop feature");
}
} catch (error) {
console.error("[AutoMode] Error stopping feature:", error);
throw error;
}
}, [removeRunningTask, addAutoModeActivity]);
return {
isRunning: isAutoModeRunning,
runningTasks: runningAutoTasks,
@@ -187,5 +216,6 @@ export function useAutoMode() {
canStartNewTask,
start,
stop,
stopFeature,
};
}

View File

@@ -208,10 +208,16 @@ export interface AutoModeAPI {
error?: string;
}>;
stopFeature: (featureId: string) => Promise<{
success: boolean;
error?: string;
}>;
status: () => Promise<{
success: boolean;
isRunning?: boolean;
currentFeatureId?: string | null;
runningFeatures?: string[];
error?: string;
}>;