From 3b7ed3542b2c23b686c76cf1015b5a7592efd732 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Thu, 12 Feb 2026 09:33:32 +0100 Subject: [PATCH 01/26] docs: add settings migration plan from desktop app to VSCode extension --- packages/kilo-vscode/docs/migrate-settings.md | 233 ++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 packages/kilo-vscode/docs/migrate-settings.md diff --git a/packages/kilo-vscode/docs/migrate-settings.md b/packages/kilo-vscode/docs/migrate-settings.md new file mode 100644 index 00000000000..a0f452e159b --- /dev/null +++ b/packages/kilo-vscode/docs/migrate-settings.md @@ -0,0 +1,233 @@ +# Settings Migration Plan: Desktop App → VSCode Extension + +## Overview + +The desktop app (`packages/app`) has settings spread across four tabs: +- **General** — appearance, notifications, sounds, updates, linux display +- **Shortcuts** — keybinds (not applicable to VSCode — handled by VS Code keybindings) +- **Providers** — connect/disconnect providers, custom providers +- **Models** — toggle model visibility per provider + +Additionally, backend `Config` (persisted by the CLI server) controls agent behavior, permissions, MCP, commands, and experimental flags. The app's **Permissions** component (`settings-permissions.tsx`) manages per-tool permission levels (allow/ask/deny) via `globalSync.updateConfig({ permission: … })`. + +This document maps every setting to an extension tab and describes the implementation path. + +--- + +## 1. Backend Config Settings (via `PATCH /config`) + +These are server-side settings stored in the opencode config file. All reads use `GET /config` (with optional `?directory=`), all writes use `PATCH /config` (merges the partial `Config` object). + +### 1.1 Mapping to Extension Tabs + +| Backend Config Field | Type | Extension Tab | Notes | +|---|---|---|---| +| `permission` | `PermissionConfig` | **Auto Approve** | Per-tool allow/ask/deny — matches app's `settings-permissions.tsx` | +| `permission.` | `allow \| ask \| deny` | **Auto Approve** | Tools: read, edit, glob, grep, list, bash, task, skill, lsp, todoread, todowrite, webfetch, websearch, codesearch, external_directory, doom_loop | +| `model` | `string` | **Providers** | Default model (format: `provider/model`) | +| `small_model` | `string` | **Providers** | Small model for title generation etc. | +| `default_agent` | `string` | **Agent Behaviour** | Default agent when none specified | +| `agent` | `Record` | **Agent Behaviour** | Per-agent config (model, prompt, temperature, etc.) | +| `agent..model` | `string` | **Agent Behaviour** (Agents subtab) | Override model per agent | +| `agent..prompt` | `string` | **Agent Behaviour** (Agents subtab) | Custom system prompt per agent | +| `agent..temperature` | `number` | **Agent Behaviour** (Agents subtab) | Temperature override | +| `agent..top_p` | `number` | **Agent Behaviour** (Agents subtab) | Top-p override | +| `agent..steps` | `number` | **Agent Behaviour** (Agents subtab) | Max agentic iterations | +| `agent..permission` | `PermissionConfig` | **Agent Behaviour** (Agents subtab) | Per-agent permission overrides | +| `provider` | `Record` | **Providers** | Custom provider configuration | +| `disabled_providers` | `string[]` | **Providers** | Providers to disable | +| `enabled_providers` | `string[]` | **Providers** | Exclusive provider allowlist | +| `mcp` | `Record` | **Agent Behaviour** (MCP subtab) | MCP server configurations | +| `command` | `Record` | **Agent Behaviour** (Commands subtab) | Custom commands | +| `instructions` | `string[]` | **Prompts** | Additional instruction file paths | +| `skills.paths` | `string[]` | **Agent Behaviour** (Skills subtab) | Additional skill folder paths | +| `skills.urls` | `string[]` | **Agent Behaviour** (Skills subtab) | Skill URLs | +| `snapshot` | `boolean` | **Checkpoints** | Enable/disable snapshots | +| `share` | `manual \| auto \| disabled` | **Experimental** | Sharing behavior | +| `username` | `string` | **Display** | Custom username in conversations | +| `watcher.ignore` | `string[]` | **Context** | File watcher ignore patterns | +| `formatter` | `false \| Record<…>` | **Experimental** | Formatter configuration | +| `lsp` | `false \| Record<…>` | **Experimental** | LSP configuration | +| `compaction.auto` | `boolean` | **Agent Behaviour** | Auto-compaction when context is full | +| `compaction.prune` | `boolean` | **Agent Behaviour** | Prune old tool outputs | +| `tools` | `Record` | **Experimental** | Enable/disable specific tools globally | +| `layout` | `auto \| stretch` | **Display** | Layout mode | +| `experimental.disable_paste_summary` | `boolean` | **Experimental** | Disable paste summary | +| `experimental.batch_tool` | `boolean` | **Experimental** | Enable batch tool | +| `experimental.primary_tools` | `string[]` | **Experimental** | Primary-agent-only tools | +| `experimental.continue_loop_on_deny` | `boolean` | **Experimental** | Continue on permission deny | +| `experimental.mcp_timeout` | `number` | **Experimental** | MCP request timeout (ms) | + +### 1.2 Desktop-Only / Not Applicable in VSCode + +| Setting | Reason | +|---|---| +| `theme` | VSCode owns theming | +| `keybinds` | VSCode keybindings system | +| `tui.*` (scroll_speed, scroll_acceleration, diff_style) | TUI-specific | +| `server.*` | Server managed by extension automatically | + +--- + +## 2. Client-Side Settings (from `app/src/context/settings.tsx`) + +These are persisted client-side in the app (localStorage). In the extension, the equivalent would be VS Code's `ExtensionContext.globalState` or webview-local storage. + +| App Setting | Type | Extension Tab | Notes | +|---|---|---|---| +| `appearance.font` | `string` | **Display** | Mono font selection — VSCode has its own font settings, could be skipped or kept for webview only | +| `appearance.fontSize` | `number` | **Display** | Font size — same consideration as above | +| `notifications.agent` | `boolean` | **Notifications** | Notify when agent completes | +| `notifications.permissions` | `boolean` | **Notifications** | Notify on permission requests | +| `notifications.errors` | `boolean` | **Notifications** | Notify on errors | +| `sounds.agent` | `string` | **Notifications** | Sound on agent completion | +| `sounds.permissions` | `string` | **Notifications** | Sound on permission request | +| `sounds.errors` | `string` | **Notifications** | Sound on error | +| `general.releaseNotes` | `boolean` | **Experimental** | Show release notes | +| `general.autoSave` | `boolean` | N/A | VSCode has its own auto-save | +| `updates.startup` | `boolean` | N/A | VSCode handles extension updates | + +--- + +## 3. Provider & Model Settings (from app) + +| Feature | Extension Tab | Backend API | Notes | +|---|---|---|---| +| List connected providers | **Providers** | `GET /provider` | Already implemented in extension | +| Connect provider (OAuth) | **Providers** | `POST /provider/{id}/oauth/authorize` + `callback` | Already implemented | +| Disconnect provider | **Providers** | `DELETE /auth/{id}` + config `disabled_providers` update | Already implemented | +| Custom provider (OpenAI-compat) | **Providers** | `PATCH /config` with `provider` field | New | +| Toggle model visibility | **Providers** | `PATCH /config` with model visibility config | New | + +--- + +## 4. Implementation Plan + +### Phase 1: Infrastructure — Config Read/Write + +1. **Add `getConfig()` to [`HttpClient`](src/services/cli-backend/http-client.ts)** + ```typescript + async getConfig(directory: string): Promise { + return this.request("GET", "/config", undefined, { directory }) + } + ``` + +2. **Add `updateConfig()` to [`HttpClient`](src/services/cli-backend/http-client.ts)** + ```typescript + async updateConfig(config: Partial, directory: string): Promise { + return this.request("PATCH", "/config", config, { directory }) + } + ``` + +3. **Add Config types to [`types.ts`](src/services/cli-backend/types.ts)** + Import or re-define `Config`, `PermissionConfig`, `AgentConfig`, `ProviderConfig` etc. from the SDK types. + +4. **Add cached config message pattern in [`KiloProvider`](src/KiloProvider.ts)** + - `fetchAndSendConfig()` method + - Handle `requestConfig` message from webview + - Handle `updateConfig` message from webview (call `httpClient.updateConfig()`, then re-fetch and push) + +5. **Add message types to [`messages.ts`](webview-ui/src/types/messages.ts)** + - `ConfigLoadedMessage` (extension→webview) + - `RequestConfigMessage` (webview→extension) + - `UpdateConfigMessage` (webview→extension, carries partial Config) + - `ConfigUpdatedMessage` (extension→webview, confirmation) + +6. **Create config context in `webview-ui/src/context/config.tsx`** + - Subscribe to `ConfigLoadedMessage` + - Expose `config()` accessor and `updateConfig(partial)` method + - `updateConfig` posts `UpdateConfigMessage` to extension, optimistically updates local state + +### Phase 2: Populate Existing Tabs with Backend Settings + +Each tab should read from the config context and call `updateConfig()` on change. + +#### Auto Approve Tab +- Per-tool permission selects (allow/ask/deny) for all 16 tools +- Reference: [`settings-permissions.tsx`](../app/src/components/settings-permissions.tsx) + +#### Agent Behaviour Tab +- Default agent selector (`config.default_agent`) +- Compaction settings (`config.compaction.auto`, `config.compaction.prune`) +- **Agents subtab**: Per-agent config — model override, prompt, temperature, top_p, max steps, per-agent permissions +- **MCP subtab**: MCP server configurations (`config.mcp`) +- **Commands subtab**: Custom commands (`config.command`) +- **Skills subtab**: Skill folder paths and URLs (`config.skills`) + +#### Providers Tab +- Default model (`config.model`) +- Small model (`config.small_model`) +- Custom provider creation (`config.provider`) +- Provider enable/disable (`config.disabled_providers`, `config.enabled_providers`) +- Model visibility toggles (reference: [`settings-models.tsx`](../app/src/components/settings-models.tsx)) + +#### Prompts Tab +- Per-agent prompt (`config.agent..prompt`) +- Additional instructions (`config.instructions`) + +#### Checkpoints Tab +- Snapshot toggle (`config.snapshot`) + +#### Display Tab +- Username (`config.username`) +- Layout mode (`config.layout`) + +#### Context Tab +- Watcher ignore patterns (`config.watcher.ignore`) + +#### Notifications Tab +- Client-side notification toggles (agent, permissions, errors) +- Sound selection (agent, permissions, errors) +- These are client-side only; store in VS Code `globalState` or webview localStorage + +#### Experimental Tab +- Share mode (`config.share`) +- Formatter config (`config.formatter`) +- LSP config (`config.lsp`) +- Tool toggles (`config.tools`) +- Remaining `config.experimental.*` flags + +#### Language Tab +- Already implemented (client-side locale selection) + +### Phase 3: Client-Side Settings + +For notification and sound settings (not backed by the CLI server): + +1. **Option A**: Store in VS Code `globalState` via extension messages (preferred — survives webview reloads) +2. **Option B**: Store in webview `localStorage` (simpler but resets on webview rebuild) + +Recommended: Option A — add `SaveLocalSettingMessage` / `LocalSettingLoadedMessage` message types, persist via `context.globalState`. + +--- + +## 5. Settings not relevant for VSCode extension + +| Setting | Reason | +|---|---| +| `theme` / `colorScheme` | VS Code owns theming | +| `keybinds` | VS Code keybindings | +| `tui.*` | TUI-only settings | +| `server.*` | Server lifecycle managed by extension | +| `general.autoSave` | VS Code has native auto-save | +| `updates.startup` | VS Code handles extension updates | +| `appearance.font` / `appearance.fontSize` | VS Code editor font settings (could optionally be kept for webview chat font) | +| Wayland display backend | Linux desktop app only | + +--- + +## 6. Priority Order + +1. **P0 — Permissions** (Auto Approve tab): Most impactful for daily use +2. **P0 — Default model/agent** (Providers + Agent Behaviour): Core workflow +3. **P1 — Agent config** (Agent Behaviour): model overrides, temperature, steps +4. **P1 — MCP servers** (Agent Behaviour subtab): Server configuration +5. **P1 — Notifications & sounds** (Notifications tab): User experience +6. **P2 — Prompts/instructions** (Prompts tab): Custom system prompts +7. **P2 — Compaction settings** (Agent Behaviour): Auto-compaction +8. **P2 — Checkpoints/snapshots** (Checkpoints tab): Snapshot toggle +9. **P3 — Display settings** (Display tab): Username, layout +10. **P3 — Context settings** (Context tab): Watcher ignore +11. **P3 — Experimental flags** (Experimental tab): All experimental.* flags +12. **P3 — Custom providers** (Providers tab): OpenAI-compatible providers +13. **P3 — Model visibility** (Providers tab): Per-model show/hide From 45415656e60a7c292d0e5c13bd26f3860e26ca2a Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Thu, 12 Feb 2026 09:45:03 +0100 Subject: [PATCH 02/26] feat(settings): add backend Config types to types.ts --- .../src/services/cli-backend/types.ts | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/packages/kilo-vscode/src/services/cli-backend/types.ts b/packages/kilo-vscode/src/services/cli-backend/types.ts index 93f841cb937..06c3a907558 100644 --- a/packages/kilo-vscode/src/services/cli-backend/types.ts +++ b/packages/kilo-vscode/src/services/cli-backend/types.ts @@ -225,3 +225,98 @@ export interface McpRemoteConfig { // Union of all MCP server config types export type McpConfig = McpLocalConfig | McpRemoteConfig + +// ============================================ +// Backend Config Types (from CLI server) +// ============================================ + +/** Permission level for a tool */ +export type PermissionLevel = "allow" | "ask" | "deny" + +/** Per-tool permission configuration */ +export type PermissionConfig = Partial> + +/** Per-agent configuration */ +export interface AgentConfig { + model?: string + prompt?: string + temperature?: number + top_p?: number + steps?: number + permission?: PermissionConfig +} + +/** Custom provider configuration (OpenAI-compatible) */ +export interface ProviderConfig { + name?: string + api_key?: string + base_url?: string + models?: Record +} + +/** MCP server configuration (backend config shape) */ +export interface McpServerConfig { + command?: string + args?: string[] + env?: Record + url?: string + headers?: Record +} + +/** Custom command configuration */ +export interface CommandConfig { + command: string + description?: string +} + +/** Skills configuration */ +export interface SkillsConfig { + paths?: string[] + urls?: string[] +} + +/** Compaction configuration */ +export interface CompactionConfig { + auto?: boolean + prune?: boolean +} + +/** Watcher configuration */ +export interface WatcherConfig { + ignore?: string[] +} + +/** Experimental flags */ +export interface ExperimentalConfig { + disable_paste_summary?: boolean + batch_tool?: boolean + primary_tools?: string[] + continue_loop_on_deny?: boolean + mcp_timeout?: number +} + +/** Full backend Config object (partial — all fields optional for PATCH) */ +export interface Config { + permission?: PermissionConfig + model?: string + small_model?: string + default_agent?: string + agent?: Record + provider?: Record + disabled_providers?: string[] + enabled_providers?: string[] + mcp?: Record + command?: Record + instructions?: string[] + skills?: SkillsConfig + snapshot?: boolean + share?: "manual" | "auto" | "disabled" + username?: string + watcher?: WatcherConfig + formatter?: false | Record + lsp?: false | Record + compaction?: CompactionConfig + tools?: Record + layout?: "auto" | "stretch" + experimental?: ExperimentalConfig +} From 4eabb8787b46d06c42ceed4d70c1f4b690d8b6f0 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Thu, 12 Feb 2026 09:46:25 +0100 Subject: [PATCH 03/26] feat(settings): add getConfig/updateConfig to HttpClient --- .../src/services/cli-backend/http-client.ts | 20 +++++++++++++++++++ .../src/services/cli-backend/index.ts | 1 + 2 files changed, 21 insertions(+) diff --git a/packages/kilo-vscode/src/services/cli-backend/http-client.ts b/packages/kilo-vscode/src/services/cli-backend/http-client.ts index ad5d4b990ad..65de4ca6253 100644 --- a/packages/kilo-vscode/src/services/cli-backend/http-client.ts +++ b/packages/kilo-vscode/src/services/cli-backend/http-client.ts @@ -9,6 +9,7 @@ import type { ProviderListResponse, McpStatus, McpConfig, + Config, } from "./types" /** @@ -174,6 +175,25 @@ export class HttpClient { return this.request("GET", "/agent", undefined, { directory }) } + // ============================================ + // Config Methods + // ============================================ + + /** + * Get the current backend configuration. + */ + async getConfig(directory: string): Promise { + return this.request("GET", "/config", undefined, { directory }) + } + + /** + * Update backend configuration (partial merge). + * The server merges the provided fields into the existing config. + */ + async updateConfig(config: Partial, directory: string): Promise { + return this.request("PATCH", "/config", config, { directory }) + } + // ============================================ // Messaging Methods // ============================================ diff --git a/packages/kilo-vscode/src/services/cli-backend/index.ts b/packages/kilo-vscode/src/services/cli-backend/index.ts index 1489987a231..8ceccad4556 100644 --- a/packages/kilo-vscode/src/services/cli-backend/index.ts +++ b/packages/kilo-vscode/src/services/cli-backend/index.ts @@ -25,6 +25,7 @@ export type { McpLocalConfig, McpRemoteConfig, McpConfig, + Config, } from "./types" export { ServerManager } from "./server-manager" From 165bdd875e11193a563315f461dbb9e104a6b83f Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Thu, 12 Feb 2026 09:48:07 +0100 Subject: [PATCH 04/26] =?UTF-8?q?feat(settings):=20add=20config=20message?= =?UTF-8?q?=20types=20for=20extension=E2=86=94webview?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../webview-ui/src/types/messages.ts | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/packages/kilo-vscode/webview-ui/src/types/messages.ts b/packages/kilo-vscode/webview-ui/src/types/messages.ts index bd3a354a7ff..4645289aaf6 100644 --- a/packages/kilo-vscode/webview-ui/src/types/messages.ts +++ b/packages/kilo-vscode/webview-ui/src/types/messages.ts @@ -207,6 +207,90 @@ export interface ModelSelection { modelID: string } +// ============================================ +// Backend Config Types (mirrored for webview) +// ============================================ + +export type PermissionLevel = "allow" | "ask" | "deny" + +export type PermissionConfig = Partial> + +export interface AgentConfig { + model?: string + prompt?: string + temperature?: number + top_p?: number + steps?: number + permission?: PermissionConfig +} + +export interface ProviderConfig { + name?: string + api_key?: string + base_url?: string + models?: Record +} + +export interface McpConfig { + command?: string + args?: string[] + env?: Record + url?: string + headers?: Record +} + +export interface CommandConfig { + command: string + description?: string +} + +export interface SkillsConfig { + paths?: string[] + urls?: string[] +} + +export interface CompactionConfig { + auto?: boolean + prune?: boolean +} + +export interface WatcherConfig { + ignore?: string[] +} + +export interface ExperimentalConfig { + disable_paste_summary?: boolean + batch_tool?: boolean + primary_tools?: string[] + continue_loop_on_deny?: boolean + mcp_timeout?: number +} + +export interface Config { + permission?: PermissionConfig + model?: string + small_model?: string + default_agent?: string + agent?: Record + provider?: Record + disabled_providers?: string[] + enabled_providers?: string[] + mcp?: Record + command?: Record + instructions?: string[] + skills?: SkillsConfig + snapshot?: boolean + share?: "manual" | "auto" | "disabled" + username?: string + watcher?: WatcherConfig + formatter?: false | Record + lsp?: false | Record + compaction?: CompactionConfig + tools?: Record + layout?: "auto" | "stretch" + experimental?: ExperimentalConfig +} + // ============================================ // Messages FROM extension TO webview // ============================================ @@ -356,6 +440,16 @@ export interface BrowserSettingsLoadedMessage { settings: BrowserSettings } +export interface ConfigLoadedMessage { + type: "configLoaded" + config: Config +} + +export interface ConfigUpdatedMessage { + type: "configUpdated" + config: Config +} + export type ExtensionMessage = | ReadyMessage | ConnectionStateMessage @@ -382,6 +476,8 @@ export type ExtensionMessage = | QuestionResolvedMessage | QuestionErrorMessage | BrowserSettingsLoadedMessage + | ConfigLoadedMessage + | ConfigUpdatedMessage // ============================================ // Messages FROM webview TO extension @@ -502,6 +598,15 @@ export interface RequestBrowserSettingsMessage { type: "requestBrowserSettings" } +export interface RequestConfigMessage { + type: "requestConfig" +} + +export interface UpdateConfigMessage { + type: "updateConfig" + config: Partial +} + export type WebviewMessage = | SendMessageRequest | AbortRequest @@ -526,6 +631,8 @@ export type WebviewMessage = | RenameSessionRequest | UpdateSettingRequest | RequestBrowserSettingsMessage + | RequestConfigMessage + | UpdateConfigMessage // ============================================ // VS Code API type From b26cb9ab3967dbc956c2412b3e945edee19f472b Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Thu, 12 Feb 2026 09:50:00 +0100 Subject: [PATCH 05/26] feat(settings): add config fetch/send/update in KiloProvider --- packages/kilo-vscode/src/KiloProvider.ts | 64 ++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 3e6468a6436..4289cb2fe4a 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -13,6 +13,8 @@ export class KiloProvider implements vscode.WebviewViewProvider { private cachedProvidersMessage: unknown = null /** Cached agentsLoaded payload so requestAgents can be served before httpClient is ready */ private cachedAgentsMessage: unknown = null + /** Cached configLoaded payload so requestConfig can be served before httpClient is ready */ + private cachedConfigMessage: unknown = null private trackedSessionIds: Set = new Set() private unsubscribeEvent: (() => void) | null = null @@ -209,6 +211,12 @@ export class KiloProvider implements vscode.WebviewViewProvider { case "questionReject": await this.handleQuestionReject(message.requestID) break + case "requestConfig": + await this.fetchAndSendConfig() + break + case "updateConfig": + await this.handleUpdateConfig(message.config) + break case "setLanguage": await vscode.workspace .getConfiguration("kilo-code.new") @@ -307,6 +315,7 @@ export class KiloProvider implements vscode.WebviewViewProvider { // Fetch providers and agents, then send to webview await this.fetchAndSendProviders() await this.fetchAndSendAgents() + await this.fetchAndSendConfig() console.log("[Kilo New] KiloProvider: ✅ initializeConnection completed successfully") } catch (error) { @@ -588,6 +597,61 @@ export class KiloProvider implements vscode.WebviewViewProvider { } } + /** + * Fetch backend config and send to webview. + */ + private async fetchAndSendConfig(): Promise { + if (!this.httpClient) { + if (this.cachedConfigMessage) { + this.postMessage(this.cachedConfigMessage) + } + return + } + + try { + const workspaceDir = this.getWorkspaceDirectory() + const config = await this.httpClient.getConfig(workspaceDir) + + const message = { + type: "configLoaded", + config, + } + this.cachedConfigMessage = message + this.postMessage(message) + } catch (error) { + console.error("[Kilo New] KiloProvider: Failed to fetch config:", error) + } + } + + /** + * Handle config update request from the webview. + * Applies a partial config update, then re-fetches and pushes the full config. + */ + private async handleUpdateConfig(partial: Record): Promise { + if (!this.httpClient) { + this.postMessage({ type: "error", message: "Not connected to CLI backend" }) + return + } + + try { + const workspaceDir = this.getWorkspaceDirectory() + const updated = await this.httpClient.updateConfig(partial, workspaceDir) + + const message = { + type: "configUpdated", + config: updated, + } + this.cachedConfigMessage = { type: "configLoaded", config: updated } + this.postMessage(message) + } catch (error) { + console.error("[Kilo New] KiloProvider: Failed to update config:", error) + this.postMessage({ + type: "error", + message: error instanceof Error ? error.message : "Failed to update config", + }) + } + } + /** * Handle sending a message from the webview. */ From 98c5255146e774871383d5dcddeea855af7c10ad Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Thu, 12 Feb 2026 09:51:29 +0100 Subject: [PATCH 06/26] feat(settings): create config context and wire into App --- packages/kilo-vscode/webview-ui/src/App.tsx | 13 +-- .../webview-ui/src/context/config.tsx | 83 +++++++++++++++++++ 2 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 packages/kilo-vscode/webview-ui/src/context/config.tsx diff --git a/packages/kilo-vscode/webview-ui/src/App.tsx b/packages/kilo-vscode/webview-ui/src/App.tsx index 381929b6b91..c90a9881d58 100644 --- a/packages/kilo-vscode/webview-ui/src/App.tsx +++ b/packages/kilo-vscode/webview-ui/src/App.tsx @@ -9,6 +9,7 @@ import ProfileView from "./components/ProfileView" import { VSCodeProvider } from "./context/vscode" import { ServerProvider, useServer } from "./context/server" import { ProviderProvider } from "./context/provider" +import { ConfigProvider } from "./context/config" import { SessionProvider, useSession } from "./context/session" import { LanguageProvider } from "./context/language" import { ChatView } from "./components/chat" @@ -165,11 +166,13 @@ const App: Component = () => { - - - - - + + + + + + + diff --git a/packages/kilo-vscode/webview-ui/src/context/config.tsx b/packages/kilo-vscode/webview-ui/src/context/config.tsx new file mode 100644 index 00000000000..9155da011ca --- /dev/null +++ b/packages/kilo-vscode/webview-ui/src/context/config.tsx @@ -0,0 +1,83 @@ +/** + * Config context + * Manages backend configuration state (permissions, agents, providers, etc.) + * and exposes an updateConfig method to apply partial updates. + */ + +import { createContext, useContext, createSignal, onCleanup, ParentComponent, Accessor } from "solid-js" +import { useVSCode } from "./vscode" +import type { Config, ExtensionMessage } from "../types/messages" + +interface ConfigContextValue { + config: Accessor + loading: Accessor + updateConfig: (partial: Partial) => void +} + +const ConfigContext = createContext() + +export const ConfigProvider: ParentComponent = (props) => { + const vscode = useVSCode() + + const [config, setConfig] = createSignal({}) + const [loading, setLoading] = createSignal(true) + + // Register handler immediately (not in onMount) so we never miss + // a configLoaded message that arrives before the DOM mount. + const unsubscribe = vscode.onMessage((message: ExtensionMessage) => { + if (message.type === "configLoaded") { + setConfig(message.config) + setLoading(false) + return + } + if (message.type === "configUpdated") { + setConfig(message.config) + return + } + }) + + onCleanup(unsubscribe) + + // Request config in case the initial push was missed. + // Retry a few times because the extension's httpClient may + // not be ready yet when the first request arrives. + let retries = 0 + const maxRetries = 5 + const retryMs = 500 + + vscode.postMessage({ type: "requestConfig" }) + + const retryTimer = setInterval(() => { + retries++ + if (!loading() || retries >= maxRetries) { + clearInterval(retryTimer) + return + } + vscode.postMessage({ type: "requestConfig" }) + }, retryMs) + + onCleanup(() => clearInterval(retryTimer)) + + function updateConfig(partial: Partial) { + // Optimistically update local state + setConfig((prev) => ({ ...prev, ...partial })) + // Send to extension for persistence + vscode.postMessage({ type: "updateConfig", config: partial }) + } + + const value: ConfigContextValue = { + config, + loading, + updateConfig, + } + + return {props.children} +} + +export function useConfig(): ConfigContextValue { + const context = useContext(ConfigContext) + if (!context) { + throw new Error("useConfig must be used within a ConfigProvider") + } + return context +} From 2adbbe7aa003ca096822e973ef1d18c4d609ea98 Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Thu, 12 Feb 2026 09:53:51 +0100 Subject: [PATCH 07/26] feat(settings): implement Auto Approve tab with per-tool permissions --- .../components/settings/AutoApproveTab.tsx | 173 ++++++++++++++++-- 1 file changed, 160 insertions(+), 13 deletions(-) diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/AutoApproveTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/AutoApproveTab.tsx index ef989fcf327..f12ef0f053a 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/AutoApproveTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/AutoApproveTab.tsx @@ -1,29 +1,176 @@ -import { Component } from "solid-js" +import { Component, For, createMemo } from "solid-js" +import { useConfig } from "../../context/config" +import type { PermissionLevel } from "../../types/messages" + +const TOOLS = [ + "read", + "edit", + "glob", + "grep", + "list", + "bash", + "task", + "skill", + "lsp", + "todoread", + "todowrite", + "webfetch", + "websearch", + "codesearch", + "external_directory", + "doom_loop", +] as const + +const LEVELS: PermissionLevel[] = ["allow", "ask", "deny"] + +const TOOL_DESCRIPTIONS: Record = { + read: "Read file contents", + edit: "Edit or create files", + glob: "Find files by pattern", + grep: "Search file contents", + list: "List directory contents", + bash: "Execute shell commands", + task: "Create sub-agent tasks", + skill: "Execute skills", + lsp: "Language server operations", + todoread: "Read todo lists", + todowrite: "Write todo lists", + webfetch: "Fetch web pages", + websearch: "Search the web", + codesearch: "Search codebase", + external_directory: "Access files outside workspace", + doom_loop: "Continue after repeated failures", +} + +const selectStyle = { + padding: "4px 8px", + "border-radius": "4px", + border: "1px solid var(--vscode-dropdown-border, var(--vscode-panel-border))", + background: "var(--vscode-dropdown-background)", + color: "var(--vscode-dropdown-foreground)", + "font-size": "12px", + "font-family": "var(--vscode-font-family)", + cursor: "pointer", + outline: "none", + "min-width": "80px", +} const AutoApproveTab: Component = () => { + const { config, updateConfig } = useConfig() + + const permissions = createMemo(() => config().permission ?? {}) + + const getLevel = (tool: string): PermissionLevel => { + return permissions()[tool] ?? permissions()["*"] ?? "ask" + } + + const setPermission = (tool: string, level: PermissionLevel) => { + updateConfig({ + permission: { ...permissions(), [tool]: level }, + }) + } + + const setAll = (level: PermissionLevel) => { + const updated: Record = {} + for (const tool of TOOLS) { + updated[tool] = level + } + updateConfig({ permission: updated }) + } + return (
+ {/* Set All control */}
-

+ Set all permissions + + +

+ + {/* Tool permission list */} +
+ + {(tool, index) => ( +
+
+
+ {tool} +
+
+ {TOOL_DESCRIPTIONS[tool] ?? tool} +
+
+ +
+ )} +
) From 1497d00b303aa6eecb9f237d8ceb987b1b7211be Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Thu, 12 Feb 2026 09:55:42 +0100 Subject: [PATCH 08/26] feat(settings): implement Checkpoints, Display, and Context tabs --- .../components/settings/CheckpointsTab.tsx | 52 ++++-- .../src/components/settings/ContextTab.tsx | 154 ++++++++++++++++-- .../src/components/settings/DisplayTab.tsx | 125 ++++++++++++-- 3 files changed, 294 insertions(+), 37 deletions(-) diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/CheckpointsTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/CheckpointsTab.tsx index fc4fc1b96ef..1fc326727f8 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/CheckpointsTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/CheckpointsTab.tsx @@ -1,29 +1,57 @@ import { Component } from "solid-js" +import { useConfig } from "../../context/config" const CheckpointsTab: Component = () => { + const { config, updateConfig } = useConfig() + return (
-

- This section is not implemented yet. It will - contain configuration options and explanatory text related to the selected settings category. During - reimplementation, use this space to validate layout, spacing, scrolling behavior, and navigation state before - wiring up real controls. -

+
+
+ Enable Snapshots +
+
+ Create checkpoints before file edits so you can restore previous states +
+
+ +
) diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/ContextTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/ContextTab.tsx index 17f7dad22d0..98b3dbfccd1 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/ContextTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/ContextTab.tsx @@ -1,29 +1,157 @@ -import { Component } from "solid-js" +import { Component, For, createSignal } from "solid-js" +import { useConfig } from "../../context/config" + +const inputStyle = { + padding: "4px 8px", + "border-radius": "4px", + border: "1px solid var(--vscode-input-border, var(--vscode-panel-border))", + background: "var(--vscode-input-background)", + color: "var(--vscode-input-foreground)", + "font-size": "12px", + "font-family": "var(--vscode-font-family)", + outline: "none", + flex: "1", +} const ContextTab: Component = () => { + const { config, updateConfig } = useConfig() + const [newPattern, setNewPattern] = createSignal("") + + const patterns = () => config().watcher?.ignore ?? [] + + const addPattern = () => { + const value = newPattern().trim() + if (!value) { + return + } + const current = [...patterns()] + if (!current.includes(value)) { + current.push(value) + updateConfig({ watcher: { ignore: current } }) + } + setNewPattern("") + } + + const removePattern = (index: number) => { + const current = [...patterns()] + current.splice(index, 1) + updateConfig({ watcher: { ignore: current } }) + } + return (
-

- This section is not implemented yet. It will - contain configuration options and explanatory text related to the selected settings category. During - reimplementation, use this space to validate layout, spacing, scrolling behavior, and navigation state before - wiring up real controls. -

+
+ File Watcher Ignore Patterns +
+
+ Glob patterns for files the watcher should ignore +
+
+ + {/* Add new pattern */} +
0 ? "1px solid var(--vscode-panel-border)" : "none", + }} + > + setNewPattern(e.currentTarget.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + addPattern() + } + }} + /> + +
+ + {/* Pattern list */} + + {(pattern, index) => ( +
+ + {pattern} + + +
+ )} +
) diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/DisplayTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/DisplayTab.tsx index 997213b66c4..5809dc33621 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/DisplayTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/DisplayTab.tsx @@ -1,29 +1,130 @@ import { Component } from "solid-js" +import { useConfig } from "../../context/config" + +const inputStyle = { + padding: "4px 8px", + "border-radius": "4px", + border: "1px solid var(--vscode-input-border, var(--vscode-panel-border))", + background: "var(--vscode-input-background)", + color: "var(--vscode-input-foreground)", + "font-size": "12px", + "font-family": "var(--vscode-font-family)", + outline: "none", + width: "200px", +} + +const selectStyle = { + padding: "4px 8px", + "border-radius": "4px", + border: "1px solid var(--vscode-dropdown-border, var(--vscode-panel-border))", + background: "var(--vscode-dropdown-background)", + color: "var(--vscode-dropdown-foreground)", + "font-size": "12px", + "font-family": "var(--vscode-font-family)", + cursor: "pointer", + outline: "none", + "min-width": "120px", +} const DisplayTab: Component = () => { + const { config, updateConfig } = useConfig() + return (
-

- This section is not implemented yet. It will - contain configuration options and explanatory text related to the selected settings category. During - reimplementation, use this space to validate layout, spacing, scrolling behavior, and navigation state before - wiring up real controls. -

+
+
+ Username +
+
+ Custom username displayed in conversations +
+
+ { + const value = e.currentTarget.value.trim() + updateConfig({ username: value || undefined }) + }} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.currentTarget.blur() + } + }} + /> +
+ + {/* Layout mode */} +
+
+
+ Layout +
+
+ Layout mode for the chat interface +
+
+ +
) From 8a3dde7af5eeff210ca5fc3178c63908bdee046e Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Thu, 12 Feb 2026 09:57:45 +0100 Subject: [PATCH 09/26] feat(settings): implement Prompts and Experimental tabs --- .../components/settings/ExperimentalTab.tsx | 226 ++++++++++++++++-- .../src/components/settings/PromptsTab.tsx | 148 +++++++++++- 2 files changed, 347 insertions(+), 27 deletions(-) diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/ExperimentalTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/ExperimentalTab.tsx index 00c0b00658a..46924bae337 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/ExperimentalTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/ExperimentalTab.tsx @@ -1,30 +1,228 @@ -import { Component } from "solid-js" +import { Component, For, Show, createMemo } from "solid-js" +import { useConfig } from "../../context/config" + +const selectStyle = { + padding: "4px 8px", + "border-radius": "4px", + border: "1px solid var(--vscode-dropdown-border, var(--vscode-panel-border))", + background: "var(--vscode-dropdown-background)", + color: "var(--vscode-dropdown-foreground)", + "font-size": "12px", + "font-family": "var(--vscode-font-family)", + cursor: "pointer", + outline: "none", + "min-width": "100px", +} + +const inputStyle = { + padding: "4px 8px", + "border-radius": "4px", + border: "1px solid var(--vscode-input-border, var(--vscode-panel-border))", + background: "var(--vscode-input-background)", + color: "var(--vscode-input-foreground)", + "font-size": "12px", + "font-family": "var(--vscode-font-family)", + outline: "none", + width: "100px", +} + +interface SettingRowProps { + label: string + description: string + last?: boolean + children: any +} + +const SettingRow: Component = (props) => ( +
+
+
{props.label}
+
+ {props.description} +
+
+ {props.children} +
+) const ExperimentalTab: Component = () => { + const { config, updateConfig } = useConfig() + + const experimental = createMemo(() => config().experimental ?? {}) + + const updateExperimental = (key: string, value: unknown) => { + updateConfig({ + experimental: { ...experimental(), [key]: value }, + }) + } + return (
-

+ + + + {/* Formatter */} + + + + + {/* LSP */} + + + + + {/* Disable paste summary */} + + + + + {/* Batch tool */} + + + + + {/* Continue loop on deny */} + + + + + {/* MCP timeout */} + + { + const value = parseInt(e.currentTarget.value, 10) + if (!isNaN(value) && value > 0) { + updateExperimental("mcp_timeout", value) + } + }} + /> + +

+ + {/* Tool toggles */} + 0}> +

- This section is not implemented yet. It will - contain configuration options and explanatory text related to the selected settings category. During - reimplementation, use this space to validate layout, spacing, scrolling behavior, and navigation state before - wiring up real controls. -

-

+ Tool Toggles + +
+ + {([name, enabled], index) => ( +
+ + {name} + + +
+ )} +
+
+ ) } diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/PromptsTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/PromptsTab.tsx index 5d2e2d09c94..5212b834ca8 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/PromptsTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/PromptsTab.tsx @@ -1,29 +1,151 @@ -import { Component } from "solid-js" +import { Component, For, createSignal } from "solid-js" +import { useConfig } from "../../context/config" + +const inputStyle = { + padding: "4px 8px", + "border-radius": "4px", + border: "1px solid var(--vscode-input-border, var(--vscode-panel-border))", + background: "var(--vscode-input-background)", + color: "var(--vscode-input-foreground)", + "font-size": "12px", + "font-family": "var(--vscode-font-family)", + outline: "none", + flex: "1", +} const PromptsTab: Component = () => { + const { config, updateConfig } = useConfig() + const [newInstruction, setNewInstruction] = createSignal("") + + const instructions = () => config().instructions ?? [] + + const addInstruction = () => { + const value = newInstruction().trim() + if (!value) { + return + } + const current = [...instructions()] + if (!current.includes(value)) { + current.push(value) + updateConfig({ instructions: current }) + } + setNewInstruction("") + } + + const removeInstruction = (index: number) => { + const current = [...instructions()] + current.splice(index, 1) + updateConfig({ instructions: current }) + } + return (
+ {/* Instructions file paths */}
-

- This section is not implemented yet. It will - contain configuration options and explanatory text related to the selected settings category. During - reimplementation, use this space to validate layout, spacing, scrolling behavior, and navigation state before - wiring up real controls. -

+
+ Additional Instruction Files +
+
+ Paths to additional instruction files that are included in the system prompt +
+
+ + {/* Add new instruction path */} +
0 ? "1px solid var(--vscode-panel-border)" : "none", + }} + > + setNewInstruction(e.currentTarget.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + addInstruction() + } + }} + /> + +
+ + {/* Instructions list */} + + {(path, index) => ( +
+ + {path} + + +
+ )} +
) From b8d827bffba663ae4a92f44538991ba32ea2153b Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Thu, 12 Feb 2026 09:59:44 +0100 Subject: [PATCH 10/26] feat(settings): implement Providers tab with model selection and provider lists --- .../src/components/settings/ProvidersTab.tsx | 401 +++++++++++++++++- 1 file changed, 389 insertions(+), 12 deletions(-) diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/ProvidersTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/ProvidersTab.tsx index ebd786e54c2..e93a0637d80 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/ProvidersTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/ProvidersTab.tsx @@ -1,29 +1,406 @@ -import { Component } from "solid-js" +import { Component, For, Show, createSignal, createMemo } from "solid-js" +import { useConfig } from "../../context/config" +import { useProvider } from "../../context/provider" + +const inputStyle = { + padding: "4px 8px", + "border-radius": "4px", + border: "1px solid var(--vscode-input-border, var(--vscode-panel-border))", + background: "var(--vscode-input-background)", + color: "var(--vscode-input-foreground)", + "font-size": "12px", + "font-family": "var(--vscode-font-family)", + outline: "none", + width: "240px", +} + +const selectStyle = { + padding: "4px 8px", + "border-radius": "4px", + border: "1px solid var(--vscode-dropdown-border, var(--vscode-panel-border))", + background: "var(--vscode-dropdown-background)", + color: "var(--vscode-dropdown-foreground)", + "font-size": "12px", + "font-family": "var(--vscode-font-family)", + cursor: "pointer", + outline: "none", + width: "260px", +} + +interface SettingRowProps { + label: string + description: string + last?: boolean + children: any +} + +const SettingRow: Component = (props) => ( +
+
+
{props.label}
+
+ {props.description} +
+
+ {props.children} +
+) const ProvidersTab: Component = () => { + const { config, updateConfig } = useConfig() + const provider = useProvider() + + // Build a flat list of available model IDs (provider/model format) from the provider catalog + const modelOptions = createMemo(() => { + const options: string[] = [] + const provs = provider.providers() + for (const provId of Object.keys(provs)) { + for (const modelId of Object.keys(provs[provId].models)) { + options.push(`${provId}/${modelId}`) + } + } + return options.sort() + }) + + // Provider IDs from the catalog + const providerIds = createMemo(() => Object.keys(provider.providers()).sort()) + + const [newDisabled, setNewDisabled] = createSignal("") + const [newEnabled, setNewEnabled] = createSignal("") + + const disabledProviders = () => config().disabled_providers ?? [] + const enabledProviders = () => config().enabled_providers ?? [] + + const addToList = (key: "disabled_providers" | "enabled_providers", value: string) => { + const current = key === "disabled_providers" ? [...disabledProviders()] : [...enabledProviders()] + if (value && !current.includes(value)) { + current.push(value) + updateConfig({ [key]: current }) + } + } + + const removeFromList = (key: "disabled_providers" | "enabled_providers", index: number) => { + const current = key === "disabled_providers" ? [...disabledProviders()] : [...enabledProviders()] + current.splice(index, 1) + updateConfig({ [key]: current }) + } + return (
+ {/* Model selection section */}
-

+ 0} + fallback={ + updateConfig({ model: e.currentTarget.value.trim() || undefined })} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.currentTarget.blur() + } + }} + /> + } + > + + + + + {/* Small model */} + + 0} + fallback={ + updateConfig({ small_model: e.currentTarget.value.trim() || undefined })} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.currentTarget.blur() + } + }} + /> + } + > + + + +

+ + {/* Disabled providers */} +

+ Disabled Providers +

+
+
0 ? "1px solid var(--vscode-panel-border)" : "none", + "font-size": "11px", color: "var(--vscode-descriptionForeground)", - margin: 0, - "line-height": "1.5", }} > - This section is not implemented yet. It will - contain configuration options and explanatory text related to the selected settings category. During - reimplementation, use this space to validate layout, spacing, scrolling behavior, and navigation state before - wiring up real controls. -

+ Providers to hide from the provider list +
+
0 ? "1px solid var(--vscode-panel-border)" : "none", + }} + > + 0} + fallback={ + setNewDisabled(e.currentTarget.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + addToList("disabled_providers", newDisabled().trim()) + setNewDisabled("") + } + }} + /> + } + > + + + +
+ + {(id, index) => ( +
+ {id} + +
+ )} +
+
+ + {/* Enabled providers (allowlist) */} +

+ Enabled Providers (Allowlist) +

+
+
+ If set, only these providers will be available (exclusive allowlist) +
+
0 ? "1px solid var(--vscode-panel-border)" : "none", + }} + > + 0} + fallback={ + setNewEnabled(e.currentTarget.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + addToList("enabled_providers", newEnabled().trim()) + setNewEnabled("") + } + }} + /> + } + > + + + +
+ + {(id, index) => ( +
+ {id} + +
+ )} +
) From 0c361bb2a496ad783ec5f2bc2c51bd81208ff5cd Mon Sep 17 00:00:00 2001 From: Mark IJbema Date: Thu, 12 Feb 2026 10:02:41 +0100 Subject: [PATCH 11/26] feat(settings): implement Agent Behaviour tab with per-agent config and subtabs --- .../components/settings/AgentBehaviourTab.tsx | 620 +++++++++++++++++- 1 file changed, 600 insertions(+), 20 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 3d51b1dd3c7..61eb6e79a53 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/AgentBehaviourTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/AgentBehaviourTab.tsx @@ -1,5 +1,8 @@ -import { Component, createSignal, For } from "solid-js" +import { Component, createSignal, createMemo, For, Show } from "solid-js" +import { useConfig } from "../../context/config" +import { useSession } from "../../context/session" import { useLanguage } from "../../context/language" +import type { AgentConfig } from "../../types/messages" type SubtabId = "modes" | "mcpServers" | "rules" | "workflows" | "skills" @@ -16,38 +19,615 @@ const subtabs: SubtabConfig[] = [ { id: "skills", labelKey: "settings.agentBehaviour.subtab.skills" }, ] +const selectStyle = { + padding: "4px 8px", + "border-radius": "4px", + border: "1px solid var(--vscode-dropdown-border, var(--vscode-panel-border))", + background: "var(--vscode-dropdown-background)", + color: "var(--vscode-dropdown-foreground)", + "font-size": "12px", + "font-family": "var(--vscode-font-family)", + cursor: "pointer", + outline: "none", + "min-width": "120px", +} + +const inputStyle = { + padding: "4px 8px", + "border-radius": "4px", + border: "1px solid var(--vscode-input-border, var(--vscode-panel-border))", + background: "var(--vscode-input-background)", + color: "var(--vscode-input-foreground)", + "font-size": "12px", + "font-family": "var(--vscode-font-family)", + outline: "none", +} + +interface SettingRowProps { + label: string + description: string + last?: boolean + children: any +} + +const SettingRow: Component = (props) => ( +
+
+
{props.label}
+
+ {props.description} +
+
+ {props.children} +
+) + +const Placeholder: Component<{ text: string }> = (props) => ( +
+

+ Not yet implemented. {props.text} +

+
+) + const AgentBehaviourTab: Component = () => { const language = useLanguage() + const { config, updateConfig } = useConfig() + const session = useSession() const [activeSubtab, setActiveSubtab] = createSignal("modes") + const [selectedAgent, setSelectedAgent] = createSignal("") + const [newSkillPath, setNewSkillPath] = createSignal("") + const [newSkillUrl, setNewSkillUrl] = createSignal("") - const renderSubtabContent = () => { - return ( -
-

{ + const names = session.agents().map((a) => a.name) + // Also include any agents from config that might not be in the agent list + const configAgents = Object.keys(config().agent ?? {}) + for (const name of configAgents) { + if (!names.includes(name)) { + names.push(name) + } + } + return names.sort() + }) + + const currentAgentConfig = createMemo(() => { + const name = selectedAgent() + if (!name) { + return {} + } + return config().agent?.[name] ?? {} + }) + + const updateAgentConfig = (name: string, partial: Partial) => { + const existing = config().agent ?? {} + const current = existing[name] ?? {} + updateConfig({ + agent: { + ...existing, + [name]: { ...current, ...partial }, + }, + }) + } + + const skillPaths = () => config().skills?.paths ?? [] + const skillUrls = () => config().skills?.urls ?? [] + + const addSkillPath = () => { + const value = newSkillPath().trim() + if (!value) { + return + } + const current = [...skillPaths()] + if (!current.includes(value)) { + current.push(value) + updateConfig({ skills: { ...config().skills, paths: current } }) + } + setNewSkillPath("") + } + + const removeSkillPath = (index: number) => { + const current = [...skillPaths()] + current.splice(index, 1) + updateConfig({ skills: { ...config().skills, paths: current } }) + } + + const addSkillUrl = () => { + const value = newSkillUrl().trim() + if (!value) { + return + } + const current = [...skillUrls()] + if (!current.includes(value)) { + current.push(value) + updateConfig({ skills: { ...config().skills, urls: current } }) + } + setNewSkillUrl("") + } + + const removeSkillUrl = (index: number) => { + const current = [...skillUrls()] + current.splice(index, 1) + updateConfig({ skills: { ...config().skills, urls: current } }) + } + + const renderModesSubtab = () => ( +

+ {/* Agent selector */} +
+ +
+ + +
- This section is not implemented yet. It will - contain configuration options for {activeSubtab()}. During reimplementation, use this space to validate - layout, spacing, scrolling behavior, and navigation state before wiring up real controls. -

+ {/* Model override */} + + + updateAgentConfig(selectedAgent(), { + model: e.currentTarget.value.trim() || undefined, + }) + } + onKeyDown={(e) => { + if (e.key === "Enter") { + e.currentTarget.blur() + } + }} + /> + + + {/* System prompt */} + +