import React, { useState, useEffect } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { Plus, Edit, Trash2, Play, Bot, Brain, Code, Sparkles, Zap, Cpu, Rocket, Shield, Terminal, ArrowLeft, History, Download, Upload, Globe, FileJson, ChevronDown } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardFooter } from "@/components/ui/card"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { api, type Agent, type AgentRunWithMetrics } from "@/lib/api"; import { save, open } from "@tauri-apps/plugin-dialog"; import { invoke } from "@tauri-apps/api/core"; import { cn } from "@/lib/utils"; import { Toast, ToastContainer } from "@/components/ui/toast"; import { CreateAgent } from "./CreateAgent"; import { AgentExecution } from "./AgentExecution"; import { AgentRunsList } from "./AgentRunsList"; import { AgentRunView } from "./AgentRunView"; import { RunningSessionsView } from "./RunningSessionsView"; import { GitHubAgentBrowser } from "./GitHubAgentBrowser"; interface CCAgentsProps { /** * Callback to go back to the main view */ onBack: () => void; /** * Optional className for styling */ className?: string; } // Available icons for agents export const AGENT_ICONS = { bot: Bot, brain: Brain, code: Code, sparkles: Sparkles, zap: Zap, cpu: Cpu, rocket: Rocket, shield: Shield, terminal: Terminal, }; export type AgentIconName = keyof typeof AGENT_ICONS; /** * CCAgents component for managing Claude Code agents * * @example * setView('home')} /> */ export const CCAgents: React.FC = ({ onBack, className }) => { const [agents, setAgents] = useState([]); const [runs, setRuns] = useState([]); const [loading, setLoading] = useState(true); const [runsLoading, setRunsLoading] = useState(false); const [error, setError] = useState(null); const [toast, setToast] = useState<{ message: string; type: "success" | "error" } | null>(null); const [currentPage, setCurrentPage] = useState(1); const [view, setView] = useState<"list" | "create" | "edit" | "execute" | "viewRun">("list"); const [activeTab, setActiveTab] = useState<"agents" | "running">("agents"); const [selectedAgent, setSelectedAgent] = useState(null); const [selectedRunId, setSelectedRunId] = useState(null); const [showGitHubBrowser, setShowGitHubBrowser] = useState(false); const AGENTS_PER_PAGE = 9; // 3x3 grid useEffect(() => { loadAgents(); loadRuns(); }, []); const loadAgents = async () => { try { setLoading(true); setError(null); const agentsList = await api.listAgents(); setAgents(agentsList); } catch (err) { console.error("Failed to load agents:", err); setError("Failed to load agents"); setToast({ message: "Failed to load agents", type: "error" }); } finally { setLoading(false); } }; const loadRuns = async () => { try { setRunsLoading(true); const runsList = await api.listAgentRuns(); setRuns(runsList); } catch (err) { console.error("Failed to load runs:", err); } finally { setRunsLoading(false); } }; const handleDeleteAgent = async (id: number) => { if (!confirm("Are you sure you want to delete this agent?")) return; try { await api.deleteAgent(id); setToast({ message: "Agent deleted successfully", type: "success" }); await loadAgents(); await loadRuns(); // Reload runs as they might be affected } catch (err) { console.error("Failed to delete agent:", err); setToast({ message: "Failed to delete agent", type: "error" }); } }; const handleEditAgent = (agent: Agent) => { setSelectedAgent(agent); setView("edit"); }; const handleExecuteAgent = (agent: Agent) => { setSelectedAgent(agent); setView("execute"); }; const handleAgentCreated = async () => { setView("list"); await loadAgents(); setToast({ message: "Agent created successfully", type: "success" }); }; const handleAgentUpdated = async () => { setView("list"); await loadAgents(); setToast({ message: "Agent updated successfully", type: "success" }); }; const handleRunClick = (run: AgentRunWithMetrics) => { if (run.id) { setSelectedRunId(run.id); setView("viewRun"); } }; const handleExecutionComplete = async () => { // Reload runs when returning from execution await loadRuns(); }; const handleExportAgent = async (agent: Agent) => { try { // Show native save dialog const filePath = await save({ defaultPath: `${agent.name.toLowerCase().replace(/\s+/g, '-')}.claudia.json`, filters: [{ name: 'Claudia Agent', extensions: ['claudia.json'] }] }); if (!filePath) { // User cancelled the dialog return; } // Export the agent to the selected file await invoke('export_agent_to_file', { id: agent.id!, filePath }); setToast({ message: `Agent "${agent.name}" exported successfully`, type: "success" }); } catch (err) { console.error("Failed to export agent:", err); setToast({ message: "Failed to export agent", type: "error" }); } }; const handleImportAgent = async () => { try { // Show native open dialog const filePath = await open({ multiple: false, filters: [{ name: 'Claudia Agent', extensions: ['claudia.json', 'json'] }] }); if (!filePath) { // User cancelled the dialog return; } // Import the agent from the selected file await api.importAgentFromFile(filePath as string); setToast({ message: "Agent imported successfully", type: "success" }); await loadAgents(); } catch (err) { console.error("Failed to import agent:", err); const errorMessage = err instanceof Error ? err.message : "Failed to import agent"; setToast({ message: errorMessage, type: "error" }); } }; // Pagination calculations const totalPages = Math.ceil(agents.length / AGENTS_PER_PAGE); const startIndex = (currentPage - 1) * AGENTS_PER_PAGE; const paginatedAgents = agents.slice(startIndex, startIndex + AGENTS_PER_PAGE); const renderIcon = (iconName: string) => { const Icon = AGENT_ICONS[iconName as AgentIconName] || Bot; return ; }; if (view === "create") { return ( setView("list")} onAgentCreated={handleAgentCreated} /> ); } if (view === "edit" && selectedAgent) { return ( setView("list")} onAgentCreated={handleAgentUpdated} /> ); } if (view === "execute" && selectedAgent) { return ( { setView("list"); handleExecutionComplete(); }} /> ); } if (view === "viewRun" && selectedRunId) { return ( setView("list")} /> ); } return (
{/* Header */}

CC Agents

Manage your Claude Code agents

From File setShowGitHubBrowser(true)}> From GitHub
{/* Error display */} {error && ( {error} )} {/* Tab Navigation */}
{/* Tab Content */}
{activeTab === "agents" && ( {/* Agents Grid */}
{loading ? (
) : agents.length === 0 ? (

No agents yet

Create your first CC Agent to get started

) : ( <>
{paginatedAgents.map((agent, index) => (
{renderIcon(agent.icon)}

{agent.name}

Created: {new Date(agent.created_at).toLocaleDateString()}

))}
{/* Pagination */} {totalPages > 1 && (
Page {currentPage} of {totalPages}
)} )}
{/* Execution History */} {!loading && agents.length > 0 && (

Recent Executions

{runsLoading ? (
) : ( )}
)}
)} {activeTab === "running" && ( )}
{/* Toast Notification */} {toast && ( setToast(null)} /> )} {/* GitHub Agent Browser */} setShowGitHubBrowser(false)} onImportSuccess={async () => { setShowGitHubBrowser(false); await loadAgents(); setToast({ message: "Agent imported successfully from GitHub", type: "success" }); }} />
); };