-
Notifications
You must be signed in to change notification settings - Fork 3.1k
feat: add ability to remove custom modes from Agent Behaviour settings #7100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7494ce6
bcf609a
0b702ea
b90a223
b8e196d
a80d495
a406741
1e5f9a6
f7d8983
a5618ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WARNING: Delete button is shown for modes this API cannot remove This filter treats every non-native agent as removable, but
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm willing to accept this for now, we need to iterate on this screen anyways. it's good enough for supporting the marketplace |
||
|
|
||
| const confirmRemoveMode = (agent: AgentInfo) => { | ||
| dialog.show(() => ( | ||
| <Dialog title={language.t("settings.agentBehaviour.removeMode.title")} fit> | ||
| <div class="dialog-confirm-body"> | ||
| <span>{language.t("settings.agentBehaviour.removeMode.confirm", { name: agent.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={() => { | ||
| dialog.close() | ||
| // Delay optimistic removal until after dialog close animation (100ms) | ||
| // to prevent the reactive list re-render from firing click handlers | ||
| // on shifted list items while the dialog overlay is still present. | ||
| setTimeout(() => session.removeMode(agent.name), 150) | ||
| }} | ||
| > | ||
| {language.t("settings.agentBehaviour.removeMode.button")} | ||
| </Button> | ||
| </div> | ||
| </div> | ||
| </Dialog> | ||
| )) | ||
| } | ||
|
|
||
| const renderAgentsSubtab = () => ( | ||
| <div> | ||
| {/* Default agent */} | ||
|
|
@@ -345,6 +375,48 @@ const AgentBehaviourTab: Component = () => { | |
| </SettingsRow> | ||
| </Card> | ||
| </Show> | ||
|
|
||
| {/* Available modes (non-native only, with remove button) */} | ||
| <Show when={removableModes().length > 0}> | ||
| <h4 style={{ "margin-top": "16px", "margin-bottom": "8px" }}> | ||
| {language.t("settings.agentBehaviour.availableModes")} | ||
| </h4> | ||
| <Card> | ||
| <For each={removableModes()}> | ||
| {(agent, index) => ( | ||
| <div | ||
| style={{ | ||
| display: "flex", | ||
| "align-items": "center", | ||
| "justify-content": "space-between", | ||
| padding: "8px 0", | ||
| "border-bottom": index() < removableModes().length - 1 ? "1px solid var(--border-weak-base)" : "none", | ||
| }} | ||
| > | ||
| <div style={{ flex: 1, "min-width": 0 }}> | ||
| <div data-slot="settings-row-label-title" style={{ "margin-bottom": "0" }}> | ||
| {agent.name} | ||
| </div> | ||
| <Show when={agent.description}> | ||
| <div data-slot="settings-row-label-subtitle" style={{ "margin-top": "4px" }}> | ||
| {agent.description} | ||
| </div> | ||
| </Show> | ||
| </div> | ||
| <IconButton | ||
| size="small" | ||
| variant="ghost" | ||
| icon="close" | ||
| onClick={(e: MouseEvent) => { | ||
| e.stopPropagation() | ||
| confirmRemoveMode(agent) | ||
| }} | ||
| /> | ||
| </div> | ||
| )} | ||
| </For> | ||
| </Card> | ||
| </Show> | ||
| </div> | ||
| ) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -122,6 +122,7 @@ interface SessionContextValue { | |
|
|
||
| // Agent/mode selection (per-session) | ||
| agents: Accessor<AgentInfo[]> | ||
| removeMode: (name: string) => void | ||
| selectedAgent: Accessor<string> | ||
| 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<SkillInfo[]>([]) | ||
|
|
||
| const removeMode = (name: string) => { | ||
| setAgents((prev) => prev.filter((a) => a.name !== name)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WARNING: Removed mode can stay selected in session state
|
||
|
|
||
| // 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<string | null>(null) | ||
|
|
||
|
|
@@ -1314,6 +1331,7 @@ export const SessionProvider: ParentComponent = (props) => { | |
| skills, | ||
| refreshSkills, | ||
| removeSkill, | ||
| removeMode, | ||
| selectedAgent: selectedAgentName, | ||
| selectAgent, | ||
| getSessionAgent: (sessionID: string) => store.agentSelections[sessionID] ?? defaultAgent(), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WARNING: Removed mode leaves stale config references behind
This only refreshes
agentsLoaded. If the deleted mode is still referenced byconfig.default_agentorconfig.agent, the webview keeps showing it from stale config state and the backend can start throwingdefault agent "<name>" not foundwhen the next prompt falls back to the default agent. Refresh or clear the config after a successful removal as well.