From 1d798a106f315dc3c1c4c78382eff7a6bd23343b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Catriel=20M=C3=BCller?= Date: Mon, 29 Jun 2026 09:29:44 -0300 Subject: [PATCH] fix(kiloclaw): restore slash command access --- .changeset/fix-kiloclaw-slash.md | 7 +++++ packages/kilo-gateway/src/index.ts | 1 + packages/kilo-gateway/src/server/handlers.ts | 18 ++++++++++++- packages/kilo-vscode/src/KiloProvider.ts | 3 +++ .../webview-ui/src/hooks/useSlashCommand.ts | 8 ++++++ .../src/types/messages/webview-messages.ts | 5 ++++ .../server/httpapi/handlers/kilo-gateway.ts | 3 ++- .../server/kilo-gateway-statuses.test.ts | 26 +++++++++++++++++++ 8 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 .changeset/fix-kiloclaw-slash.md diff --git a/.changeset/fix-kiloclaw-slash.md b/.changeset/fix-kiloclaw-slash.md new file mode 100644 index 0000000000..a6b2f113ec --- /dev/null +++ b/.changeset/fix-kiloclaw-slash.md @@ -0,0 +1,7 @@ +--- +"@kilocode/cli": patch +"@kilocode/kilo-gateway": patch +"kilo-code": patch +--- + +Fix opening KiloClaw from the CLI and VS Code slash commands. diff --git a/packages/kilo-gateway/src/index.ts b/packages/kilo-gateway/src/index.ts index 642e13efc6..850047c4dd 100644 --- a/packages/kilo-gateway/src/index.ts +++ b/packages/kilo-gateway/src/index.ts @@ -76,6 +76,7 @@ export { getNotifications, getProfile, getToken, + normalizeClawStatus, setOrganization, } from "./server/handlers.js" diff --git a/packages/kilo-gateway/src/server/handlers.ts b/packages/kilo-gateway/src/server/handlers.ts index 722eef71f8..167bfbf4db 100644 --- a/packages/kilo-gateway/src/server/handlers.ts +++ b/packages/kilo-gateway/src/server/handlers.ts @@ -120,7 +120,23 @@ export async function getClawStatus(auth: AuthStore) { const response = await fetch(`${KILO_API_BASE}/api/kiloclaw/status`, { headers }) if (!response.ok) throw new GatewayError(await response.text(), response.status) - return response.json() + return normalizeClawStatus(await response.json()) +} + +function normalizeTime(value: unknown) { + if (typeof value === "number" && Number.isFinite(value)) return new Date(value).toISOString() + return value +} + +export function normalizeClawStatus(input: unknown) { + if (!input || typeof input !== "object" || Array.isArray(input)) return input + + const data = input as Record + return { + ...data, + ...("lastStartedAt" in data ? { lastStartedAt: normalizeTime(data.lastStartedAt) } : {}), + ...("lastStoppedAt" in data ? { lastStoppedAt: normalizeTime(data.lastStoppedAt) } : {}), + } } export async function getClawChatCredentials(auth: AuthStore): Promise { diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index d8f663c313..d924828078 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -1037,6 +1037,9 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper case "openSettingsPanel": vscode.commands.executeCommand("kilo-code.new.settingsButtonClicked", message.tab) break + case "openKiloClaw": + vscode.commands.executeCommand("kilo-code.new.kiloClawOpen") + break case "openVSCodeSettings": vscode.commands.executeCommand("workbench.action.openSettings", message.query) break diff --git a/packages/kilo-vscode/webview-ui/src/hooks/useSlashCommand.ts b/packages/kilo-vscode/webview-ui/src/hooks/useSlashCommand.ts index ff6e8f53f6..0ee4d79232 100644 --- a/packages/kilo-vscode/webview-ui/src/hooks/useSlashCommand.ts +++ b/packages/kilo-vscode/webview-ui/src/hooks/useSlashCommand.ts @@ -143,6 +143,14 @@ export function useSlashCommand( vscode.postMessage({ type: "toggleRemote" }) }, }, + { + name: "kiloclaw", + description: "Open KiloClaw chat", + hints: ["claw"], + action: () => { + vscode.postMessage({ type: "openKiloClaw" }) + }, + }, { name: "sandbox", description: "Toggle sandbox", 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 b77530566d..e8d7624f02 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 @@ -228,6 +228,10 @@ export interface OpenAdvancedWorktreeRequest { type: "openAdvancedWorktree" } +export interface OpenKiloClawRequest { + type: "openKiloClaw" +} + export interface RequestAgentsMessage { type: "requestAgents" } @@ -1172,6 +1176,7 @@ export type WebviewMessage = | OpenMarketplacePanelRequest | OpenAgentManagerRequest | OpenAdvancedWorktreeRequest + | OpenKiloClawRequest | OpenFileRequest | ValidateFilesRequest | CancelLoginRequest diff --git a/packages/opencode/src/kilocode/server/httpapi/handlers/kilo-gateway.ts b/packages/opencode/src/kilocode/server/httpapi/handlers/kilo-gateway.ts index e8cc1200c9..dc37a5628a 100644 --- a/packages/opencode/src/kilocode/server/httpapi/handlers/kilo-gateway.ts +++ b/packages/opencode/src/kilocode/server/httpapi/handlers/kilo-gateway.ts @@ -6,6 +6,7 @@ import { getOrganizationId, getToken, importSessionToDb, + normalizeClawStatus, } from "@kilocode/kilo-gateway" import { HEADER_FEATURE, @@ -360,7 +361,7 @@ export const kiloGatewayHandlers = HttpApiBuilder.group(InstanceHttpApi, "kilo", try: async () => { const response = await fetch(`${KILO_API_BASE}/api/kiloclaw/status`, { headers }) if (!response.ok) throw new GatewayError(await response.text(), response.status) - return Schema.decodeUnknownPromise(ClawStatus)(await response.json()) + return Schema.decodeUnknownPromise(ClawStatus)(normalizeClawStatus(await response.json())) }, catch: (err) => err, }).pipe( diff --git a/packages/opencode/test/kilocode/server/kilo-gateway-statuses.test.ts b/packages/opencode/test/kilocode/server/kilo-gateway-statuses.test.ts index 5426a3b105..f19063e5f5 100644 --- a/packages/opencode/test/kilocode/server/kilo-gateway-statuses.test.ts +++ b/packages/opencode/test/kilocode/server/kilo-gateway-statuses.test.ts @@ -183,6 +183,32 @@ describe("Kilo gateway HttpApi statuses", () => { }), ) + it.live("normalizes numeric KiloClaw timestamps", () => + Effect.gen(function* () { + const started = 1_700_000_000_000 + yield* stub(() => + Response.json({ + status: "running", + sandboxId: "sandbox", + userId: "user", + lastStartedAt: started, + lastStoppedAt: null, + }), + ) + + const response = yield* HttpClient.get(KiloGatewayPaths.clawStatus) + + expect(response.status).toBe(200) + expect(yield* response.json).toEqual({ + status: "running", + sandboxId: "sandbox", + userId: "user", + lastStartedAt: new Date(started).toISOString(), + lastStoppedAt: null, + }) + }), + ) + it.live("maps KiloClaw transport failures to bad gateway", () => Effect.gen(function* () { yield* stub(() => Promise.reject(new TypeError("network error")))