From 2e4070d438978d9deb0398c188e19dffefd11077 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Mon, 16 Mar 2026 10:44:40 +0100 Subject: [PATCH 01/10] feat: add ability to remove discovered skills from Agent Behaviour settings Add a remove button (X icon) to each discovered skill in the skills subtab with a confirmation dialog. On confirm, the skill's directory is deleted from disk and the skills list is refreshed. --- packages/kilo-vscode/src/KiloProvider.ts | 20 ++++++ .../components/settings/AgentBehaviourTab.tsx | 62 +++++++++++++++---- .../kilo-vscode/webview-ui/src/i18n/en.ts | 3 + .../webview-ui/src/types/messages.ts | 6 ++ 4 files changed, 79 insertions(+), 12 deletions(-) diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 5f5d6f3aeaf..fe9143a3f09 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -429,6 +429,11 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper case "requestSkills": this.fetchAndSendSkills().catch((e) => console.error("[Kilo New] fetchAndSendSkills failed:", e)) break + case "removeSkill": + this.handleRemoveSkill(message.location).catch((e) => + console.error("[Kilo New] handleRemoveSkill failed:", e), + ) + break case "questionReply": await this.handleQuestionReply(message.requestID, message.answers) break @@ -1127,6 +1132,21 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper } } + /** + * Remove a skill by deleting its directory from disk, then refresh the skills list. + * The location is the path to the SKILL.md file; the skill directory is its parent. + */ + private async handleRemoveSkill(location: string): Promise { + const dir = path.dirname(location) + try { + await vscode.workspace.fs.delete(vscode.Uri.file(dir), { recursive: true }) + } catch (error) { + console.error("[Kilo New] KiloProvider: Failed to delete skill directory:", dir, error) + } + this.cachedSkillsMessage = null + await this.fetchAndSendSkills() + } + /** * Fetch backend config and send to webview. */ diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/AgentBehaviourTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/AgentBehaviourTab.tsx index 2ce51e38c23..3b0d278b92e 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/AgentBehaviourTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/AgentBehaviourTab.tsx @@ -4,6 +4,8 @@ import { TextField } from "@kilocode/kilo-ui/text-field" import { Card } from "@kilocode/kilo-ui/card" import { Button } from "@kilocode/kilo-ui/button" import { IconButton } from "@kilocode/kilo-ui/icon-button" +import { Dialog } from "@kilocode/kilo-ui/dialog" +import { useDialog } from "@kilocode/kilo-ui/context/dialog" import { useConfig } from "../../context/config" import { useSession } from "../../context/session" @@ -53,6 +55,7 @@ const AgentBehaviourTab: Component = () => { const { config, updateConfig } = useConfig() const session = useSession() const vscode = useVSCode() + const dialog = useDialog() const [activeSubtab, setActiveSubtab] = createSignal("agents") const [selectedAgent, setSelectedAgent] = createSignal("") const [newSkillPath, setNewSkillPath] = createSignal("") @@ -179,6 +182,35 @@ const AgentBehaviourTab: Component = () => { updateConfig({ skills: { ...config().skills, urls: current } }) } + const removeSkill = (skill: SkillInfo) => { + vscode.postMessage({ type: "removeSkill", location: skill.location }) + } + + const confirmRemoveSkill = (skill: SkillInfo) => { + dialog.show(() => ( + +
+ {language.t("settings.agentBehaviour.removeSkill.confirm", { name: skill.name })} +
+ + +
+
+
+ )) + } + const renderAgentsSubtab = () => (
{/* Default agent */} @@ -410,24 +442,30 @@ const AgentBehaviourTab: Component = () => { {(skill, index) => (
-
- {skill.name} -
-
-
{skill.description}
-
{skill.location}
+
+
+ {skill.name} +
+
+
{skill.description}
+
{skill.location}
+
+ confirmRemoveSkill(skill)} />
)} diff --git a/packages/kilo-vscode/webview-ui/src/i18n/en.ts b/packages/kilo-vscode/webview-ui/src/i18n/en.ts index cffafc12273..1582971d356 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/en.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/en.ts @@ -971,6 +971,9 @@ export const dict = { "No skills discovered. Add skill folder paths or URLs below to make skills available.", "settings.agentBehaviour.skillPaths": "Skill Folder Paths", "settings.agentBehaviour.skillUrls": "Skill URLs", + "settings.agentBehaviour.removeSkill.title": "Remove skill", + "settings.agentBehaviour.removeSkill.confirm": 'Remove skill "{{name}}"? This will delete the skill files from disk.', + "settings.agentBehaviour.removeSkill.button": "Remove", "settings.agentBehaviour.instructionFiles": "Additional Instruction Files", "settings.agentBehaviour.instructionFiles.description": "Paths to additional instruction files that are included in the system prompt", diff --git a/packages/kilo-vscode/webview-ui/src/types/messages.ts b/packages/kilo-vscode/webview-ui/src/types/messages.ts index e9fa87dedad..e0da4d52ec2 100644 --- a/packages/kilo-vscode/webview-ui/src/types/messages.ts +++ b/packages/kilo-vscode/webview-ui/src/types/messages.ts @@ -1248,6 +1248,11 @@ export interface RequestSkillsMessage { type: "requestSkills" } +export interface RemoveSkillMessage { + type: "removeSkill" + location: string +} + export interface SetLanguageRequest { type: "setLanguage" locale: string @@ -1628,6 +1633,7 @@ export type WebviewMessage = | CompactRequest | RequestAgentsMessage | RequestSkillsMessage + | RemoveSkillMessage | SetLanguageRequest | QuestionReplyRequest | QuestionRejectRequest From 3da18dee40605d954d8ad63a50090f6002cd7b0c Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Mon, 16 Mar 2026 10:50:28 +0100 Subject: [PATCH 02/10] refactor: move skills state to session context with refreshSkills/removeSkill Move discoveredSkills signal from AgentBehaviourTab into the session context so any component can access session.skills(), session.refreshSkills(), and session.removeSkill(location). removeSkill optimistically removes the skill from the UI signal before sending the message to the extension, so the screen updates immediately. --- .../components/settings/AgentBehaviourTab.tsx | 31 +++++-------------- .../webview-ui/src/context/session.tsx | 29 +++++++++++++++++ 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/AgentBehaviourTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/AgentBehaviourTab.tsx index 3b0d278b92e..1d4236d87a1 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/AgentBehaviourTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/AgentBehaviourTab.tsx @@ -1,4 +1,4 @@ -import { Component, createSignal, createMemo, createEffect, For, Show, onCleanup } from "solid-js" +import { Component, createSignal, createMemo, createEffect, For, Show } from "solid-js" import { Select } from "@kilocode/kilo-ui/select" import { TextField } from "@kilocode/kilo-ui/text-field" import { Card } from "@kilocode/kilo-ui/card" @@ -10,8 +10,7 @@ import { useDialog } from "@kilocode/kilo-ui/context/dialog" import { useConfig } from "../../context/config" import { useSession } from "../../context/session" import { useLanguage } from "../../context/language" -import { useVSCode } from "../../context/vscode" -import type { AgentConfig, SkillInfo, ExtensionMessage } from "../../types/messages" +import type { AgentConfig, SkillInfo } from "../../types/messages" type SubtabId = "agents" | "mcpServers" | "rules" | "workflows" | "skills" @@ -54,31 +53,20 @@ const AgentBehaviourTab: Component = () => { const language = useLanguage() const { config, updateConfig } = useConfig() const session = useSession() - const vscode = useVSCode() const dialog = useDialog() const [activeSubtab, setActiveSubtab] = createSignal("agents") const [selectedAgent, setSelectedAgent] = createSignal("") const [newSkillPath, setNewSkillPath] = createSignal("") const [newSkillUrl, setNewSkillUrl] = createSignal("") const [newInstruction, setNewInstruction] = createSignal("") - const [discoveredSkills, setDiscoveredSkills] = createSignal([]) - - // Subscribe to skillsLoaded messages from the extension - const unsub = vscode.onMessage((message: ExtensionMessage) => { - if (message.type === "skillsLoaded") { - setDiscoveredSkills(message.skills) - } - }) // Fetch skills whenever the skills subtab becomes active createEffect(() => { if (activeSubtab() === "skills") { - vscode.postMessage({ type: "requestSkills" }) + session.refreshSkills() } }) - onCleanup(() => unsub()) - const agentNames = createMemo(() => { const names = session.agents().map((a) => a.name) // Also include any agents from config that might not be in the agent list @@ -182,10 +170,6 @@ const AgentBehaviourTab: Component = () => { updateConfig({ skills: { ...config().skills, urls: current } }) } - const removeSkill = (skill: SkillInfo) => { - vscode.postMessage({ type: "removeSkill", location: skill.location }) - } - const confirmRemoveSkill = (skill: SkillInfo) => { dialog.show(() => ( @@ -199,7 +183,7 @@ const AgentBehaviourTab: Component = () => { variant="primary" size="large" onClick={() => { - removeSkill(skill) + session.removeSkill(skill.location) dialog.close() }} > @@ -430,7 +414,7 @@ const AgentBehaviourTab: Component = () => { {language.t("settings.agentBehaviour.discoveredSkills")} 0} + when={session.skills().length > 0} fallback={
{language.t("settings.agentBehaviour.noSkillsFound")}
@@ -438,7 +422,7 @@ const AgentBehaviourTab: Component = () => { } > - + {(skill, index) => (
{ "align-items": "center", "justify-content": "space-between", padding: "8px 0", - "border-bottom": - index() < discoveredSkills().length - 1 ? "1px solid var(--border-weak-base)" : "none", + "border-bottom": index() < session.skills().length - 1 ? "1px solid var(--border-weak-base)" : "none", }} >
diff --git a/packages/kilo-vscode/webview-ui/src/context/session.tsx b/packages/kilo-vscode/webview-ui/src/context/session.tsx index 24219c433a8..9bec4955fd9 100644 --- a/packages/kilo-vscode/webview-ui/src/context/session.tsx +++ b/packages/kilo-vscode/webview-ui/src/context/session.tsx @@ -36,6 +36,7 @@ import type { ModelSelection, ContextUsage, AgentInfo, + SkillInfo, ExtensionMessage, FileAttachment, SendMessageFailedMessage, @@ -114,6 +115,11 @@ interface SessionContextValue { totalCost: Accessor contextUsage: Accessor + // Skills loaded from the CLI backend + skills: Accessor + refreshSkills: () => void + removeSkill: (location: string) => void + // Agent/mode selection (per-session) agents: Accessor selectedAgent: Accessor @@ -204,6 +210,9 @@ export const SessionProvider: ParentComponent = (props) => { const [agents, setAgents] = createSignal([]) const [defaultAgent, setDefaultAgent] = createSignal("code") + // Skills loaded from the CLI backend + const [skills, setSkills] = createSignal([]) + // Pending agent selection for before a session exists const [pendingAgentSelection, setPendingAgentSelection] = createSignal(null) @@ -341,8 +350,25 @@ export const SessionProvider: ParentComponent = (props) => { vscode.postMessage({ type: "requestAgents" }) }, agentRetryMs) + // Skills loaded from the CLI backend + const unsubSkills = vscode.onMessage((message: ExtensionMessage) => { + if (message.type === "skillsLoaded") { + setSkills(message.skills) + } + }) + + const refreshSkills = () => { + vscode.postMessage({ type: "requestSkills" }) + } + + const removeSkill = (location: string) => { + setSkills((prev) => prev.filter((s) => s.location !== location)) + vscode.postMessage({ type: "removeSkill", location }) + } + onCleanup(() => { unsubAgents() + unsubSkills() clearInterval(agentRetryTimer) }) @@ -1285,6 +1311,9 @@ export const SessionProvider: ParentComponent = (props) => { totalCost, contextUsage, agents, + skills, + refreshSkills, + removeSkill, selectedAgent: selectedAgentName, selectAgent, getSessionAgent: (sessionID: string) => store.agentSelections[sessionID] ?? defaultAgent(), From ec544e828867fa6f50d22e9e1d35c2441496ef82 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Mon, 16 Mar 2026 11:17:07 +0100 Subject: [PATCH 03/10] feat: add DELETE /skill endpoint and use CLI backend for skill removal Add Skill.remove() that deletes the skill directory from disk and mutates the cached Instance.state in-place so subsequent GET /skill calls return the updated list. Add DELETE /skill?location=... server endpoint, regenerate the SDK, and update KiloProvider to call the SDK method instead of deleting files directly from the extension. --- packages/kilo-vscode/src/KiloProvider.ts | 10 ++--- packages/opencode/src/server/server.ts | 32 +++++++++++++ packages/opencode/src/skill/skill.ts | 14 ++++++ packages/sdk/js/src/v2/gen/sdk.gen.ts | 34 ++++++++++++++ packages/sdk/js/src/v2/gen/types.gen.ts | 29 ++++++++++++ packages/sdk/openapi.json | 57 ++++++++++++++++++++++++ 6 files changed, 171 insertions(+), 5 deletions(-) diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index fe9143a3f09..79d29ac0ea2 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -1133,15 +1133,15 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper } /** - * Remove a skill by deleting its directory from disk, then refresh the skills list. - * The location is the path to the SKILL.md file; the skill directory is its parent. + * Remove a skill via the CLI backend (deletes from disk + clears cache), then refresh. */ private async handleRemoveSkill(location: string): Promise { - const dir = path.dirname(location) + if (!this.client) return try { - await vscode.workspace.fs.delete(vscode.Uri.file(dir), { recursive: true }) + const dir = this.getWorkspaceDirectory() + await this.client.app.removeSkill({ location, directory: dir }, { throwOnError: true }) } catch (error) { - console.error("[Kilo New] KiloProvider: Failed to delete skill directory:", dir, error) + console.error("[Kilo New] KiloProvider: Failed to remove skill:", error) } this.cachedSkillsMessage = null await this.fetchAndSendSkills() diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index d37112823a1..1169642058f 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -500,6 +500,38 @@ export namespace Server { return c.json(skills) }, ) + // kilocode_change start + .delete( + "/skill", + describeRoute({ + summary: "Remove a skill", + description: "Remove a skill by deleting its directory from disk and clearing it from cache.", + operationId: "app.removeSkill", + responses: { + 200: { + description: "Skill removed", + content: { + "application/json": { + schema: resolver(z.boolean()), + }, + }, + }, + ...errors(400), + }, + }), + validator( + "query", + z.object({ + location: z.string(), + }), + ), + async (c) => { + const { location } = c.req.valid("query") + await Skill.remove(location) + return c.json(true) + }, + ) + // kilocode_change end .get( "/lsp", describeRoute({ diff --git a/packages/opencode/src/skill/skill.ts b/packages/opencode/src/skill/skill.ts index d0b4eddeb87..aebac03faf4 100644 --- a/packages/opencode/src/skill/skill.ts +++ b/packages/opencode/src/skill/skill.ts @@ -1,6 +1,7 @@ import z from "zod" import path from "path" import os from "os" +import { rm } from "fs/promises" import { Config } from "../config/config" import { Instance } from "../project/instance" import { NamedError } from "@opencode-ai/util/error" @@ -213,4 +214,17 @@ export namespace Skill { export async function dirs() { return state().then((x) => x.dirs) } + + // kilocode_change start + export async function remove(location: string) { + const dir = path.dirname(location) + await rm(dir, { recursive: true, force: true }) + const s = await state() + const name = Object.keys(s.skills).find((k) => s.skills[k].location === location) + if (name) { + delete s.skills[name] + s.dirs = s.dirs.filter((d) => d !== dir) + } + } + // kilocode_change end } diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index 9104c813c2e..b7ff65c3cf7 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -7,6 +7,8 @@ import type { AppAgentsResponses, AppLogErrors, AppLogResponses, + AppRemoveSkillErrors, + AppRemoveSkillResponses, AppSkillsResponses, Auth as Auth3, AuthRemoveErrors, @@ -4330,6 +4332,38 @@ export class App extends HeyApiClient { }) } + /** + * Remove a skill + * + * Remove a skill by deleting its directory from disk and clearing it from cache. + */ + public removeSkill( + parameters: { + directory?: string + workspace?: string + location: string + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + { in: "query", key: "location" }, + ], + }, + ], + ) + return (options?.client ?? this.client).delete({ + url: "/skill", + ...options, + ...params, + }) + } + /** * List skills * diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 5c724d3621f..83dbdbf153b 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -5483,6 +5483,35 @@ export type AppAgentsResponses = { export type AppAgentsResponse = AppAgentsResponses[keyof AppAgentsResponses] +export type AppRemoveSkillData = { + body?: never + path?: never + query: { + directory?: string + workspace?: string + location: string + } + url: "/skill" +} + +export type AppRemoveSkillErrors = { + /** + * Bad request + */ + 400: BadRequestError +} + +export type AppRemoveSkillError = AppRemoveSkillErrors[keyof AppRemoveSkillErrors] + +export type AppRemoveSkillResponses = { + /** + * Skill removed + */ + 200: boolean +} + +export type AppRemoveSkillResponse = AppRemoveSkillResponses[keyof AppRemoveSkillResponses] + export type AppSkillsData = { body?: never path?: never diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 45dd0752210..66adf212510 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -7935,6 +7935,63 @@ "source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.app.skills({\n ...\n})" } ] + }, + "delete": { + "operationId": "app.removeSkill", + "parameters": [ + { + "in": "query", + "name": "directory", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "workspace", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "location", + "schema": { + "type": "string" + }, + "required": true + } + ], + "summary": "Remove a skill", + "description": "Remove a skill by deleting its directory from disk and clearing it from cache.", + "responses": { + "200": { + "description": "Skill removed", + "content": { + "application/json": { + "schema": { + "type": "boolean" + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequestError" + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "js", + "source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.app.removeSkill({\n ...\n})" + } + ] } }, "/lsp": { From 5618b810fb6168ea168eba2bef07a02fc77c9c39 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Mon, 16 Mar 2026 11:32:32 +0100 Subject: [PATCH 04/10] i18n: add skill removal translations for all 15 languages --- packages/kilo-vscode/webview-ui/src/i18n/ar.ts | 4 ++++ packages/kilo-vscode/webview-ui/src/i18n/br.ts | 4 ++++ packages/kilo-vscode/webview-ui/src/i18n/bs.ts | 4 ++++ packages/kilo-vscode/webview-ui/src/i18n/da.ts | 4 ++++ packages/kilo-vscode/webview-ui/src/i18n/de.ts | 4 ++++ packages/kilo-vscode/webview-ui/src/i18n/es.ts | 4 ++++ packages/kilo-vscode/webview-ui/src/i18n/fr.ts | 4 ++++ packages/kilo-vscode/webview-ui/src/i18n/ja.ts | 4 ++++ packages/kilo-vscode/webview-ui/src/i18n/ko.ts | 4 ++++ packages/kilo-vscode/webview-ui/src/i18n/no.ts | 4 ++++ packages/kilo-vscode/webview-ui/src/i18n/pl.ts | 4 ++++ packages/kilo-vscode/webview-ui/src/i18n/ru.ts | 4 ++++ packages/kilo-vscode/webview-ui/src/i18n/th.ts | 4 ++++ packages/kilo-vscode/webview-ui/src/i18n/zh.ts | 3 +++ packages/kilo-vscode/webview-ui/src/i18n/zht.ts | 3 +++ 15 files changed, 58 insertions(+) diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts index 2f56d1df2af..3cb7863ceff 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts @@ -951,6 +951,10 @@ export const dict = { "لم يتم العثور على مهارات. أضف مسارات مجلدات أو عناوين URL أدناه لإتاحة المهارات.", "settings.agentBehaviour.skillPaths": "مسارات مجلدات المهارات", "settings.agentBehaviour.skillUrls": "عناوين URL للمهارات", + "settings.agentBehaviour.removeSkill.title": "إزالة المهارة", + "settings.agentBehaviour.removeSkill.confirm": + 'هل تريد إزالة المهارة "{{name}}"؟ سيؤدي هذا إلى حذف ملفات المهارة من القرص.', + "settings.agentBehaviour.removeSkill.button": "إزالة", "settings.agentBehaviour.instructionFiles": "ملفات تعليمات إضافية", "settings.agentBehaviour.instructionFiles.description": "مسارات ملفات التعليمات الإضافية في موجه النظام", "settings.agentBehaviour.mcpEmpty": "لم يتم تهيئة خوادم MCP. قم بتحرير ملف تهيئة opencode لإضافة خوادم MCP.", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/br.ts b/packages/kilo-vscode/webview-ui/src/i18n/br.ts index 7af43d62fe7..36d2f565aa7 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/br.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/br.ts @@ -963,6 +963,10 @@ export const dict = { "Nenhuma habilidade encontrada. Adicione caminhos de pastas ou URLs abaixo para disponibilizar habilidades.", "settings.agentBehaviour.skillPaths": "Caminhos de pastas de habilidades", "settings.agentBehaviour.skillUrls": "URLs de habilidades", + "settings.agentBehaviour.removeSkill.title": "Remover habilidade", + "settings.agentBehaviour.removeSkill.confirm": + 'Remover a habilidade "{{name}}"? Isso excluirá os arquivos da habilidade do disco.', + "settings.agentBehaviour.removeSkill.button": "Remover", "settings.agentBehaviour.instructionFiles": "Arquivos de instruções adicionais", "settings.agentBehaviour.instructionFiles.description": "Caminhos para arquivos de instruções adicionais no prompt do sistema", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts index f5741c147fe..d7b78449e6b 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts @@ -963,6 +963,10 @@ export const dict = { "Nisu pronađene vještine. Dodajte putanje mapa ili URL-ove ispod kako biste učinili vještine dostupnim.", "settings.agentBehaviour.skillPaths": "Putanje mapa vještina", "settings.agentBehaviour.skillUrls": "URL-ovi vještina", + "settings.agentBehaviour.removeSkill.title": "Ukloni vještinu", + "settings.agentBehaviour.removeSkill.confirm": + 'Ukloniti vještinu "{{name}}"? Ovo će obrisati datoteke vještine sa diska.', + "settings.agentBehaviour.removeSkill.button": "Ukloni", "settings.agentBehaviour.instructionFiles": "Dodatne datoteke uputa", "settings.agentBehaviour.instructionFiles.description": "Putanje do dodatnih datoteka uputa u sistemskom promptu", "settings.agentBehaviour.mcpEmpty": diff --git a/packages/kilo-vscode/webview-ui/src/i18n/da.ts b/packages/kilo-vscode/webview-ui/src/i18n/da.ts index ee236fceef0..e8039761794 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/da.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/da.ts @@ -959,6 +959,10 @@ export const dict = { "Ingen skills fundet. Tilføj skill-mappestier eller URL'er nedenfor for at gøre skills tilgængelige.", "settings.agentBehaviour.skillPaths": "Skill-mappestier", "settings.agentBehaviour.skillUrls": "Skill-URL'er", + "settings.agentBehaviour.removeSkill.title": "Fjern færdighed", + "settings.agentBehaviour.removeSkill.confirm": + 'Vil du fjerne færdigheden "{{name}}"? Dette vil slette færdighedsfilerne fra disken.', + "settings.agentBehaviour.removeSkill.button": "Fjern", "settings.agentBehaviour.instructionFiles": "Yderligere instruktionsfiler", "settings.agentBehaviour.instructionFiles.description": "Stier til yderligere instruktionsfiler i systemprompten", "settings.agentBehaviour.mcpEmpty": diff --git a/packages/kilo-vscode/webview-ui/src/i18n/de.ts b/packages/kilo-vscode/webview-ui/src/i18n/de.ts index f7e20e15c31..3b877fe0057 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/de.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/de.ts @@ -974,6 +974,10 @@ export const dict = { "Keine Skills gefunden. Fügen Sie unten Skill-Ordnerpfade oder URLs hinzu, um Skills verfügbar zu machen.", "settings.agentBehaviour.skillPaths": "Skill-Ordnerpfade", "settings.agentBehaviour.skillUrls": "Skill-URLs", + "settings.agentBehaviour.removeSkill.title": "Skill entfernen", + "settings.agentBehaviour.removeSkill.confirm": + 'Skill "{{name}}" entfernen? Dadurch werden die Skill-Dateien vom Datenträger gelöscht.', + "settings.agentBehaviour.removeSkill.button": "Entfernen", "settings.agentBehaviour.instructionFiles": "Zusätzliche Anweisungsdateien", "settings.agentBehaviour.instructionFiles.description": "Pfade zu zusätzlichen Anweisungsdateien im System-Prompt", "settings.agentBehaviour.mcpEmpty": diff --git a/packages/kilo-vscode/webview-ui/src/i18n/es.ts b/packages/kilo-vscode/webview-ui/src/i18n/es.ts index 1a9b99a0303..3787b776cf1 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/es.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/es.ts @@ -966,6 +966,10 @@ export const dict = { "No se encontraron habilidades. Agregue rutas de carpetas o URLs abajo para hacer disponibles las habilidades.", "settings.agentBehaviour.skillPaths": "Rutas de carpetas de habilidades", "settings.agentBehaviour.skillUrls": "URLs de habilidades", + "settings.agentBehaviour.removeSkill.title": "Eliminar habilidad", + "settings.agentBehaviour.removeSkill.confirm": + '¿Eliminar la habilidad "{{name}}"? Esto borrará los archivos de la habilidad del disco.', + "settings.agentBehaviour.removeSkill.button": "Eliminar", "settings.agentBehaviour.instructionFiles": "Archivos de instrucciones adicionales", "settings.agentBehaviour.instructionFiles.description": "Rutas a archivos de instrucciones adicionales incluidos en el prompt del sistema", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts index 8d0755d572a..3f10e46bd65 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts @@ -976,6 +976,10 @@ export const dict = { "Aucune compétence découverte. Ajoutez des chemins de dossiers ou des URLs ci-dessous pour rendre les compétences disponibles.", "settings.agentBehaviour.skillPaths": "Chemins des dossiers de compétences", "settings.agentBehaviour.skillUrls": "URLs de compétences", + "settings.agentBehaviour.removeSkill.title": "Supprimer la compétence", + "settings.agentBehaviour.removeSkill.confirm": + 'Supprimer la compétence "{{name}}" ? Cela supprimera les fichiers de la compétence du disque.', + "settings.agentBehaviour.removeSkill.button": "Supprimer", "settings.agentBehaviour.instructionFiles": "Fichiers d'instructions supplémentaires", "settings.agentBehaviour.instructionFiles.description": "Chemins vers des fichiers d'instructions supplémentaires", "settings.agentBehaviour.mcpEmpty": diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts index 0500017fe6f..0319039a1eb 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts @@ -959,6 +959,10 @@ export const dict = { "スキルが見つかりません。スキルを利用可能にするには、以下にスキルフォルダパスまたはURLを追加してください。", "settings.agentBehaviour.skillPaths": "スキルフォルダパス", "settings.agentBehaviour.skillUrls": "スキルURL", + "settings.agentBehaviour.removeSkill.title": "スキルを削除", + "settings.agentBehaviour.removeSkill.confirm": + 'スキル "{{name}}" を削除しますか?これにより、ディスクからスキルファイルが削除されます。', + "settings.agentBehaviour.removeSkill.button": "削除", "settings.agentBehaviour.instructionFiles": "追加の指示ファイル", "settings.agentBehaviour.instructionFiles.description": "システムプロンプトに含まれる追加の指示ファイルへのパス", "settings.agentBehaviour.mcpEmpty": diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts index 618e280fdc3..9c29adae7e0 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts @@ -956,6 +956,10 @@ export const dict = { "스킬을 찾을 수 없습니다. 스킬을 사용하려면 아래에 스킬 폴더 경로 또는 URL을 추가하세요.", "settings.agentBehaviour.skillPaths": "스킬 폴더 경로", "settings.agentBehaviour.skillUrls": "스킬 URL", + "settings.agentBehaviour.removeSkill.title": "스킬 제거", + "settings.agentBehaviour.removeSkill.confirm": + '스킬 "{{name}}"을(를) 제거하시겠습니까? 디스크에서 스킬 파일이 삭제됩니다.', + "settings.agentBehaviour.removeSkill.button": "제거", "settings.agentBehaviour.instructionFiles": "추가 지시 파일", "settings.agentBehaviour.instructionFiles.description": "시스템 프롬프트에 포함되는 추가 지시 파일 경로", "settings.agentBehaviour.mcpEmpty": diff --git a/packages/kilo-vscode/webview-ui/src/i18n/no.ts b/packages/kilo-vscode/webview-ui/src/i18n/no.ts index 740f463b0e3..bc81bcde276 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/no.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/no.ts @@ -961,6 +961,10 @@ export const dict = { "Ingen ferdigheter funnet. Legg til ferdighetsmappestier eller URLer nedenfor for å gjøre ferdigheter tilgjengelige.", "settings.agentBehaviour.skillPaths": "Ferdighetsmappe-stier", "settings.agentBehaviour.skillUrls": "Ferdighets-URLer", + "settings.agentBehaviour.removeSkill.title": "Fjern ferdighet", + "settings.agentBehaviour.removeSkill.confirm": + 'Vil du fjerne ferdigheten "{{name}}"? Dette vil slette ferdighetsfilene fra disken.', + "settings.agentBehaviour.removeSkill.button": "Fjern", "settings.agentBehaviour.instructionFiles": "Ekstra instruksjonsfiler", "settings.agentBehaviour.instructionFiles.description": "Stier til ekstra instruksjonsfiler i systemprompten", "settings.agentBehaviour.mcpEmpty": diff --git a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts index 508a496b792..098b89b72c3 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts @@ -961,6 +961,10 @@ export const dict = { "Nie znaleziono umiejętności. Dodaj ścieżki folderów lub adresy URL poniżej, aby udostępnić umiejętności.", "settings.agentBehaviour.skillPaths": "Ścieżki folderów umiejętności", "settings.agentBehaviour.skillUrls": "Adresy URL umiejętności", + "settings.agentBehaviour.removeSkill.title": "Usuń umiejętność", + "settings.agentBehaviour.removeSkill.confirm": + 'Usunąć umiejętność "{{name}}"? Spowoduje to usunięcie plików umiejętności z dysku.', + "settings.agentBehaviour.removeSkill.button": "Usuń", "settings.agentBehaviour.instructionFiles": "Dodatkowe pliki instrukcji", "settings.agentBehaviour.instructionFiles.description": "Ścieżki do dodatkowych plików instrukcji w prompcie systemowym", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts index db6d9ad9686..af44e040a58 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts @@ -964,6 +964,10 @@ export const dict = { "Навыки не обнаружены. Добавьте пути к папкам навыков или URL-адреса ниже, чтобы сделать навыки доступными.", "settings.agentBehaviour.skillPaths": "Пути папок навыков", "settings.agentBehaviour.skillUrls": "URL навыков", + "settings.agentBehaviour.removeSkill.title": "Удалить навык", + "settings.agentBehaviour.removeSkill.confirm": + 'Удалить навык "{{name}}"? Это приведет к удалению файлов навыка с диска.', + "settings.agentBehaviour.removeSkill.button": "Удалить", "settings.agentBehaviour.instructionFiles": "Дополнительные файлы инструкций", "settings.agentBehaviour.instructionFiles.description": "Пути к дополнительным файлам инструкций в системном промпте", "settings.agentBehaviour.mcpEmpty": diff --git a/packages/kilo-vscode/webview-ui/src/i18n/th.ts b/packages/kilo-vscode/webview-ui/src/i18n/th.ts index 528c8eff8d7..647b93b1ee4 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/th.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/th.ts @@ -951,6 +951,10 @@ export const dict = { "settings.agentBehaviour.noSkillsFound": "ไม่พบทักษะ เพิ่มเส้นทางโฟลเดอร์หรือ URL ด้านล่างเพื่อทำให้ทักษะพร้อมใช้งาน", "settings.agentBehaviour.skillPaths": "เส้นทางโฟลเดอร์ทักษะ", "settings.agentBehaviour.skillUrls": "URL ทักษะ", + "settings.agentBehaviour.removeSkill.title": "ลบทักษะ", + "settings.agentBehaviour.removeSkill.confirm": + 'ต้องการลบทักษะ "{{name}}" หรือไม่? การดำเนินการนี้จะลบไฟล์ทักษะออกจากดิสก์', + "settings.agentBehaviour.removeSkill.button": "ลบ", "settings.agentBehaviour.instructionFiles": "ไฟล์คำสั่งเพิ่มเติม", "settings.agentBehaviour.instructionFiles.description": "เส้นทางไฟล์คำสั่งเพิ่มเติมในพรอมต์ระบบ", "settings.agentBehaviour.mcpEmpty": diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts index 78544fae708..694b4333c9a 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts @@ -945,6 +945,9 @@ export const dict = { "settings.agentBehaviour.noSkillsFound": "未发现任何技能。请在下方添加技能文件夹路径或 URL 以使技能可用。", "settings.agentBehaviour.skillPaths": "技能文件夹路径", "settings.agentBehaviour.skillUrls": "技能 URL", + "settings.agentBehaviour.removeSkill.title": "移除技能", + "settings.agentBehaviour.removeSkill.confirm": '移除技能 "{{name}}" 吗?这会从磁盘中删除该技能文件。', + "settings.agentBehaviour.removeSkill.button": "移除", "settings.agentBehaviour.instructionFiles": "附加指令文件", "settings.agentBehaviour.instructionFiles.description": "包含在系统提示词中的附加指令文件路径", "settings.agentBehaviour.mcpEmpty": "未配置 MCP 服务器。编辑 opencode 配置文件以添加 MCP 服务器。", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts index b8e91245c06..4e3a720f433 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts @@ -946,6 +946,9 @@ export const dict = { "settings.agentBehaviour.noSkillsFound": "未發現任何 Skill。請在下方新增 Skill 資料夾路徑或 URL 以使 Skill 可用。", "settings.agentBehaviour.skillPaths": "Skill 資料夾路徑", "settings.agentBehaviour.skillUrls": "Skill URL", + "settings.agentBehaviour.removeSkill.title": "移除技能", + "settings.agentBehaviour.removeSkill.confirm": '要移除技能 "{{name}}" 嗎?這將從磁碟中刪除該技能檔案。', + "settings.agentBehaviour.removeSkill.button": "移除", "settings.agentBehaviour.instructionFiles": "附加指令檔案", "settings.agentBehaviour.instructionFiles.description": "包含在系統提示詞中的附加指令檔案路徑", "settings.agentBehaviour.mcpEmpty": "未設定 MCP 伺服器。編輯 opencode 設定檔以新增 MCP 伺服器。", From 07d16dff237b6c4097521214f60da4a149e97433 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Mon, 16 Mar 2026 11:38:39 +0100 Subject: [PATCH 05/10] refactor: move DELETE /skill to dedicated route file Extract the inline DELETE /skill endpoint from server.ts into a dedicated packages/opencode/src/server/routes/skill.ts file, reducing kilo-specific changes in the shared server.ts and minimizing future merge conflicts with upstream. The route is mounted at /skill and uses the same lazy() pattern as other kilo-specific route files (telemetry, enhance-prompt, etc.). --- packages/kilo-vscode/src/KiloProvider.ts | 2 +- packages/opencode/src/server/routes/skill.ts | 40 +++ packages/opencode/src/server/server.ts | 34 +-- packages/sdk/js/src/v2/gen/sdk.gen.ts | 299 ++++++++++--------- packages/sdk/js/src/v2/gen/types.gen.ts | 106 +++---- packages/sdk/openapi.json | 232 +++++++------- 6 files changed, 365 insertions(+), 348 deletions(-) create mode 100644 packages/opencode/src/server/routes/skill.ts diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 79d29ac0ea2..34a1a3dbdf5 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -1139,7 +1139,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper if (!this.client) return try { const dir = this.getWorkspaceDirectory() - await this.client.app.removeSkill({ location, directory: dir }, { throwOnError: true }) + await this.client.skill.remove({ location, directory: dir }, { throwOnError: true }) } catch (error) { console.error("[Kilo New] KiloProvider: Failed to remove skill:", error) } diff --git a/packages/opencode/src/server/routes/skill.ts b/packages/opencode/src/server/routes/skill.ts new file mode 100644 index 00000000000..fbf49607166 --- /dev/null +++ b/packages/opencode/src/server/routes/skill.ts @@ -0,0 +1,40 @@ +// kilocode_change - new file +import { Hono } from "hono" +import { describeRoute, validator, resolver } from "hono-openapi" +import z from "zod" +import { Skill } from "../../skill/skill" +import { lazy } from "../../util/lazy" +import { errors } from "../error" + +export const SkillRoutes = lazy(() => + new Hono().delete( + "/", + describeRoute({ + summary: "Remove a skill", + description: "Remove a skill by deleting its directory from disk and clearing it from cache.", + operationId: "skill.remove", + responses: { + 200: { + description: "Skill removed", + content: { + "application/json": { + schema: resolver(z.boolean()), + }, + }, + }, + ...errors(400), + }, + }), + validator( + "query", + z.object({ + location: z.string(), + }), + ), + async (c) => { + const { location } = c.req.valid("query") + await Skill.remove(location) + return c.json(true) + }, + ), +) diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index 1169642058f..80ce513cb09 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -47,6 +47,7 @@ import { HTTPException } from "hono/http-exception" import { errors } from "./error" import { CommitMessageRoutes } from "./routes/commit-message" import { EnhancePromptRoutes } from "./routes/enhance-prompt" // kilocode_change +import { SkillRoutes } from "./routes/skill" // kilocode_change import { QuestionRoutes } from "./routes/question" import { PermissionRoutes } from "./routes/permission" import { GlobalRoutes } from "./routes/global" @@ -268,6 +269,7 @@ export namespace Server { .route("/telemetry", TelemetryRoutes()) // kilocode_change .route("/commit-message", CommitMessageRoutes()) // kilocode_change .route("/enhance-prompt", EnhancePromptRoutes()) // kilocode_change + .route("/skill", SkillRoutes()) // kilocode_change // kilocode_change start - Kilo Gateway routes .route( "/kilo", @@ -500,38 +502,6 @@ export namespace Server { return c.json(skills) }, ) - // kilocode_change start - .delete( - "/skill", - describeRoute({ - summary: "Remove a skill", - description: "Remove a skill by deleting its directory from disk and clearing it from cache.", - operationId: "app.removeSkill", - responses: { - 200: { - description: "Skill removed", - content: { - "application/json": { - schema: resolver(z.boolean()), - }, - }, - }, - ...errors(400), - }, - }), - validator( - "query", - z.object({ - location: z.string(), - }), - ), - async (c) => { - const { location } = c.req.valid("query") - await Skill.remove(location) - return c.json(true) - }, - ) - // kilocode_change end .get( "/lsp", describeRoute({ diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index b7ff65c3cf7..60c3805c7ab 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -7,8 +7,6 @@ import type { AppAgentsResponses, AppLogErrors, AppLogResponses, - AppRemoveSkillErrors, - AppRemoveSkillResponses, AppSkillsResponses, Auth as Auth3, AuthRemoveErrors, @@ -170,6 +168,8 @@ import type { SessionUnshareResponses, SessionUpdateErrors, SessionUpdateResponses, + SkillRemoveErrors, + SkillRemoveResponses, SubtaskPartInput, TelemetryCaptureErrors, TelemetryCaptureResponses, @@ -2930,6 +2930,147 @@ export class EnhancePrompt extends HeyApiClient { } } +export class Skill extends HeyApiClient { + /** + * Remove a skill + * + * Remove a skill by deleting its directory from disk and clearing it from cache. + */ + public remove( + parameters: { + directory?: string + workspace?: string + location: string + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + { in: "query", key: "location" }, + ], + }, + ], + ) + return (options?.client ?? this.client).delete({ + url: "/skill", + ...options, + ...params, + }) + } +} + +export class App extends HeyApiClient { + /** + * List skills + * + * Get a list of all available skills in the OpenCode system. + */ + public skills( + parameters?: { + directory?: string + workspace?: string + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + ], + }, + ], + ) + return (options?.client ?? this.client).get({ + url: "/skill", + ...options, + ...params, + }) + } + + /** + * Write log + * + * Write a log entry to the server logs with specified level and metadata. + */ + public log( + parameters?: { + directory?: string + workspace?: string + service?: string + level?: "debug" | "info" | "error" | "warn" + message?: string + extra?: { + [key: string]: unknown + } + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + { in: "body", key: "service" }, + { in: "body", key: "level" }, + { in: "body", key: "message" }, + { in: "body", key: "extra" }, + ], + }, + ], + ) + return (options?.client ?? this.client).post({ + url: "/log", + ...options, + ...params, + headers: { + "Content-Type": "application/json", + ...options?.headers, + ...params.headers, + }, + }) + } + + /** + * List agents + * + * Get a list of all available AI agents in the OpenCode system. + */ + public agents( + parameters?: { + directory?: string + workspace?: string + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + ], + }, + ], + ) + return (options?.client ?? this.client).get({ + url: "/agent", + ...options, + ...params, + }) + } +} + export class Organization extends HeyApiClient { /** * Update Kilo Gateway organization @@ -4256,145 +4397,6 @@ export class Command extends HeyApiClient { } } -export class App extends HeyApiClient { - /** - * Write log - * - * Write a log entry to the server logs with specified level and metadata. - */ - public log( - parameters?: { - directory?: string - workspace?: string - service?: string - level?: "debug" | "info" | "error" | "warn" - message?: string - extra?: { - [key: string]: unknown - } - }, - options?: Options, - ) { - const params = buildClientParams( - [parameters], - [ - { - args: [ - { in: "query", key: "directory" }, - { in: "query", key: "workspace" }, - { in: "body", key: "service" }, - { in: "body", key: "level" }, - { in: "body", key: "message" }, - { in: "body", key: "extra" }, - ], - }, - ], - ) - return (options?.client ?? this.client).post({ - url: "/log", - ...options, - ...params, - headers: { - "Content-Type": "application/json", - ...options?.headers, - ...params.headers, - }, - }) - } - - /** - * List agents - * - * Get a list of all available AI agents in the OpenCode system. - */ - public agents( - parameters?: { - directory?: string - workspace?: string - }, - options?: Options, - ) { - const params = buildClientParams( - [parameters], - [ - { - args: [ - { in: "query", key: "directory" }, - { in: "query", key: "workspace" }, - ], - }, - ], - ) - return (options?.client ?? this.client).get({ - url: "/agent", - ...options, - ...params, - }) - } - - /** - * Remove a skill - * - * Remove a skill by deleting its directory from disk and clearing it from cache. - */ - public removeSkill( - parameters: { - directory?: string - workspace?: string - location: string - }, - options?: Options, - ) { - const params = buildClientParams( - [parameters], - [ - { - args: [ - { in: "query", key: "directory" }, - { in: "query", key: "workspace" }, - { in: "query", key: "location" }, - ], - }, - ], - ) - return (options?.client ?? this.client).delete({ - url: "/skill", - ...options, - ...params, - }) - } - - /** - * List skills - * - * Get a list of all available skills in the OpenCode system. - */ - public skills( - parameters?: { - directory?: string - workspace?: string - }, - options?: Options, - ) { - const params = buildClientParams( - [parameters], - [ - { - args: [ - { in: "query", key: "directory" }, - { in: "query", key: "workspace" }, - ], - }, - ], - ) - return (options?.client ?? this.client).get({ - url: "/skill", - ...options, - ...params, - }) - } -} - export class Lsp extends HeyApiClient { /** * Get LSP status @@ -4579,6 +4581,16 @@ export class KiloClient extends HeyApiClient { return (this._enhancePrompt ??= new EnhancePrompt({ client: this.client })) } + private _skill?: Skill + get skill(): Skill { + return (this._skill ??= new Skill({ client: this.client })) + } + + private _app?: App + get app(): App { + return (this._app ??= new App({ client: this.client })) + } + private _kilo?: Kilo get kilo(): Kilo { return (this._kilo ??= new Kilo({ client: this.client })) @@ -4624,11 +4636,6 @@ export class KiloClient extends HeyApiClient { return (this._command ??= new Command({ client: this.client })) } - private _app?: App - get app(): App { - return (this._app ??= new App({ client: this.client })) - } - private _lsp?: Lsp get lsp(): Lsp { return (this._lsp ??= new Lsp({ client: this.client })) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 83dbdbf153b..cc00335cfd2 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -4402,6 +4402,59 @@ export type EnhancePromptEnhanceResponses = { export type EnhancePromptEnhanceResponse = EnhancePromptEnhanceResponses[keyof EnhancePromptEnhanceResponses] +export type SkillRemoveData = { + body?: never + path?: never + query: { + directory?: string + workspace?: string + location: string + } + url: "/skill" +} + +export type SkillRemoveErrors = { + /** + * Bad request + */ + 400: BadRequestError +} + +export type SkillRemoveError = SkillRemoveErrors[keyof SkillRemoveErrors] + +export type SkillRemoveResponses = { + /** + * Skill removed + */ + 200: boolean +} + +export type SkillRemoveResponse = SkillRemoveResponses[keyof SkillRemoveResponses] + +export type AppSkillsData = { + body?: never + path?: never + query?: { + directory?: string + workspace?: string + } + url: "/skill" +} + +export type AppSkillsResponses = { + /** + * List of skills + */ + 200: Array<{ + name: string + description: string + location: string + content: string + }> +} + +export type AppSkillsResponse = AppSkillsResponses[keyof AppSkillsResponses] + export type KiloProfileData = { body?: never path?: never @@ -5483,59 +5536,6 @@ export type AppAgentsResponses = { export type AppAgentsResponse = AppAgentsResponses[keyof AppAgentsResponses] -export type AppRemoveSkillData = { - body?: never - path?: never - query: { - directory?: string - workspace?: string - location: string - } - url: "/skill" -} - -export type AppRemoveSkillErrors = { - /** - * Bad request - */ - 400: BadRequestError -} - -export type AppRemoveSkillError = AppRemoveSkillErrors[keyof AppRemoveSkillErrors] - -export type AppRemoveSkillResponses = { - /** - * Skill removed - */ - 200: boolean -} - -export type AppRemoveSkillResponse = AppRemoveSkillResponses[keyof AppRemoveSkillResponses] - -export type AppSkillsData = { - body?: never - path?: never - query?: { - directory?: string - workspace?: string - } - url: "/skill" -} - -export type AppSkillsResponses = { - /** - * List of skills - */ - 200: Array<{ - name: string - description: string - location: string - content: string - }> -} - -export type AppSkillsResponse = AppSkillsResponses[keyof AppSkillsResponses] - export type LspStatusData = { body?: never path?: never diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 66adf212510..bed3949ac4b 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -5372,6 +5372,122 @@ ] } }, + "/skill": { + "delete": { + "operationId": "skill.remove", + "parameters": [ + { + "in": "query", + "name": "directory", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "workspace", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "location", + "schema": { + "type": "string" + }, + "required": true + } + ], + "summary": "Remove a skill", + "description": "Remove a skill by deleting its directory from disk and clearing it from cache.", + "responses": { + "200": { + "description": "Skill removed", + "content": { + "application/json": { + "schema": { + "type": "boolean" + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequestError" + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "js", + "source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.skill.remove({\n ...\n})" + } + ] + }, + "get": { + "operationId": "app.skills", + "parameters": [ + { + "in": "query", + "name": "directory", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "workspace", + "schema": { + "type": "string" + } + } + ], + "summary": "List skills", + "description": "Get a list of all available skills in the OpenCode system.", + "responses": { + "200": { + "description": "List of skills", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "location": { + "type": "string" + }, + "content": { + "type": "string" + } + }, + "required": ["name", "description", "location", "content"] + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "js", + "source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.app.skills({\n ...\n})" + } + ] + } + }, "/kilo/profile": { "get": { "operationId": "kilo.profile", @@ -7878,122 +7994,6 @@ ] } }, - "/skill": { - "get": { - "operationId": "app.skills", - "parameters": [ - { - "in": "query", - "name": "directory", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "workspace", - "schema": { - "type": "string" - } - } - ], - "summary": "List skills", - "description": "Get a list of all available skills in the OpenCode system.", - "responses": { - "200": { - "description": "List of skills", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "location": { - "type": "string" - }, - "content": { - "type": "string" - } - }, - "required": ["name", "description", "location", "content"] - } - } - } - } - } - }, - "x-codeSamples": [ - { - "lang": "js", - "source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.app.skills({\n ...\n})" - } - ] - }, - "delete": { - "operationId": "app.removeSkill", - "parameters": [ - { - "in": "query", - "name": "directory", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "workspace", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "location", - "schema": { - "type": "string" - }, - "required": true - } - ], - "summary": "Remove a skill", - "description": "Remove a skill by deleting its directory from disk and clearing it from cache.", - "responses": { - "200": { - "description": "Skill removed", - "content": { - "application/json": { - "schema": { - "type": "boolean" - } - } - } - }, - "400": { - "description": "Bad request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BadRequestError" - } - } - } - } - }, - "x-codeSamples": [ - { - "lang": "js", - "source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.app.removeSkill({\n ...\n})" - } - ] - } - }, "/lsp": { "get": { "operationId": "lsp.status", From 2d9e4473991b62e52ca6a9f051b37bd87a743ca4 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Mon, 16 Mar 2026 11:52:11 +0100 Subject: [PATCH 06/10] refactor: rename to KilocodeRoutes at /kilocode prefix Rename routes/skill.ts to routes/kilocode.ts with KilocodeRoutes export, mounted at /kilocode. This is the single home for all future kilo-specific CLI endpoints, avoiding conflicts with the existing /kilo gateway routes and keeping all kilo changes out of server.ts. SDK: client.kilocode.removeSkill() --- packages/kilo-vscode/src/KiloProvider.ts | 2 +- .../server/routes/{skill.ts => kilocode.ts} | 8 +- packages/opencode/src/server/server.ts | 4 +- packages/sdk/js/src/v2/gen/sdk.gen.ts | 246 +++++++++--------- packages/sdk/js/src/v2/gen/types.gen.ts | 60 ++--- packages/sdk/openapi.json | 122 ++++----- 6 files changed, 225 insertions(+), 217 deletions(-) rename packages/opencode/src/server/routes/{skill.ts => kilocode.ts} (79%) diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 34a1a3dbdf5..d833f9e0c09 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -1139,7 +1139,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper if (!this.client) return try { const dir = this.getWorkspaceDirectory() - await this.client.skill.remove({ location, directory: dir }, { throwOnError: true }) + await this.client.kilocode.removeSkill({ location, directory: dir }, { throwOnError: true }) } catch (error) { console.error("[Kilo New] KiloProvider: Failed to remove skill:", error) } diff --git a/packages/opencode/src/server/routes/skill.ts b/packages/opencode/src/server/routes/kilocode.ts similarity index 79% rename from packages/opencode/src/server/routes/skill.ts rename to packages/opencode/src/server/routes/kilocode.ts index fbf49607166..60d077fe1b5 100644 --- a/packages/opencode/src/server/routes/skill.ts +++ b/packages/opencode/src/server/routes/kilocode.ts @@ -1,4 +1,6 @@ // kilocode_change - new file +// Kilo-specific routes that live in the CLI package (direct access to internals). +// All future kilo-specific endpoints should be added here. import { Hono } from "hono" import { describeRoute, validator, resolver } from "hono-openapi" import z from "zod" @@ -6,13 +8,13 @@ import { Skill } from "../../skill/skill" import { lazy } from "../../util/lazy" import { errors } from "../error" -export const SkillRoutes = lazy(() => +export const KilocodeRoutes = lazy(() => new Hono().delete( - "/", + "/skill", describeRoute({ summary: "Remove a skill", description: "Remove a skill by deleting its directory from disk and clearing it from cache.", - operationId: "skill.remove", + operationId: "kilocode.removeSkill", responses: { 200: { description: "Skill removed", diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index 80ce513cb09..9d7026dc9d4 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -47,7 +47,7 @@ import { HTTPException } from "hono/http-exception" import { errors } from "./error" import { CommitMessageRoutes } from "./routes/commit-message" import { EnhancePromptRoutes } from "./routes/enhance-prompt" // kilocode_change -import { SkillRoutes } from "./routes/skill" // kilocode_change +import { KilocodeRoutes } from "./routes/kilocode" // kilocode_change import { QuestionRoutes } from "./routes/question" import { PermissionRoutes } from "./routes/permission" import { GlobalRoutes } from "./routes/global" @@ -269,7 +269,7 @@ export namespace Server { .route("/telemetry", TelemetryRoutes()) // kilocode_change .route("/commit-message", CommitMessageRoutes()) // kilocode_change .route("/enhance-prompt", EnhancePromptRoutes()) // kilocode_change - .route("/skill", SkillRoutes()) // kilocode_change + .route("/kilocode", KilocodeRoutes()) // kilocode_change // kilocode_change start - Kilo Gateway routes .route( "/kilo", diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index 60c3805c7ab..b88b95c24a7 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -57,6 +57,8 @@ import type { KiloCloudSessionImportResponses, KiloCloudSessionsErrors, KiloCloudSessionsResponses, + KilocodeRemoveSkillErrors, + KilocodeRemoveSkillResponses, KiloFimErrors, KiloFimResponses, KiloNotificationsErrors, @@ -168,8 +170,6 @@ import type { SessionUnshareResponses, SessionUpdateErrors, SessionUpdateResponses, - SkillRemoveErrors, - SkillRemoveResponses, SubtaskPartInput, TelemetryCaptureErrors, TelemetryCaptureResponses, @@ -2930,13 +2930,13 @@ export class EnhancePrompt extends HeyApiClient { } } -export class Skill extends HeyApiClient { +export class Kilocode extends HeyApiClient { /** * Remove a skill * * Remove a skill by deleting its directory from disk and clearing it from cache. */ - public remove( + public removeSkill( parameters: { directory?: string workspace?: string @@ -2956,115 +2956,12 @@ export class Skill extends HeyApiClient { }, ], ) - return (options?.client ?? this.client).delete({ - url: "/skill", - ...options, - ...params, - }) - } -} - -export class App extends HeyApiClient { - /** - * List skills - * - * Get a list of all available skills in the OpenCode system. - */ - public skills( - parameters?: { - directory?: string - workspace?: string - }, - options?: Options, - ) { - const params = buildClientParams( - [parameters], - [ - { - args: [ - { in: "query", key: "directory" }, - { in: "query", key: "workspace" }, - ], - }, - ], - ) - return (options?.client ?? this.client).get({ - url: "/skill", - ...options, - ...params, - }) - } - - /** - * Write log - * - * Write a log entry to the server logs with specified level and metadata. - */ - public log( - parameters?: { - directory?: string - workspace?: string - service?: string - level?: "debug" | "info" | "error" | "warn" - message?: string - extra?: { - [key: string]: unknown - } - }, - options?: Options, - ) { - const params = buildClientParams( - [parameters], - [ - { - args: [ - { in: "query", key: "directory" }, - { in: "query", key: "workspace" }, - { in: "body", key: "service" }, - { in: "body", key: "level" }, - { in: "body", key: "message" }, - { in: "body", key: "extra" }, - ], - }, - ], - ) - return (options?.client ?? this.client).post({ - url: "/log", - ...options, - ...params, - headers: { - "Content-Type": "application/json", - ...options?.headers, - ...params.headers, - }, - }) - } - - /** - * List agents - * - * Get a list of all available AI agents in the OpenCode system. - */ - public agents( - parameters?: { - directory?: string - workspace?: string - }, - options?: Options, - ) { - const params = buildClientParams( - [parameters], - [ - { - args: [ - { in: "query", key: "directory" }, - { in: "query", key: "workspace" }, - ], - }, - ], - ) - return (options?.client ?? this.client).get({ - url: "/agent", + return (options?.client ?? this.client).delete< + KilocodeRemoveSkillResponses, + KilocodeRemoveSkillErrors, + ThrowOnError + >({ + url: "/kilocode/skill", ...options, ...params, }) @@ -4397,6 +4294,113 @@ export class Command extends HeyApiClient { } } +export class App extends HeyApiClient { + /** + * Write log + * + * Write a log entry to the server logs with specified level and metadata. + */ + public log( + parameters?: { + directory?: string + workspace?: string + service?: string + level?: "debug" | "info" | "error" | "warn" + message?: string + extra?: { + [key: string]: unknown + } + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + { in: "body", key: "service" }, + { in: "body", key: "level" }, + { in: "body", key: "message" }, + { in: "body", key: "extra" }, + ], + }, + ], + ) + return (options?.client ?? this.client).post({ + url: "/log", + ...options, + ...params, + headers: { + "Content-Type": "application/json", + ...options?.headers, + ...params.headers, + }, + }) + } + + /** + * List agents + * + * Get a list of all available AI agents in the OpenCode system. + */ + public agents( + parameters?: { + directory?: string + workspace?: string + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + ], + }, + ], + ) + return (options?.client ?? this.client).get({ + url: "/agent", + ...options, + ...params, + }) + } + + /** + * List skills + * + * Get a list of all available skills in the OpenCode system. + */ + public skills( + parameters?: { + directory?: string + workspace?: string + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + ], + }, + ], + ) + return (options?.client ?? this.client).get({ + url: "/skill", + ...options, + ...params, + }) + } +} + export class Lsp extends HeyApiClient { /** * Get LSP status @@ -4581,14 +4585,9 @@ export class KiloClient extends HeyApiClient { return (this._enhancePrompt ??= new EnhancePrompt({ client: this.client })) } - private _skill?: Skill - get skill(): Skill { - return (this._skill ??= new Skill({ client: this.client })) - } - - private _app?: App - get app(): App { - return (this._app ??= new App({ client: this.client })) + private _kilocode?: Kilocode + get kilocode(): Kilocode { + return (this._kilocode ??= new Kilocode({ client: this.client })) } private _kilo?: Kilo @@ -4636,6 +4635,11 @@ export class KiloClient extends HeyApiClient { return (this._command ??= new Command({ client: this.client })) } + private _app?: App + get app(): App { + return (this._app ??= new App({ client: this.client })) + } + private _lsp?: Lsp get lsp(): Lsp { return (this._lsp ??= new Lsp({ client: this.client })) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index cc00335cfd2..47c1300d35f 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -4402,7 +4402,7 @@ export type EnhancePromptEnhanceResponses = { export type EnhancePromptEnhanceResponse = EnhancePromptEnhanceResponses[keyof EnhancePromptEnhanceResponses] -export type SkillRemoveData = { +export type KilocodeRemoveSkillData = { body?: never path?: never query: { @@ -4410,50 +4410,26 @@ export type SkillRemoveData = { workspace?: string location: string } - url: "/skill" + url: "/kilocode/skill" } -export type SkillRemoveErrors = { +export type KilocodeRemoveSkillErrors = { /** * Bad request */ 400: BadRequestError } -export type SkillRemoveError = SkillRemoveErrors[keyof SkillRemoveErrors] +export type KilocodeRemoveSkillError = KilocodeRemoveSkillErrors[keyof KilocodeRemoveSkillErrors] -export type SkillRemoveResponses = { +export type KilocodeRemoveSkillResponses = { /** * Skill removed */ 200: boolean } -export type SkillRemoveResponse = SkillRemoveResponses[keyof SkillRemoveResponses] - -export type AppSkillsData = { - body?: never - path?: never - query?: { - directory?: string - workspace?: string - } - url: "/skill" -} - -export type AppSkillsResponses = { - /** - * List of skills - */ - 200: Array<{ - name: string - description: string - location: string - content: string - }> -} - -export type AppSkillsResponse = AppSkillsResponses[keyof AppSkillsResponses] +export type KilocodeRemoveSkillResponse = KilocodeRemoveSkillResponses[keyof KilocodeRemoveSkillResponses] export type KiloProfileData = { body?: never @@ -5536,6 +5512,30 @@ export type AppAgentsResponses = { export type AppAgentsResponse = AppAgentsResponses[keyof AppAgentsResponses] +export type AppSkillsData = { + body?: never + path?: never + query?: { + directory?: string + workspace?: string + } + url: "/skill" +} + +export type AppSkillsResponses = { + /** + * List of skills + */ + 200: Array<{ + name: string + description: string + location: string + content: string + }> +} + +export type AppSkillsResponse = AppSkillsResponses[keyof AppSkillsResponses] + export type LspStatusData = { body?: never path?: never diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index bed3949ac4b..5e09dbd374e 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -5372,9 +5372,9 @@ ] } }, - "/skill": { + "/kilocode/skill": { "delete": { - "operationId": "skill.remove", + "operationId": "kilocode.removeSkill", "parameters": [ { "in": "query", @@ -5426,64 +5426,7 @@ "x-codeSamples": [ { "lang": "js", - "source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.skill.remove({\n ...\n})" - } - ] - }, - "get": { - "operationId": "app.skills", - "parameters": [ - { - "in": "query", - "name": "directory", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "workspace", - "schema": { - "type": "string" - } - } - ], - "summary": "List skills", - "description": "Get a list of all available skills in the OpenCode system.", - "responses": { - "200": { - "description": "List of skills", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "location": { - "type": "string" - }, - "content": { - "type": "string" - } - }, - "required": ["name", "description", "location", "content"] - } - } - } - } - } - }, - "x-codeSamples": [ - { - "lang": "js", - "source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.app.skills({\n ...\n})" + "source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.kilocode.removeSkill({\n ...\n})" } ] } @@ -7994,6 +7937,65 @@ ] } }, + "/skill": { + "get": { + "operationId": "app.skills", + "parameters": [ + { + "in": "query", + "name": "directory", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "workspace", + "schema": { + "type": "string" + } + } + ], + "summary": "List skills", + "description": "Get a list of all available skills in the OpenCode system.", + "responses": { + "200": { + "description": "List of skills", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "location": { + "type": "string" + }, + "content": { + "type": "string" + } + }, + "required": ["name", "description", "location", "content"] + } + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "js", + "source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.app.skills({\n ...\n})" + } + ] + } + }, "/lsp": { "get": { "operationId": "lsp.status", From f998ee32dceac414639878bdff42c111711b6c4b Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Mon, 16 Mar 2026 12:07:34 +0100 Subject: [PATCH 07/10] fix: return updated skills list from DELETE endpoint to fix stale UI The DELETE /kilocode/skill endpoint now returns the updated skills list (Skill.Info[]) instead of a boolean. The extension uses this response directly to update the webview, eliminating the need for a separate GET /skill round-trip that could return stale cached data. Also normalize paths in Skill.remove() with path.resolve() to ensure location strings match regardless of path format differences. --- packages/kilo-vscode/src/KiloProvider.ts | 13 ++++++---- .../opencode/src/server/routes/kilocode.ts | 10 ++++---- packages/opencode/src/skill/skill.ts | 9 ++++--- packages/sdk/js/src/v2/gen/sdk.gen.ts | 2 +- packages/sdk/js/src/v2/gen/types.gen.ts | 9 +++++-- packages/sdk/openapi.json | 24 ++++++++++++++++--- 6 files changed, 50 insertions(+), 17 deletions(-) diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index d833f9e0c09..9db1031fd6f 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -1133,18 +1133,23 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper } /** - * Remove a skill via the CLI backend (deletes from disk + clears cache), then refresh. + * Remove a skill via the CLI backend (deletes from disk + clears cache). + * The DELETE endpoint returns the updated skills list, which we send directly to the webview. */ private async handleRemoveSkill(location: string): Promise { if (!this.client) return try { const dir = this.getWorkspaceDirectory() - await this.client.kilocode.removeSkill({ location, directory: dir }, { throwOnError: true }) + const { data: skills } = await this.client.kilocode.removeSkill( + { location, directory: dir }, + { throwOnError: true }, + ) + const message = { type: "skillsLoaded", skills } + this.cachedSkillsMessage = message + this.postMessage(message) } catch (error) { console.error("[Kilo New] KiloProvider: Failed to remove skill:", error) } - this.cachedSkillsMessage = null - await this.fetchAndSendSkills() } /** diff --git a/packages/opencode/src/server/routes/kilocode.ts b/packages/opencode/src/server/routes/kilocode.ts index 60d077fe1b5..3e120d7d2e8 100644 --- a/packages/opencode/src/server/routes/kilocode.ts +++ b/packages/opencode/src/server/routes/kilocode.ts @@ -13,14 +13,15 @@ export const KilocodeRoutes = lazy(() => "/skill", describeRoute({ summary: "Remove a skill", - description: "Remove a skill by deleting its directory from disk and clearing it from cache.", + description: + "Remove a skill by deleting its directory from disk and clearing it from cache. Returns the updated skills list.", operationId: "kilocode.removeSkill", responses: { 200: { - description: "Skill removed", + description: "Updated list of skills after removal", content: { "application/json": { - schema: resolver(z.boolean()), + schema: resolver(Skill.Info.array()), }, }, }, @@ -36,7 +37,8 @@ export const KilocodeRoutes = lazy(() => async (c) => { const { location } = c.req.valid("query") await Skill.remove(location) - return c.json(true) + const skills = await Skill.all() + return c.json(skills) }, ), ) diff --git a/packages/opencode/src/skill/skill.ts b/packages/opencode/src/skill/skill.ts index aebac03faf4..7c42239de54 100644 --- a/packages/opencode/src/skill/skill.ts +++ b/packages/opencode/src/skill/skill.ts @@ -217,13 +217,16 @@ export namespace Skill { // kilocode_change start export async function remove(location: string) { - const dir = path.dirname(location) + const resolved = path.resolve(location) + const dir = path.dirname(resolved) await rm(dir, { recursive: true, force: true }) const s = await state() - const name = Object.keys(s.skills).find((k) => s.skills[k].location === location) + const name = Object.keys(s.skills).find((k) => path.resolve(s.skills[k].location) === resolved) if (name) { delete s.skills[name] - s.dirs = s.dirs.filter((d) => d !== dir) + s.dirs = s.dirs.filter((d) => path.resolve(d) !== dir) + } else { + log.warn("skill not found in cache during remove", { location, resolved }) } } // kilocode_change end diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index b88b95c24a7..7328e0d95af 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -2934,7 +2934,7 @@ export class Kilocode extends HeyApiClient { /** * Remove a skill * - * Remove a skill by deleting its directory from disk and clearing it from cache. + * Remove a skill by deleting its directory from disk and clearing it from cache. Returns the updated skills list. */ public removeSkill( parameters: { diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 47c1300d35f..4c9a0741520 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -4424,9 +4424,14 @@ export type KilocodeRemoveSkillError = KilocodeRemoveSkillErrors[keyof KilocodeR export type KilocodeRemoveSkillResponses = { /** - * Skill removed + * Updated list of skills after removal */ - 200: boolean + 200: Array<{ + name: string + description: string + location: string + content: string + }> } export type KilocodeRemoveSkillResponse = KilocodeRemoveSkillResponses[keyof KilocodeRemoveSkillResponses] diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 5e09dbd374e..686994f0d9d 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -5400,14 +5400,32 @@ } ], "summary": "Remove a skill", - "description": "Remove a skill by deleting its directory from disk and clearing it from cache.", + "description": "Remove a skill by deleting its directory from disk and clearing it from cache. Returns the updated skills list.", "responses": { "200": { - "description": "Skill removed", + "description": "Updated list of skills after removal", "content": { "application/json": { "schema": { - "type": "boolean" + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "location": { + "type": "string" + }, + "content": { + "type": "string" + } + }, + "required": ["name", "description", "location", "content"] + } } } } From 9507b01c4c2a18a29a42c598086c3cda9ece7394 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Mon, 16 Mar 2026 12:24:07 +0100 Subject: [PATCH 08/10] fix: rely on optimistic removal instead of stale backend response The DELETE endpoint was returning data that somehow rendered as empty rows in the webview. Simplify the approach: - Webview optimistically removes the skill from its signal immediately - Extension fires DELETE to the backend (deletes from disk + cache) - Extension invalidates cachedSkillsMessage so next tab visit fetches fresh data - No second skillsLoaded message is sent after removal, avoiding the stale data overwrite that caused the UI corruption --- packages/kilo-vscode/src/KiloProvider.ts | 14 ++++------- .../opencode/src/server/routes/kilocode.ts | 10 ++++---- packages/sdk/js/src/v2/gen/sdk.gen.ts | 2 +- packages/sdk/js/src/v2/gen/types.gen.ts | 9 ++----- packages/sdk/openapi.json | 24 +++---------------- 5 files changed, 15 insertions(+), 44 deletions(-) diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 9db1031fd6f..b8ce386a0ad 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -1133,23 +1133,19 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper } /** - * Remove a skill via the CLI backend (deletes from disk + clears cache). - * The DELETE endpoint returns the updated skills list, which we send directly to the webview. + * Remove a skill via the CLI backend (deletes from disk + clears cache), then refresh. + * The webview optimistically removes the skill from its list before this runs. */ private async handleRemoveSkill(location: string): Promise { if (!this.client) return try { const dir = this.getWorkspaceDirectory() - const { data: skills } = await this.client.kilocode.removeSkill( - { location, directory: dir }, - { throwOnError: true }, - ) - const message = { type: "skillsLoaded", skills } - this.cachedSkillsMessage = message - this.postMessage(message) + await this.client.kilocode.removeSkill({ location, directory: dir }) } catch (error) { console.error("[Kilo New] KiloProvider: Failed to remove skill:", error) } + // Invalidate cache so next requestSkills fetches fresh data + this.cachedSkillsMessage = null } /** diff --git a/packages/opencode/src/server/routes/kilocode.ts b/packages/opencode/src/server/routes/kilocode.ts index 3e120d7d2e8..60d077fe1b5 100644 --- a/packages/opencode/src/server/routes/kilocode.ts +++ b/packages/opencode/src/server/routes/kilocode.ts @@ -13,15 +13,14 @@ export const KilocodeRoutes = lazy(() => "/skill", describeRoute({ summary: "Remove a skill", - description: - "Remove a skill by deleting its directory from disk and clearing it from cache. Returns the updated skills list.", + description: "Remove a skill by deleting its directory from disk and clearing it from cache.", operationId: "kilocode.removeSkill", responses: { 200: { - description: "Updated list of skills after removal", + description: "Skill removed", content: { "application/json": { - schema: resolver(Skill.Info.array()), + schema: resolver(z.boolean()), }, }, }, @@ -37,8 +36,7 @@ export const KilocodeRoutes = lazy(() => async (c) => { const { location } = c.req.valid("query") await Skill.remove(location) - const skills = await Skill.all() - return c.json(skills) + return c.json(true) }, ), ) diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index 7328e0d95af..b88b95c24a7 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -2934,7 +2934,7 @@ export class Kilocode extends HeyApiClient { /** * Remove a skill * - * Remove a skill by deleting its directory from disk and clearing it from cache. Returns the updated skills list. + * Remove a skill by deleting its directory from disk and clearing it from cache. */ public removeSkill( parameters: { diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 4c9a0741520..47c1300d35f 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -4424,14 +4424,9 @@ export type KilocodeRemoveSkillError = KilocodeRemoveSkillErrors[keyof KilocodeR export type KilocodeRemoveSkillResponses = { /** - * Updated list of skills after removal + * Skill removed */ - 200: Array<{ - name: string - description: string - location: string - content: string - }> + 200: boolean } export type KilocodeRemoveSkillResponse = KilocodeRemoveSkillResponses[keyof KilocodeRemoveSkillResponses] diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 686994f0d9d..5e09dbd374e 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -5400,32 +5400,14 @@ } ], "summary": "Remove a skill", - "description": "Remove a skill by deleting its directory from disk and clearing it from cache. Returns the updated skills list.", + "description": "Remove a skill by deleting its directory from disk and clearing it from cache.", "responses": { "200": { - "description": "Updated list of skills after removal", + "description": "Skill removed", "content": { "application/json": { "schema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "location": { - "type": "string" - }, - "content": { - "type": "string" - } - }, - "required": ["name", "description", "location", "content"] - } + "type": "boolean" } } } From cfe4e8c46c704b9d33ef0a007b75e05ae4bc1503 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Mon, 16 Mar 2026 12:33:26 +0100 Subject: [PATCH 09/10] fix: switch skill removal to POST with JSON body instead of DELETE with query params File paths as query parameters can fail due to URL encoding issues. Switch to POST /kilocode/skill/remove with the location in the JSON request body, which avoids encoding problems entirely. Also log the SDK error response to aid debugging. --- packages/kilo-vscode/src/KiloProvider.ts | 5 +++- .../opencode/src/server/routes/kilocode.ts | 8 +++--- packages/sdk/js/src/v2/gen/sdk.gen.ts | 27 ++++++++++--------- packages/sdk/js/src/v2/gen/types.gen.ts | 13 ++++----- packages/sdk/openapi.json | 27 ++++++++++++------- 5 files changed, 47 insertions(+), 33 deletions(-) diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index b8ce386a0ad..f65ba2dc39b 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -1140,7 +1140,10 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper if (!this.client) return try { const dir = this.getWorkspaceDirectory() - await this.client.kilocode.removeSkill({ location, directory: dir }) + const result = await this.client.kilocode.removeSkill({ location, directory: dir }) + if (result.error) { + console.error("[Kilo New] KiloProvider: removeSkill returned error:", result.error) + } } catch (error) { console.error("[Kilo New] KiloProvider: Failed to remove skill:", error) } diff --git a/packages/opencode/src/server/routes/kilocode.ts b/packages/opencode/src/server/routes/kilocode.ts index 60d077fe1b5..3edb468fbc2 100644 --- a/packages/opencode/src/server/routes/kilocode.ts +++ b/packages/opencode/src/server/routes/kilocode.ts @@ -9,8 +9,8 @@ import { lazy } from "../../util/lazy" import { errors } from "../error" export const KilocodeRoutes = lazy(() => - new Hono().delete( - "/skill", + new Hono().post( + "/skill/remove", describeRoute({ summary: "Remove a skill", description: "Remove a skill by deleting its directory from disk and clearing it from cache.", @@ -28,13 +28,13 @@ export const KilocodeRoutes = lazy(() => }, }), validator( - "query", + "json", z.object({ location: z.string(), }), ), async (c) => { - const { location } = c.req.valid("query") + const { location } = c.req.valid("json") await Skill.remove(location) return c.json(true) }, diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index b88b95c24a7..9fbc4210568 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -2937,10 +2937,10 @@ export class Kilocode extends HeyApiClient { * Remove a skill by deleting its directory from disk and clearing it from cache. */ public removeSkill( - parameters: { + parameters?: { directory?: string workspace?: string - location: string + location?: string }, options?: Options, ) { @@ -2951,20 +2951,23 @@ export class Kilocode extends HeyApiClient { args: [ { in: "query", key: "directory" }, { in: "query", key: "workspace" }, - { in: "query", key: "location" }, + { in: "body", key: "location" }, ], }, ], ) - return (options?.client ?? this.client).delete< - KilocodeRemoveSkillResponses, - KilocodeRemoveSkillErrors, - ThrowOnError - >({ - url: "/kilocode/skill", - ...options, - ...params, - }) + return (options?.client ?? this.client).post( + { + url: "/kilocode/skill/remove", + ...options, + ...params, + headers: { + "Content-Type": "application/json", + ...options?.headers, + ...params.headers, + }, + }, + ) } } diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 47c1300d35f..fea79d1ecfa 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -4403,14 +4403,15 @@ export type EnhancePromptEnhanceResponses = { export type EnhancePromptEnhanceResponse = EnhancePromptEnhanceResponses[keyof EnhancePromptEnhanceResponses] export type KilocodeRemoveSkillData = { - body?: never - path?: never - query: { - directory?: string - workspace?: string + body?: { location: string } - url: "/kilocode/skill" + path?: never + query?: { + directory?: string + workspace?: string + } + url: "/kilocode/skill/remove" } export type KilocodeRemoveSkillErrors = { diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 5e09dbd374e..5c04569c5f1 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -5372,8 +5372,8 @@ ] } }, - "/kilocode/skill": { - "delete": { + "/kilocode/skill/remove": { + "post": { "operationId": "kilocode.removeSkill", "parameters": [ { @@ -5389,14 +5389,6 @@ "schema": { "type": "string" } - }, - { - "in": "query", - "name": "location", - "schema": { - "type": "string" - }, - "required": true } ], "summary": "Remove a skill", @@ -5423,6 +5415,21 @@ } } }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "type": "string" + } + }, + "required": ["location"] + } + } + } + }, "x-codeSamples": [ { "lang": "js", From bce8c6e6dd3cbc3dc36503b7586e9afb7ddadd5f Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Mon, 16 Mar 2026 13:43:34 +0100 Subject: [PATCH 10/10] fix: validate skill location against registry before deletion and re-sync webview on failure --- packages/kilo-vscode/src/KiloProvider.ts | 7 +++++++ packages/opencode/src/skill/skill.ts | 21 ++++++++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index f65ba2dc39b..99c9e4143ef 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -1135,6 +1135,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper /** * Remove a skill via the CLI backend (deletes from disk + clears cache), then refresh. * The webview optimistically removes the skill from its list before this runs. + * On failure, re-fetches skills so the webview reverts to the authoritative state. */ private async handleRemoveSkill(location: string): Promise { if (!this.client) return @@ -1143,9 +1144,15 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper const result = await this.client.kilocode.removeSkill({ location, directory: dir }) if (result.error) { console.error("[Kilo New] KiloProvider: removeSkill returned error:", result.error) + this.cachedSkillsMessage = null + await this.fetchAndSendSkills() + return } } catch (error) { console.error("[Kilo New] KiloProvider: Failed to remove skill:", error) + this.cachedSkillsMessage = null + await this.fetchAndSendSkills() + return } // Invalidate cache so next requestSkills fetches fresh data this.cachedSkillsMessage = null diff --git a/packages/opencode/src/skill/skill.ts b/packages/opencode/src/skill/skill.ts index 7c42239de54..d4c1d97144b 100644 --- a/packages/opencode/src/skill/skill.ts +++ b/packages/opencode/src/skill/skill.ts @@ -216,18 +216,25 @@ export namespace Skill { } // kilocode_change start + export const RemoveError = NamedError.create( + "SkillRemoveError", + z.object({ + location: z.string(), + message: z.string(), + }), + ) + export async function remove(location: string) { const resolved = path.resolve(location) - const dir = path.dirname(resolved) - await rm(dir, { recursive: true, force: true }) const s = await state() const name = Object.keys(s.skills).find((k) => path.resolve(s.skills[k].location) === resolved) - if (name) { - delete s.skills[name] - s.dirs = s.dirs.filter((d) => path.resolve(d) !== dir) - } else { - log.warn("skill not found in cache during remove", { location, resolved }) + if (!name) { + throw new RemoveError({ location: resolved, message: "skill not found in registry" }) } + const dir = path.dirname(resolved) + await rm(dir, { recursive: true, force: true }) + delete s.skills[name] + s.dirs = s.dirs.filter((d) => path.resolve(d) !== dir) } // kilocode_change end }