Merge pull request #7070 from Kilo-Org/mark/add-skill-removal

feat: add ability to remove discovered skills
This commit is contained in:
Mark IJbema
2026-03-16 14:43:38 +01:00
committed by GitHub
26 changed files with 391 additions and 31 deletions
+31
View File
@@ -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,32 @@ 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<void> {
if (!this.client) return
try {
const dir = this.getWorkspaceDirectory()
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
}
/**
* Fetch backend config and send to webview.
*/
@@ -1,15 +1,16 @@
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"
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"
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"
@@ -52,30 +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<SubtabId>("agents")
const [selectedAgent, setSelectedAgent] = createSignal<string>("")
const [newSkillPath, setNewSkillPath] = createSignal("")
const [newSkillUrl, setNewSkillUrl] = createSignal("")
const [newInstruction, setNewInstruction] = createSignal("")
const [discoveredSkills, setDiscoveredSkills] = createSignal<SkillInfo[]>([])
// 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
@@ -179,6 +170,31 @@ const AgentBehaviourTab: Component = () => {
updateConfig({ skills: { ...config().skills, urls: current } })
}
const confirmRemoveSkill = (skill: SkillInfo) => {
dialog.show(() => (
<Dialog title={language.t("settings.agentBehaviour.removeSkill.title")} fit>
<div class="dialog-confirm-body">
<span>{language.t("settings.agentBehaviour.removeSkill.confirm", { name: skill.name })}</span>
<div class="dialog-confirm-actions">
<Button variant="ghost" size="large" onClick={() => dialog.close()}>
{language.t("common.cancel")}
</Button>
<Button
variant="primary"
size="large"
onClick={() => {
session.removeSkill(skill.location)
dialog.close()
}}
>
{language.t("settings.agentBehaviour.removeSkill.button")}
</Button>
</div>
</div>
</Dialog>
))
}
const renderAgentsSubtab = () => (
<div>
{/* Default agent */}
@@ -398,7 +414,7 @@ const AgentBehaviourTab: Component = () => {
{language.t("settings.agentBehaviour.discoveredSkills")}
</h4>
<Show
when={discoveredSkills().length > 0}
when={session.skills().length > 0}
fallback={
<Card style={{ "margin-bottom": "16px" }}>
<div data-slot="settings-row-label-subtitle">{language.t("settings.agentBehaviour.noSkillsFound")}</div>
@@ -406,28 +422,33 @@ const AgentBehaviourTab: Component = () => {
}
>
<Card style={{ "margin-bottom": "16px" }}>
<For each={discoveredSkills()}>
<For each={session.skills()}>
{(skill, index) => (
<div
style={{
display: "flex",
"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",
}}
>
<div data-slot="settings-row-label-title" style={{ "margin-bottom": "0" }}>
{skill.name}
</div>
<div
data-slot="settings-row-label-subtitle"
style={{
"margin-top": "4px",
"font-family": "var(--vscode-editor-font-family, monospace)",
}}
>
<div>{skill.description}</div>
<div>{skill.location}</div>
<div style={{ flex: 1, "min-width": 0 }}>
<div data-slot="settings-row-label-title" style={{ "margin-bottom": "0" }}>
{skill.name}
</div>
<div
data-slot="settings-row-label-subtitle"
style={{
"margin-top": "4px",
"font-family": "var(--vscode-editor-font-family, monospace)",
}}
>
<div>{skill.description}</div>
<div>{skill.location}</div>
</div>
</div>
<IconButton size="small" variant="ghost" icon="close" onClick={() => confirmRemoveSkill(skill)} />
</div>
)}
</For>
@@ -36,6 +36,7 @@ import type {
ModelSelection,
ContextUsage,
AgentInfo,
SkillInfo,
ExtensionMessage,
FileAttachment,
SendMessageFailedMessage,
@@ -114,6 +115,11 @@ interface SessionContextValue {
totalCost: Accessor<number>
contextUsage: Accessor<ContextUsage | undefined>
// Skills loaded from the CLI backend
skills: Accessor<SkillInfo[]>
refreshSkills: () => void
removeSkill: (location: string) => void
// Agent/mode selection (per-session)
agents: Accessor<AgentInfo[]>
selectedAgent: Accessor<string>
@@ -204,6 +210,9 @@ export const SessionProvider: ParentComponent = (props) => {
const [agents, setAgents] = createSignal<AgentInfo[]>([])
const [defaultAgent, setDefaultAgent] = createSignal("code")
// Skills loaded from the CLI backend
const [skills, setSkills] = createSignal<SkillInfo[]>([])
// Pending agent selection for before a session exists
const [pendingAgentSelection, setPendingAgentSelection] = createSignal<string | null>(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(),
@@ -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.",
@@ -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",
@@ -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":
@@ -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":
@@ -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":
@@ -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",
@@ -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",
@@ -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":
@@ -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":
@@ -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":
@@ -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":
@@ -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",
@@ -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":
@@ -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":
@@ -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 服务器。",
@@ -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 伺服器。",
@@ -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
@@ -0,0 +1,42 @@
// 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"
import { Skill } from "../../skill/skill"
import { lazy } from "../../util/lazy"
import { errors } from "../error"
export const KilocodeRoutes = lazy(() =>
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.",
operationId: "kilocode.removeSkill",
responses: {
200: {
description: "Skill removed",
content: {
"application/json": {
schema: resolver(z.boolean()),
},
},
},
...errors(400),
},
}),
validator(
"json",
z.object({
location: z.string(),
}),
),
async (c) => {
const { location } = c.req.valid("json")
await Skill.remove(location)
return c.json(true)
},
),
)
+2
View File
@@ -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 { KilocodeRoutes } from "./routes/kilocode" // 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("/kilocode", KilocodeRoutes()) // kilocode_change
// kilocode_change start - Kilo Gateway routes
.route(
"/kilo",
+24
View File
@@ -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,27 @@ export namespace Skill {
export async function dirs() {
return state().then((x) => x.dirs)
}
// 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 s = await state()
const name = Object.keys(s.skills).find((k) => path.resolve(s.skills[k].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
}
+48
View File
@@ -57,6 +57,8 @@ import type {
KiloCloudSessionImportResponses,
KiloCloudSessionsErrors,
KiloCloudSessionsResponses,
KilocodeRemoveSkillErrors,
KilocodeRemoveSkillResponses,
KiloFimErrors,
KiloFimResponses,
KiloNotificationsErrors,
@@ -2928,6 +2930,47 @@ export class EnhancePrompt 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 removeSkill<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
workspace?: string
location?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "workspace" },
{ in: "body", key: "location" },
],
},
],
)
return (options?.client ?? this.client).post<KilocodeRemoveSkillResponses, KilocodeRemoveSkillErrors, ThrowOnError>(
{
url: "/kilocode/skill/remove",
...options,
...params,
headers: {
"Content-Type": "application/json",
...options?.headers,
...params.headers,
},
},
)
}
}
export class Organization extends HeyApiClient {
/**
* Update Kilo Gateway organization
@@ -4545,6 +4588,11 @@ export class KiloClient extends HeyApiClient {
return (this._enhancePrompt ??= new EnhancePrompt({ client: this.client }))
}
private _kilocode?: Kilocode
get kilocode(): Kilocode {
return (this._kilocode ??= new Kilocode({ client: this.client }))
}
private _kilo?: Kilo
get kilo(): Kilo {
return (this._kilo ??= new Kilo({ client: this.client }))
+30
View File
@@ -4402,6 +4402,36 @@ export type EnhancePromptEnhanceResponses = {
export type EnhancePromptEnhanceResponse = EnhancePromptEnhanceResponses[keyof EnhancePromptEnhanceResponses]
export type KilocodeRemoveSkillData = {
body?: {
location: string
}
path?: never
query?: {
directory?: string
workspace?: string
}
url: "/kilocode/skill/remove"
}
export type KilocodeRemoveSkillErrors = {
/**
* Bad request
*/
400: BadRequestError
}
export type KilocodeRemoveSkillError = KilocodeRemoveSkillErrors[keyof KilocodeRemoveSkillErrors]
export type KilocodeRemoveSkillResponses = {
/**
* Skill removed
*/
200: boolean
}
export type KilocodeRemoveSkillResponse = KilocodeRemoveSkillResponses[keyof KilocodeRemoveSkillResponses]
export type KiloProfileData = {
body?: never
path?: never
+66
View File
@@ -5372,6 +5372,72 @@
]
}
},
"/kilocode/skill/remove": {
"post": {
"operationId": "kilocode.removeSkill",
"parameters": [
{
"in": "query",
"name": "directory",
"schema": {
"type": "string"
}
},
{
"in": "query",
"name": "workspace",
"schema": {
"type": "string"
}
}
],
"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"
}
}
}
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"location": {
"type": "string"
}
},
"required": ["location"]
}
}
}
},
"x-codeSamples": [
{
"lang": "js",
"source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.kilocode.removeSkill({\n ...\n})"
}
]
}
},
"/kilo/profile": {
"get": {
"operationId": "kilo.profile",