diff --git a/.changeset/cli-auth-reminder.md b/.changeset/cli-auth-reminder.md new file mode 100644 index 00000000000..73be0e9f982 --- /dev/null +++ b/.changeset/cli-auth-reminder.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Agent Manager: remind first-time CLI installs to run `kilocode auth` after opening the install terminal, with translations. diff --git a/.changeset/ripe-bats-wish.md b/.changeset/ripe-bats-wish.md new file mode 100644 index 00000000000..3fa8281fbe2 --- /dev/null +++ b/.changeset/ripe-bats-wish.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Fix styling issue on task headers diff --git a/apps/kilocode-docs/docs/plans/about.md b/apps/kilocode-docs/docs/plans/about.md index 398d18c923f..704b3612e47 100644 --- a/apps/kilocode-docs/docs/plans/about.md +++ b/apps/kilocode-docs/docs/plans/about.md @@ -25,6 +25,7 @@ No credits are included with a Teams or Enterprise plan purchase. - **Centralized billing** - one invoice for your whole team - **Complete transparency** - see every request, cost, and usage pattern - **Team management** - roles, permissions, and usage controls +- **AI Adoption Score** - see how well your team is using AI to accelerate development **Cost:** $15 per user per month diff --git a/packages/telemetry/src/BaseTelemetryClient.ts b/packages/telemetry/src/BaseTelemetryClient.ts index 013da95997f..a5713f766f2 100644 --- a/packages/telemetry/src/BaseTelemetryClient.ts +++ b/packages/telemetry/src/BaseTelemetryClient.ts @@ -54,6 +54,14 @@ export abstract class BaseTelemetryClient implements TelemetryClient { // Event properties take precedence in case of conflicts. const mergedProperties = { ...providerProperties, ...(event.properties || {}) } + // kilocode_change start + // Add organization ID if available from provider properties + // This ensures all events include the organization ID when present + if (providerProperties.kilocodeOrganizationId && !mergedProperties.kilocodeOrganizationId) { + mergedProperties.kilocodeOrganizationId = providerProperties.kilocodeOrganizationId + } + // kilocode_change end + // Filter out properties that shouldn't be captured by this client return Object.fromEntries(Object.entries(mergedProperties).filter(([key]) => this.isPropertyCapturable(key))) } diff --git a/packages/telemetry/src/__tests__/PostHogTelemetryClient.test.ts b/packages/telemetry/src/__tests__/PostHogTelemetryClient.test.ts index 68961a99fda..64decfaa6b1 100644 --- a/packages/telemetry/src/__tests__/PostHogTelemetryClient.test.ts +++ b/packages/telemetry/src/__tests__/PostHogTelemetryClient.test.ts @@ -183,6 +183,108 @@ describe("PostHogTelemetryClient", () => { }) }) + it("should include organization ID from provider properties", async () => { + const client = new PostHogTelemetryClient() + + const mockProvider: TelemetryPropertiesProvider = { + getTelemetryProperties: vi.fn().mockResolvedValue({ + appVersion: "1.0.0", + vscodeVersion: "1.60.0", + platform: "darwin", + editorName: "vscode", + language: "en", + mode: "code", + kilocodeOrganizationId: "org-123", + }), + } + + client.setProvider(mockProvider) + + const getEventProperties = getPrivateProperty< + (event: { event: TelemetryEventName; properties?: Record }) => Promise> + >(client, "getEventProperties").bind(client) + + const result = await getEventProperties({ + event: TelemetryEventName.TASK_CREATED, + properties: { + customProp: "value", + }, + }) + + // Organization ID should be included + expect(result).toEqual({ + appVersion: "1.0.0", + vscodeVersion: "1.60.0", + platform: "darwin", + editorName: "vscode", + language: "en", + mode: "code", + kilocodeOrganizationId: "org-123", + customProp: "value", + }) + }) + + it("should not override organization ID from event properties", async () => { + const client = new PostHogTelemetryClient() + + const mockProvider: TelemetryPropertiesProvider = { + getTelemetryProperties: vi.fn().mockResolvedValue({ + appVersion: "1.0.0", + kilocodeOrganizationId: "org-from-provider", + }), + } + + client.setProvider(mockProvider) + + const getEventProperties = getPrivateProperty< + (event: { event: TelemetryEventName; properties?: Record }) => Promise> + >(client, "getEventProperties").bind(client) + + const result = await getEventProperties({ + event: TelemetryEventName.TASK_CREATED, + properties: { + kilocodeOrganizationId: "org-from-event", + }, + }) + + // Event property should take precedence + expect(result.kilocodeOrganizationId).toBe("org-from-event") + }) + + it("should handle missing organization ID gracefully", async () => { + const client = new PostHogTelemetryClient() + + const mockProvider: TelemetryPropertiesProvider = { + getTelemetryProperties: vi.fn().mockResolvedValue({ + appVersion: "1.0.0", + vscodeVersion: "1.60.0", + platform: "darwin", + }), + } + + client.setProvider(mockProvider) + + const getEventProperties = getPrivateProperty< + (event: { event: TelemetryEventName; properties?: Record }) => Promise> + >(client, "getEventProperties").bind(client) + + const result = await getEventProperties({ + event: TelemetryEventName.TASK_CREATED, + properties: { + customProp: "value", + }, + }) + + // Should not have organization ID + expect(result).not.toHaveProperty("kilocodeOrganizationId") + expect(result).toEqual({ + appVersion: "1.0.0", + vscodeVersion: "1.60.0", + platform: "darwin", + customProp: "value", + }) + }) + it("should handle errors from provider gracefully", async () => { const client = new PostHogTelemetryClient() @@ -327,6 +429,77 @@ describe("PostHogTelemetryClient", () => { expect(captureCall.properties).not.toHaveProperty("repositoryName") expect(captureCall.properties).not.toHaveProperty("defaultBranch") }) + + it("should include organization ID in captured events", async () => { + const client = new PostHogTelemetryClient() + client.updateTelemetryState(true) + + const mockProvider: TelemetryPropertiesProvider = { + getTelemetryProperties: vi.fn().mockResolvedValue({ + appVersion: "1.0.0", + vscodeVersion: "1.60.0", + platform: "darwin", + editorName: "vscode", + language: "en", + mode: "code", + kilocodeOrganizationId: "org-456", + }), + } + + client.setProvider(mockProvider) + + await client.capture({ + event: TelemetryEventName.TASK_CREATED, + properties: { test: "value" }, + }) + + expect(mockPostHogClient.capture).toHaveBeenCalledWith({ + distinctId: "test-machine-id", + event: TelemetryEventName.TASK_CREATED, + properties: expect.objectContaining({ + appVersion: "1.0.0", + test: "value", + kilocodeOrganizationId: "org-456", + }), + }) + + // Verify organization ID is included + const captureCall = mockPostHogClient.capture.mock.calls[0][0] + expect(captureCall.properties.kilocodeOrganizationId).toBe("org-456") + }) + + it("should capture events without organization ID when not provided", async () => { + const client = new PostHogTelemetryClient() + client.updateTelemetryState(true) + + const mockProvider: TelemetryPropertiesProvider = { + getTelemetryProperties: vi.fn().mockResolvedValue({ + appVersion: "1.0.0", + vscodeVersion: "1.60.0", + platform: "darwin", + }), + } + + client.setProvider(mockProvider) + + await client.capture({ + event: TelemetryEventName.TASK_CREATED, + properties: { test: "value" }, + }) + + expect(mockPostHogClient.capture).toHaveBeenCalledWith({ + distinctId: "test-machine-id", + event: TelemetryEventName.TASK_CREATED, + properties: expect.objectContaining({ + appVersion: "1.0.0", + test: "value", + }), + }) + + // Verify organization ID is not included + const captureCall = mockPostHogClient.capture.mock.calls[0][0] + expect(captureCall.properties).not.toHaveProperty("kilocodeOrganizationId") + }) }) describe("updateTelemetryState", () => { diff --git a/src/core/kilocode/agent-manager/AgentManagerProvider.ts b/src/core/kilocode/agent-manager/AgentManagerProvider.ts index 4ba4f57500a..fee039175bc 100644 --- a/src/core/kilocode/agent-manager/AgentManagerProvider.ts +++ b/src/core/kilocode/agent-manager/AgentManagerProvider.ts @@ -10,6 +10,7 @@ import { parseParallelModeCompletionBranch, } from "./parallelModeParser" import { findKilocodeCli } from "./CliPathResolver" +import { canInstallCli, getCliInstallCommand } from "./CliInstaller" import { CliProcessHandler, type CliProcessHandlerCallbacks } from "./CliProcessHandler" import type { StreamEvent, KilocodeStreamEvent, KilocodePayload, WelcomeStreamEvent } from "./CliOutputParser" import { RemoteSessionService } from "./RemoteSessionService" @@ -787,32 +788,111 @@ export class AgentManagerProvider implements vscode.Disposable { this.showCliError({ type: "spawn_error", message: "CLI not found" }) } + /** + * Open a terminal and run the CLI install command. + * Uses the terminal to ensure the user's shell environment (nvm, fnm, volta, etc.) is respected. + */ + private runInstallInTerminal(): void { + const shellPath = process.platform === "win32" ? undefined : process.env.SHELL + const shellName = shellPath ? path.basename(shellPath) : undefined + const shellArgs = process.platform === "win32" ? undefined : shellName === "zsh" ? ["-l", "-i"] : ["-l"] + + const terminal = vscode.window.createTerminal({ + name: "Install Kilocode CLI", + message: t("kilocode:agentManager.terminal.installMessage"), + shellPath, + shellArgs, + }) + terminal.show() + terminal.sendText(getCliInstallCommand()) + const authLabel = t("kilocode:agentManager.actions.loginCli") + void vscode.window + .showInformationMessage(t("kilocode:agentManager.terminal.authReminder"), authLabel) + .then((selection) => { + if (selection === authLabel) { + terminal.sendText("kilocode auth") + } + }) + } + private showCliError(error?: { type: "cli_outdated" | "spawn_error" | "unknown"; message: string }): void { - let errorMessage: string - let actionLabel: string + const hasNpm = canInstallCli((msg) => this.outputChannel.appendLine(`[AgentManager] ${msg}`)) switch (error?.type) { case "cli_outdated": - errorMessage = t("kilocode:agentManager.errors.cliOutdated") - actionLabel = t("kilocode:agentManager.actions.updateInstructions") + if (hasNpm) { + // Offer to update via terminal + const updateInTerminal = t("kilocode:agentManager.actions.runInTerminal") + const manualUpdate = t("kilocode:agentManager.actions.updateInstructions") + vscode.window + .showWarningMessage( + t("kilocode:agentManager.errors.cliOutdated"), + updateInTerminal, + manualUpdate, + ) + .then((selection) => { + if (selection === updateInTerminal) { + this.runInstallInTerminal() + } else if (selection === manualUpdate) { + void vscode.env.openExternal(vscode.Uri.parse("https://kilo.ai/docs/cli")) + } + }) + } else { + // No npm available, show manual instructions + const actionLabel = t("kilocode:agentManager.actions.updateInstructions") + vscode.window + .showErrorMessage(t("kilocode:agentManager.errors.cliOutdated"), actionLabel) + .then((selection) => { + if (selection === actionLabel) { + void vscode.env.openExternal(vscode.Uri.parse("https://kilo.ai/docs/cli")) + } + }) + } break - case "spawn_error": - errorMessage = t("kilocode:agentManager.errors.cliNotFound") - actionLabel = t("kilocode:agentManager.actions.installInstructions") + case "spawn_error": { + if (hasNpm) { + // Offer to install via terminal + const installInTerminal = t("kilocode:agentManager.actions.runInTerminal") + const manualInstall = t("kilocode:agentManager.actions.installInstructions") + vscode.window + .showErrorMessage( + t("kilocode:agentManager.errors.cliNotFound"), + installInTerminal, + manualInstall, + ) + .then((selection) => { + if (selection === installInTerminal) { + this.runInstallInTerminal() + } else if (selection === manualInstall) { + void vscode.env.openExternal(vscode.Uri.parse("https://kilo.ai/docs/cli")) + } + }) + } else { + // No npm available, show manual instructions + const actionLabel = t("kilocode:agentManager.actions.installInstructions") + vscode.window + .showErrorMessage(t("kilocode:agentManager.errors.cliNotFound"), actionLabel) + .then((selection) => { + if (selection === actionLabel) { + void vscode.env.openExternal(vscode.Uri.parse("https://kilo.ai/docs/cli")) + } + }) + } break - default: - errorMessage = error?.message + } + default: { + const errorMessage = error?.message ? t("kilocode:agentManager.errors.sessionFailedWithMessage", { message: error.message }) : t("kilocode:agentManager.errors.sessionFailed") - actionLabel = t("kilocode:agentManager.actions.getHelp") + const actionLabel = t("kilocode:agentManager.actions.getHelp") + vscode.window.showErrorMessage(errorMessage, actionLabel).then((selection) => { + if (selection === actionLabel) { + void vscode.env.openExternal(vscode.Uri.parse("https://kilo.ai/docs/cli")) + } + }) break - } - - vscode.window.showErrorMessage(errorMessage, actionLabel).then((selection) => { - if (selection === actionLabel) { - void vscode.env.openExternal(vscode.Uri.parse("https://kilo.ai/docs/cli")) } - }) + } } /** diff --git a/src/core/kilocode/agent-manager/CliInstaller.ts b/src/core/kilocode/agent-manager/CliInstaller.ts new file mode 100644 index 00000000000..92936d8a2e8 --- /dev/null +++ b/src/core/kilocode/agent-manager/CliInstaller.ts @@ -0,0 +1,56 @@ +import { execSync } from "node:child_process" + +const CLI_PACKAGE_NAME = "@kilocode/cli" + +/** + * Get the npm install command for the CLI. + * Useful for displaying to users or running in terminal. + */ +export function getCliInstallCommand(): string { + return `npm install -g ${CLI_PACKAGE_NAME}` +} + +/** + * Check if Node.js is available in the system. + * Returns the path to the node executable if found, null otherwise. + */ +export function findNodeExecutable(log?: (msg: string) => void): string | null { + const cmd = process.platform === "win32" ? "where node" : "which node" + try { + const nodePath = execSync(cmd, { encoding: "utf-8" }).split(/\r?\n/)[0]?.trim() + if (nodePath) { + log?.(`Found Node.js at: ${nodePath}`) + return nodePath + } + } catch { + log?.("Node.js not found in PATH") + } + return null +} + +/** + * Check if npm is available in the system. + * Returns the path to the npm executable if found, null otherwise. + */ +export function findNpmExecutable(log?: (msg: string) => void): string | null { + const cmd = process.platform === "win32" ? "where npm" : "which npm" + try { + const npmPath = execSync(cmd, { encoding: "utf-8" }).split(/\r?\n/)[0]?.trim() + if (npmPath) { + log?.(`Found npm at: ${npmPath}`) + return npmPath + } + } catch { + log?.("npm not found in PATH") + } + return null +} + +/** + * Check if Node.js and npm are available for CLI installation. + */ +export function canInstallCli(log?: (msg: string) => void): boolean { + const hasNode = findNodeExecutable(log) !== null + const hasNpm = findNpmExecutable(log) !== null + return hasNode && hasNpm +} diff --git a/src/core/kilocode/agent-manager/CliPathResolver.ts b/src/core/kilocode/agent-manager/CliPathResolver.ts index 9942474ec0e..237a257b53d 100644 --- a/src/core/kilocode/agent-manager/CliPathResolver.ts +++ b/src/core/kilocode/agent-manager/CliPathResolver.ts @@ -1,4 +1,5 @@ import * as path from "node:path" +import * as fs from "node:fs" import { execSync } from "node:child_process" import { fileExistsAtPath } from "../../../utils/fs" @@ -8,8 +9,15 @@ import { fileExistsAtPath } from "../../../utils/fs" * Resolution order: * 1. VS Code setting `kiloCode.agentManager.cliPath` * 2. Workspace-local build at /cli/dist/index.js - * 3. PATH using `which`/`where` - * 4. Common npm installation paths + * 3. Login shell lookup (respects user's nvm, fnm, volta, asdf config) + * 4. Direct PATH lookup (fallback for system-wide installs) + * 5. Common npm installation paths (last resort) + * + * IMPORTANT: Login shell is checked BEFORE direct PATH because: + * - The user's shell environment is the source of truth for which node/npm they use + * - Direct PATH might find stale system-wide installations (e.g., old homebrew version) + * - When we auto-update via `npm install -g`, it installs to the user's node (nvm etc.) + * - So we need to find the CLI in the same location where updates go */ export async function findKilocodeCli(log?: (msg: string) => void): Promise { // 1) Explicit override from settings @@ -39,14 +47,24 @@ export async function findKilocodeCli(log?: (msg: string) => void): Promise void): Promise void): string | null { const cmd = process.platform === "win32" ? "where kilocode" : "which kilocode" try { - return execSync(cmd, { encoding: "utf-8" }).split(/\r?\n/)[0]?.trim() || null + const result = execSync(cmd, { encoding: "utf-8", timeout: 5000 }).split(/\r?\n/)[0]?.trim() + if (result) { + log?.(`Found CLI in PATH: ${result}`) + return result + } } catch { - log?.("kilocode not in PATH") + log?.("kilocode not found in direct PATH lookup") + } + return null +} + +/** + * Try to find kilocode by running `which` in a login shell. + * This sources the user's shell profile (~/.zshrc, ~/.bashrc, etc.) + * which sets up version managers like nvm, fnm, volta, asdf, etc. + * + * This is the most reliable way to find CLI installed via version managers + * because VS Code's extension host doesn't inherit the user's shell environment. + */ +function findViaLoginShell(log?: (msg: string) => void): string | null { + if (process.platform === "win32") { + // Windows doesn't have the same shell environment concept return null } + + // Detect user's shell from SHELL env var, default to bash + const userShell = process.env.SHELL || "/bin/bash" + const shellName = path.basename(userShell) + + // Use login shell (-l) to source profile files, interactive (-i) for some shells + // that only source certain files in interactive mode + const shellFlags = shellName === "zsh" ? "-l -i" : "-l" + const cmd = `${userShell} ${shellFlags} -c 'which kilocode' 2>/dev/null` + + try { + log?.(`Trying login shell lookup: ${cmd}`) + const result = execSync(cmd, { + encoding: "utf-8", + timeout: 10000, // 10s timeout - login shells can be slow + env: { ...process.env, HOME: process.env.HOME }, // Ensure HOME is set + }) + .split(/\r?\n/)[0] + ?.trim() + + if (result && !result.includes("not found")) { + log?.(`Found CLI via login shell: ${result}`) + return result + } + } catch (error) { + // This is expected if CLI is not installed or shell init is slow/broken + log?.(`Login shell lookup failed (this is normal if CLI not installed via version manager): ${error}`) + } + + return null } -function getNpmPaths(): string[] { +/** + * Get fallback paths to check for CLI installation. + * This is used when login shell lookup fails or on Windows. + */ +function getNpmPaths(log?: (msg: string) => void): string[] { const home = process.env.HOME || process.env.USERPROFILE || "" if (process.platform === "win32") { const appData = process.env.APPDATA || "" - return appData ? [path.join(appData, "npm", "kilocode.cmd")] : [] + const localAppData = process.env.LOCALAPPDATA || "" + return [ + appData ? path.join(appData, "npm", "kilocode.cmd") : "", + appData ? path.join(appData, "npm", "kilocode") : "", + localAppData ? path.join(localAppData, "npm", "kilocode.cmd") : "", + ].filter(Boolean) } - return [ + // macOS and Linux paths + const paths = [ + // macOS Homebrew (Apple Silicon) "/opt/homebrew/bin/kilocode", + // macOS Homebrew (Intel) and Linux standard "/usr/local/bin/kilocode", + // Common user-local npm prefix path.join(home, ".npm-global", "bin", "kilocode"), - ].filter(Boolean) + // nvm: scan installed versions + ...getNvmPaths(home, log), + // fnm + path.join(home, ".local", "share", "fnm", "aliases", "default", "bin", "kilocode"), + // volta + path.join(home, ".volta", "bin", "kilocode"), + // asdf nodejs plugin + path.join(home, ".asdf", "shims", "kilocode"), + // Linux snap + "/snap/bin/kilocode", + // Linux user local bin + path.join(home, ".local", "bin", "kilocode"), + ] + + return paths.filter(Boolean) +} + +/** + * Get potential nvm paths for the kilocode CLI. + * nvm installs node versions in ~/.nvm/versions/node/ + * + * Note: This is a fallback - the login shell approach (findViaLoginShell) + * is preferred because it respects the user's shell configuration. + */ +function getNvmPaths(home: string, log?: (msg: string) => void): string[] { + const nvmDir = process.env.NVM_DIR || path.join(home, ".nvm") + const versionsDir = path.join(nvmDir, "versions", "node") + + const paths: string[] = [] + + // Check NVM_BIN if set (current nvm version in the shell) + if (process.env.NVM_BIN) { + paths.push(path.join(process.env.NVM_BIN, "kilocode")) + } + + // Scan the nvm versions directory for installed node versions + try { + if (fs.existsSync(versionsDir)) { + const versions = fs.readdirSync(versionsDir) + // Sort versions in reverse order to check newer versions first + versions.sort().reverse() + log?.(`Found ${versions.length} nvm node versions to check`) + for (const version of versions) { + paths.push(path.join(versionsDir, version, "bin", "kilocode")) + } + } + } catch (error) { + // This is normal if user doesn't have nvm installed + log?.(`Could not scan nvm versions directory: ${error}`) + } + + return paths } diff --git a/src/core/kilocode/agent-manager/__tests__/CliInstaller.spec.ts b/src/core/kilocode/agent-manager/__tests__/CliInstaller.spec.ts new file mode 100644 index 00000000000..746bc6cf8d0 --- /dev/null +++ b/src/core/kilocode/agent-manager/__tests__/CliInstaller.spec.ts @@ -0,0 +1,119 @@ +import { describe, expect, it, vi, beforeEach, afterEach } from "vitest" + +describe("CliInstaller", () => { + beforeEach(() => { + vi.resetModules() + }) + + afterEach(() => { + vi.restoreAllMocks() + }) + + describe("getCliInstallCommand", () => { + it("returns the npm install command for the CLI", async () => { + const { getCliInstallCommand } = await import("../CliInstaller") + const command = getCliInstallCommand() + expect(command).toBe("npm install -g @kilocode/cli") + }) + }) + + describe("findNodeExecutable", () => { + it("finds node in PATH", async () => { + vi.doMock("node:child_process", () => ({ + execSync: vi.fn().mockReturnValue("/usr/local/bin/node\n"), + })) + + const { findNodeExecutable } = await import("../CliInstaller") + const result = findNodeExecutable() + + expect(result).toBe("/usr/local/bin/node") + }) + + it("returns null when node is not found", async () => { + vi.doMock("node:child_process", () => ({ + execSync: vi.fn().mockImplementation(() => { + throw new Error("not found") + }), + })) + + const { findNodeExecutable } = await import("../CliInstaller") + const logMock = vi.fn() + const result = findNodeExecutable(logMock) + + expect(result).toBeNull() + expect(logMock).toHaveBeenCalledWith("Node.js not found in PATH") + }) + + it("logs when node is found", async () => { + vi.doMock("node:child_process", () => ({ + execSync: vi.fn().mockReturnValue("/usr/local/bin/node"), + })) + + const { findNodeExecutable } = await import("../CliInstaller") + const logMock = vi.fn() + findNodeExecutable(logMock) + + expect(logMock).toHaveBeenCalledWith("Found Node.js at: /usr/local/bin/node") + }) + }) + + describe("findNpmExecutable", () => { + it("finds npm in PATH", async () => { + vi.doMock("node:child_process", () => ({ + execSync: vi.fn().mockReturnValue("/usr/local/bin/npm\n"), + })) + + const { findNpmExecutable } = await import("../CliInstaller") + const result = findNpmExecutable() + + expect(result).toBe("/usr/local/bin/npm") + }) + + it("returns null when npm is not found", async () => { + vi.doMock("node:child_process", () => ({ + execSync: vi.fn().mockImplementation(() => { + throw new Error("not found") + }), + })) + + const { findNpmExecutable } = await import("../CliInstaller") + const logMock = vi.fn() + const result = findNpmExecutable(logMock) + + expect(result).toBeNull() + expect(logMock).toHaveBeenCalledWith("npm not found in PATH") + }) + }) + + describe("canInstallCli", () => { + it("returns true when both node and npm are available", async () => { + vi.doMock("node:child_process", () => ({ + execSync: vi.fn().mockReturnValue("/usr/local/bin/node"), + })) + + const { canInstallCli } = await import("../CliInstaller") + const result = canInstallCli() + + expect(result).toBe(true) + }) + + it("returns false when node is not available", async () => { + let callCount = 0 + vi.doMock("node:child_process", () => ({ + execSync: vi.fn().mockImplementation((cmd: string) => { + callCount++ + // First call for node fails + if (callCount === 1 || cmd.includes("node")) { + throw new Error("not found") + } + return "/usr/local/bin/npm" + }), + })) + + const { canInstallCli } = await import("../CliInstaller") + const result = canInstallCli() + + expect(result).toBe(false) + }) + }) +}) diff --git a/src/core/kilocode/agent-manager/__tests__/CliPathResolver.spec.ts b/src/core/kilocode/agent-manager/__tests__/CliPathResolver.spec.ts index 045b07588bf..663a65cb5c3 100644 --- a/src/core/kilocode/agent-manager/__tests__/CliPathResolver.spec.ts +++ b/src/core/kilocode/agent-manager/__tests__/CliPathResolver.spec.ts @@ -1,25 +1,52 @@ import { describe, expect, it, vi, beforeEach } from "vitest" +const isWindows = process.platform === "win32" + describe("findKilocodeCli", () => { beforeEach(() => { vi.resetModules() }) - it("finds CLI in PATH and returns trimmed result", async () => { - const execSyncMock = vi.fn().mockReturnValue("/usr/local/bin/kilocode\n") + const loginShellTests = isWindows ? it.skip : it + + loginShellTests("finds CLI via login shell and returns trimmed result", async () => { + // Login shell is tried first, so mock it to succeed + const execSyncMock = vi.fn().mockReturnValue("/Users/test/.nvm/versions/node/v20/bin/kilocode\n") vi.doMock("node:child_process", () => ({ execSync: execSyncMock })) vi.doMock("../../../../utils/fs", () => ({ fileExistsAtPath: vi.fn().mockResolvedValue(false) })) const { findKilocodeCli } = await import("../CliPathResolver") const result = await findKilocodeCli() - expect(result).toBe("/usr/local/bin/kilocode") - expect(execSyncMock).toHaveBeenCalledWith(expect.stringMatching(/^(which|where) kilocode$/), { - encoding: "utf-8", + expect(result).toBe("/Users/test/.nvm/versions/node/v20/bin/kilocode") + // First call should be login shell (on non-Windows) + expect(execSyncMock).toHaveBeenCalledWith( + expect.stringContaining("which kilocode"), + expect.objectContaining({ encoding: "utf-8" }), + ) + }) + + loginShellTests("falls back to direct PATH when login shell fails", async () => { + let callCount = 0 + const execSyncMock = vi.fn().mockImplementation((cmd: string) => { + callCount++ + // First call (login shell) fails, second call (direct PATH) succeeds + if (callCount === 1) { + throw new Error("login shell failed") + } + return "/usr/local/bin/kilocode\n" }) + vi.doMock("node:child_process", () => ({ execSync: execSyncMock })) + vi.doMock("../../../../utils/fs", () => ({ fileExistsAtPath: vi.fn().mockResolvedValue(false) })) + + const { findKilocodeCli } = await import("../CliPathResolver") + const result = await findKilocodeCli() + + expect(result).toBe("/usr/local/bin/kilocode") + expect(execSyncMock).toHaveBeenCalledTimes(2) }) - it("falls back to npm paths when PATH lookup fails", async () => { + it("falls back to npm paths when all PATH lookups fail", async () => { const execSyncMock = vi.fn().mockImplementation(() => { throw new Error("not found") }) @@ -53,7 +80,7 @@ describe("findKilocodeCli", () => { expect(logMock).toHaveBeenCalledWith("kilocode CLI not found") }) - it("logs when kilocode not in PATH", async () => { + it("logs when kilocode not in direct PATH", async () => { vi.doMock("node:child_process", () => ({ execSync: vi.fn().mockImplementation(() => { throw new Error("not found") @@ -65,6 +92,6 @@ describe("findKilocodeCli", () => { const logMock = vi.fn() await findKilocodeCli(logMock) - expect(logMock).toHaveBeenCalledWith("kilocode not in PATH") + expect(logMock).toHaveBeenCalledWith("kilocode not found in direct PATH lookup") }) }) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index e7c54776ad5..b5ce245a8cd 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -3441,6 +3441,10 @@ ${prompt} ...getFastApply(), ...getOpenRouter(), ...getAutoApproveSettings(), + // Add organization ID if available + ...(apiConfiguration.kilocodeOrganizationId && { + kilocodeOrganizationId: apiConfiguration.kilocodeOrganizationId, + }), // kilocode_change end ...(await this.getTaskProperties()), ...(await this.getGitProperties()), diff --git a/src/i18n/locales/ar/kilocode.json b/src/i18n/locales/ar/kilocode.json index 11bfb2e6463..13f8717553b 100644 --- a/src/i18n/locales/ar/kilocode.json +++ b/src/i18n/locales/ar/kilocode.json @@ -186,7 +186,13 @@ "actions": { "updateInstructions": "تعليمات التحديث", "installInstructions": "تعليمات التثبيت", - "getHelp": "الحصول على المساعدة" + "getHelp": "الحصول على المساعدة", + "runInTerminal": "تشغيل في الطرفية", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "تشغيل npm install لتثبيت Kilocode CLI. بمجرد الانتهاء، يمكنك إغلاق هذه الطرفية ومحاولة بدء Agent Manager مرة أخرى.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } } } diff --git a/src/i18n/locales/ca/kilocode.json b/src/i18n/locales/ca/kilocode.json index 12e20c1c6ba..6497ad82e76 100644 --- a/src/i18n/locales/ca/kilocode.json +++ b/src/i18n/locales/ca/kilocode.json @@ -182,7 +182,13 @@ "actions": { "updateInstructions": "Instruccions d'Actualització", "installInstructions": "Instruccions d'Instal·lació", - "getHelp": "Obtenir Ajuda" + "getHelp": "Obtenir Ajuda", + "runInTerminal": "Executar al Terminal", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Executant npm install per instal·lar Kilocode CLI. Un cop completat, pots tancar aquest terminal i intentar iniciar el Gestor d'Agents de nou.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } } } diff --git a/src/i18n/locales/cs/kilocode.json b/src/i18n/locales/cs/kilocode.json index 64d457098b6..5bf7e948fff 100644 --- a/src/i18n/locales/cs/kilocode.json +++ b/src/i18n/locales/cs/kilocode.json @@ -112,7 +112,13 @@ "actions": { "updateInstructions": "Instrukce k Aktualizaci", "installInstructions": "Instrukce k Instalaci", - "getHelp": "Získat Pomoc" + "getHelp": "Získat Pomoc", + "runInTerminal": "Spustit v Terminálu", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Spouštím npm install pro instalaci Kilocode CLI. Po dokončení můžeš tento terminál zavřít a zkusit Agent Manager spustit znovu.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } }, "ghost": { diff --git a/src/i18n/locales/de/kilocode.json b/src/i18n/locales/de/kilocode.json index 9b2e2f09d0b..519daa69da0 100644 --- a/src/i18n/locales/de/kilocode.json +++ b/src/i18n/locales/de/kilocode.json @@ -182,7 +182,13 @@ "actions": { "updateInstructions": "Update-Anleitung", "installInstructions": "Installationsanleitung", - "getHelp": "Hilfe erhalten" + "getHelp": "Hilfe erhalten", + "runInTerminal": "Im Terminal ausführen", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Führe npm install aus, um die Kilocode CLI zu installieren. Sobald die Installation abgeschlossen ist, kannst du dieses Terminal schließen und versuchen, den Agent Manager erneut zu starten.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } } } diff --git a/src/i18n/locales/en/kilocode.json b/src/i18n/locales/en/kilocode.json index 7d8ff4e333f..a02fdee5c60 100644 --- a/src/i18n/locales/en/kilocode.json +++ b/src/i18n/locales/en/kilocode.json @@ -167,7 +167,13 @@ "actions": { "updateInstructions": "Update Instructions", "installInstructions": "Install Instructions", - "getHelp": "Get Help" + "getHelp": "Get Help", + "runInTerminal": "Run in Terminal", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Running npm install to install the Kilocode CLI. Once complete, you can close this terminal and try starting the Agent Manager again.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } }, "terminalCommandGenerator": { diff --git a/src/i18n/locales/es/kilocode.json b/src/i18n/locales/es/kilocode.json index 7ce17bf3573..4b0735e38a5 100644 --- a/src/i18n/locales/es/kilocode.json +++ b/src/i18n/locales/es/kilocode.json @@ -182,7 +182,13 @@ "actions": { "updateInstructions": "Instrucciones de Actualización", "installInstructions": "Instrucciones de Instalación", - "getHelp": "Obtener Ayuda" + "getHelp": "Obtener Ayuda", + "runInTerminal": "Ejecutar en Terminal", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Ejecutando npm install para instalar Kilocode CLI. Una vez completado, puedes cerrar este terminal e intentar iniciar el Gestor de Agentes nuevamente.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } } } diff --git a/src/i18n/locales/fr/kilocode.json b/src/i18n/locales/fr/kilocode.json index 425b123002c..97c4ba11459 100644 --- a/src/i18n/locales/fr/kilocode.json +++ b/src/i18n/locales/fr/kilocode.json @@ -182,7 +182,13 @@ "actions": { "updateInstructions": "Instructions de Mise à Jour", "installInstructions": "Instructions d'Installation", - "getHelp": "Obtenir de l'Aide" + "getHelp": "Obtenir de l'Aide", + "runInTerminal": "Exécuter dans le Terminal", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Exécution de npm install pour installer Kilocode CLI. Une fois terminé, tu peux fermer ce terminal et essayer de démarrer le Gestionnaire d'Agents à nouveau.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } } } diff --git a/src/i18n/locales/hi/kilocode.json b/src/i18n/locales/hi/kilocode.json index 0ab9b3bf854..bfbfaa96e8d 100644 --- a/src/i18n/locales/hi/kilocode.json +++ b/src/i18n/locales/hi/kilocode.json @@ -112,7 +112,13 @@ "actions": { "updateInstructions": "अपडेट निर्देश", "installInstructions": "इंस्टॉल निर्देश", - "getHelp": "सहायता प्राप्त करें" + "getHelp": "सहायता प्राप्त करें", + "runInTerminal": "टर्मिनल में चलाएं", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Kilocode CLI इंस्टॉल करने के लिए npm install चल रहा है। पूर्ण होने के बाद, आप यह टर्मिनल बंद कर सकते हैं और Agent Manager को फिर से शुरू करने का प्रयास कर सकते हैं।", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } }, "ghost": { diff --git a/src/i18n/locales/id/kilocode.json b/src/i18n/locales/id/kilocode.json index 223ae5e1807..3dfad0b16c3 100644 --- a/src/i18n/locales/id/kilocode.json +++ b/src/i18n/locales/id/kilocode.json @@ -182,7 +182,13 @@ "actions": { "updateInstructions": "Petunjuk Pembaruan", "installInstructions": "Petunjuk Instalasi", - "getHelp": "Dapatkan Bantuan" + "getHelp": "Dapatkan Bantuan", + "runInTerminal": "Jalankan di Terminal", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Menjalankan npm install untuk menginstal Kilocode CLI. Setelah selesai, kamu dapat menutup terminal ini dan mencoba memulai Agent Manager lagi.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } } } diff --git a/src/i18n/locales/it/kilocode.json b/src/i18n/locales/it/kilocode.json index c45bf7fa76b..c2fe0ab31d3 100644 --- a/src/i18n/locales/it/kilocode.json +++ b/src/i18n/locales/it/kilocode.json @@ -182,7 +182,13 @@ "actions": { "updateInstructions": "Istruzioni di Aggiornamento", "installInstructions": "Istruzioni di Installazione", - "getHelp": "Ottieni Aiuto" + "getHelp": "Ottieni Aiuto", + "runInTerminal": "Esegui nel Terminale", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Esecuzione di npm install per installare Kilocode CLI. Una volta completato, puoi chiudere questo terminale e provare ad avviare nuovamente l'Agent Manager.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } } } diff --git a/src/i18n/locales/ja/kilocode.json b/src/i18n/locales/ja/kilocode.json index 251ff8805dd..6c620fbc94a 100644 --- a/src/i18n/locales/ja/kilocode.json +++ b/src/i18n/locales/ja/kilocode.json @@ -182,7 +182,13 @@ "actions": { "updateInstructions": "更新手順", "installInstructions": "インストール手順", - "getHelp": "ヘルプを表示" + "getHelp": "ヘルプを表示", + "runInTerminal": "ターミナルで実行", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Kilocode CLIをインストールするためにnpm installを実行しています。完了したら、このターミナルを閉じてAgent Managerを再度起動してみてください。", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } } } diff --git a/src/i18n/locales/ko/kilocode.json b/src/i18n/locales/ko/kilocode.json index 1df16d6f694..f56c5c932ee 100644 --- a/src/i18n/locales/ko/kilocode.json +++ b/src/i18n/locales/ko/kilocode.json @@ -182,7 +182,13 @@ "actions": { "updateInstructions": "업데이트 지침", "installInstructions": "설치 지침", - "getHelp": "도움말 보기" + "getHelp": "도움말 보기", + "runInTerminal": "터미널에서 실행", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Kilocode CLI를 설치하기 위해 npm install을 실행합니다. 완료되면 이 터미널을 닫고 Agent Manager를 다시 시작해 보세요.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } } } diff --git a/src/i18n/locales/nl/kilocode.json b/src/i18n/locales/nl/kilocode.json index b8ac262ea11..a986182be3b 100644 --- a/src/i18n/locales/nl/kilocode.json +++ b/src/i18n/locales/nl/kilocode.json @@ -182,7 +182,13 @@ "actions": { "updateInstructions": "Update-instructies", "installInstructions": "Installatie-instructies", - "getHelp": "Hulp krijgen" + "getHelp": "Hulp krijgen", + "runInTerminal": "Uitvoeren in Terminal", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "npm install uitvoeren om Kilocode CLI te installeren. Zodra voltooid, kun je dit terminal sluiten en proberen de Agent Manager opnieuw te starten.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } } } diff --git a/src/i18n/locales/pl/kilocode.json b/src/i18n/locales/pl/kilocode.json index 09b5920ca6c..66681d1e047 100644 --- a/src/i18n/locales/pl/kilocode.json +++ b/src/i18n/locales/pl/kilocode.json @@ -112,7 +112,13 @@ "actions": { "updateInstructions": "Instrukcje Aktualizacji", "installInstructions": "Instrukcje Instalacji", - "getHelp": "Uzyskaj Pomoc" + "getHelp": "Uzyskaj Pomoc", + "runInTerminal": "Uruchom w Terminalu", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Uruchamianie npm install w celu zainstalowania Kilocode CLI. Po zakończeniu możesz zamknąć ten terminal i spróbować ponownie uruchomić Agent Managera.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } }, "ghost": { diff --git a/src/i18n/locales/pt-BR/kilocode.json b/src/i18n/locales/pt-BR/kilocode.json index 8e992198213..3aaa9071fdb 100644 --- a/src/i18n/locales/pt-BR/kilocode.json +++ b/src/i18n/locales/pt-BR/kilocode.json @@ -182,7 +182,13 @@ "actions": { "updateInstructions": "Instruções de Atualização", "installInstructions": "Instruções de Instalação", - "getHelp": "Obter Ajuda" + "getHelp": "Obter Ajuda", + "runInTerminal": "Executar no Terminal", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Executando npm install para instalar o Kilocode CLI. Após a conclusão, você pode fechar este terminal e tentar iniciar o Gerenciador de Agentes novamente.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } } } diff --git a/src/i18n/locales/ru/kilocode.json b/src/i18n/locales/ru/kilocode.json index 24ed008c001..5a1cf7ef61c 100644 --- a/src/i18n/locales/ru/kilocode.json +++ b/src/i18n/locales/ru/kilocode.json @@ -182,7 +182,13 @@ "actions": { "updateInstructions": "Инструкции по Обновлению", "installInstructions": "Инструкции по Установке", - "getHelp": "Получить Помощь" + "getHelp": "Получить Помощь", + "runInTerminal": "Запустить в Терминале", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Выполнение npm install для установки Kilocode CLI. После завершения можешь закрыть этот терминал и попробовать запустить Agent Manager снова.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } } } diff --git a/src/i18n/locales/th/kilocode.json b/src/i18n/locales/th/kilocode.json index 249b848491b..6ff64b9fa4a 100644 --- a/src/i18n/locales/th/kilocode.json +++ b/src/i18n/locales/th/kilocode.json @@ -112,7 +112,13 @@ "actions": { "updateInstructions": "คำแนะนำการอัปเดต", "installInstructions": "คำแนะนำการติดตั้ง", - "getHelp": "รับความช่วยเหลือ" + "getHelp": "รับความช่วยเหลือ", + "runInTerminal": "เรียกใช้ในเทอร์มินัล", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "กำลังรัน npm install เพื่อติดตั้ง Kilocode CLI เมื่อเสร็จแล้ว คุณสามารถปิดเทอร์มินัลนี้และลองเริ่ม Agent Manager อีกครั้ง", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } }, "ghost": { diff --git a/src/i18n/locales/tr/kilocode.json b/src/i18n/locales/tr/kilocode.json index 5ed438640c1..8b5684e57a1 100644 --- a/src/i18n/locales/tr/kilocode.json +++ b/src/i18n/locales/tr/kilocode.json @@ -182,7 +182,13 @@ "actions": { "updateInstructions": "Güncelleme Talimatları", "installInstructions": "Kurulum Talimatları", - "getHelp": "Yardım Al" + "getHelp": "Yardım Al", + "runInTerminal": "Terminalde Çalıştır", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Kilocode CLI'yi yüklemek için npm install çalıştırılıyor. Tamamlandığında, bu terminali kapatıp Agent Manager'ı tekrar başlatmayı deneyebilirsin.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } } } diff --git a/src/i18n/locales/uk/kilocode.json b/src/i18n/locales/uk/kilocode.json index b1168bb7c3d..635183d36ae 100644 --- a/src/i18n/locales/uk/kilocode.json +++ b/src/i18n/locales/uk/kilocode.json @@ -182,7 +182,13 @@ "actions": { "updateInstructions": "Інструкції з Оновлення", "installInstructions": "Інструкції з Встановлення", - "getHelp": "Отримати Допомогу" + "getHelp": "Отримати Допомогу", + "runInTerminal": "Запустити в Терміналі", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Виконання npm install для встановлення Kilocode CLI. Після завершення можеш закрити цей термінал і спробувати запустити Agent Manager знову.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } } } diff --git a/src/i18n/locales/vi/kilocode.json b/src/i18n/locales/vi/kilocode.json index 8026ab31c17..956dd2681a4 100644 --- a/src/i18n/locales/vi/kilocode.json +++ b/src/i18n/locales/vi/kilocode.json @@ -182,7 +182,13 @@ "actions": { "updateInstructions": "Hướng Dẫn Cập Nhật", "installInstructions": "Hướng Dẫn Cài Đặt", - "getHelp": "Nhận Trợ Giúp" + "getHelp": "Nhận Trợ Giúp", + "runInTerminal": "Chạy trong Terminal", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "Đang chạy npm install để cài đặt Kilocode CLI. Sau khi hoàn tất, bạn có thể đóng terminal này và thử khởi động lại Agent Manager.", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } } } diff --git a/src/i18n/locales/zh-CN/kilocode.json b/src/i18n/locales/zh-CN/kilocode.json index b33348787c0..7f67dbfbd30 100644 --- a/src/i18n/locales/zh-CN/kilocode.json +++ b/src/i18n/locales/zh-CN/kilocode.json @@ -104,7 +104,7 @@ }, "agentManager": { "errors": { - "cliOutdated": "你的 Kilocode CLI 版本已过时,不支持 Agent Manager。请更新到最新版本。", + "cliOutdated": "你的 Kilocode CLI 版本已过时,不支持 Agent Manager。请更新到最新版本。", "cliNotFound": "未找到 Kilocode CLI。请安装以使用 Agent Manager。", "sessionFailed": "启动代理会话失败", "sessionFailedWithMessage": "启动代理会话失败: {{message}}" @@ -112,7 +112,13 @@ "actions": { "updateInstructions": "更新说明", "installInstructions": "安装说明", - "getHelp": "获取帮助" + "getHelp": "获取帮助", + "runInTerminal": "运行命令", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "运行 npm install 安装 Kilocode CLI。完成后可关闭此终端,然后重新启动 Agent Manager。", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } }, "ghost": { diff --git a/src/i18n/locales/zh-TW/kilocode.json b/src/i18n/locales/zh-TW/kilocode.json index 0d255c2fa27..b9bebdee55d 100644 --- a/src/i18n/locales/zh-TW/kilocode.json +++ b/src/i18n/locales/zh-TW/kilocode.json @@ -182,7 +182,13 @@ "actions": { "updateInstructions": "更新說明", "installInstructions": "安裝說明", - "getHelp": "取得協助" + "getHelp": "取得協助", + "runInTerminal": "在終端機執行", + "loginCli": "Run kilocode auth" + }, + "terminal": { + "installMessage": "執行 npm install 以安裝 Kilocode CLI。完成後,您可以關閉此終端機並嘗試再次啟動 Agent Manager。", + "authReminder": "If this is your first install, run `kilocode auth` to sign in before starting the Agent Manager." } } } diff --git a/webview-ui/src/components/kilocode/KiloTaskHeader.tsx b/webview-ui/src/components/kilocode/KiloTaskHeader.tsx index 899135e8e2c..2f4c8fbf254 100644 --- a/webview-ui/src/components/kilocode/KiloTaskHeader.tsx +++ b/webview-ui/src/components/kilocode/KiloTaskHeader.tsx @@ -89,7 +89,7 @@ const KiloTaskHeader = ({