diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 99c9e4143ef..0c8c3a1827d 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -434,6 +434,9 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper console.error("[Kilo New] handleRemoveSkill failed:", e), ) break + case "removeMode": + this.handleRemoveMode(message.name).catch((e) => console.error("[Kilo New] handleRemoveMode failed:", e)) + break case "questionReply": await this.handleQuestionReply(message.requestID, message.answers) break @@ -1158,6 +1161,33 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper this.cachedSkillsMessage = null } + /** + * Remove a custom mode via the CLI backend (deletes from disk + refreshes state). + * The webview optimistically removes the mode from its list before this runs. + * On failure, re-fetches agents so the webview reverts to the authoritative state. + */ + private async handleRemoveMode(name: string): Promise { + if (!this.client) return + try { + const dir = this.getWorkspaceDirectory() + const result = await this.client.kilocode.removeAgent({ name, directory: dir }) + if (result.error) { + console.error("[Kilo New] KiloProvider: removeAgent returned error:", result.error) + this.cachedAgentsMessage = null + await this.fetchAndSendAgents() + return + } + } catch (error) { + console.error("[Kilo New] KiloProvider: Failed to remove mode:", error) + this.cachedAgentsMessage = null + await this.fetchAndSendAgents() + return + } + // Invalidate cache so next requestAgents fetches fresh data + this.cachedAgentsMessage = null + await this.fetchAndSendAgents() + } + /** * Fetch backend config and send to webview. */ diff --git a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/settings/agent-behaviour-agents-chromium-linux.png b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/settings/agent-behaviour-agents-chromium-linux.png index 0905d16a431..31ad1644938 100644 --- a/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/settings/agent-behaviour-agents-chromium-linux.png +++ b/packages/kilo-vscode/tests/visual-regression.spec.ts-snapshots/settings/agent-behaviour-agents-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dcbffafb87bb4b68a3abcd9479cebf2844bf2290aa8c41c00f5f1a3e6129f078 -size 22968 +oid sha256:283f0a2d9531d41cb532ea77ded1c784d96c4e751462f378bbac4217d5e4f715 +size 36402 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 1d4236d87a1..ada39f81c6e 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/AgentBehaviourTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/AgentBehaviourTab.tsx @@ -10,7 +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 type { AgentConfig, SkillInfo } from "../../types/messages" +import type { AgentConfig, AgentInfo, SkillInfo } from "../../types/messages" type SubtabId = "agents" | "mcpServers" | "rules" | "workflows" | "skills" @@ -195,6 +195,36 @@ const AgentBehaviourTab: Component = () => { )) } + const removableModes = createMemo(() => session.agents().filter((a) => !a.native)) + + const confirmRemoveMode = (agent: AgentInfo) => { + dialog.show(() => ( + +
+ {language.t("settings.agentBehaviour.removeMode.confirm", { name: agent.name })} +
+ + +
+
+
+ )) + } + const renderAgentsSubtab = () => (
{/* Default agent */} @@ -345,6 +375,48 @@ const AgentBehaviourTab: Component = () => { + + {/* Available modes (non-native only, with remove button) */} + 0}> +

+ {language.t("settings.agentBehaviour.availableModes")} +

+ + + {(agent, index) => ( +
+
+
+ {agent.name} +
+ +
+ {agent.description} +
+
+
+ { + e.stopPropagation() + confirmRemoveMode(agent) + }} + /> +
+ )} +
+
+
) diff --git a/packages/kilo-vscode/webview-ui/src/context/session.tsx b/packages/kilo-vscode/webview-ui/src/context/session.tsx index 9bec4955fd9..69934ccf753 100644 --- a/packages/kilo-vscode/webview-ui/src/context/session.tsx +++ b/packages/kilo-vscode/webview-ui/src/context/session.tsx @@ -122,6 +122,7 @@ interface SessionContextValue { // Agent/mode selection (per-session) agents: Accessor + removeMode: (name: string) => void selectedAgent: Accessor selectAgent: (name: string) => void getSessionAgent: (sessionID: string) => string @@ -213,6 +214,22 @@ export const SessionProvider: ParentComponent = (props) => { // Skills loaded from the CLI backend const [skills, setSkills] = createSignal([]) + const removeMode = (name: string) => { + setAgents((prev) => prev.filter((a) => a.name !== name)) + + // Clear stale selections so selectedAgentName() falls back to the default + if (pendingAgentSelection() === name) { + setPendingAgentSelection(null) + } + for (const sid of Object.keys(store.agentSelections)) { + if (store.agentSelections[sid] === name) { + setStore("agentSelections", sid, undefined as unknown as string) + } + } + + vscode.postMessage({ type: "removeMode", name }) + } + // Pending agent selection for before a session exists const [pendingAgentSelection, setPendingAgentSelection] = createSignal(null) @@ -1314,6 +1331,7 @@ export const SessionProvider: ParentComponent = (props) => { skills, refreshSkills, removeSkill, + removeMode, selectedAgent: selectedAgentName, selectAgent, getSessionAgent: (sessionID: string) => store.agentSelections[sessionID] ?? defaultAgent(), diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts index 3cb7863ceff..e4518b3a0bd 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ar.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ar.ts @@ -949,6 +949,12 @@ export const dict = { "settings.agentBehaviour.discoveredSkills": "المهارات المكتشفة", "settings.agentBehaviour.noSkillsFound": "لم يتم العثور على مهارات. أضف مسارات مجلدات أو عناوين URL أدناه لإتاحة المهارات.", + "settings.agentBehaviour.availableModes": "الأوضاع المخصصة المتاحة", + "settings.agentBehaviour.noModesFound": "لم يتم العثور على أوضاع.", + "settings.agentBehaviour.removeMode.title": "إزالة الوضع", + "settings.agentBehaviour.removeMode.confirm": + 'هل تريد إزالة الوضع "{{name}}"؟ سيؤدي هذا إلى تعطيل الوضع عن طريق تحديث الإعدادات.', + "settings.agentBehaviour.removeMode.button": "إزالة", "settings.agentBehaviour.skillPaths": "مسارات مجلدات المهارات", "settings.agentBehaviour.skillUrls": "عناوين URL للمهارات", "settings.agentBehaviour.removeSkill.title": "إزالة المهارة", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/br.ts b/packages/kilo-vscode/webview-ui/src/i18n/br.ts index 36d2f565aa7..25245fd392f 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/br.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/br.ts @@ -961,6 +961,12 @@ export const dict = { "settings.agentBehaviour.discoveredSkills": "Habilidades descobertas", "settings.agentBehaviour.noSkillsFound": "Nenhuma habilidade encontrada. Adicione caminhos de pastas ou URLs abaixo para disponibilizar habilidades.", + "settings.agentBehaviour.availableModes": "Modos personalizados disponíveis", + "settings.agentBehaviour.noModesFound": "Nenhum modo encontrado.", + "settings.agentBehaviour.removeMode.title": "Remover modo", + "settings.agentBehaviour.removeMode.confirm": + 'Remover o modo "{{name}}"? Isso desativará o modo atualizando sua configuração.', + "settings.agentBehaviour.removeMode.button": "Remover", "settings.agentBehaviour.skillPaths": "Caminhos de pastas de habilidades", "settings.agentBehaviour.skillUrls": "URLs de habilidades", "settings.agentBehaviour.removeSkill.title": "Remover habilidade", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts index d7b78449e6b..f2c3badcc87 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/bs.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/bs.ts @@ -961,6 +961,12 @@ export const dict = { "settings.agentBehaviour.discoveredSkills": "Otkrivene vještine", "settings.agentBehaviour.noSkillsFound": "Nisu pronađene vještine. Dodajte putanje mapa ili URL-ove ispod kako biste učinili vještine dostupnim.", + "settings.agentBehaviour.availableModes": "Dostupni prilagođeni modovi", + "settings.agentBehaviour.noModesFound": "Nisu pronađeni modovi.", + "settings.agentBehaviour.removeMode.title": "Ukloni mod", + "settings.agentBehaviour.removeMode.confirm": + 'Ukloniti mod "{{name}}"? Ovo će onemogućiti mod ažuriranjem vaše konfiguracije.', + "settings.agentBehaviour.removeMode.button": "Ukloni", "settings.agentBehaviour.skillPaths": "Putanje mapa vještina", "settings.agentBehaviour.skillUrls": "URL-ovi vještina", "settings.agentBehaviour.removeSkill.title": "Ukloni vještinu", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/da.ts b/packages/kilo-vscode/webview-ui/src/i18n/da.ts index e8039761794..ccd3b763f26 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/da.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/da.ts @@ -957,6 +957,12 @@ export const dict = { "settings.agentBehaviour.discoveredSkills": "Opdagede skills", "settings.agentBehaviour.noSkillsFound": "Ingen skills fundet. Tilføj skill-mappestier eller URL'er nedenfor for at gøre skills tilgængelige.", + "settings.agentBehaviour.availableModes": "Tilgængelige brugerdefinerede tilstande", + "settings.agentBehaviour.noModesFound": "Ingen tilstande fundet.", + "settings.agentBehaviour.removeMode.title": "Fjern tilstand", + "settings.agentBehaviour.removeMode.confirm": + 'Vil du fjerne tilstanden "{{name}}"? Dette vil deaktivere tilstanden ved at opdatere din konfiguration.', + "settings.agentBehaviour.removeMode.button": "Fjern", "settings.agentBehaviour.skillPaths": "Skill-mappestier", "settings.agentBehaviour.skillUrls": "Skill-URL'er", "settings.agentBehaviour.removeSkill.title": "Fjern færdighed", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/de.ts b/packages/kilo-vscode/webview-ui/src/i18n/de.ts index 3b877fe0057..4168c4757b2 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/de.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/de.ts @@ -972,6 +972,12 @@ export const dict = { "settings.agentBehaviour.discoveredSkills": "Erkannte Skills", "settings.agentBehaviour.noSkillsFound": "Keine Skills gefunden. Fügen Sie unten Skill-Ordnerpfade oder URLs hinzu, um Skills verfügbar zu machen.", + "settings.agentBehaviour.availableModes": "Verfügbare benutzerdefinierte Modi", + "settings.agentBehaviour.noModesFound": "Keine Modi gefunden.", + "settings.agentBehaviour.removeMode.title": "Modus entfernen", + "settings.agentBehaviour.removeMode.confirm": + 'Modus "{{name}}" entfernen? Dadurch wird der Modus durch Aktualisierung Ihrer Konfiguration deaktiviert.', + "settings.agentBehaviour.removeMode.button": "Entfernen", "settings.agentBehaviour.skillPaths": "Skill-Ordnerpfade", "settings.agentBehaviour.skillUrls": "Skill-URLs", "settings.agentBehaviour.removeSkill.title": "Skill entfernen", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/en.ts b/packages/kilo-vscode/webview-ui/src/i18n/en.ts index 1582971d356..b954d4488d4 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/en.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/en.ts @@ -969,6 +969,12 @@ export const dict = { "settings.agentBehaviour.discoveredSkills": "Discovered Skills", "settings.agentBehaviour.noSkillsFound": "No skills discovered. Add skill folder paths or URLs below to make skills available.", + "settings.agentBehaviour.availableModes": "Available Custom Modes", + "settings.agentBehaviour.noModesFound": "No modes found.", + "settings.agentBehaviour.removeMode.title": "Remove mode", + "settings.agentBehaviour.removeMode.confirm": + 'Remove mode "{{name}}"? This will disable the mode by updating your config.', + "settings.agentBehaviour.removeMode.button": "Remove", "settings.agentBehaviour.skillPaths": "Skill Folder Paths", "settings.agentBehaviour.skillUrls": "Skill URLs", "settings.agentBehaviour.removeSkill.title": "Remove skill", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/es.ts b/packages/kilo-vscode/webview-ui/src/i18n/es.ts index 3787b776cf1..d23bb917e99 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/es.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/es.ts @@ -964,6 +964,12 @@ export const dict = { "settings.agentBehaviour.discoveredSkills": "Habilidades descubiertas", "settings.agentBehaviour.noSkillsFound": "No se encontraron habilidades. Agregue rutas de carpetas o URLs abajo para hacer disponibles las habilidades.", + "settings.agentBehaviour.availableModes": "Modos personalizados disponibles", + "settings.agentBehaviour.noModesFound": "No se encontraron modos.", + "settings.agentBehaviour.removeMode.title": "Eliminar modo", + "settings.agentBehaviour.removeMode.confirm": + '¿Eliminar el modo "{{name}}"? Esto desactivará el modo actualizando su configuración.', + "settings.agentBehaviour.removeMode.button": "Eliminar", "settings.agentBehaviour.skillPaths": "Rutas de carpetas de habilidades", "settings.agentBehaviour.skillUrls": "URLs de habilidades", "settings.agentBehaviour.removeSkill.title": "Eliminar habilidad", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts index 3f10e46bd65..40aa5c629b0 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/fr.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/fr.ts @@ -974,6 +974,12 @@ export const dict = { "settings.agentBehaviour.discoveredSkills": "Compétences découvertes", "settings.agentBehaviour.noSkillsFound": "Aucune compétence découverte. Ajoutez des chemins de dossiers ou des URLs ci-dessous pour rendre les compétences disponibles.", + "settings.agentBehaviour.availableModes": "Modes personnalisés disponibles", + "settings.agentBehaviour.noModesFound": "Aucun mode trouvé.", + "settings.agentBehaviour.removeMode.title": "Supprimer le mode", + "settings.agentBehaviour.removeMode.confirm": + 'Supprimer le mode "{{name}}" ? Cela désactivera le mode en mettant à jour votre configuration.', + "settings.agentBehaviour.removeMode.button": "Supprimer", "settings.agentBehaviour.skillPaths": "Chemins des dossiers de compétences", "settings.agentBehaviour.skillUrls": "URLs de compétences", "settings.agentBehaviour.removeSkill.title": "Supprimer la compétence", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts index 0319039a1eb..e749cf94fc2 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ja.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ja.ts @@ -957,6 +957,12 @@ export const dict = { "settings.agentBehaviour.discoveredSkills": "検出されたスキル", "settings.agentBehaviour.noSkillsFound": "スキルが見つかりません。スキルを利用可能にするには、以下にスキルフォルダパスまたはURLを追加してください。", + "settings.agentBehaviour.availableModes": "利用可能なカスタムモード", + "settings.agentBehaviour.noModesFound": "モードが見つかりません。", + "settings.agentBehaviour.removeMode.title": "モードを削除", + "settings.agentBehaviour.removeMode.confirm": + 'モード "{{name}}" を削除しますか?設定を更新してモードを無効にします。', + "settings.agentBehaviour.removeMode.button": "削除", "settings.agentBehaviour.skillPaths": "スキルフォルダパス", "settings.agentBehaviour.skillUrls": "スキルURL", "settings.agentBehaviour.removeSkill.title": "スキルを削除", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts index 9c29adae7e0..5e050a64613 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ko.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ko.ts @@ -954,6 +954,12 @@ export const dict = { "settings.agentBehaviour.discoveredSkills": "검색된 스킬", "settings.agentBehaviour.noSkillsFound": "스킬을 찾을 수 없습니다. 스킬을 사용하려면 아래에 스킬 폴더 경로 또는 URL을 추가하세요.", + "settings.agentBehaviour.availableModes": "사용 가능한 사용자 정의 모드", + "settings.agentBehaviour.noModesFound": "모드를 찾을 수 없습니다.", + "settings.agentBehaviour.removeMode.title": "모드 제거", + "settings.agentBehaviour.removeMode.confirm": + '모드 "{{name}}"을(를) 제거하시겠습니까? 구성을 업데이트하여 모드를 비활성화합니다.', + "settings.agentBehaviour.removeMode.button": "제거", "settings.agentBehaviour.skillPaths": "스킬 폴더 경로", "settings.agentBehaviour.skillUrls": "스킬 URL", "settings.agentBehaviour.removeSkill.title": "스킬 제거", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/no.ts b/packages/kilo-vscode/webview-ui/src/i18n/no.ts index bc81bcde276..dbaabf4c5cc 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/no.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/no.ts @@ -959,6 +959,12 @@ export const dict = { "settings.agentBehaviour.discoveredSkills": "Oppdagede ferdigheter", "settings.agentBehaviour.noSkillsFound": "Ingen ferdigheter funnet. Legg til ferdighetsmappestier eller URLer nedenfor for å gjøre ferdigheter tilgjengelige.", + "settings.agentBehaviour.availableModes": "Tilgjengelige egendefinerte moduser", + "settings.agentBehaviour.noModesFound": "Ingen moduser funnet.", + "settings.agentBehaviour.removeMode.title": "Fjern modus", + "settings.agentBehaviour.removeMode.confirm": + 'Vil du fjerne modusen "{{name}}"? Dette vil deaktivere modusen ved å oppdatere konfigurasjonen din.', + "settings.agentBehaviour.removeMode.button": "Fjern", "settings.agentBehaviour.skillPaths": "Ferdighetsmappe-stier", "settings.agentBehaviour.skillUrls": "Ferdighets-URLer", "settings.agentBehaviour.removeSkill.title": "Fjern ferdighet", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts index 098b89b72c3..ea2e4192677 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/pl.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/pl.ts @@ -959,6 +959,12 @@ export const dict = { "settings.agentBehaviour.discoveredSkills": "Wykryte umiejętności", "settings.agentBehaviour.noSkillsFound": "Nie znaleziono umiejętności. Dodaj ścieżki folderów lub adresy URL poniżej, aby udostępnić umiejętności.", + "settings.agentBehaviour.availableModes": "Dostępne tryby niestandardowe", + "settings.agentBehaviour.noModesFound": "Nie znaleziono trybów.", + "settings.agentBehaviour.removeMode.title": "Usuń tryb", + "settings.agentBehaviour.removeMode.confirm": + 'Usunąć tryb "{{name}}"? Spowoduje to wyłączenie trybu poprzez aktualizację konfiguracji.', + "settings.agentBehaviour.removeMode.button": "Usuń", "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ść", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts index af44e040a58..224ece5b1f9 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/ru.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/ru.ts @@ -962,6 +962,12 @@ export const dict = { "settings.agentBehaviour.discoveredSkills": "Обнаруженные навыки", "settings.agentBehaviour.noSkillsFound": "Навыки не обнаружены. Добавьте пути к папкам навыков или URL-адреса ниже, чтобы сделать навыки доступными.", + "settings.agentBehaviour.availableModes": "Доступные пользовательские режимы", + "settings.agentBehaviour.noModesFound": "Режимы не найдены.", + "settings.agentBehaviour.removeMode.title": "Удалить режим", + "settings.agentBehaviour.removeMode.confirm": + 'Удалить режим "{{name}}"? Это отключит режим, обновив вашу конфигурацию.', + "settings.agentBehaviour.removeMode.button": "Удалить", "settings.agentBehaviour.skillPaths": "Пути папок навыков", "settings.agentBehaviour.skillUrls": "URL навыков", "settings.agentBehaviour.removeSkill.title": "Удалить навык", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/th.ts b/packages/kilo-vscode/webview-ui/src/i18n/th.ts index 647b93b1ee4..c8e25f4aef0 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/th.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/th.ts @@ -949,6 +949,12 @@ export const dict = { "settings.agentBehaviour.maxSteps.description": "จำนวนรอบเอเจนต์สูงสุด", "settings.agentBehaviour.discoveredSkills": "ทักษะที่ค้นพบ", "settings.agentBehaviour.noSkillsFound": "ไม่พบทักษะ เพิ่มเส้นทางโฟลเดอร์หรือ URL ด้านล่างเพื่อทำให้ทักษะพร้อมใช้งาน", + "settings.agentBehaviour.availableModes": "โหมดกำหนดเองที่ใช้ได้", + "settings.agentBehaviour.noModesFound": "ไม่พบโหมด", + "settings.agentBehaviour.removeMode.title": "ลบโหมด", + "settings.agentBehaviour.removeMode.confirm": + 'ต้องการลบโหมด "{{name}}" หรือไม่? การดำเนินการนี้จะปิดใช้งานโหมดโดยอัปเดตการกำหนดค่าของคุณ', + "settings.agentBehaviour.removeMode.button": "ลบ", "settings.agentBehaviour.skillPaths": "เส้นทางโฟลเดอร์ทักษะ", "settings.agentBehaviour.skillUrls": "URL ทักษะ", "settings.agentBehaviour.removeSkill.title": "ลบทักษะ", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts index 694b4333c9a..233bc28223e 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zh.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zh.ts @@ -943,6 +943,11 @@ export const dict = { "settings.agentBehaviour.maxSteps.description": "最大智能体迭代次数", "settings.agentBehaviour.discoveredSkills": "已发现的技能", "settings.agentBehaviour.noSkillsFound": "未发现任何技能。请在下方添加技能文件夹路径或 URL 以使技能可用。", + "settings.agentBehaviour.availableModes": "可用自定义模式", + "settings.agentBehaviour.noModesFound": "未找到模式。", + "settings.agentBehaviour.removeMode.title": "移除模式", + "settings.agentBehaviour.removeMode.confirm": '移除模式 "{{name}}" 吗?这将通过更新配置来禁用该模式。', + "settings.agentBehaviour.removeMode.button": "移除", "settings.agentBehaviour.skillPaths": "技能文件夹路径", "settings.agentBehaviour.skillUrls": "技能 URL", "settings.agentBehaviour.removeSkill.title": "移除技能", diff --git a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts index 4e3a720f433..95552807499 100644 --- a/packages/kilo-vscode/webview-ui/src/i18n/zht.ts +++ b/packages/kilo-vscode/webview-ui/src/i18n/zht.ts @@ -944,6 +944,11 @@ export const dict = { "settings.agentBehaviour.maxSteps.description": "最大 Agent 迭代次數", "settings.agentBehaviour.discoveredSkills": "已發現的 Skill", "settings.agentBehaviour.noSkillsFound": "未發現任何 Skill。請在下方新增 Skill 資料夾路徑或 URL 以使 Skill 可用。", + "settings.agentBehaviour.availableModes": "可用自訂模式", + "settings.agentBehaviour.noModesFound": "未找到模式。", + "settings.agentBehaviour.removeMode.title": "移除模式", + "settings.agentBehaviour.removeMode.confirm": '要移除模式 "{{name}}" 嗎?這將透過更新設定來停用該模式。', + "settings.agentBehaviour.removeMode.button": "移除", "settings.agentBehaviour.skillPaths": "Skill 資料夾路徑", "settings.agentBehaviour.skillUrls": "Skill URL", "settings.agentBehaviour.removeSkill.title": "移除技能", diff --git a/packages/kilo-vscode/webview-ui/src/types/messages.ts b/packages/kilo-vscode/webview-ui/src/types/messages.ts index e0da4d52ec2..191043f81ea 100644 --- a/packages/kilo-vscode/webview-ui/src/types/messages.ts +++ b/packages/kilo-vscode/webview-ui/src/types/messages.ts @@ -1253,6 +1253,11 @@ export interface RemoveSkillMessage { location: string } +export interface RemoveModeMessage { + type: "removeMode" + name: string +} + export interface SetLanguageRequest { type: "setLanguage" locale: string @@ -1634,6 +1639,7 @@ export type WebviewMessage = | RequestAgentsMessage | RequestSkillsMessage | RemoveSkillMessage + | RemoveModeMessage | SetLanguageRequest | QuestionReplyRequest | QuestionRejectRequest diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index 509568b3562..e8137eff4a5 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -17,6 +17,8 @@ import PROMPT_ORCHESTRATOR from "./prompt/orchestrator.txt" import PROMPT_SUMMARY from "./prompt/summary.txt" import PROMPT_TITLE from "./prompt/title.txt" import { PermissionNext } from "@/permission/next" +import { NamedError } from "@opencode-ai/util/error" // kilocode_change +import { Glob } from "../util/glob" // kilocode_change import { mergeDeep, pipe, sortBy, values } from "remeda" import { Global } from "@/global" import path from "path" @@ -438,4 +440,78 @@ export namespace Agent { const result = await generateObject(params) return result.object } + + // kilocode_change start + export const RemoveError = NamedError.create( + "AgentRemoveError", + z.object({ + name: z.string(), + message: z.string(), + }), + ) + + /** + * Remove a custom agent by deleting its markdown source file and/or + * removing it from legacy .kilocodemodes YAML files. + * Scans all config directories for agent/mode .md files matching the name, + * then also checks the .kilocodemodes files the ModesMigrator reads. + */ + export async function remove(name: string) { + const agents = await state() + const agent = agents[name] + if (!agent) throw new RemoveError({ name, message: "agent not found" }) + if (agent.native) throw new RemoveError({ name, message: "cannot remove native agent" }) + + const { unlink, readFile, writeFile } = await import("fs/promises") + let found = false + + // 1. Delete .md files from config directories + const dirs = await Config.directories() + const patterns = ["{agent,agents}/**/" + name + ".md", "{mode,modes}/" + name + ".md"] + for (const dir of dirs) { + for (const pattern of patterns) { + const matches = await Glob.scan(pattern, { cwd: dir, absolute: true, dot: true }) + for (const file of matches) { + if (await Bun.file(file).exists()) { + await unlink(file) + found = true + } + } + } + } + + // 2. Remove from legacy .kilocodemodes YAML files (read by ModesMigrator) + const { ModesMigrator } = await import("@/kilocode/modes-migrator") + const { KilocodePaths } = await import("@/kilocode/paths") + const os = await import("os") + const matter = (await import("gray-matter")).default + const home = os.default.homedir() + const modesFiles = [ + path.join(KilocodePaths.vscodeGlobalStorage(), "settings", "custom_modes.yaml"), + path.join(home, ".kilocode", "cli", "global", "settings", "custom_modes.yaml"), + path.join(home, ".kilocodemodes"), + path.join(Instance.directory, ".kilocodemodes"), + ] + + for (const file of modesFiles) { + const modes = await ModesMigrator.readModesFile(file) + if (!modes.length) continue + + const filtered = modes.filter((m) => m.slug !== name) + if (filtered.length === modes.length) continue + + // Rewrite the file without the removed mode + const yaml = matter + .stringify("", { customModes: filtered }) + .replace(/^---\n/, "") + .replace(/\n---\n?$/, "") + await writeFile(file, yaml) + found = true + } + + if (!found) throw new RemoveError({ name, message: "no agent file found on disk" }) + + await Instance.dispose() + } + // kilocode_change end } diff --git a/packages/opencode/src/server/routes/kilocode.ts b/packages/opencode/src/server/routes/kilocode.ts index 3edb468fbc2..aa7d0503994 100644 --- a/packages/opencode/src/server/routes/kilocode.ts +++ b/packages/opencode/src/server/routes/kilocode.ts @@ -5,38 +5,70 @@ import { Hono } from "hono" import { describeRoute, validator, resolver } from "hono-openapi" import z from "zod" import { Skill } from "../../skill/skill" +import { Agent } from "../../agent/agent" 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()), + 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), }, - ...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) }, - }), - validator( - "json", - z.object({ - location: z.string(), + ) + .post( + "/agent/remove", + describeRoute({ + summary: "Remove a custom agent", + description: "Remove a custom (non-native) agent by deleting its markdown file from disk and refreshing state.", + operationId: "kilocode.removeAgent", + responses: { + 200: { + description: "Agent removed", + content: { + "application/json": { + schema: resolver(z.boolean()), + }, + }, + }, + ...errors(400), + }, }), + validator( + "json", + z.object({ + name: z.string(), + }), + ), + async (c) => { + const { name } = c.req.valid("json") + await Agent.remove(name) + return c.json(true) + }, ), - async (c) => { - 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 9fbc4210568..adb6602bade 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, + KilocodeRemoveAgentErrors, + KilocodeRemoveAgentResponses, KilocodeRemoveSkillErrors, KilocodeRemoveSkillResponses, KiloFimErrors, @@ -2969,6 +2971,45 @@ export class Kilocode extends HeyApiClient { }, ) } + + /** + * Remove a custom agent + * + * Remove a custom (non-native) agent by deleting its markdown file from disk and refreshing state. + */ + public removeAgent( + parameters?: { + directory?: string + workspace?: string + name?: string + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "query", key: "directory" }, + { in: "query", key: "workspace" }, + { in: "body", key: "name" }, + ], + }, + ], + ) + return (options?.client ?? this.client).post( + { + url: "/kilocode/agent/remove", + ...options, + ...params, + headers: { + "Content-Type": "application/json", + ...options?.headers, + ...params.headers, + }, + }, + ) + } } export class Organization extends HeyApiClient { diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index fea79d1ecfa..06f61fb976f 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -4432,6 +4432,36 @@ export type KilocodeRemoveSkillResponses = { export type KilocodeRemoveSkillResponse = KilocodeRemoveSkillResponses[keyof KilocodeRemoveSkillResponses] +export type KilocodeRemoveAgentData = { + body?: { + name: string + } + path?: never + query?: { + directory?: string + workspace?: string + } + url: "/kilocode/agent/remove" +} + +export type KilocodeRemoveAgentErrors = { + /** + * Bad request + */ + 400: BadRequestError +} + +export type KilocodeRemoveAgentError = KilocodeRemoveAgentErrors[keyof KilocodeRemoveAgentErrors] + +export type KilocodeRemoveAgentResponses = { + /** + * Agent removed + */ + 200: boolean +} + +export type KilocodeRemoveAgentResponse = KilocodeRemoveAgentResponses[keyof KilocodeRemoveAgentResponses] + export type KiloProfileData = { body?: never path?: never diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 5c04569c5f1..1a53f6fef8f 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -5438,6 +5438,72 @@ ] } }, + "/kilocode/agent/remove": { + "post": { + "operationId": "kilocode.removeAgent", + "parameters": [ + { + "in": "query", + "name": "directory", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "workspace", + "schema": { + "type": "string" + } + } + ], + "summary": "Remove a custom agent", + "description": "Remove a custom (non-native) agent by deleting its markdown file from disk and refreshing state.", + "responses": { + "200": { + "description": "Agent 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": { + "name": { + "type": "string" + } + }, + "required": ["name"] + } + } + } + }, + "x-codeSamples": [ + { + "lang": "js", + "source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.kilocode.removeAgent({\n ...\n})" + } + ] + } + }, "/kilo/profile": { "get": { "operationId": "kilo.profile",