import { useCallback } from "react"; export type QuickActionType = | "create_file" | "create_task" | "run_task" | "advance_phase" | "derive_tasks" | "update_file"; export interface QuickAction { type: QuickActionType; label: string; description?: string; data?: Record; } interface QuickActionButtonsProps { actions: QuickAction[]; onAction: (action: QuickAction) => void; loading?: boolean; } const ACTION_ICONS: Record = { create_file: "[+]", create_task: "[T]", run_task: "[>]", advance_phase: "[→]", derive_tasks: "[≡]", update_file: "[*]", }; const ACTION_COLORS: Record = { create_file: "border-blue-400/30 hover:border-blue-400/60 text-blue-400", create_task: "border-green-400/30 hover:border-green-400/60 text-green-400", run_task: "border-yellow-400/30 hover:border-yellow-400/60 text-yellow-400", advance_phase: "border-purple-400/30 hover:border-purple-400/60 text-purple-400", derive_tasks: "border-cyan-400/30 hover:border-cyan-400/60 text-cyan-400", update_file: "border-orange-400/30 hover:border-orange-400/60 text-orange-400", }; export function QuickActionButtons({ actions, onAction, loading = false, }: QuickActionButtonsProps) { const handleClick = useCallback( (action: QuickAction) => { if (!loading) { onAction(action); } }, [onAction, loading] ); if (actions.length === 0) return null; return (
{actions.map((action, index) => ( ))}
); } /** * Parse tool call results to extract suggested quick actions. * This is used by ContractCliInput to detect actionable results. */ export function parseActionsFromToolCalls( toolCalls: { name: string; success: boolean; message: string }[] ): QuickAction[] { const actions: QuickAction[] = []; for (const tc of toolCalls) { if (!tc.success) continue; switch (tc.name) { case "derive_tasks_from_file": // When tasks are parsed, offer to create them if (tc.message.includes("task") || tc.message.includes("Found")) { actions.push({ type: "derive_tasks", label: "Review & Create Tasks", description: "Review parsed tasks and create them with chaining", }); } break; case "process_task_completion": // Check for suggested actions in the result if (tc.message.includes("next task")) { actions.push({ type: "run_task", label: "Run Next Task", description: "Continue with the next chained task", }); } if (tc.message.includes("advance") || tc.message.includes("phase")) { actions.push({ type: "advance_phase", label: "Advance Phase", description: "Move to the next contract phase", }); } break; case "get_phase_checklist": // When checklist shows missing items, offer to create them if (tc.message.includes("missing") || tc.message.includes("not created")) { actions.push({ type: "create_file", label: "Create Missing Files", description: "Create files from recommended templates", }); } break; case "advance_phase": // After phase transition, suggest creating files actions.push({ type: "create_file", label: "Create Phase Files", description: "Create recommended files for this phase", }); break; } } return actions; } /** * Parse LLM response text to detect suggested actions. * Used as a fallback when structured action data isn't available. */ export function parseActionsFromText(text: string): QuickAction[] { const actions: QuickAction[] = []; const lower = text.toLowerCase(); // Detect file creation suggestions if ( lower.includes("create a file") || lower.includes("create the file") || lower.includes("should i create") ) { actions.push({ type: "create_file", label: "Create File", description: "Create the suggested file", }); } // Detect task creation suggestions if ( lower.includes("create tasks") || lower.includes("create these tasks") || lower.includes("create chained tasks") ) { actions.push({ type: "create_task", label: "Create Tasks", description: "Create the suggested tasks", }); } // Detect phase advancement suggestions if ( lower.includes("advance to") || lower.includes("ready to move to") || lower.includes("transition to") ) { const phases = ["specify", "plan", "execute", "review"]; for (const phase of phases) { if (lower.includes(phase)) { actions.push({ type: "advance_phase", label: `Advance to ${phase.charAt(0).toUpperCase() + phase.slice(1)}`, description: `Move to the ${phase} phase`, data: { phase }, }); break; } } } // Detect run task suggestions if ( lower.includes("run the task") || lower.includes("start the task") || lower.includes("run task") ) { actions.push({ type: "run_task", label: "Run Task", description: "Start the suggested task", }); } return actions; }