From 87044a747b47bd83249d61a45842c7f7b2eae56d Mon Sep 17 00:00:00 2001 From: soryu Date: Sun, 11 Jan 2026 05:52:14 +0000 Subject: Contract system --- .../components/contracts/QuickActionButtons.tsx | 217 +++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 makima/frontend/src/components/contracts/QuickActionButtons.tsx (limited to 'makima/frontend/src/components/contracts/QuickActionButtons.tsx') diff --git a/makima/frontend/src/components/contracts/QuickActionButtons.tsx b/makima/frontend/src/components/contracts/QuickActionButtons.tsx new file mode 100644 index 0000000..4dbb90c --- /dev/null +++ b/makima/frontend/src/components/contracts/QuickActionButtons.tsx @@ -0,0 +1,217 @@ +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; +} -- cgit v1.2.3