diff --git a/apps/web/app/(dashboard)/_components/app-sidebar.tsx b/apps/web/app/(dashboard)/_components/app-sidebar.tsx index b1ebd3b4..f79c5509 100644 --- a/apps/web/app/(dashboard)/_components/app-sidebar.tsx +++ b/apps/web/app/(dashboard)/_components/app-sidebar.tsx @@ -13,6 +13,7 @@ import { LogOut, Plus, Check, + Sparkles, } from "lucide-react"; import { MulticaIcon } from "@/components/multica-icon"; import { @@ -52,6 +53,7 @@ import { useWorkspaceStore } from "@/features/workspace"; const navItems = [ { href: "/inbox", label: "Inbox", icon: Inbox }, { href: "/agents", label: "Agents", icon: Bot }, + { href: "/skills", label: "Skills", icon: Sparkles }, { href: "/issues", label: "Issues", icon: ListTodo }, { href: "/knowledge-base", label: "Knowledge Base", icon: BookOpen }, ]; diff --git a/apps/web/app/(dashboard)/agents/page.tsx b/apps/web/app/(dashboard)/agents/page.tsx index 7e9c6ea4..9233133b 100644 --- a/apps/web/app/(dashboard)/agents/page.tsx +++ b/apps/web/app/(dashboard)/agents/page.tsx @@ -43,7 +43,6 @@ import { } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; -import { Textarea } from "@/components/ui/textarea"; import { Label } from "@/components/ui/label"; import { api } from "@/shared/api"; import { useAuthStore } from "@/features/auth"; @@ -305,28 +304,40 @@ function AgentListItem({ } // --------------------------------------------------------------------------- -// Skills Tab +// Skills Tab (picker — skills are managed on /skills page) // --------------------------------------------------------------------------- function SkillsTab({ agent, - onSave, }: { agent: Agent; - onSave: (skills: string) => Promise; }) { - const [skills, setSkills] = useState(agent.skills); + const workspaceSkills = useWorkspaceStore((s) => s.skills); + const refreshAgents = useWorkspaceStore((s) => s.refreshAgents); const [saving, setSaving] = useState(false); - const isDirty = skills !== agent.skills; + const [showPicker, setShowPicker] = useState(false); - useEffect(() => { - setSkills(agent.skills); - }, [agent.id, agent.skills]); + const agentSkillIds = new Set(agent.skills.map((s) => s.id)); + const availableSkills = workspaceSkills.filter((s) => !agentSkillIds.has(s.id)); - const handleSave = async () => { + const handleAdd = async (skillId: string) => { setSaving(true); try { - await onSave(skills); + const newIds = [...agent.skills.map((s) => s.id), skillId]; + await api.setAgentSkills(agent.id, { skill_ids: newIds }); + await refreshAgents(); + } finally { + setSaving(false); + setShowPicker(false); + } + }; + + const handleRemove = async (skillId: string) => { + setSaving(true); + try { + const newIds = agent.skills.filter((s) => s.id !== skillId).map((s) => s.id); + await api.setAgentSkills(agent.id, { skill_ids: newIds }); + await refreshAgents(); } finally { setSaving(false); } @@ -338,26 +349,114 @@ function SkillsTab({

Skills

- Define what this agent does and how it should accomplish tasks. Supports Markdown. + Reusable skills assigned to this agent. Manage skills on the Skills page.

- {isDirty && ( - - )} + -