diff --git a/.changeset/command-model-selector-sync.md b/.changeset/command-model-selector-sync.md new file mode 100644 index 00000000000..cd64ab6f90e --- /dev/null +++ b/.changeset/command-model-selector-sync.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Update the active model, mode, and thinking selectors when executing a slash command with configured overrides. diff --git a/packages/kilo-vscode/src/kilo-provider/commands.ts b/packages/kilo-vscode/src/kilo-provider/commands.ts index d641fff1f38..822ef45ba2e 100644 --- a/packages/kilo-vscode/src/kilo-provider/commands.ts +++ b/packages/kilo-vscode/src/kilo-provider/commands.ts @@ -13,12 +13,18 @@ export async function loadCommands(client: KiloClient, dir: string): Promise client.command.list({ directory: dir }, { throwOnError: true })).then(({ data }) => ({ type: "commandsLoaded", - commands: data.map((cmd) => ({ - name: cmd.name, - description: cmd.description, - source: cmd.source, - hints: cmd.hints, - })), + commands: data.map((cmd) => { + const item = cmd as typeof cmd & { variant?: string } + return { + name: item.name, + description: item.description, + agent: item.agent, + model: item.model, + variant: item.variant, + source: item.source, + hints: item.hints, + } + }), })) promises.set(dir, promise) diff --git a/packages/kilo-vscode/tests/unit/prompt-send-contract.test.ts b/packages/kilo-vscode/tests/unit/prompt-send-contract.test.ts index 8cce4945257..f94736a9603 100644 --- a/packages/kilo-vscode/tests/unit/prompt-send-contract.test.ts +++ b/packages/kilo-vscode/tests/unit/prompt-send-contract.test.ts @@ -78,6 +78,15 @@ describe("sendCommand dismisses pending tool requests", () => { it("rejects questions before sending", () => { expect(body).toContain("dismissQuestion") }) + + it("applies model, agent, and variant overrides when provided by a command", () => { + expect(body).toContain("if (overrides?.agent)") + expect(body).toContain("selectAgent(overrides.agent, scope)") + expect(body).toContain("if (overrides?.model)") + expect(body).toContain("selectModel(parsed.providerID, parsed.modelID, scope)") + expect(body).toContain("if (overrides?.variant)") + expect(body).toContain("selectVariant(overrides.variant, scope)") + }) }) describe("static command completion contract", () => { diff --git a/packages/kilo-vscode/tests/unit/use-slash-command.test.ts b/packages/kilo-vscode/tests/unit/use-slash-command.test.ts index 710ca744bef..03862380fe9 100644 --- a/packages/kilo-vscode/tests/unit/use-slash-command.test.ts +++ b/packages/kilo-vscode/tests/unit/use-slash-command.test.ts @@ -188,4 +188,31 @@ describe("useSlashCommand sandbox action", () => { expect(ctx.slash.results()[0]?.description).toBe("Toggle sandbox") ctx.dispose() }) + + it("preserves model, agent, and variant metadata on loaded server commands", () => { + const ctx = setup(() => {}) + + ctx.fire({ + type: "commandsLoaded", + commands: [ + { + name: "ship", + description: "Ship PR", + agent: "code", + model: "openai/gpt-5.6-luna-fast", + variant: "xhigh", + hints: ["deploy"], + }, + ], + }) + + ctx.slash.onInput("/ship", 5) + const matches = ctx.slash.results() + expect(matches).toHaveLength(1) + expect(matches[0]?.name).toBe("ship") + expect(matches[0]?.agent).toBe("code") + expect(matches[0]?.model).toBe("openai/gpt-5.6-luna-fast") + expect(matches[0]?.variant).toBe("xhigh") + ctx.dispose() + }) }) diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx index 075b925b73b..da9e7789a8f 100644 --- a/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx @@ -1239,6 +1239,11 @@ export const PromptInput: Component = (props) => { pendingId, context, origin ?? null, + { + agent: matched.agent, + model: matched.model, + variant: matched.variant, + }, ) } else { session.sendMessage(message, sel?.providerID, sel?.modelID, attachments, pendingId, context, data, origin ?? null) diff --git a/packages/kilo-vscode/webview-ui/src/context/session.tsx b/packages/kilo-vscode/webview-ui/src/context/session.tsx index 1e09519fa5d..8831d7edfb0 100644 --- a/packages/kilo-vscode/webview-ui/src/context/session.tsx +++ b/packages/kilo-vscode/webview-ui/src/context/session.tsx @@ -283,6 +283,7 @@ interface SessionContextValue { draftID?: string, context?: string, origin?: string | null, + overrides?: { agent?: string; model?: string; variant?: string }, ) => void abort: () => void compact: () => void @@ -2352,31 +2353,51 @@ export const SessionProvider: ParentComponent = (props) => { draftID?: string, context?: string, origin?: string | null, + overrides?: { agent?: string; model?: string; variant?: string }, ) { if (!server.isConnected()) { console.warn("[Kilo New] Cannot send command: not connected") return } - // Cloud previews need import-then-command; post importAndSend with command metadata const sid = origin === undefined ? currentSessionID() : (origin ?? undefined) - const selection = providerID && modelID ? { providerID, modelID } : selected(sid) - recordModelUsage(selection?.providerID, selection?.modelID) + const effectiveDraftID = !sid && !draftID ? crypto.randomUUID() : draftID + const scope = effectiveDraftID ?? sid + if (!sid && !draftID && effectiveDraftID) agentDrafts.seed(effectiveDraftID) + + if (overrides?.agent) { + selectAgent(overrides.agent, scope) + } + if (overrides?.model) { + const parsed = parseModelString(overrides.model) + if (parsed) { + selectModel(parsed.providerID, parsed.modelID, scope) + } + } + if (overrides?.variant) { + selectVariant(overrides.variant, scope) + } + + const effectiveSelection = selected(scope) + const effectiveProvider = effectiveSelection?.providerID ?? providerID + const effectiveModel = effectiveSelection?.modelID ?? modelID + recordModelUsage(effectiveProvider, effectiveModel) + + // Cloud previews need import-then-command; post importAndSend with command metadata const preview = sid?.startsWith("cloud:") ? sid.slice("cloud:".length) : origin === undefined ? cloudPreviewId() : null if (preview) { - const scope = draftID ?? sid const agent = promptAgent(scope) vscode.postMessage({ type: "importAndSend", cloudSessionId: preview, text: `/${command} ${args}`.trim(), messageID: Identifier.ascending("message"), - providerID, - modelID, + providerID: effectiveProvider, + modelID: effectiveModel, agent, variant: currentVariant(scope), files, @@ -2393,9 +2414,6 @@ export const SessionProvider: ParentComponent = (props) => { dismissQuestion(q.id) } - const effectiveDraftID = !sid && !draftID ? crypto.randomUUID() : draftID - const scope = effectiveDraftID ?? sid - if (!sid && !draftID && effectiveDraftID) agentDrafts.seed(effectiveDraftID) if (scope) { clearClose(scope) addOptimistic(scope, messageID, `/${command} ${args}`.trim(), files) @@ -2414,8 +2432,8 @@ export const SessionProvider: ParentComponent = (props) => { messageID, sessionID: sid, draftID: effectiveDraftID, - providerID, - modelID, + providerID: effectiveProvider, + modelID: effectiveModel, agent, variant: currentVariant(scope), files, diff --git a/packages/kilo-vscode/webview-ui/src/types/messages/agents.ts b/packages/kilo-vscode/webview-ui/src/types/messages/agents.ts index 4079413853d..90a69408180 100644 --- a/packages/kilo-vscode/webview-ui/src/types/messages/agents.ts +++ b/packages/kilo-vscode/webview-ui/src/types/messages/agents.ts @@ -11,6 +11,9 @@ export interface SkillInfo { export interface SlashCommandInfo { name: string description?: string + agent?: string + model?: string + variant?: string source?: "command" | "mcp" | "skill" hints: string[] }