diff --git a/.changeset/continue-worktree-sessions.md b/.changeset/continue-worktree-sessions.md new file mode 100644 index 0000000000..95a49a2b80 --- /dev/null +++ b/.changeset/continue-worktree-sessions.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Keep continued Agent Manager sessions in the selected worktree instead of moving them to Local. diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 0aa9c66aba..95cda9630b 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -135,6 +135,7 @@ import type { KiloProviderOptions } from "./kilo-provider/options" import { fetchKiloEmbeddingModelCatalog } from "@kilocode/kilo-gateway" type MessageLoadMode = "replace" | "prepend" | "focus" | "reconcile" +type ContextMessage = { contextDirectory?: unknown } // Helper to map agent data to the subset of fields sent to the webview const mapAgent = (a: Agent) => ({ name: a.name, @@ -634,7 +635,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper this.readyResolvers.splice(0).forEach((r) => r()) break case "sendMessage": { - const files = parseMessageFiles(message.files) + const msg = message as typeof message & ContextMessage await this.handleSendMessage( message.text, typeof message.messageID === "string" ? message.messageID : undefined, @@ -644,13 +645,14 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper message.modelID, message.agent, message.variant, - files, + parseMessageFiles(message.files), typeof message.agentManagerContext === "string" ? message.agentManagerContext : undefined, + typeof msg.contextDirectory === "string" ? msg.contextDirectory : undefined, ) break } case "sendCommand": { - const files = parseMessageFiles(message.files) + const msg = message as typeof message & ContextMessage await this.handleSendCommand( message.command, message.arguments, @@ -661,8 +663,9 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper message.modelID, message.agent, message.variant, - files, + parseMessageFiles(message.files), typeof message.agentManagerContext === "string" ? message.agentManagerContext : undefined, + typeof msg.contextDirectory === "string" ? msg.contextDirectory : undefined, ) break } @@ -1068,13 +1071,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper break } case "fetchMarketplaceData": { - const workspace = this.getProjectDirectory(this.currentSession?.id) - const mp = this.getMarketplace() - // Fetch skills from CLI backend (authoritative source) so the - // marketplace doesn't need to duplicate the CLI's skill scanning. - const skills = await this.fetchCliSkills() - const data = await mp.fetchData(workspace, skills) - this.postMessage({ type: "marketplaceData", ...data }) + await this.handleFetchMarketplaceData() break } case "filterMarketplaceItems": { @@ -1124,6 +1121,14 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper this.diffVirtualProvider.open(d) } + private async handleFetchMarketplaceData(): Promise { + const workspace = this.getProjectDirectory(this.currentSession?.id) + const mp = this.getMarketplace() + const skills = await this.fetchCliSkills() + const data = await mp.fetchData(workspace, skills) + this.postMessage({ type: "marketplaceData", ...data }) + } + /** * Initialize connection to the CLI backend server. * Subscribes to the shared KiloConnectionService. @@ -1522,6 +1527,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper client.session.list({ directory: dir, roots: true }, { throwOnError: true }).then(({ data }) => data) : null, sessionDirectories: this.sessionDirectories, + worktreeDirectories: this.opts.worktreeDirectories, workspaceDirectory: this.getWorkspaceDirectory(), postMessage: (msg: unknown) => this.postMessage(msg), } @@ -2421,7 +2427,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper details: getConfigErrorDetails(error), }) } - private async resolveSession(sessionID?: string, draftID?: string, context?: string) { + private async resolveSession(sessionID?: string, draftID?: string, context?: string, contextDirectory?: string) { if (!this.client) return undefined const dir = resolveNewSessionDirectory({ @@ -2429,6 +2435,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper currentSessionID: this.currentSession?.id, contextSessionID: this.contextSessionID, agentManagerContext: context, + contextDirectory, sessionDirectories: this.sessionDirectories, workspaceDirectory: this.getRootDirectory(), }) @@ -2538,6 +2545,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper variant?: string, files?: MessageFile[], context?: string, + contextDirectory?: string, ): Promise { if (!this.client) { this.postMessage({ @@ -2554,7 +2562,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper let resolved: { sid: string; dir: string } | undefined try { - resolved = await this.resolveSession(sessionID, draftID, context) + resolved = await this.resolveSession(sessionID, draftID, context, contextDirectory) const parts: Array = [] if (files) { @@ -2615,6 +2623,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper variant?: string, files?: MessageFile[], context?: string, + contextDirectory?: string, ): Promise { if (!this.client) { this.postMessage({ @@ -2631,7 +2640,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper let resolved: { sid: string; dir: string } | undefined try { - resolved = await this.resolveSession(sessionID, draftID, context) + resolved = await this.resolveSession(sessionID, draftID, context, contextDirectory) if (messageID) { this.connectionService.recordMessageSessionId(messageID, resolved!.sid) diff --git a/packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts b/packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts index f88e32e770..683139f49d 100644 --- a/packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts +++ b/packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts @@ -181,6 +181,7 @@ export class AgentManagerProvider implements Disposable { this.attachPanel( this.host.openPanel({ onBeforeMessage: (msg) => this.onMessage(msg), + worktreeDirectories: () => this.getWorktreeDirectories(), }), ) } @@ -378,7 +379,7 @@ export class AgentManagerProvider implements Disposable { if (m.type === "agentManager.deleteWorktree") return this.onDeleteWorktree(m.worktreeId) if (m.type === "agentManager.removeStaleWorktree") return this.onRemoveStaleWorktree(m.worktreeId) if (m.type === "agentManager.promoteSession") return this.onPromoteSession(m.sessionId) - if (m.type === "agentManager.addSessionToWorktree") return this.onAddSessionToWorktree(m.worktreeId) + if (m.type === "agentManager.addSessionToWorktree") return this.onAddSessionToWorktree(m.worktreeId, m.sessionId) if (m.type === "agentManager.forkSession") return this.onForkSession(m.sessionId, m.worktreeId, m.messageId) if (m.type === "agentManager.closeSession") return this.onCloseSession(m.sessionId) } @@ -418,6 +419,15 @@ export class AgentManagerProvider implements Disposable { return null } + if ((m.type === "sendMessage" || m.type === "sendCommand") && !m.sessionID) { + const ctx = typeof m.agentManagerContext === "string" ? m.agentManagerContext : undefined + const worktree = ctx && ctx !== "local" ? this.getStateManager()?.getWorktree(ctx) : undefined + if (worktree) { + this.activeSessionId = m.draftID + return { ...msg, contextDirectory: worktree.path } + } + } + if ((m.type === "sendMessage" || m.type === "sendCommand") && m.draftID && !m.sessionID) { this.activeSessionId = m.draftID return msg @@ -1049,7 +1059,7 @@ export class AgentManagerProvider implements Disposable { } /** Add a new session to an existing worktree. */ - private async onAddSessionToWorktree(worktreeId: string): Promise { + private async onAddSessionToWorktree(worktreeId: string, sessionId?: string): Promise { let client: KiloClient try { client = this.connectionService.getClient() @@ -1068,6 +1078,26 @@ export class AgentManagerProvider implements Disposable { return null } + if (sessionId) { + if (state.getSession(sessionId)) state.moveSession(sessionId, worktreeId) + else state.addSession(sessionId, worktreeId) + this.registerWorktreeSession(sessionId, worktree.path) + this.pushState() + this.postToWebview({ + type: "agentManager.sessionAdded", + sessionId, + worktreeId, + }) + this.host.capture("Agent Manager Session Started", { + source: PLATFORM, + sessionId, + worktreeId, + existing: true, + }) + this.log(`Added existing session ${sessionId} to worktree ${worktreeId}`) + return null + } + let session: Session try { const { data } = await client.session.create( @@ -1648,6 +1678,10 @@ export class AgentManagerProvider implements Disposable { return this.panel?.sessions.getSessionDirectories() ?? new Map() } + public getWorktreeDirectories(): string[] { + return this.getStateManager()?.getWorktrees().map((wt) => wt.path) ?? [] + } + /** * Continue a sidebar session in a new worktree. * Captures git state, creates worktree, applies state, forks session. diff --git a/packages/kilo-vscode/src/agent-manager/__tests__/AgentManagerProvider.spec.ts b/packages/kilo-vscode/src/agent-manager/__tests__/AgentManagerProvider.spec.ts index 0c4dd458fc..bf1f2dbd43 100644 --- a/packages/kilo-vscode/src/agent-manager/__tests__/AgentManagerProvider.spec.ts +++ b/packages/kilo-vscode/src/agent-manager/__tests__/AgentManagerProvider.spec.ts @@ -101,6 +101,7 @@ function createHarness() { prBridge: { handleMessage: ReturnType } activeSessionId: string | undefined stateReady: Promise | undefined + contextTarget: ReturnType createWorktreeOnDisk: ReturnType runSetupScriptForWorktree: ReturnType createSessionInWorktree: ReturnType @@ -121,6 +122,7 @@ function createHarness() { manager.prBridge = { handleMessage: vi.fn().mockReturnValue(false) } manager.activeSessionId = undefined manager.stateReady = Promise.resolve() + manager.contextTarget = vi.fn() manager.createWorktreeOnDisk = vi.fn() manager.runSetupScriptForWorktree = vi.fn().mockResolvedValue(undefined) manager.createSessionInWorktree = vi.fn() @@ -202,4 +204,28 @@ describe("AgentManagerProvider worktree creation", () => { expect(result).toEqual({ type: "requestFileSearch", query: "src", requestId: "r1", sessionID: "session-wt" }) }) + + it("resolves new sends to the selected worktree directory", async () => { + const manager = createHarness() + const state = { + getWorktree: vi.fn().mockReturnValue({ id: "wt-1", path: "/repo/.kilo/worktrees/wt-1" }), + } + manager.getStateManager.mockReturnValue(state) + manager.contextTarget.mockResolvedValue(undefined) + + const result = await manager.onMessage({ + type: "sendMessage", + text: "continue", + agentManagerContext: "wt-1", + draftID: "draft-1", + }) + + expect(result).toEqual({ + type: "sendMessage", + text: "continue", + agentManagerContext: "wt-1", + draftID: "draft-1", + contextDirectory: "/repo/.kilo/worktrees/wt-1", + }) + }) }) diff --git a/packages/kilo-vscode/src/agent-manager/host.ts b/packages/kilo-vscode/src/agent-manager/host.ts index 92970ecac7..079b610f59 100644 --- a/packages/kilo-vscode/src/agent-manager/host.ts +++ b/packages/kilo-vscode/src/agent-manager/host.ts @@ -93,6 +93,7 @@ export interface Host { */ openPanel(opts: { onBeforeMessage: (msg: Record) => Promise | null> + worktreeDirectories?: () => string[] }): PanelContext /** Get the workspace/project root path. */ diff --git a/packages/kilo-vscode/src/agent-manager/types.ts b/packages/kilo-vscode/src/agent-manager/types.ts index 4a74212b7a..f6f312fe46 100644 --- a/packages/kilo-vscode/src/agent-manager/types.ts +++ b/packages/kilo-vscode/src/agent-manager/types.ts @@ -348,6 +348,7 @@ interface OpenLocallyIn { interface AddSessionToWorktreeIn { type: "agentManager.addSessionToWorktree" worktreeId: string + sessionId?: string } interface CloseSessionIn { @@ -606,6 +607,7 @@ interface SendMessageIn { variant?: string files?: Array<{ mime: string; url: string; filename?: string; source?: FileSourceIn }> agentManagerContext?: string + contextDirectory?: string } interface SendCommandIn { @@ -621,6 +623,7 @@ interface SendCommandIn { variant?: string files?: Array<{ mime: string; url: string; filename?: string; source?: FileSourceIn }> agentManagerContext?: string + contextDirectory?: string } interface RequestTerminalContextIn { diff --git a/packages/kilo-vscode/src/agent-manager/vscode-host.ts b/packages/kilo-vscode/src/agent-manager/vscode-host.ts index 222ea03857..5059978d45 100644 --- a/packages/kilo-vscode/src/agent-manager/vscode-host.ts +++ b/packages/kilo-vscode/src/agent-manager/vscode-host.ts @@ -35,6 +35,7 @@ export class VscodeHost implements Host { openPanel(opts: { onBeforeMessage: (msg: Record) => Promise | null> + worktreeDirectories?: () => string[] }): PanelContext { const panel = vscode.window.createWebviewPanel( "kilo-code.new.AgentManagerPanel", @@ -54,6 +55,7 @@ export class VscodeHost implements Host { panel: vscode.WebviewPanel, opts: { onBeforeMessage: (msg: Record) => Promise | null> + worktreeDirectories?: () => string[] }, ): PanelContext { return this.wirePanel(panel, opts) @@ -63,6 +65,7 @@ export class VscodeHost implements Host { panel: vscode.WebviewPanel, opts: { onBeforeMessage: (msg: Record) => Promise | null> + worktreeDirectories?: () => string[] }, ): PanelContext { panel.webview.options = { @@ -86,6 +89,7 @@ export class VscodeHost implements Host { const provider = new KiloProvider(this.extensionUri, this.connectionService, this.context, { slimEditMetadata: true, + worktreeDirectories: () => opts.worktreeDirectories?.() ?? [], }) if (this.diffVirtual) { provider.setDiffVirtualProvider(this.diffVirtual) diff --git a/packages/kilo-vscode/src/extension.ts b/packages/kilo-vscode/src/extension.ts index 8c2ae51cda..1ee680bc9b 100644 --- a/packages/kilo-vscode/src/extension.ts +++ b/packages/kilo-vscode/src/extension.ts @@ -171,6 +171,7 @@ export function activate(context: vscode.ExtensionContext) { deserializeWebviewPanel(panel: vscode.WebviewPanel) { const ctx = agentManagerHost.wrapExistingPanel(panel, { onBeforeMessage: (msg) => agentManagerProvider.handleMessage(msg), + worktreeDirectories: () => agentManagerProvider.getWorktreeDirectories(), }) agentManagerProvider.deserializePanel(ctx) return Promise.resolve() diff --git a/packages/kilo-vscode/src/kilo-provider-utils.ts b/packages/kilo-vscode/src/kilo-provider-utils.ts index caa1f565e1..115cb69d15 100644 --- a/packages/kilo-vscode/src/kilo-provider-utils.ts +++ b/packages/kilo-vscode/src/kilo-provider-utils.ts @@ -222,6 +222,7 @@ export interface SessionRefreshContext { connectionState: "connecting" | "connected" | "disconnected" | "error" listSessions: ((dir: string) => Promise) | null sessionDirectories: Map + worktreeDirectories?: () => string[] workspaceDirectory: string postMessage(message: unknown): void } @@ -245,7 +246,7 @@ export async function loadSessions(ctx: SessionRefreshContext): Promise() const extra = await Promise.all( [...worktreeDirs].map((dir) => @@ -339,6 +340,7 @@ export function resolveNewSessionDirectory(input: { currentSessionID?: string contextSessionID?: string agentManagerContext?: string + contextDirectory?: string sessionDirectories: Map workspaceDirectory: string }) { @@ -350,6 +352,8 @@ export function resolveNewSessionDirectory(input: { }) } + if (input.contextDirectory) return input.contextDirectory + return resolveContextDirectory({ currentSessionID: input.currentSessionID, contextSessionID: input.contextSessionID, diff --git a/packages/kilo-vscode/src/kilo-provider/options.ts b/packages/kilo-vscode/src/kilo-provider/options.ts index 5889a6ea5e..4109abfd78 100644 --- a/packages/kilo-vscode/src/kilo-provider/options.ts +++ b/packages/kilo-vscode/src/kilo-provider/options.ts @@ -2,4 +2,5 @@ export type KiloProviderOptions = { projectDirectory?: string | null slimEditMetadata?: boolean tabTitle?: (title: string) => void + worktreeDirectories?: () => string[] } diff --git a/packages/kilo-vscode/tests/unit/kilo-provider-worktree-context.test.ts b/packages/kilo-vscode/tests/unit/kilo-provider-worktree-context.test.ts index 8e0a9afbac..017b305cc9 100644 --- a/packages/kilo-vscode/tests/unit/kilo-provider-worktree-context.test.ts +++ b/packages/kilo-vscode/tests/unit/kilo-provider-worktree-context.test.ts @@ -93,6 +93,17 @@ describe("resolveNewSessionDirectory", () => { expect(dir).toBe("/repo/.kilo/worktrees/feature") }) + it("uses explicit Agent Manager worktree context after the active session was cleared", () => { + const dir = resolveNewSessionDirectory({ + agentManagerContext: "wt_feature", + contextDirectory: "/repo/.kilo/worktrees/feature", + sessionDirectories: new Map(), + workspaceDirectory: "/repo", + }) + + expect(dir).toBe("/repo/.kilo/worktrees/feature") + }) + it("creates local Agent Manager sessions in the workspace root after a worktree was selected", () => { const dir = resolveNewSessionDirectory({ contextSessionID: "ses_worktree", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx b/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx index 1e7c0daf91..b968d70db1 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx @@ -130,6 +130,7 @@ import { createMarkdownRender } from "./review-preferences" import { createSidebarCollapse } from "./sidebar-collapse" import { SidebarToggleButton } from "./SidebarToggleButton" import { setTabWidths } from "./tab-widths" +import { buildShortcutCategories } from "./shortcuts" import "./agent-manager.css" import "./agent-manager-review.css" const REVIEW_TAB_ID = "review" @@ -185,76 +186,6 @@ const defaultBindings: Record = { ), } -/** Shortcut category definition for the keyboard shortcuts dialog */ -interface ShortcutEntry { - label: string - binding: string -} - -interface ShortcutCategory { - title: string - shortcuts: ShortcutEntry[] -} - -/** Build the categorized list of keyboard shortcuts from the current bindings */ -function buildShortcutCategories( - bindings: Record, - t: (key: string, params?: Record) => string, -): ShortcutCategory[] { - return [ - { - title: t("agentManager.shortcuts.category.quickSwitch"), - shortcuts: [ - { - label: t("agentManager.shortcuts.jumpToItem"), - binding: (() => { - const first = bindings.jumpTo1 ?? "" - const prefix = first.replace(/\d+$/, "") - return prefix ? `${prefix}1-9` : "" - })(), - }, - ], - }, - { - title: t("agentManager.shortcuts.category.sidebar"), - shortcuts: [ - { label: t("agentManager.shortcuts.previousItem"), binding: bindings.previousSession ?? "" }, - { label: t("agentManager.shortcuts.nextItem"), binding: bindings.nextSession ?? "" }, - { label: t("agentManager.shortcuts.newWorktree"), binding: bindings.newWorktree ?? "" }, - { label: t("agentManager.shortcuts.advancedWorktree"), binding: bindings.advancedWorktree ?? "" }, - { label: t("agentManager.shortcuts.deleteWorktree"), binding: bindings.closeWorktree ?? "" }, - { label: t("agentManager.shortcuts.openWorktree"), binding: bindings.openWorktree ?? "" }, - ], - }, - { - title: t("agentManager.shortcuts.category.tabs"), - shortcuts: [ - { label: t("agentManager.shortcuts.previousTab"), binding: bindings.previousTab ?? "" }, - { label: t("agentManager.shortcuts.nextTab"), binding: bindings.nextTab ?? "" }, - { label: t("agentManager.shortcuts.newTab"), binding: bindings.newTab ?? "" }, - { label: t("agentManager.shortcuts.closeTab"), binding: bindings.closeTab ?? "" }, - ], - }, - { - title: t("agentManager.shortcuts.category.terminal"), - shortcuts: [ - { label: t("agentManager.shortcuts.toggleTerminal"), binding: bindings.showTerminal ?? "" }, - { label: t("agentManager.shortcuts.runScript"), binding: bindings.runScript ?? "" }, - { label: t("agentManager.shortcuts.toggleDiff"), binding: bindings.toggleDiff ?? "" }, - ], - }, - { - title: t("agentManager.shortcuts.category.global"), - shortcuts: [ - { label: t("agentManager.shortcuts.openAgentManager"), binding: bindings.agentManagerOpen ?? "" }, - { label: t("agentManager.shortcuts.cycleAgentMode"), binding: bindings.cycleAgentMode ?? "" }, - { label: t("agentManager.shortcuts.cyclePreviousAgentMode"), binding: bindings.cyclePreviousAgentMode ?? "" }, - { label: t("agentManager.shortcuts.showShortcuts"), binding: bindings.showShortcuts ?? "" }, - ].filter((s) => s.binding), - }, - ].filter((c) => c.shortcuts.length > 0) -} - import { parseBindingTokens } from "./keybind-tokens" const AgentManagerContent: Component = () => { @@ -1003,6 +934,27 @@ const AgentManagerContent: Component = () => { setReviewActive(remembered === REVIEW_TAB_ID && reviewOpenByContext()[worktreeId] === true) } + const addSessionToCurrentWorktree = (sid: string) => { + const sel = selection() + if (!sel || sel === LOCAL) return false + const current = managedSessions().find((entry) => entry.id === sid) + if (current?.worktreeId) return focusManagedSession(current.worktreeId, sid) + saveTabMemory() + setHistory(false) + setReviewActive(false) + appendToTabOrder(sel, sid) + evictLocal(sid) + vscode.postMessage({ type: "agentManager.addSessionToWorktree", worktreeId: sel, sessionId: sid }) + return true + } + + const focusManagedSession = (worktreeId: string, sid: string) => { + selectWorktree(worktreeId) + setHistory(false) + session.selectSession(sid) + return true + } + const cycleAgent = (direction: 1 | -1) => { const available = session.agents().filter((a) => a.mode !== "subagent" && !a.hidden) if (available.length <= 1) return @@ -1220,6 +1172,7 @@ const AgentManagerContent: Component = () => { saveTabMemory() appendToTabOrder(ev.worktreeId, ev.sessionId) setSelection(ev.worktreeId) + evictLocal(ev.sessionId) drafts.apply(ev.worktreeId, ev.sessionId) session.selectSession(ev.sessionId) } @@ -2987,6 +2940,7 @@ const AgentManagerContent: Component = () => { { + if (addSessionToCurrentWorktree(id)) return setHistory(false) if (localSessionIDs().includes(id)) { saveTabMemory() @@ -3024,6 +2978,7 @@ const AgentManagerContent: Component = () => {
{ + if (addSessionToCurrentWorktree(id)) return if (localSessionIDs().includes(id)) { session.selectSession(id) if (selection() === null) setSelection(LOCAL) diff --git a/packages/kilo-vscode/webview-ui/agent-manager/shortcuts.ts b/packages/kilo-vscode/webview-ui/agent-manager/shortcuts.ts new file mode 100644 index 0000000000..8f21d698a1 --- /dev/null +++ b/packages/kilo-vscode/webview-ui/agent-manager/shortcuts.ts @@ -0,0 +1,67 @@ +export interface ShortcutEntry { + label: string + binding: string +} + +export interface ShortcutCategory { + title: string + shortcuts: ShortcutEntry[] +} + +export function buildShortcutCategories( + bindings: Record, + t: (key: string, params?: Record) => string, +): ShortcutCategory[] { + return [ + { + title: t("agentManager.shortcuts.category.quickSwitch"), + shortcuts: [ + { + label: t("agentManager.shortcuts.jumpToItem"), + binding: (() => { + const first = bindings.jumpTo1 ?? "" + const prefix = first.replace(/\d+$/, "") + return prefix ? `${prefix}1-9` : "" + })(), + }, + ], + }, + { + title: t("agentManager.shortcuts.category.sidebar"), + shortcuts: [ + { label: t("agentManager.shortcuts.previousItem"), binding: bindings.previousSession ?? "" }, + { label: t("agentManager.shortcuts.nextItem"), binding: bindings.nextSession ?? "" }, + { label: t("agentManager.shortcuts.newWorktree"), binding: bindings.newWorktree ?? "" }, + { label: t("agentManager.shortcuts.advancedWorktree"), binding: bindings.advancedWorktree ?? "" }, + { label: t("agentManager.shortcuts.deleteWorktree"), binding: bindings.closeWorktree ?? "" }, + { label: t("agentManager.shortcuts.openWorktree"), binding: bindings.openWorktree ?? "" }, + ], + }, + { + title: t("agentManager.shortcuts.category.tabs"), + shortcuts: [ + { label: t("agentManager.shortcuts.previousTab"), binding: bindings.previousTab ?? "" }, + { label: t("agentManager.shortcuts.nextTab"), binding: bindings.nextTab ?? "" }, + { label: t("agentManager.shortcuts.newTab"), binding: bindings.newTab ?? "" }, + { label: t("agentManager.shortcuts.closeTab"), binding: bindings.closeTab ?? "" }, + ], + }, + { + title: t("agentManager.shortcuts.category.terminal"), + shortcuts: [ + { label: t("agentManager.shortcuts.toggleTerminal"), binding: bindings.showTerminal ?? "" }, + { label: t("agentManager.shortcuts.runScript"), binding: bindings.runScript ?? "" }, + { label: t("agentManager.shortcuts.toggleDiff"), binding: bindings.toggleDiff ?? "" }, + ], + }, + { + title: t("agentManager.shortcuts.category.global"), + shortcuts: [ + { label: t("agentManager.shortcuts.openAgentManager"), binding: bindings.agentManagerOpen ?? "" }, + { label: t("agentManager.shortcuts.cycleAgentMode"), binding: bindings.cycleAgentMode ?? "" }, + { label: t("agentManager.shortcuts.cyclePreviousAgentMode"), binding: bindings.cyclePreviousAgentMode ?? "" }, + { label: t("agentManager.shortcuts.showShortcuts"), binding: bindings.showShortcuts ?? "" }, + ].filter((s) => s.binding), + }, + ].filter((c) => c.shortcuts.length > 0) +} diff --git a/packages/kilo-vscode/webview-ui/src/types/messages/webview-messages.ts b/packages/kilo-vscode/webview-ui/src/types/messages/webview-messages.ts index 51c37652c1..8db01a3f93 100644 --- a/packages/kilo-vscode/webview-ui/src/types/messages/webview-messages.ts +++ b/packages/kilo-vscode/webview-ui/src/types/messages/webview-messages.ts @@ -29,6 +29,7 @@ export interface SendMessageRequest { variant?: string files?: FileAttachment[] agentManagerContext?: string + contextDirectory?: string } export interface AbortRequest { @@ -240,6 +241,7 @@ export interface SendCommandRequest { variant?: string files?: FileAttachment[] agentManagerContext?: string + contextDirectory?: string } export interface RemoveSkillMessage { @@ -479,6 +481,7 @@ export interface OpenLocallyRequest { export interface AddSessionToWorktreeRequest { type: "agentManager.addSessionToWorktree" worktreeId: string + sessionId?: string } // Fork an existing session (copies conversation history)