From 10de9ebfe6cc3829b3aa83744b5d853bd1c3c1d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Catriel=20M=C3=BCller?= Date: Tue, 26 May 2026 16:11:01 -0300 Subject: [PATCH] refactor: agent builder --- .../src/routes/config/AgentsRoute.tsx | 562 +++++++++++++----- .../src/routes/config/ConfigSidebar.tsx | 2 +- .../src/routes/config/ToolsRoute.tsx | 94 +-- .../src/routes/config/sections.tsx | 4 +- .../src/routes/config/state/agents.ts | 347 +++++++++-- packages/kilo-config-ui/src/shared/utils.ts | 87 ++- packages/kilo-config-ui/src/styles.css | 74 ++- .../opencode/src/kilocode/config/overlay.ts | 33 +- 8 files changed, 926 insertions(+), 277 deletions(-) diff --git a/packages/kilo-config-ui/src/routes/config/AgentsRoute.tsx b/packages/kilo-config-ui/src/routes/config/AgentsRoute.tsx index 0014dab74ed..2027d64f875 100644 --- a/packages/kilo-config-ui/src/routes/config/AgentsRoute.tsx +++ b/packages/kilo-config-ui/src/routes/config/AgentsRoute.tsx @@ -1,103 +1,322 @@ -import { For, Show } from "solid-js" +import { createMemo, For, Show, type JSX } from "solid-js" +import { useLocation, useNavigate, useParams } from "@solidjs/router" import { Button } from "@kilocode/kilo-ui/button" +import { IconButton } from "@kilocode/kilo-ui/icon-button" import { Tag } from "@kilocode/kilo-ui/tag" -import { toMode, toAction } from "../../shared/utils" +import { useConfig } from "../../context/config" +import { toAction, toMode, toolCapabilities, toolName } from "../../shared/utils" import { ConfigPage, SourceBadge } from "./ConfigPage" -import { snippets, useAgentBuilder } from "./state/agents" +import { + agentEditable, + agentTitle, + snippets, + useAgentBuilder, + type AgentEntry, + type AgentItem, +} from "./state/agents" + +type Row = { item: AgentItem; entry?: AgentEntry; rank: number } + +function base(input: string) { + const index = input.indexOf("/settings") + if (index > 0) return `${input.slice(0, index)}/settings` + if (input.startsWith("/config")) return "/config" + return "/settings" +} + +function desc(item: AgentItem) { + return item.description ?? "No description available." +} + +function label(rank: number) { + if (rank === 0) return "Project Agents" + return "Global Agents" +} + +function useAgentLinks() { + const loc = useLocation() + const nav = useNavigate() + const href = (id?: string) => { + const suffix = id ? `/${encodeURIComponent(id)}` : "" + return `${base(loc.pathname)}/agents${suffix}${loc.search}` + } + return { href, nav } +} + +function countTools(input: string[]) { + if (input.length === 1) return "1 tool" + return `${input.length} tools` +} + +function FieldCard(props: { label: string; actions?: JSX.Element; children: JSX.Element }) { + return ( +
+
+ {props.label} + +
{props.actions}
+
+
+
{props.children}
+
+ ) +} export function AgentsRoute() { - const state = useAgentBuilder() + const links = useAgentLinks() + const ctx = useConfig() + const snap = () => ctx.data() + const rows = createMemo(() => { + const data = snap() + if (!data) return [] + const scope = ctx.query()?.scope ?? "global" + const entries = new Map((data.overlay.collections.agent ?? []).map((entry) => [entry.key, entry])) + const local = new Set( + (data.overlay.collections.agent ?? []).filter((entry) => entry.source === "project").map((entry) => entry.key), + ) + return data.agents + .filter((item) => { + const entry = entries.get(item.name) + if (scope === "global") return !entry?.local + return local.has(item.name) || !entry?.local + }) + .map((item) => ({ item, entry: entries.get(item.name), rank: local.has(item.name) ? 0 : 1 })) + .sort((a, b) => a.rank - b.rank || agentTitle(a.item).localeCompare(agentTitle(b.item))) + }) + const groups = createMemo(() => [0, 1].map((rank) => rows().filter((row) => row.rank === rank)).filter((row) => row.length)) + + return ( + + {(data) => ( + + + {rows().length} + + } + > +
+ No agents loaded.

}> + + {(group) => ( +
+ +

{label(group[0]?.rank ?? 1)}

+
+ + {(row) => ( +
+
+
+
+ {agentTitle(row.item)} + {row.item.name} +
+
+ +
+

{desc(row.item)}

+
+ {row.item.mode} + {row.item.native ? "Native" : "Custom"} + + Hidden + + + Deprecated + + + {(entry) => ( + + )} + +
+
+ )} +
+
+ )} +
+
+
+
+ )} +
+ ) +} + +export function AgentBuilderRoute() { + const links = useAgentLinks() + const params = useParams() + const agent = () => (params.agentID === "new" ? undefined : params.agentID) + const state = useAgentBuilder(agent) + const title = createMemo(() => { + if (!agent()) return "Agent Builder" + if (state.locked()) return "Inspect Agent" + return "Edit Agent" + }) return ( - {(data) => ( - {data().agents.length}}> + {(_data) => ( + + + + + + links.nav(links.href())} + /> + + } + >
-
- -