From e85362fb116a8124392dcbe38ebd98dd3e4289f2 Mon Sep 17 00:00:00 2001 From: cte Date: Wed, 7 Jan 2026 01:34:27 -0800 Subject: [PATCH] New task slash command --- apps/cli/src/__tests__/extension-host.test.ts | 18 - apps/cli/src/__tests__/globalCommands.test.ts | 103 ++++ apps/cli/src/extension-host.ts | 468 ++++++++++-------- apps/cli/src/globalCommands.ts | 62 +++ apps/cli/src/ui/App.tsx | 98 ++-- .../triggers/SlashCommandTrigger.tsx | 13 +- apps/cli/src/ui/types.ts | 4 + packages/types/src/git.ts | 13 + packages/types/src/index.ts | 2 + packages/types/src/marketplace.ts | 5 + packages/types/src/mcp.ts | 91 +++- packages/types/src/model.ts | 5 + packages/types/src/vscode-extension-host.ts | 327 ++++++++++++ src/api/providers/fetchers/huggingface.ts | 3 +- src/api/providers/fetchers/io-intelligence.ts | 4 +- src/api/providers/fetchers/litellm.ts | 2 +- src/api/providers/fetchers/modelCache.ts | 4 +- .../providers/fetchers/modelEndpointCache.ts | 6 +- src/api/providers/fetchers/roo.ts | 3 +- src/api/providers/huggingface.ts | 4 +- src/api/providers/openrouter.ts | 5 +- src/api/providers/requesty.ts | 3 +- src/api/providers/roo.ts | 5 +- src/api/providers/router-provider.ts | 4 +- src/core/auto-approval/index.ts | 10 +- src/core/auto-approval/mcp.ts | 4 +- .../native-tools/__tests__/mcp_server.spec.ts | 7 +- src/core/task/Task.ts | 10 + src/core/webview/ClineProvider.ts | 4 +- .../webview/__tests__/ClineProvider.spec.ts | 3 +- .../__tests__/webviewMessageHandler.spec.ts | 3 +- src/core/webview/webviewMessageHandler.ts | 3 +- src/services/mcp/McpHub.ts | 21 +- src/shared/ExtensionMessage.ts | 348 ------------- src/shared/api.ts | 6 - src/shared/mcp.ts | 83 ---- src/utils/git.ts | 18 +- webview-ui/src/App.tsx | 4 +- .../__tests__/command-autocomplete.spec.ts | 2 +- .../BrowserPanelStateProvider.tsx | 3 +- .../browser-session/BrowserSessionPanel.tsx | 14 +- .../src/components/chat/ChatTextArea.tsx | 3 +- webview-ui/src/components/chat/ChatView.tsx | 4 +- .../src/components/chat/CommandExecution.tsx | 4 +- .../src/components/chat/ContextMenu.tsx | 8 +- .../src/components/chat/McpExecution.tsx | 9 +- .../src/components/chat/SlashCommandItem.tsx | 2 +- .../chat/SlashCommandItemSimple.tsx | 2 +- .../__tests__/SlashCommandItemSimple.spec.tsx | 4 +- .../components/cloud/OrganizationSwitcher.tsx | 8 +- .../MarketplaceViewStateManager.ts | 4 +- .../src/components/mcp/McpEnabledToggle.tsx | 3 +- webview-ui/src/components/mcp/McpErrorRow.tsx | 2 +- .../src/components/mcp/McpResourceRow.tsx | 2 +- webview-ui/src/components/mcp/McpToolRow.tsx | 2 +- webview-ui/src/components/mcp/McpView.tsx | 2 +- .../settings/SlashCommandsSettings.tsx | 2 +- .../components/settings/TerminalSettings.tsx | 2 +- .../__tests__/SlashCommandsSettings.spec.tsx | 2 +- .../components/settings/providers/Chutes.tsx | 4 +- .../settings/providers/ClaudeCode.tsx | 3 + .../settings/providers/DeepInfra.tsx | 9 +- .../settings/providers/HuggingFace.tsx | 3 +- .../settings/providers/LMStudio.tsx | 4 +- .../components/settings/providers/LiteLLM.tsx | 8 +- .../components/settings/providers/Mistral.tsx | 4 +- .../components/settings/providers/Ollama.tsx | 5 +- .../settings/providers/OpenAICompatible.tsx | 3 +- .../settings/providers/OpenRouter.tsx | 9 +- .../settings/providers/QwenCode.tsx | 1 + .../settings/providers/Requesty.tsx | 9 +- .../src/components/settings/providers/Roo.tsx | 9 +- .../components/settings/providers/Unbound.tsx | 9 +- .../settings/providers/VSCodeLM.tsx | 4 +- .../settings/providers/VercelAiGateway.tsx | 9 +- .../src/components/settings/providers/ZAi.tsx | 2 +- .../components/ui/hooks/useLmStudioModels.ts | 3 +- .../components/ui/hooks/useOllamaModels.ts | 3 +- .../ui/hooks/useRooCreditBalance.ts | 4 +- .../components/ui/hooks/useRouterModels.ts | 3 +- .../components/ui/hooks/useSelectedModel.ts | 4 +- .../src/context/ExtensionStateContext.tsx | 10 +- .../__tests__/ExtensionStateContext.spec.tsx | 9 +- .../src/utils/__tests__/validate.spec.ts | 4 +- webview-ui/src/utils/context-mentions.ts | 3 +- webview-ui/src/utils/mcp.ts | 2 +- webview-ui/src/utils/validate.ts | 3 +- 87 files changed, 1142 insertions(+), 861 deletions(-) create mode 100644 apps/cli/src/__tests__/globalCommands.test.ts create mode 100644 apps/cli/src/globalCommands.ts create mode 100644 packages/types/src/git.ts create mode 100644 packages/types/src/vscode-extension-host.ts delete mode 100644 src/shared/mcp.ts diff --git a/apps/cli/src/__tests__/extension-host.test.ts b/apps/cli/src/__tests__/extension-host.test.ts index b796eb1559..55f299d0e8 100644 --- a/apps/cli/src/__tests__/extension-host.test.ts +++ b/apps/cli/src/__tests__/extension-host.test.ts @@ -433,24 +433,6 @@ describe("ExtensionHost", () => { expect(handleMsgUpdatedSpy).toHaveBeenCalled() }) - - it("should route action messages to handleActionMessage", () => { - const host = createTestHost() - const handleActionSpy = spyOnPrivate(host, "handleActionMessage") - - callPrivate(host, "handleExtensionMessage", { type: "action", action: "test" }) - - expect(handleActionSpy).toHaveBeenCalled() - }) - - it("should route invoke messages to handleInvokeMessage", () => { - const host = createTestHost() - const handleInvokeSpy = spyOnPrivate(host, "handleInvokeMessage") - - callPrivate(host, "handleExtensionMessage", { type: "invoke", invoke: "test" }) - - expect(handleInvokeSpy).toHaveBeenCalled() - }) }) describe("handleSayMessage", () => { diff --git a/apps/cli/src/__tests__/globalCommands.test.ts b/apps/cli/src/__tests__/globalCommands.test.ts new file mode 100644 index 0000000000..a3b1261861 --- /dev/null +++ b/apps/cli/src/__tests__/globalCommands.test.ts @@ -0,0 +1,103 @@ +import { describe, it, expect } from "vitest" +import { + GLOBAL_COMMANDS, + getGlobalCommand, + getGlobalCommandsForAutocomplete, + type GlobalCommand, + type GlobalCommandAction, +} from "../globalCommands.js" + +describe("globalCommands", () => { + describe("GLOBAL_COMMANDS", () => { + it("should contain the /new command", () => { + const newCommand = GLOBAL_COMMANDS.find((cmd) => cmd.name === "new") + expect(newCommand).toBeDefined() + expect(newCommand?.action).toBe("clearTask") + expect(newCommand?.description).toBe("Start a new task") + }) + + it("should have valid structure for all commands", () => { + for (const cmd of GLOBAL_COMMANDS) { + expect(cmd.name).toBeTruthy() + expect(typeof cmd.name).toBe("string") + expect(cmd.description).toBeTruthy() + expect(typeof cmd.description).toBe("string") + expect(cmd.action).toBeTruthy() + expect(typeof cmd.action).toBe("string") + } + }) + }) + + describe("getGlobalCommand", () => { + it("should return the command when found", () => { + const cmd = getGlobalCommand("new") + expect(cmd).toBeDefined() + expect(cmd?.name).toBe("new") + expect(cmd?.action).toBe("clearTask") + }) + + it("should return undefined for unknown commands", () => { + const cmd = getGlobalCommand("unknown-command") + expect(cmd).toBeUndefined() + }) + + it("should be case-sensitive", () => { + const cmd = getGlobalCommand("NEW") + expect(cmd).toBeUndefined() + }) + }) + + describe("getGlobalCommandsForAutocomplete", () => { + it("should return commands in autocomplete format", () => { + const commands = getGlobalCommandsForAutocomplete() + expect(commands.length).toBe(GLOBAL_COMMANDS.length) + + for (const cmd of commands) { + expect(cmd.name).toBeTruthy() + expect(cmd.source).toBe("global") + expect(cmd.action).toBeTruthy() + } + }) + + it("should include the /new command with correct format", () => { + const commands = getGlobalCommandsForAutocomplete() + const newCommand = commands.find((cmd) => cmd.name === "new") + + expect(newCommand).toBeDefined() + expect(newCommand?.description).toBe("Start a new task") + expect(newCommand?.source).toBe("global") + expect(newCommand?.action).toBe("clearTask") + }) + + it("should not include argumentHint for action commands", () => { + const commands = getGlobalCommandsForAutocomplete() + // Action commands don't have argument hints + for (const cmd of commands) { + expect(cmd).not.toHaveProperty("argumentHint") + } + }) + }) + + describe("type safety", () => { + it("should have valid GlobalCommandAction types", () => { + // This test ensures the type is properly constrained + const validActions: GlobalCommandAction[] = ["clearTask"] + + for (const cmd of GLOBAL_COMMANDS) { + expect(validActions).toContain(cmd.action) + } + }) + + it("should match GlobalCommand interface", () => { + const testCommand: GlobalCommand = { + name: "test", + description: "Test command", + action: "clearTask", + } + + expect(testCommand.name).toBe("test") + expect(testCommand.description).toBe("Test command") + expect(testCommand.action).toBe("clearTask") + }) + }) +}) diff --git a/apps/cli/src/extension-host.ts b/apps/cli/src/extension-host.ts index b2259c614e..98f6580292 100644 --- a/apps/cli/src/extension-host.ts +++ b/apps/cli/src/extension-host.ts @@ -13,10 +13,11 @@ import { createRequire } from "module" import path from "path" import { fileURLToPath } from "url" import fs from "fs" +import os from "os" import readline from "readline" +import { ProviderName, ReasoningEffortExtended, RooCodeSettings, ExtensionMessage } from "@roo-code/types" import { createVSCodeAPI, setRuntimeConfigValues } from "@roo-code/vscode-shim" -import { ProviderName, ReasoningEffortExtended, RooCodeSettings } from "@roo-code/types" // Get the CLI package root directory (for finding node_modules/@vscode/ripgrep) // When bundled, import.meta.url points to dist/index.js, so go up to package root @@ -53,6 +54,8 @@ interface WebviewViewProvider { resolveWebviewView?(webviewView: unknown, context: unknown, token: unknown): void | Promise } +const DEBUG_LOG_PATH = path.join(os.homedir(), ".roo", "cli-debug.log") + export class ExtensionHost extends EventEmitter { private vscode: ReturnType | null = null private extensionModule: ExtensionModule | null = null @@ -61,7 +64,7 @@ export class ExtensionHost extends EventEmitter { private options: ExtensionHostOptions private isWebviewReady = false private pendingMessages: unknown[] = [] - private messageListener: ((message: unknown) => void) | null = null + private messageListener: ((message: ExtensionMessage) => void) | null = null private originalConsole: { log: typeof console.log @@ -85,9 +88,6 @@ export class ExtensionHost extends EventEmitter { // Track streamed content by ts for delta computation private streamedContent: Map = new Map() - // Track message processing for verbose debug output - private processedMessageCount = 0 - // Track if we're currently streaming a message (to manage newlines) private currentlyStreamingTs: number | null = null @@ -97,28 +97,64 @@ export class ExtensionHost extends EventEmitter { constructor(options: ExtensionHostOptions) { super() this.options = options - // Initialize currentMode from options to track mode changes this.currentMode = options.mode || null } - private log(...args: unknown[]): void { - if (this.options.verbose) { - // Use original console if available to avoid quiet mode suppression - const logFn = this.originalConsole?.log || console.log - logFn("[ExtensionHost]", ...args) + /** + * Write debug log entry to ~/.roo/cli-debug.log + * This avoids console output which breaks the TUI. + */ + private log(message: string, data?: unknown): void { + try { + const logDir = path.dirname(DEBUG_LOG_PATH) + + if (!fs.existsSync(logDir)) { + fs.mkdirSync(logDir, { recursive: true }) + } + + const timestamp = new Date().toISOString() + + const entry = data + ? `[${timestamp}] ${message}: ${JSON.stringify(data, null, 2)}\n` + : `[${timestamp}] ${message}\n` + + fs.appendFileSync(DEBUG_LOG_PATH, entry) + } catch { + // NO-OP } } /** - * Suppress Node.js warnings (like MaxListenersExceededWarning) - * This is called regardless of quiet mode to prevent warnings from interrupting output + * Get the shape (keys) of an object for logging */ + private getMessageShape(msg: unknown): Record { + if (!msg || typeof msg !== "object") { + return { _type: typeof msg } + } + + const shape: Record = {} + + for (const [key, value] of Object.entries(msg as Record)) { + if (value === null) { + shape[key] = "null" + } else if (Array.isArray(value)) { + shape[key] = `array[${value.length}]` + } else if (typeof value === "object") { + shape[key] = `object{${Object.keys(value).join(",")}}` + } else { + shape[key] = typeof value + } + } + + return shape + } + private suppressNodeWarnings(): void { - // Suppress process warnings (like MaxListenersExceededWarning) + // Suppress process warnings (like MaxListenersExceededWarning). this.originalProcessEmitWarning = process.emitWarning process.emitWarning = () => {} - // Also suppress via the warning event handler + // Also suppress via the warning event handler. process.on("warning", () => {}) } @@ -169,8 +205,6 @@ export class ExtensionHost extends EventEmitter { } async activate(): Promise { - this.log("Activating extension...") - // Suppress Node.js warnings (like MaxListenersExceededWarning) before anything else this.suppressNodeWarnings() @@ -179,14 +213,13 @@ export class ExtensionHost extends EventEmitter { // Verify extension path exists const bundlePath = path.join(this.options.extensionPath, "extension.js") + if (!fs.existsSync(bundlePath)) { this.restoreConsole() throw new Error(`Extension bundle not found at: ${bundlePath}`) } // 1. Create VSCode API mock - this.log("Creating VSCode API mock...") - this.log("Using appRoot:", CLI_PACKAGE_ROOT) this.vscode = createVSCodeAPI( this.options.extensionPath, this.options.workspacePath, @@ -228,8 +261,6 @@ export class ExtensionHost extends EventEmitter { require: require, } as unknown as NodeJS.Module - this.log("Loading extension bundle from:", bundlePath) - // 5. Load extension bundle try { this.extensionModule = require(bundlePath) as ExtensionModule @@ -244,12 +275,9 @@ export class ExtensionHost extends EventEmitter { // 6. Restore module resolution Module._resolveFilename = originalResolve - this.log("Activating extension...") - // 7. Activate extension try { this.extensionAPI = await this.extensionModule.activate(this.vscode.context) - this.log("Extension activated successfully") } catch (error) { throw new Error(`Failed to activate extension: ${error instanceof Error ? error.message : String(error)}`) } @@ -260,18 +288,13 @@ export class ExtensionHost extends EventEmitter { * This is triggered when the extension registers its sidebar webview provider */ registerWebviewProvider(viewId: string, provider: WebviewViewProvider): void { - this.log(`Webview provider registered: ${viewId}`) this.webviewProviders.set(viewId, provider) - - // The WindowAPI will call resolveWebviewView automatically - // We don't need to do anything here } /** * Called when a webview provider is disposed */ unregisterWebviewProvider(viewId: string): void { - this.log(`Webview provider unregistered: ${viewId}`) this.webviewProviders.delete(viewId) } @@ -288,11 +311,8 @@ export class ExtensionHost extends EventEmitter { * This indicates the webview is ready to receive messages */ markWebviewReady(): void { - this.log("Webview marked as ready") this.isWebviewReady = true this.emit("webviewReady") - - // Flush any pending messages this.flushPendingMessages() } @@ -301,10 +321,10 @@ export class ExtensionHost extends EventEmitter { */ private flushPendingMessages(): void { if (this.pendingMessages.length > 0) { - this.log(`Flushing ${this.pendingMessages.length} pending messages`) for (const message of this.pendingMessages) { this.emit("webviewMessage", message) } + this.pendingMessages = [] } } @@ -314,12 +334,10 @@ export class ExtensionHost extends EventEmitter { */ sendToExtension(message: unknown): void { if (!this.isWebviewReady) { - this.log("Queueing message (webview not ready):", message) this.pendingMessages.push(message) return } - this.log("Sending message to extension:", message) this.emit("webviewMessage", message) } @@ -360,6 +378,7 @@ export class ExtensionHost extends EventEmitter { xai: "XAI_API_KEY", groq: "GROQ_API_KEY", } + const envVar = envVarMap[provider.toLowerCase()] || `${provider.toUpperCase().replace(/-/g, "_")}_API_KEY` return process.env[envVar] } @@ -528,15 +547,8 @@ export class ExtensionHost extends EventEmitter { return config } - /** - * Run a task with the given prompt - */ async runTask(prompt: string): Promise { - this.log("Running task:", prompt) - - // Wait for webview to be ready if (!this.isWebviewReady) { - this.log("Waiting for webview to be ready...") await new Promise((resolve) => { this.once("webviewReady", resolve) }) @@ -549,8 +561,6 @@ export class ExtensionHost extends EventEmitter { // In non-interactive mode (-y flag), enable auto-approval for everything // In interactive mode (default), we'll prompt the user for each action if (this.options.nonInteractive) { - this.log("Non-interactive mode: enabling auto-approval settings...") - const settings: RooCodeSettings = { autoApprovalEnabled: true, alwaysAllowReadOnly: true, @@ -572,8 +582,6 @@ export class ExtensionHost extends EventEmitter { this.sendToExtension({ type: "updateSettings", updatedSettings: settings }) await new Promise((resolve) => setTimeout(resolve, 100)) } else { - this.log("Interactive mode: user will be prompted for approvals...") - const settings: RooCodeSettings = { autoApprovalEnabled: false, } @@ -585,7 +593,6 @@ export class ExtensionHost extends EventEmitter { // Always send API configuration - it may include API key from environment variables const apiConfig = this.buildApiConfiguration() - this.log("Sending initial API configuration:", JSON.stringify(apiConfig)) this.sendToExtension({ type: "updateSettings", updatedSettings: apiConfig }) await new Promise((resolve) => setTimeout(resolve, 100)) @@ -597,52 +604,64 @@ export class ExtensionHost extends EventEmitter { * Set up listener for messages from the extension */ private setupMessageListener(): void { - this.messageListener = (message: unknown) => { - this.handleExtensionMessage(message) - } - + this.messageListener = (message: ExtensionMessage) => this.handleExtensionMessage(message) this.on("extensionWebviewMessage", this.messageListener) } - /** - * Handle messages from the extension - */ - private handleExtensionMessage(message: unknown): void { - const msg = message as Record + private handleExtensionMessage(msg: ExtensionMessage): void { + // Log all incoming messages for debugging + this.log(`[MSG] type=${msg.type}`, this.getMessageShape(msg)) - if (this.options.verbose) { - this.log("Received message from extension:", JSON.stringify(msg, null, 2)) + // For state messages, log additional details about the state + if (msg.type === "state" && msg.state) { + const state = msg.state + // Extract model ID based on provider (different providers use different fields) + const apiConfig = state.apiConfiguration + let modelId = apiConfig?.apiModelId + + if (apiConfig?.apiProvider === "openrouter") { + modelId = apiConfig?.openRouterModelId + } else if (apiConfig?.apiProvider === "openai") { + modelId = apiConfig?.openAiModelId + } else if (apiConfig?.apiProvider === "ollama") { + modelId = apiConfig?.ollamaModelId + } + + this.log(`[STATE] mode=${state.mode}, clineMessages=${state.clineMessages?.length || 0}`, { + apiProvider: apiConfig?.apiProvider, + modelId: modelId, + mode: state.mode, + cliProvider: this.options.apiProvider, + cliModel: this.options.model, + }) + + // Log any ask messages in the state that might indicate resume + if (state.clineMessages) { + for (const clineMsg of state.clineMessages) { + if (clineMsg?.ask === "resume_task" || clineMsg?.ask === "resume_completed_task") { + this.log(`[RESUME DETECTED] ask=${clineMsg.ask}, ts=${clineMsg.ts}`) + } + } + } } - // Handle different message types switch (msg.type) { case "state": this.handleStateMessage(msg) break case "messageUpdated": - // This is the streaming update - handle individual message updates + // This is the streaming update - handle individual message updates. this.handleMessageUpdated(msg) break - case "action": - this.handleActionMessage(msg) - break - - case "invoke": - this.handleInvokeMessage(msg) - break - case "modes": - // Forward modes list to the TUI + // Forward modes list to the TUI. this.emit("extensionWebviewMessage", msg) break default: - // Log unknown message types in verbose mode - if (this.options.verbose) { - this.log("Unknown message type:", msg.type) - } + // NO-OP } } @@ -655,6 +674,7 @@ export class ExtensionHost extends EventEmitter { if (this.options.disableOutput) { return } + const text = args.map((arg) => (typeof arg === "string" ? arg : JSON.stringify(arg))).join(" ") process.stdout.write(text + "\n") } @@ -668,65 +688,113 @@ export class ExtensionHost extends EventEmitter { if (this.options.disableOutput) { return } + const text = args.map((arg) => (typeof arg === "string" ? arg : JSON.stringify(arg))).join(" ") process.stderr.write(text + "\n") } /** - * Handle state update messages from the extension + * Get the expected model ID from CLI options */ - private handleStateMessage(msg: Record): void { - const state = msg.state as Record | undefined - if (!state) return + private getExpectedModelId(): string | undefined { + return this.options.model + } - // Detect mode changes and re-apply API configuration - // This preserves the CLI-provided provider/model settings across mode switches - const newMode = state.mode as string | undefined - if (this.options.verbose) { - this.log(`State update: mode=${newMode}, currentMode=${this.currentMode}`) + /** + * Get the current model ID from state's apiConfiguration + */ + private getStateModelId(apiConfig: Record | undefined): string | undefined { + if (!apiConfig) { + return undefined } + + const provider = apiConfig.apiProvider as string | undefined + switch (provider) { + case "openrouter": + return apiConfig.openRouterModelId as string | undefined + case "openai": + return apiConfig.openAiModelId as string | undefined + case "ollama": + return apiConfig.ollamaModelId as string | undefined + case "litellm": + return apiConfig.litellmModelId as string | undefined + case "lmstudio": + return apiConfig.lmStudioModelId as string | undefined + case "huggingface": + return apiConfig.huggingFaceModelId as string | undefined + case "unbound": + return apiConfig.unboundModelId as string | undefined + case "requesty": + return apiConfig.requestyModelId as string | undefined + case "deepinfra": + return apiConfig.deepInfraModelId as string | undefined + case "vercel-ai-gateway": + return apiConfig.vercelAiGatewayModelId as string | undefined + case "io-intelligence": + return apiConfig.ioIntelligenceModelId as string | undefined + default: + return apiConfig.apiModelId as string | undefined + } + } + + /** + * Handle state update messages from the extension. + */ + private handleStateMessage(msg: ExtensionMessage): void { + const state = msg.state + + if (!state) { + return + } + + // Track current mode for mode switch detection (in tool execution). + const newMode = state.mode + if (newMode && this.currentMode !== null && this.currentMode !== newMode) { - const apiConfig = this.buildApiConfiguration() - this.log(`Mode changed from ${this.currentMode} to ${newMode}, re-applying API configuration...`) - if (this.options.verbose) { - this.log(`API config: ${JSON.stringify(apiConfig)}`) - } - this.sendToExtension({ type: "updateSettings", updatedSettings: apiConfig }) + this.log(`[MODE CHANGE] from=${this.currentMode} to=${newMode}, re-applying CLI settings`) + const updatedSettings = this.buildApiConfiguration() + this.sendToExtension({ type: "updateSettings", updatedSettings }) } + if (newMode) { this.currentMode = newMode } - const clineMessages = state.clineMessages as Array> | undefined + // Detect when the model in state differs from CLI-specified model. + // This catches task resume scenarios where the extension loads stored apiConfiguration + // which may differ from CLI-provided settings. We re-apply CLI settings proactively. + const apiConfig = state.apiConfiguration as Record | undefined + const stateModelId = this.getStateModelId(apiConfig) + const expectedModelId = this.getExpectedModelId() + + if (expectedModelId && stateModelId && stateModelId !== expectedModelId) { + this.log(`[MODEL MISMATCH] state has ${stateModelId}, CLI expects ${expectedModelId}, re-applying settings`) + const updatedSettings = this.buildApiConfiguration() + this.sendToExtension({ type: "updateSettings", updatedSettings }) + } + + const clineMessages = state.clineMessages if (clineMessages && clineMessages.length > 0) { - // Track message processing for verbose debug output - this.processedMessageCount++ - - // Verbose: log state update summary - if (this.options.verbose) { - this.log(`State update #${this.processedMessageCount}: ${clineMessages.length} messages`) - } - - // Process all messages to find new or updated ones for (const message of clineMessages) { - if (!message) continue - - const ts = message.ts as number | undefined - const isPartial = message.partial as boolean | undefined - const text = message.text as string - const type = message.type as string - const say = message.say as string | undefined - const ask = message.ask as string | undefined - - if (!ts) continue - - // Handle "say" type messages - if (type === "say" && say) { - this.handleSayMessage(ts, say, text, isPartial) + if (!message) { + continue } - // Handle "ask" type messages - else if (type === "ask" && ask) { + + const ts = message.ts + const isPartial = message.partial + const text = message.text + const type = message.type + const say = message.say + const ask = message.ask + + if (!ts) { + continue + } + + if (type === "say" && say && typeof text === "string") { + this.handleSayMessage(ts, say, text, isPartial) + } else if (type === "ask" && ask && typeof text === "string") { this.handleAskMessage(ts, ask, text, isPartial) } } @@ -737,25 +805,27 @@ export class ExtensionHost extends EventEmitter { * Handle messageUpdated - individual streaming updates for a single message * This is where real-time streaming happens! */ - private handleMessageUpdated(msg: Record): void { - const clineMessage = msg.clineMessage as Record | undefined - if (!clineMessage) return + private handleMessageUpdated(msg: ExtensionMessage): void { + const clineMessage = msg.clineMessage - const ts = clineMessage.ts as number | undefined - const isPartial = clineMessage.partial as boolean | undefined - const text = clineMessage.text as string - const type = clineMessage.type as string - const say = clineMessage.say as string | undefined - const ask = clineMessage.ask as string | undefined - - if (!ts) return - - // Handle "say" type messages - if (type === "say" && say) { - this.handleSayMessage(ts, say, text, isPartial) + if (!clineMessage) { + return } - // Handle "ask" type messages - else if (type === "ask" && ask) { + + const ts = clineMessage.ts + const isPartial = clineMessage.partial + const text = clineMessage.text + const type = clineMessage.type + const say = clineMessage.say + const ask = clineMessage.ask + + if (!ts) { + return + } + + if (type === "say" && say && typeof text === "string") { + this.handleSayMessage(ts, say, text, isPartial) + } else if (type === "ask" && ask && typeof text === "string") { this.handleAskMessage(ts, ask, text, isPartial) } } @@ -768,6 +838,7 @@ export class ExtensionHost extends EventEmitter { if (this.options.disableOutput) { return } + process.stdout.write(text) } @@ -823,6 +894,7 @@ export class ExtensionHost extends EventEmitter { } else if (!isPartial && text && !alreadyDisplayedComplete) { // Message complete - ensure all content is output const streamed = this.streamedContent.get(ts) + if (streamed) { // We were streaming - output any remaining delta and finish if (text.length > streamed.text.length && text.startsWith(streamed.text)) { @@ -834,6 +906,7 @@ export class ExtensionHost extends EventEmitter { // Not streamed yet - output complete message this.output("\n[assistant]", text) } + this.displayedMessages.set(ts, { text, partial: false }) this.streamedContent.set(ts, { text, headerShown: true }) } @@ -842,13 +915,13 @@ export class ExtensionHost extends EventEmitter { case "thinking": case "reasoning": // Stream reasoning content in real-time. - this.log(`Received ${say} message: partial=${isPartial}, textLength=${text?.length ?? 0}`) if (isPartial && text) { this.streamContent(ts, text, "[reasoning]") this.displayedMessages.set(ts, { text, partial: true }) } else if (!isPartial && text && !alreadyDisplayedComplete) { // Reasoning complete - finish the stream. const streamed = this.streamedContent.get(ts) + if (streamed) { if (text.length > streamed.text.length && text.startsWith(streamed.text)) { const delta = text.slice(streamed.text.length) @@ -858,6 +931,7 @@ export class ExtensionHost extends EventEmitter { } else { this.output("\n[reasoning]", text) } + this.displayedMessages.set(ts, { text, partial: false }) } break @@ -870,11 +944,13 @@ export class ExtensionHost extends EventEmitter { } else if (!isPartial && text && !alreadyDisplayedComplete) { // Command output complete - finish the stream. const streamed = this.streamedContent.get(ts) + if (streamed) { if (text.length > streamed.text.length && text.startsWith(streamed.text)) { const delta = text.slice(streamed.text.length) this.writeStream(delta) } + this.finishStream(ts) } else { this.writeStream("\n[command output] ") @@ -907,7 +983,6 @@ export class ExtensionHost extends EventEmitter { break case "tool": - // Tool usage - show when complete if (text && !alreadyDisplayedComplete) { this.output("\n[tool]", text) this.displayedMessages.set(ts, { text, partial: false }) @@ -915,21 +990,10 @@ export class ExtensionHost extends EventEmitter { break case "api_req_started": - // API request started - log in verbose mode - if (this.options.verbose) { - this.log(`API request started: ts=${ts}`) - } break default: - // Other say types - show in verbose mode - if (this.options.verbose) { - this.log(`Unknown say type: ${say}, text length: ${text?.length ?? 0}, partial: ${isPartial}`) - if (text && !alreadyDisplayedComplete) { - this.output(`\n[${say}]`, text || "") - this.displayedMessages.set(ts, { text: text || "", partial: false }) - } - } + // NO-OP } } @@ -986,7 +1050,7 @@ export class ExtensionHost extends EventEmitter { case "followup": if (!alreadyDisplayed) { // In non-interactive mode, still prompt the user but with a 10s timeout - // that auto-selects the first option if no input is received + // that auto-selects the first option if no input is received. this.pendingAsks.add(ts) this.handleFollowupQuestionWithTimeout(ts, text) this.displayedMessages.set(ts, { text, partial: false }) @@ -1000,7 +1064,7 @@ export class ExtensionHost extends EventEmitter { } break - // Note: command_output is handled separately in handleCommandOutputAsk + // Note: command_output is handled separately in handleCommandOutputAsk. case "tool": if (!alreadyDisplayed && text) { @@ -1008,11 +1072,16 @@ export class ExtensionHost extends EventEmitter { const toolInfo = JSON.parse(text) const toolName = toolInfo.tool || "unknown" this.output(`\n[tool] ${toolName}`) - // Display all tool parameters (excluding 'tool' which is the name) + + // Display all tool parameters (excluding 'tool' which is the name). for (const [key, value] of Object.entries(toolInfo)) { - if (key === "tool") continue + if (key === "tool") { + continue + } + // Format the value - truncate long strings let displayValue: string + if (typeof value === "string") { displayValue = value.length > 200 ? value.substring(0, 200) + "..." : value } else if (typeof value === "object" && value !== null) { @@ -1021,21 +1090,13 @@ export class ExtensionHost extends EventEmitter { } else { displayValue = String(value) } - this.output(` ${key}: ${displayValue}`) - } - // Proactively send API config when switchMode tool is detected - // This helps preserve provider/model settings across mode switches - if (toolName === "switchMode") { - this.log("switchMode tool detected, proactively sending API configuration...") - this.sendToExtension({ - type: "updateSettings", - updatedSettings: this.buildApiConfiguration(), - }) + this.output(` ${key}: ${displayValue}`) } } catch { this.output("\n[tool]", text) } + this.displayedMessages.set(ts, { text, partial: false }) } break @@ -1394,15 +1455,20 @@ export class ExtensionHost extends EventEmitter { toolInfo = JSON.parse(text) as Record toolName = (toolInfo.tool as string) || "unknown" } catch { - // Use raw text if not JSON + // Use raw text if not JSON. } this.output(`\n[Tool Request] ${toolName}`) + // Display all tool parameters (excluding 'tool' which is the name) for (const [key, value] of Object.entries(toolInfo)) { - if (key === "tool") continue + if (key === "tool") { + continue + } + // Format the value - truncate long strings let displayValue: string + if (typeof value === "string") { displayValue = value.length > 200 ? value.substring(0, 200) + "..." : value } else if (typeof value === "object" && value !== null) { @@ -1411,6 +1477,7 @@ export class ExtensionHost extends EventEmitter { } else { displayValue = String(value) } + this.output(` ${key}: ${displayValue}`) } @@ -1431,7 +1498,10 @@ export class ExtensionHost extends EventEmitter { */ private async handleBrowserApproval(ts: number, text: string): Promise { this.output("\n[browser action request]") - if (text) this.output(` Action: ${text}`) + + if (text) { + this.output(` Action: ${text}`) + } try { const approved = await this.promptForYesNo("Allow browser action? (y/n): ") @@ -1440,7 +1510,8 @@ export class ExtensionHost extends EventEmitter { this.output("[Defaulting to: no]") this.sendApprovalResponse(false) } - // Note: Don't delete from pendingAsks - see handleFollowupQuestion comment + + // Note: Don't delete from pendingAsks - see handleFollowupQuestion comment. } /** @@ -1454,19 +1525,26 @@ export class ExtensionHost extends EventEmitter { try { const mcpInfo = JSON.parse(text) serverName = mcpInfo.server_name || "unknown" + if (mcpInfo.type === "use_mcp_tool") { toolName = mcpInfo.tool_name || "" } else if (mcpInfo.type === "access_mcp_resource") { resourceUri = mcpInfo.uri || "" } } catch { - // Use raw text if not JSON + // Use raw text if not JSON. } this.output("\n[mcp request]") this.output(` Server: ${serverName}`) - if (toolName) this.output(` Tool: ${toolName}`) - if (resourceUri) this.output(` Resource: ${resourceUri}`) + + if (toolName) { + this.output(` Tool: ${toolName}`) + } + + if (resourceUri) { + this.output(` Resource: ${resourceUri}`) + } try { const approved = await this.promptForYesNo("Allow MCP access? (y/n): ") @@ -1475,7 +1553,8 @@ export class ExtensionHost extends EventEmitter { this.output("[Defaulting to: no]") this.sendApprovalResponse(false) } - // Note: Don't delete from pendingAsks - see handleFollowupQuestion comment + + // Note: Don't delete from pendingAsks - see handleFollowupQuestion comment. } /** @@ -1501,7 +1580,10 @@ export class ExtensionHost extends EventEmitter { private async handleResumeTask(ts: number, ask: string, text: string): Promise { const isCompleted = ask === "resume_completed_task" this.output(`\n[Resume ${isCompleted ? "Completed " : ""}Task]`) - if (text) this.output(` ${text}`) + + if (text) { + this.output(` ${text}`) + } try { const resume = await this.promptForYesNo("Continue with this task? (y/n): ") @@ -1510,7 +1592,8 @@ export class ExtensionHost extends EventEmitter { this.output("[Defaulting to: no]") this.sendApprovalResponse(false) } - // Note: Don't delete from pendingAsks - see handleFollowupQuestion comment + + // Note: Don't delete from pendingAsks - see handleFollowupQuestion comment. } /** @@ -1518,7 +1601,10 @@ export class ExtensionHost extends EventEmitter { */ private async handleGenericApproval(ts: number, ask: string, text: string): Promise { this.output(`\n[${ask}]`) - if (text) this.output(` ${text}`) + + if (text) { + this.output(` ${text}`) + } try { const approved = await this.promptForYesNo("Approve? (y/n): ") @@ -1527,7 +1613,8 @@ export class ExtensionHost extends EventEmitter { this.output("[Defaulting to: no]") this.sendApprovalResponse(false) } - // Note: Don't delete from pendingAsks - see handleFollowupQuestion comment + + // Note: Don't delete from pendingAsks - see handleFollowupQuestion comment. } /** @@ -1546,18 +1633,21 @@ export class ExtensionHost extends EventEmitter { // Message complete - output any remaining content and send approval if (text && !alreadyDisplayedComplete) { const streamed = this.streamedContent.get(ts) + if (streamed) { // We were streaming - output any remaining delta and finish. if (text.length > streamed.text.length && text.startsWith(streamed.text)) { const delta = text.slice(streamed.text.length) this.writeStream(delta) } + this.finishStream(ts) } else { this.writeStream("\n[command output] ") this.writeStream(text) this.writeStream("\n") } + this.displayedMessages.set(ts, { text, partial: false }) this.streamedContent.set(ts, { text, headerShown: true }) } @@ -1629,11 +1719,7 @@ export class ExtensionHost extends EventEmitter { * Send a followup response (text answer) to the extension */ private sendFollowupResponse(text: string): void { - this.sendToExtension({ - type: "askResponse", - askResponse: "messageResponse", - text, - }) + this.sendToExtension({ type: "askResponse", askResponse: "messageResponse", text }) } /** @@ -1646,28 +1732,6 @@ export class ExtensionHost extends EventEmitter { }) } - /** - * Handle action messages - */ - private handleActionMessage(msg: Record): void { - const action = msg.action as string - - if (this.options.verbose) { - this.log("Action:", action) - } - } - - /** - * Handle invoke messages - */ - private handleInvokeMessage(msg: Record): void { - const invoke = msg.invoke as string - - if (this.options.verbose) { - this.log("Invoke:", invoke) - } - } - /** * Wait for the task to complete */ @@ -1710,8 +1774,6 @@ export class ExtensionHost extends EventEmitter { * Clean up resources */ async dispose(): Promise { - this.log("Disposing extension host...") - // Clear pending asks this.pendingAsks.clear() @@ -1731,8 +1793,8 @@ export class ExtensionHost extends EventEmitter { if (this.extensionModule?.deactivate) { try { await this.extensionModule.deactivate() - } catch (error) { - this.log("Error deactivating extension:", error) + } catch (_error) { + // NO-OP } } @@ -1748,7 +1810,5 @@ export class ExtensionHost extends EventEmitter { // Restore console if it was suppressed this.restoreConsole() - - this.log("Extension host disposed") } } diff --git a/apps/cli/src/globalCommands.ts b/apps/cli/src/globalCommands.ts new file mode 100644 index 0000000000..32459e0a2a --- /dev/null +++ b/apps/cli/src/globalCommands.ts @@ -0,0 +1,62 @@ +/** + * CLI-specific global slash commands + * + * These commands are handled entirely within the CLI and trigger actions + * by sending messages to the extension host. They are separate from the + * extension's built-in commands which expand into prompt content. + */ + +/** + * Action types that can be triggered by global commands. + * Each action corresponds to a message type sent to the extension host. + */ +export type GlobalCommandAction = "clearTask" + +/** + * Definition of a CLI global command + */ +export interface GlobalCommand { + /** Command name (without the leading /) */ + name: string + /** Description shown in the autocomplete picker */ + description: string + /** Action to trigger when the command is executed */ + action: GlobalCommandAction +} + +/** + * CLI-specific global slash commands + * These commands trigger actions rather than expanding into prompt content. + */ +export const GLOBAL_COMMANDS: GlobalCommand[] = [ + { + name: "new", + description: "Start a new task", + action: "clearTask", + }, +] + +/** + * Get a global command by name + */ +export function getGlobalCommand(name: string): GlobalCommand | undefined { + return GLOBAL_COMMANDS.find((cmd) => cmd.name === name) +} + +/** + * Get global commands formatted for autocomplete + * Returns commands in the SlashCommandResult format expected by the autocomplete trigger + */ +export function getGlobalCommandsForAutocomplete(): Array<{ + name: string + description?: string + source: "global" | "project" | "built-in" + action?: string +}> { + return GLOBAL_COMMANDS.map((cmd) => ({ + name: cmd.name, + description: cmd.description, + source: "global" as const, + action: cmd.action, + })) +} diff --git a/apps/cli/src/ui/App.tsx b/apps/cli/src/ui/App.tsx index cf404fa4fc..5e8f0007ae 100644 --- a/apps/cli/src/ui/App.tsx +++ b/apps/cli/src/ui/App.tsx @@ -44,6 +44,7 @@ import type { SlashCommandResult, ModeResult, } from "./types.js" +import { getGlobalCommand, getGlobalCommandsForAutocomplete } from "../globalCommands.js" // Layout constants const PICKER_HEIGHT = 10 // Max height for picker when open @@ -333,7 +334,13 @@ function AppInner({ }) const slashCommandTrigger = createSlashCommandTrigger({ - getCommands: () => allSlashCommandsRef.current.map(toSlashCommandResult), + getCommands: () => { + // Merge CLI global commands with extension commands + const extensionCommands = allSlashCommandsRef.current.map(toSlashCommandResult) + const globalCommands = getGlobalCommandsForAutocomplete().map(toSlashCommandResult) + // Global commands appear first, then extension commands + return [...globalCommands, ...extensionCommands] + }, }) const modeTrigger = createModeTrigger({ @@ -815,7 +822,6 @@ function AppInner({ } }, []) // Run once on mount - // Handle user input submission const handleSubmit = useCallback( async (text: string) => { if (!hostRef.current || !text.trim()) { @@ -828,18 +834,34 @@ function AppInner({ return } + // Check for CLI global action commands (e.g., /new). + if (trimmedText.startsWith("/")) { + const commandMatch = trimmedText.match(/^\/(\w+)(?:\s|$)/) + + if (commandMatch && commandMatch[1]) { + const globalCommand = getGlobalCommand(commandMatch[1]) + + if (globalCommand?.action === "clearTask") { + // Reset CLI state and send clearTask to extension. + useCLIStore.getState().reset() + // Reset component-level refs to avoid stale message tracking. + seenMessageIds.current.clear() + firstTextMessageSkipped.current = false + hostRef.current.sendToExtension({ type: "clearTask" }) + return + } + } + } + if (pendingAsk) { - addMessage({ - id: randomUUID(), - role: "user", - content: trimmedText, - }) + addMessage({ id: randomUUID(), role: "user", content: trimmedText }) hostRef.current.sendToExtension({ type: "askResponse", askResponse: "messageResponse", text: trimmedText, }) + setPendingAsk(null) setShowCustomInput(false) isTransitioningToCustomInput.current = false @@ -847,12 +869,7 @@ function AppInner({ } else if (!hasStartedTask) { setHasStartedTask(true) setLoading(true) - - addMessage({ - id: randomUUID(), - role: "user", - content: trimmedText, - }) + addMessage({ id: randomUUID(), role: "user", content: trimmedText }) try { await hostRef.current.runTask(trimmedText) @@ -864,13 +881,9 @@ function AppInner({ if (isComplete) { setComplete(false) } - setLoading(true) - addMessage({ - id: randomUUID(), - role: "user", - content: trimmedText, - }) + setLoading(true) + addMessage({ id: randomUUID(), role: "user", content: trimmedText }) hostRef.current.sendToExtension({ type: "askResponse", @@ -894,24 +907,22 @@ function AppInner({ // Handle approval (Y key) const handleApprove = useCallback(() => { - if (!hostRef.current) return + if (!hostRef.current) { + return + } - hostRef.current.sendToExtension({ - type: "askResponse", - askResponse: "yesButtonClicked", - }) + hostRef.current.sendToExtension({ type: "askResponse", askResponse: "yesButtonClicked" }) setPendingAsk(null) setLoading(true) }, [setPendingAsk, setLoading]) // Handle rejection (N key) const handleReject = useCallback(() => { - if (!hostRef.current) return + if (!hostRef.current) { + return + } - hostRef.current.sendToExtension({ - type: "askResponse", - askResponse: "noButtonClicked", - }) + hostRef.current.sendToExtension({ type: "askResponse", askResponse: "noButtonClicked" }) setPendingAsk(null) setLoading(true) }, [setPendingAsk, setLoading]) @@ -920,6 +931,7 @@ function AppInner({ useInput((input) => { if (pendingAsk && pendingAsk.type !== "followup") { const lower = input.toLowerCase() + if (lower === "y") { handleApprove() } else if (lower === "n") { @@ -930,9 +942,7 @@ function AppInner({ // Handle picker state changes from AutocompleteInput // eslint-disable-next-line @typescript-eslint/no-explicit-any - const handlePickerStateChange = useCallback((state: AutocompletePickerState) => { - setPickerState(state) - }, []) + const handlePickerStateChange = useCallback((state: AutocompletePickerState) => setPickerState(state), []) // Handle item selection from external PickerSelect const handlePickerSelect = useCallback( @@ -941,13 +951,12 @@ function AppInner({ // Check if this is a mode selection if (pickerState.activeTrigger?.id === "mode" && item && typeof item === "object" && "slug" in item) { const modeItem = item as ModeItem + // Send mode change message to extension if (hostRef.current) { - hostRef.current.sendToExtension({ - type: "switchMode", - mode: modeItem.slug, - }) + hostRef.current.sendToExtension({ type: "switchMode", mode: modeItem.slug }) } + // Close the picker autocompleteRef.current?.closePicker() followupAutocompleteRef.current?.closePicker() @@ -1379,13 +1388,20 @@ function parseMarkdownChecklist(markdown: string): TodoItem[] { for (let i = 0; i < lines.length; i++) { const line = lines[i] - if (!line) continue + + if (!line) { + continue + } const trimmedLine = line.trim() - if (!trimmedLine) continue + + if (!trimmedLine) { + continue + } // Match markdown checkbox patterns const checkboxMatch = trimmedLine.match(/^\[([x\-\s])\]\s*(.+)$/i) + if (checkboxMatch) { const statusChar = checkboxMatch[1] ?? " " const content = checkboxMatch[2] ?? "" @@ -1397,11 +1413,7 @@ function parseMarkdownChecklist(markdown: string): TodoItem[] { status = "in_progress" } - todos.push({ - id: `todo-${i}`, - content: content.trim(), - status, - }) + todos.push({ id: `todo-${i}`, content: content.trim(), status }) } } diff --git a/apps/cli/src/ui/components/autocomplete/triggers/SlashCommandTrigger.tsx b/apps/cli/src/ui/components/autocomplete/triggers/SlashCommandTrigger.tsx index 0cb9400a43..7cfe0aa133 100644 --- a/apps/cli/src/ui/components/autocomplete/triggers/SlashCommandTrigger.tsx +++ b/apps/cli/src/ui/components/autocomplete/triggers/SlashCommandTrigger.tsx @@ -16,6 +16,8 @@ export interface SlashCommandResult extends AutocompleteItem { argumentHint?: string /** Source of the command */ source: "global" | "project" | "built-in" + /** Action to trigger for CLI global commands (only present for action commands) */ + action?: string } /** @@ -92,8 +94,15 @@ export function createSlashCommandTrigger(config: SlashCommandTriggerConfig): Au }, renderItem: (item: SlashCommandResult, isSelected: boolean) => { - // Source indicator icons - const sourceIcon = item.source === "built-in" ? "⚡" : item.source === "project" ? "📁" : "🌐" + // Source indicator icons: + // ⚙️ for action commands (CLI global), ⚡ built-in, 📁 project, 🌐 global (content) + const sourceIcon = item.action + ? "⚙️" + : item.source === "built-in" + ? "⚡" + : item.source === "project" + ? "📁" + : "🌐" return ( diff --git a/apps/cli/src/ui/types.ts b/apps/cli/src/ui/types.ts index 907d0eda42..5f708bf4c6 100644 --- a/apps/cli/src/ui/types.ts +++ b/apps/cli/src/ui/types.ts @@ -1,5 +1,7 @@ import type { ClineAsk, ClineSay, TodoItem } from "@roo-code/types" +import type { GlobalCommandAction } from "../globalCommands.js" + // Re-export TodoItem for convenience export type { TodoItem } @@ -86,6 +88,8 @@ export interface SlashCommandResult { description?: string argumentHint?: string source: "global" | "project" | "built-in" + /** Action to trigger for CLI global commands (e.g., clearTask for /new) */ + action?: GlobalCommandAction } export interface ModeResult { diff --git a/packages/types/src/git.ts b/packages/types/src/git.ts new file mode 100644 index 0000000000..5457e15b2f --- /dev/null +++ b/packages/types/src/git.ts @@ -0,0 +1,13 @@ +export interface GitRepositoryInfo { + repositoryUrl?: string + repositoryName?: string + defaultBranch?: string +} + +export interface GitCommit { + hash: string + shortHash: string + subject: string + author: string + date: string +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index c4e5088d63..c3c2b93774 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -7,6 +7,7 @@ export * from "./custom-tool.js" export * from "./events.js" export * from "./experiment.js" export * from "./followup.js" +export * from "./git.js" export * from "./global-settings.js" export * from "./history.js" export * from "./image-generation.js" @@ -24,6 +25,7 @@ export * from "./terminal.js" export * from "./tool.js" export * from "./tool-params.js" export * from "./type-fu.js" +export * from "./vscode-extension-host.js" export * from "./vscode.js" export * from "./providers/index.js" diff --git a/packages/types/src/marketplace.ts b/packages/types/src/marketplace.ts index f2821e1b74..7428d8fb4e 100644 --- a/packages/types/src/marketplace.ts +++ b/packages/types/src/marketplace.ts @@ -86,3 +86,8 @@ export const installMarketplaceItemOptionsSchema = z.object({ }) export type InstallMarketplaceItemOptions = z.infer + +export interface MarketplaceInstalledMetadata { + project: Record + global: Record +} diff --git a/packages/types/src/mcp.ts b/packages/types/src/mcp.ts index ed930f4a16..92e238efbb 100644 --- a/packages/types/src/mcp.ts +++ b/packages/types/src/mcp.ts @@ -1,8 +1,9 @@ import { z } from "zod" /** - * MCP Server Use Types + * McpServerUse */ + export interface McpServerUse { type: string serverName: string @@ -39,3 +40,91 @@ export const mcpExecutionStatusSchema = z.discriminatedUnion("status", [ ]) export type McpExecutionStatus = z.infer + +/** + * McpServer + */ + +export type McpServer = { + name: string + config: string + status: "connected" | "connecting" | "disconnected" + error?: string + errorHistory?: McpErrorEntry[] + tools?: McpTool[] + resources?: McpResource[] + resourceTemplates?: McpResourceTemplate[] + disabled?: boolean + timeout?: number + source?: "global" | "project" + projectPath?: string + instructions?: string +} + +export type McpTool = { + name: string + description?: string + inputSchema?: object + alwaysAllow?: boolean + enabledForPrompt?: boolean +} + +export type McpResource = { + uri: string + name: string + mimeType?: string + description?: string +} + +export type McpResourceTemplate = { + uriTemplate: string + name: string + description?: string + mimeType?: string +} + +export type McpResourceResponse = { + _meta?: Record // eslint-disable-line @typescript-eslint/no-explicit-any + contents: Array<{ + uri: string + mimeType?: string + text?: string + blob?: string + }> +} + +export type McpToolCallResponse = { + _meta?: Record // eslint-disable-line @typescript-eslint/no-explicit-any + content: Array< + | { + type: "text" + text: string + } + | { + type: "image" + data: string + mimeType: string + } + | { + type: "audio" + data: string + mimeType: string + } + | { + type: "resource" + resource: { + uri: string + mimeType?: string + text?: string + blob?: string + } + } + > + isError?: boolean +} + +export type McpErrorEntry = { + message: string + timestamp: number + level: "error" | "warn" | "info" +} diff --git a/packages/types/src/model.ts b/packages/types/src/model.ts index 6c7d0a4b4b..21d36bca85 100644 --- a/packages/types/src/model.ts +++ b/packages/types/src/model.ts @@ -1,4 +1,5 @@ import { z } from "zod" +import { DynamicProvider, LocalProvider } from "./provider-settings.js" /** * ReasoningEffort @@ -140,3 +141,7 @@ export const modelInfoSchema = z.object({ }) export type ModelInfo = z.infer + +export type ModelRecord = Record + +export type RouterModels = Record diff --git a/packages/types/src/vscode-extension-host.ts b/packages/types/src/vscode-extension-host.ts new file mode 100644 index 0000000000..68e132f572 --- /dev/null +++ b/packages/types/src/vscode-extension-host.ts @@ -0,0 +1,327 @@ +import type { GlobalSettings } from "./global-settings.js" +import type { ProviderSettings, ProviderSettingsEntry } from "./provider-settings.js" +import type { HistoryItem } from "./history.js" +import type { ModeConfig } from "./mode.js" +import type { TelemetrySetting } from "./telemetry.js" +import type { Experiments } from "./experiment.js" +import type { ClineMessage, QueuedMessage } from "./message.js" +import type { MarketplaceItem, MarketplaceInstalledMetadata } from "./marketplace.js" +import type { TodoItem } from "./todo.js" +import type { CloudUserInfo, CloudOrganizationMembership, OrganizationAllowList, ShareVisibility } from "./cloud.js" +import type { SerializedCustomToolDefinition } from "./custom-tool.js" +import type { GitCommit } from "./git.js" +import type { McpServer } from "./mcp.js" +import type { ModelRecord, RouterModels } from "./model.js" + +// Represents JSON data that is sent from extension to the webview or cli. +export interface ExtensionMessage { + type: + | "action" + | "state" + | "selectedImages" + | "theme" + | "workspaceUpdated" + | "invoke" + | "messageUpdated" + | "mcpServers" + | "enhancedPrompt" + | "commitSearchResults" + | "listApiConfig" + | "routerModels" + | "openAiModels" + | "ollamaModels" + | "lmStudioModels" + | "vsCodeLmModels" + | "huggingFaceModels" + | "vsCodeLmApiAvailable" + | "updatePrompt" + | "systemPrompt" + | "autoApprovalEnabled" + | "updateCustomMode" + | "deleteCustomMode" + | "exportModeResult" + | "importModeResult" + | "checkRulesDirectoryResult" + | "deleteCustomModeCheck" + | "currentCheckpointUpdated" + | "checkpointInitWarning" + | "browserToolEnabled" + | "browserConnectionResult" + | "remoteBrowserEnabled" + | "ttsStart" + | "ttsStop" + | "maxReadFileLine" + | "fileSearchResults" + | "toggleApiConfigPin" + | "acceptInput" + | "setHistoryPreviewCollapsed" + | "commandExecutionStatus" + | "mcpExecutionStatus" + | "vsCodeSetting" + | "authenticatedUser" + | "condenseTaskContextStarted" + | "condenseTaskContextResponse" + | "singleRouterModelFetchResponse" + | "rooCreditBalance" + | "indexingStatusUpdate" + | "indexCleared" + | "codebaseIndexConfig" + | "marketplaceInstallResult" + | "marketplaceRemoveResult" + | "marketplaceData" + | "shareTaskSuccess" + | "codeIndexSettingsSaved" + | "codeIndexSecretStatus" + | "showDeleteMessageDialog" + | "showEditMessageDialog" + | "commands" + | "insertTextIntoTextarea" + | "dismissedUpsells" + | "organizationSwitchResult" + | "interactionRequired" + | "browserSessionUpdate" + | "browserSessionNavigate" + | "claudeCodeRateLimits" + | "customToolsResult" + | "modes" + text?: string + payload?: any // eslint-disable-line @typescript-eslint/no-explicit-any + checkpointWarning?: { + type: "WAIT_TIMEOUT" | "INIT_TIMEOUT" + timeout: number + } + action?: + | "chatButtonClicked" + | "settingsButtonClicked" + | "historyButtonClicked" + | "marketplaceButtonClicked" + | "cloudButtonClicked" + | "didBecomeVisible" + | "focusInput" + | "switchTab" + | "toggleAutoApprove" + invoke?: "newChat" | "sendMessage" | "primaryButtonClick" | "secondaryButtonClick" | "setChatBoxMessage" + state?: ExtensionState + images?: string[] + filePaths?: string[] + openedTabs?: Array<{ + label: string + isActive: boolean + path?: string + }> + clineMessage?: ClineMessage + routerModels?: RouterModels + openAiModels?: string[] + ollamaModels?: ModelRecord + lmStudioModels?: ModelRecord + vsCodeLmModels?: { vendor?: string; family?: string; version?: string; id?: string }[] + huggingFaceModels?: Array<{ + id: string + object: string + created: number + owned_by: string + providers: Array<{ + provider: string + status: "live" | "staging" | "error" + supports_tools?: boolean + supports_structured_output?: boolean + context_length?: number + pricing?: { + input: number + output: number + } + }> + }> + mcpServers?: McpServer[] + commits?: GitCommit[] + listApiConfig?: ProviderSettingsEntry[] + mode?: string + customMode?: ModeConfig + slug?: string + success?: boolean + values?: Record // eslint-disable-line @typescript-eslint/no-explicit-any + requestId?: string + promptText?: string + results?: + | { path: string; type: "file" | "folder"; label?: string }[] + | { name: string; description?: string; argumentHint?: string; source: "global" | "project" | "built-in" }[] + error?: string + setting?: string + value?: any // eslint-disable-line @typescript-eslint/no-explicit-any + hasContent?: boolean + items?: MarketplaceItem[] + userInfo?: CloudUserInfo + organizationAllowList?: OrganizationAllowList + tab?: string + marketplaceItems?: MarketplaceItem[] + organizationMcps?: MarketplaceItem[] + marketplaceInstalledMetadata?: MarketplaceInstalledMetadata + errors?: string[] + visibility?: ShareVisibility + rulesFolderPath?: string + settings?: any // eslint-disable-line @typescript-eslint/no-explicit-any + messageTs?: number + hasCheckpoint?: boolean + context?: string + commands?: Command[] + queuedMessages?: QueuedMessage[] + list?: string[] // For dismissedUpsells + organizationId?: string | null // For organizationSwitchResult + browserSessionMessages?: ClineMessage[] // For browser session panel updates + isBrowserSessionActive?: boolean // For browser session panel updates + stepIndex?: number // For browserSessionNavigate: the target step index to display + tools?: SerializedCustomToolDefinition[] // For customToolsResult + modes?: { slug: string; name: string }[] // For modes response +} + +export type ExtensionState = Pick< + GlobalSettings, + | "currentApiConfigName" + | "listApiConfigMeta" + | "pinnedApiConfigs" + | "customInstructions" + | "dismissedUpsells" + | "autoApprovalEnabled" + | "alwaysAllowReadOnly" + | "alwaysAllowReadOnlyOutsideWorkspace" + | "alwaysAllowWrite" + | "alwaysAllowWriteOutsideWorkspace" + | "alwaysAllowWriteProtected" + | "alwaysAllowBrowser" + | "alwaysAllowMcp" + | "alwaysAllowModeSwitch" + | "alwaysAllowSubtasks" + | "alwaysAllowFollowupQuestions" + | "alwaysAllowExecute" + | "followupAutoApproveTimeoutMs" + | "allowedCommands" + | "deniedCommands" + | "allowedMaxRequests" + | "allowedMaxCost" + | "browserToolEnabled" + | "browserViewportSize" + | "screenshotQuality" + | "remoteBrowserEnabled" + | "cachedChromeHostUrl" + | "remoteBrowserHost" + | "ttsEnabled" + | "ttsSpeed" + | "soundEnabled" + | "soundVolume" + | "maxConcurrentFileReads" + | "terminalOutputLineLimit" + | "terminalOutputCharacterLimit" + | "terminalShellIntegrationTimeout" + | "terminalShellIntegrationDisabled" + | "terminalCommandDelay" + | "terminalPowershellCounter" + | "terminalZshClearEolMark" + | "terminalZshOhMy" + | "terminalZshP10k" + | "terminalZdotdir" + | "terminalCompressProgressBar" + | "diagnosticsEnabled" + | "diffEnabled" + | "fuzzyMatchThreshold" + | "language" + | "modeApiConfigs" + | "customModePrompts" + | "customSupportPrompts" + | "enhancementApiConfigId" + | "condensingApiConfigId" + | "customCondensingPrompt" + | "codebaseIndexConfig" + | "codebaseIndexModels" + | "profileThresholds" + | "includeDiagnosticMessages" + | "maxDiagnosticMessages" + | "imageGenerationProvider" + | "openRouterImageGenerationSelectedModel" + | "includeTaskHistoryInEnhance" + | "reasoningBlockCollapsed" + | "enterBehavior" + | "includeCurrentTime" + | "includeCurrentCost" + | "maxGitStatusFiles" + | "requestDelaySeconds" +> & { + version: string + clineMessages: ClineMessage[] + currentTaskItem?: HistoryItem + currentTaskTodos?: TodoItem[] // Initial todos for the current task + apiConfiguration: ProviderSettings + uriScheme?: string + shouldShowAnnouncement: boolean + + taskHistory: HistoryItem[] + + writeDelayMs: number + + enableCheckpoints: boolean + checkpointTimeout: number // Timeout for checkpoint initialization in seconds (default: 15) + maxOpenTabsContext: number // Maximum number of VSCode open tabs to include in context (0-500) + maxWorkspaceFiles: number // Maximum number of files to include in current working directory details (0-500) + showRooIgnoredFiles: boolean // Whether to show .rooignore'd files in listings + enableSubfolderRules: boolean // Whether to load rules from subdirectories + maxReadFileLine: number // Maximum number of lines to read from a file before truncating + maxImageFileSize: number // Maximum size of image files to process in MB + maxTotalImageSize: number // Maximum total size for all images in a single read operation in MB + + experiments: Experiments // Map of experiment IDs to their enabled state + + mcpEnabled: boolean + enableMcpServerCreation: boolean + + mode: string + customModes: ModeConfig[] + toolRequirements?: Record // Map of tool names to their requirements (e.g. {"apply_diff": true} if diffEnabled) + + cwd?: string // Current working directory + telemetrySetting: TelemetrySetting + telemetryKey?: string + machineId?: string + + renderContext: "sidebar" | "editor" + settingsImportedAt?: number + historyPreviewCollapsed?: boolean + + cloudUserInfo: CloudUserInfo | null + cloudIsAuthenticated: boolean + cloudAuthSkipModel?: boolean // Flag indicating auth completed without model selection (user should pick 3rd-party provider) + cloudApiUrl?: string + cloudOrganizations?: CloudOrganizationMembership[] + sharingEnabled: boolean + publicSharingEnabled: boolean + organizationAllowList: OrganizationAllowList + organizationSettingsVersion?: number + + isBrowserSessionActive: boolean // Actual browser session state + + autoCondenseContext: boolean + autoCondenseContextPercent: number + marketplaceItems?: MarketplaceItem[] + // eslint-disable-next-line @typescript-eslint/no-explicit-any + marketplaceInstalledMetadata?: { project: Record; global: Record } + profileThresholds: Record + hasOpenedModeSelector: boolean + openRouterImageApiKey?: string + messageQueue?: QueuedMessage[] + lastShownAnnouncementId?: string + apiModelId?: string + mcpServers?: McpServer[] + hasSystemPromptOverride?: boolean + mdmCompliant?: boolean + remoteControlEnabled: boolean + taskSyncEnabled: boolean + featureRoomoteControlEnabled: boolean + claudeCodeIsAuthenticated?: boolean + debug?: boolean +} + +export interface Command { + name: string + source: "global" | "project" | "built-in" + filePath?: string + description?: string + argumentHint?: string +} diff --git a/src/api/providers/fetchers/huggingface.ts b/src/api/providers/fetchers/huggingface.ts index 1a7a995bc6..16963edc75 100644 --- a/src/api/providers/fetchers/huggingface.ts +++ b/src/api/providers/fetchers/huggingface.ts @@ -3,14 +3,13 @@ import { z } from "zod" import { type ModelInfo, + type ModelRecord, HUGGINGFACE_API_URL, HUGGINGFACE_CACHE_DURATION, HUGGINGFACE_DEFAULT_MAX_TOKENS, HUGGINGFACE_DEFAULT_CONTEXT_WINDOW, } from "@roo-code/types" -import type { ModelRecord } from "../../../shared/api" - const huggingFaceProviderSchema = z.object({ provider: z.string(), status: z.enum(["live", "staging", "error"]), diff --git a/src/api/providers/fetchers/io-intelligence.ts b/src/api/providers/fetchers/io-intelligence.ts index 42d88083b9..a0ea5dedae 100644 --- a/src/api/providers/fetchers/io-intelligence.ts +++ b/src/api/providers/fetchers/io-intelligence.ts @@ -1,9 +1,7 @@ import axios from "axios" import { z } from "zod" -import { type ModelInfo, IO_INTELLIGENCE_CACHE_DURATION } from "@roo-code/types" - -import type { ModelRecord } from "../../../shared/api" +import { type ModelInfo, type ModelRecord, IO_INTELLIGENCE_CACHE_DURATION } from "@roo-code/types" const ioIntelligenceModelSchema = z.object({ id: z.string(), diff --git a/src/api/providers/fetchers/litellm.ts b/src/api/providers/fetchers/litellm.ts index 4c68569805..3b25e8a530 100644 --- a/src/api/providers/fetchers/litellm.ts +++ b/src/api/providers/fetchers/litellm.ts @@ -1,6 +1,6 @@ import axios from "axios" -import type { ModelRecord } from "../../../shared/api" +import type { ModelRecord } from "@roo-code/types" import { DEFAULT_HEADERS } from "../constants" /** diff --git a/src/api/providers/fetchers/modelCache.ts b/src/api/providers/fetchers/modelCache.ts index d22abf9c91..51ca19e2bc 100644 --- a/src/api/providers/fetchers/modelCache.ts +++ b/src/api/providers/fetchers/modelCache.ts @@ -5,7 +5,7 @@ import * as fsSync from "fs" import NodeCache from "node-cache" import { z } from "zod" -import type { ProviderName } from "@roo-code/types" +import type { ProviderName, ModelRecord } from "@roo-code/types" import { modelInfoSchema, TelemetryEventName } from "@roo-code/types" import { TelemetryService } from "@roo-code/telemetry" @@ -13,7 +13,7 @@ import { safeWriteJson } from "../../../utils/safeWriteJson" import { ContextProxy } from "../../../core/config/ContextProxy" import { getCacheDirectoryPath } from "../../../utils/storage" -import type { RouterName, ModelRecord } from "../../../shared/api" +import type { RouterName } from "../../../shared/api" import { fileExistsAtPath } from "../../../utils/fs" import { getOpenRouterModels } from "./openrouter" diff --git a/src/api/providers/fetchers/modelEndpointCache.ts b/src/api/providers/fetchers/modelEndpointCache.ts index 60c627cbcc..7ee8745b71 100644 --- a/src/api/providers/fetchers/modelEndpointCache.ts +++ b/src/api/providers/fetchers/modelEndpointCache.ts @@ -2,13 +2,15 @@ import * as path from "path" import fs from "fs/promises" import NodeCache from "node-cache" -import { safeWriteJson } from "../../../utils/safeWriteJson" import sanitize from "sanitize-filename" +import type { ModelRecord } from "@roo-code/types" + import { ContextProxy } from "../../../core/config/ContextProxy" +import { RouterName } from "../../../shared/api" import { getCacheDirectoryPath } from "../../../utils/storage" -import { RouterName, ModelRecord } from "../../../shared/api" import { fileExistsAtPath } from "../../../utils/fs" +import { safeWriteJson } from "../../../utils/safeWriteJson" import { getOpenRouterModelEndpoints } from "./openrouter" import { getModels } from "./modelCache" diff --git a/src/api/providers/fetchers/roo.ts b/src/api/providers/fetchers/roo.ts index 65a2db77c3..5d7c101697 100644 --- a/src/api/providers/fetchers/roo.ts +++ b/src/api/providers/fetchers/roo.ts @@ -1,6 +1,5 @@ -import { RooModelsResponseSchema, type ModelInfo } from "@roo-code/types" +import { RooModelsResponseSchema, type ModelInfo, type ModelRecord } from "@roo-code/types" -import type { ModelRecord } from "../../../shared/api" import { parseApiPrice } from "../../../shared/cost" import { DEFAULT_HEADERS } from "../constants" diff --git a/src/api/providers/huggingface.ts b/src/api/providers/huggingface.ts index 7b62046b99..21e429aaab 100644 --- a/src/api/providers/huggingface.ts +++ b/src/api/providers/huggingface.ts @@ -1,7 +1,9 @@ import OpenAI from "openai" import { Anthropic } from "@anthropic-ai/sdk" -import type { ApiHandlerOptions, ModelRecord } from "../../shared/api" +import type { ModelRecord } from "@roo-code/types" + +import type { ApiHandlerOptions } from "../../shared/api" import { ApiStream } from "../transform/stream" import { convertToOpenAiMessages } from "../transform/openai-format" import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 4a8a078018..8f56cddc5c 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -3,18 +3,19 @@ import OpenAI from "openai" import { z } from "zod" import { + type ModelRecord, + ApiProviderError, openRouterDefaultModelId, openRouterDefaultModelInfo, OPENROUTER_DEFAULT_PROVIDER_NAME, OPEN_ROUTER_PROMPT_CACHING_MODELS, DEEP_SEEK_DEFAULT_TEMPERATURE, - ApiProviderError, } from "@roo-code/types" import { TelemetryService } from "@roo-code/telemetry" import { NativeToolCallParser } from "../../core/assistant-message/NativeToolCallParser" -import type { ApiHandlerOptions, ModelRecord } from "../../shared/api" +import type { ApiHandlerOptions } from "../../shared/api" import { convertToOpenAiMessages } from "../transform/openai-format" import { normalizeMistralToolCallId } from "../transform/mistral-format" diff --git a/src/api/providers/requesty.ts b/src/api/providers/requesty.ts index 85efeb800f..eb05bfd0a1 100644 --- a/src/api/providers/requesty.ts +++ b/src/api/providers/requesty.ts @@ -3,13 +3,14 @@ import OpenAI from "openai" import { type ModelInfo, + type ModelRecord, requestyDefaultModelId, requestyDefaultModelInfo, TOOL_PROTOCOL, NATIVE_TOOL_DEFAULTS, } from "@roo-code/types" -import type { ApiHandlerOptions, ModelRecord } from "../../shared/api" +import type { ApiHandlerOptions } from "../../shared/api" import { resolveToolProtocol } from "../../utils/resolveToolProtocol" import { calculateApiCostOpenAI } from "../../shared/cost" diff --git a/src/api/providers/roo.ts b/src/api/providers/roo.ts index bfd99750bf..b7a076ca55 100644 --- a/src/api/providers/roo.ts +++ b/src/api/providers/roo.ts @@ -2,11 +2,12 @@ import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" import { rooDefaultModelId, getApiProtocol, type ImageGenerationApiMethod } from "@roo-code/types" -import { NativeToolCallParser } from "../../core/assistant-message/NativeToolCallParser" import { CloudService } from "@roo-code/cloud" +import { NativeToolCallParser } from "../../core/assistant-message/NativeToolCallParser" + import { Package } from "../../shared/package" -import type { ApiHandlerOptions, ModelRecord } from "../../shared/api" +import type { ApiHandlerOptions } from "../../shared/api" import { ApiStream } from "../transform/stream" import { getModelParams } from "../transform/model-params" import { convertToOpenAiMessages } from "../transform/openai-format" diff --git a/src/api/providers/router-provider.ts b/src/api/providers/router-provider.ts index 01942e2172..4721f21666 100644 --- a/src/api/providers/router-provider.ts +++ b/src/api/providers/router-provider.ts @@ -1,8 +1,8 @@ import OpenAI from "openai" -import { type ModelInfo, NATIVE_TOOL_DEFAULTS } from "@roo-code/types" +import { type ModelInfo, type ModelRecord, NATIVE_TOOL_DEFAULTS } from "@roo-code/types" -import { ApiHandlerOptions, RouterName, ModelRecord } from "../../shared/api" +import { ApiHandlerOptions, RouterName } from "../../shared/api" import { BaseProvider } from "./base-provider" import { getModels, getModelsFromCache } from "./fetchers/modelCache" diff --git a/src/core/auto-approval/index.ts b/src/core/auto-approval/index.ts index da099d6aeb..3d340c98f1 100644 --- a/src/core/auto-approval/index.ts +++ b/src/core/auto-approval/index.ts @@ -1,6 +1,12 @@ -import { type ClineAsk, type McpServerUse, type FollowUpData, isNonBlockingAsk } from "@roo-code/types" +import { + type ClineAsk, + type McpServerUse, + type FollowUpData, + type ExtensionState, + isNonBlockingAsk, +} from "@roo-code/types" -import type { ClineSayTool, ExtensionState } from "../../shared/ExtensionMessage" +import type { ClineSayTool } from "../../shared/ExtensionMessage" import { ClineAskResponse } from "../../shared/WebviewMessage" import { isWriteToolAction, isReadOnlyToolAction } from "./tools" diff --git a/src/core/auto-approval/mcp.ts b/src/core/auto-approval/mcp.ts index 0cd1f243e8..4e576f4e38 100644 --- a/src/core/auto-approval/mcp.ts +++ b/src/core/auto-approval/mcp.ts @@ -1,6 +1,4 @@ -import type { McpServerUse } from "@roo-code/types" - -import type { McpServer, McpTool } from "../../shared/mcp" +import type { McpServerUse, McpServer, McpTool } from "@roo-code/types" export function isMcpToolAlwaysAllowed(mcpServerUse: McpServerUse, mcpServers: McpServer[] | undefined): boolean { if (mcpServerUse.type === "use_mcp_tool" && mcpServerUse.toolName) { diff --git a/src/core/prompts/tools/native-tools/__tests__/mcp_server.spec.ts b/src/core/prompts/tools/native-tools/__tests__/mcp_server.spec.ts index 2b6efd3543..ddd7caaccf 100644 --- a/src/core/prompts/tools/native-tools/__tests__/mcp_server.spec.ts +++ b/src/core/prompts/tools/native-tools/__tests__/mcp_server.spec.ts @@ -1,7 +1,10 @@ import type OpenAI from "openai" -import { getMcpServerTools } from "../mcp_server" + +import type { McpServer, McpTool } from "@roo-code/types" + import type { McpHub } from "../../../../../services/mcp/McpHub" -import type { McpServer, McpTool } from "../../../../../shared/mcp" + +import { getMcpServerTools } from "../mcp_server" // Helper type to access function tools type FunctionTool = OpenAI.Chat.ChatCompletionTool & { type: "function" } diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index eb47f96b12..0bfb0385f2 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1688,6 +1688,16 @@ export class Task extends EventEmitter implements TaskLike { } private async resumeTaskFromHistory() { + // Reset abort and streaming state to ensure clean continuation. + // This matches the behavior in resumeAfterDelegation() and prevents + // corrupted state from a previous cancellation. + this.abort = false + this.abandoned = false + this.abortReason = undefined + this.didFinishAbortingStream = false + this.isStreaming = false + this.isWaitingForFirstChunk = false + if (this.enableBridge) { try { await BridgeOrchestrator.subscribeToTask(this) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index a34fb817ee..1c5ceb64ae 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -34,6 +34,9 @@ import { type CreateTaskOptions, type TokenUsage, type ToolUsage, + type ExtensionMessage, + type ExtensionState, + type MarketplaceInstalledMetadata, RooCodeEventName, requestyDefaultModelId, openRouterDefaultModelId, @@ -51,7 +54,6 @@ import { Package } from "../../shared/package" import { findLast } from "../../shared/array" import { supportPrompt } from "../../shared/support-prompt" import { GlobalFileNames } from "../../shared/globalFileNames" -import type { ExtensionMessage, ExtensionState, MarketplaceInstalledMetadata } from "../../shared/ExtensionMessage" import { Mode, defaultModeSlug, getModeBySlug } from "../../shared/modes" import { experimentDefault } from "../../shared/experiments" import { formatLanguage } from "../../shared/language" diff --git a/src/core/webview/__tests__/ClineProvider.spec.ts b/src/core/webview/__tests__/ClineProvider.spec.ts index 6c125f490d..18cabb4fb7 100644 --- a/src/core/webview/__tests__/ClineProvider.spec.ts +++ b/src/core/webview/__tests__/ClineProvider.spec.ts @@ -7,12 +7,13 @@ import axios from "axios" import { type ProviderSettingsEntry, type ClineMessage, + type ExtensionMessage, + type ExtensionState, ORGANIZATION_ALLOW_ALL, DEFAULT_CHECKPOINT_TIMEOUT_SECONDS, } from "@roo-code/types" import { TelemetryService } from "@roo-code/telemetry" -import { ExtensionMessage, ExtensionState } from "../../../shared/ExtensionMessage" import { defaultModeSlug } from "../../../shared/modes" import { experimentDefault } from "../../../shared/experiments" import { setTtsEnabled } from "../../../utils/tts" diff --git a/src/core/webview/__tests__/webviewMessageHandler.spec.ts b/src/core/webview/__tests__/webviewMessageHandler.spec.ts index 66423dd379..d994684fce 100644 --- a/src/core/webview/__tests__/webviewMessageHandler.spec.ts +++ b/src/core/webview/__tests__/webviewMessageHandler.spec.ts @@ -10,10 +10,11 @@ vi.mock("../diagnosticsHandler", () => ({ generateErrorDiagnostics: vi.fn().mockResolvedValue({ success: true, filePath: "/tmp/diagnostics.json" }), })) +import type { ModelRecord } from "@roo-code/types" + import { webviewMessageHandler } from "../webviewMessageHandler" import type { ClineProvider } from "../ClineProvider" import { getModels } from "../../../api/providers/fetchers/modelCache" -import type { ModelRecord } from "../../../shared/api" const mockGetModels = getModels as Mock diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 7cb0ebd21d..5505f77c55 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -12,6 +12,7 @@ import { type ClineMessage, type TelemetrySetting, type UserSettingsConfig, + type ModelRecord, TelemetryEventName, RooCodeSettings, ExperimentId, @@ -29,7 +30,7 @@ import { handleCheckpointRestoreOperation } from "./checkpointRestoreHandler" import { generateErrorDiagnostics } from "./diagnosticsHandler" import { changeLanguage, t } from "../../i18n" import { Package } from "../../shared/package" -import { type RouterName, type ModelRecord, toRouterName } from "../../shared/api" +import { type RouterName, toRouterName } from "../../shared/api" import { MessageEnhancer } from "./messageEnhancer" import { diff --git a/src/services/mcp/McpHub.ts b/src/services/mcp/McpHub.ts index 518adf06d8..52eb4a064b 100644 --- a/src/services/mcp/McpHub.ts +++ b/src/services/mcp/McpHub.ts @@ -1,3 +1,7 @@ +import * as fs from "fs/promises" +import * as path from "path" + +import * as vscode from "vscode" import { Client } from "@modelcontextprotocol/sdk/client/index.js" import { StdioClientTransport, getDefaultEnvironment } from "@modelcontextprotocol/sdk/client/stdio.js" import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js" @@ -13,22 +17,23 @@ import { import chokidar, { FSWatcher } from "chokidar" import delay from "delay" import deepEqual from "fast-deep-equal" -import * as fs from "fs/promises" -import * as path from "path" -import * as vscode from "vscode" import { z } from "zod" -import { t } from "../../i18n" -import { ClineProvider } from "../../core/webview/ClineProvider" -import { GlobalFileNames } from "../../shared/globalFileNames" -import { +import type { McpResource, McpResourceResponse, McpResourceTemplate, McpServer, McpTool, McpToolCallResponse, -} from "../../shared/mcp" +} from "@roo-code/types" + +import { t } from "../../i18n" + +import { ClineProvider } from "../../core/webview/ClineProvider" + +import { GlobalFileNames } from "../../shared/globalFileNames" + import { fileExistsAtPath } from "../../utils/fs" import { arePathsEqual, getWorkspacePath } from "../../utils/path" import { injectVariables } from "../../utils/config" diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 95586c25d3..4a29784a40 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -1,44 +1,3 @@ -import type { - GlobalSettings, - ProviderSettingsEntry, - ProviderSettings, - HistoryItem, - ModeConfig, - TelemetrySetting, - Experiments, - ClineMessage, - MarketplaceItem, - TodoItem, - CloudUserInfo, - CloudOrganizationMembership, - OrganizationAllowList, - ShareVisibility, - QueuedMessage, - SerializedCustomToolDefinition, -} from "@roo-code/types" - -import { GitCommit } from "../utils/git" - -import { McpServer } from "./mcp" -import { Mode } from "./modes" -import { ModelRecord, RouterModels } from "./api" - -// Command interface for frontend/backend communication -export interface Command { - name: string - source: "global" | "project" | "built-in" - filePath?: string - description?: string - argumentHint?: string -} - -// Type for marketplace installed metadata -export interface MarketplaceInstalledMetadata { - project: Record - global: Record -} - -// Indexing status types export interface IndexingStatus { systemStatus: string message?: string @@ -60,313 +19,6 @@ export interface LanguageModelChatSelector { id?: string } -// Represents JSON data that is sent from extension to webview, called -// ExtensionMessage and has 'type' enum which can be 'plusButtonClicked' or -// 'settingsButtonClicked' or 'hello'. Webview will hold state. -export interface ExtensionMessage { - type: - | "action" - | "state" - | "selectedImages" - | "theme" - | "workspaceUpdated" - | "invoke" - | "messageUpdated" - | "mcpServers" - | "enhancedPrompt" - | "commitSearchResults" - | "listApiConfig" - | "routerModels" - | "openAiModels" - | "ollamaModels" - | "lmStudioModels" - | "vsCodeLmModels" - | "huggingFaceModels" - | "vsCodeLmApiAvailable" - | "updatePrompt" - | "systemPrompt" - | "autoApprovalEnabled" - | "updateCustomMode" - | "deleteCustomMode" - | "exportModeResult" - | "importModeResult" - | "checkRulesDirectoryResult" - | "deleteCustomModeCheck" - | "currentCheckpointUpdated" - | "checkpointInitWarning" - | "browserToolEnabled" - | "browserConnectionResult" - | "remoteBrowserEnabled" - | "ttsStart" - | "ttsStop" - | "maxReadFileLine" - | "fileSearchResults" - | "toggleApiConfigPin" - | "acceptInput" - | "setHistoryPreviewCollapsed" - | "commandExecutionStatus" - | "mcpExecutionStatus" - | "vsCodeSetting" - | "authenticatedUser" - | "condenseTaskContextStarted" - | "condenseTaskContextResponse" - | "singleRouterModelFetchResponse" - | "rooCreditBalance" - | "indexingStatusUpdate" - | "indexCleared" - | "codebaseIndexConfig" - | "marketplaceInstallResult" - | "marketplaceRemoveResult" - | "marketplaceData" - | "shareTaskSuccess" - | "codeIndexSettingsSaved" - | "codeIndexSecretStatus" - | "showDeleteMessageDialog" - | "showEditMessageDialog" - | "commands" - | "insertTextIntoTextarea" - | "dismissedUpsells" - | "organizationSwitchResult" - | "interactionRequired" - | "browserSessionUpdate" - | "browserSessionNavigate" - | "claudeCodeRateLimits" - | "customToolsResult" - | "modes" - text?: string - payload?: any // Add a generic payload for now, can refine later - // Checkpoint warning message - checkpointWarning?: { - type: "WAIT_TIMEOUT" | "INIT_TIMEOUT" - timeout: number - } - action?: - | "chatButtonClicked" - | "settingsButtonClicked" - | "historyButtonClicked" - | "marketplaceButtonClicked" - | "cloudButtonClicked" - | "didBecomeVisible" - | "focusInput" - | "switchTab" - | "toggleAutoApprove" - invoke?: "newChat" | "sendMessage" | "primaryButtonClick" | "secondaryButtonClick" | "setChatBoxMessage" - state?: ExtensionState - images?: string[] - filePaths?: string[] - openedTabs?: Array<{ - label: string - isActive: boolean - path?: string - }> - clineMessage?: ClineMessage - routerModels?: RouterModels - openAiModels?: string[] - ollamaModels?: ModelRecord - lmStudioModels?: ModelRecord - vsCodeLmModels?: { vendor?: string; family?: string; version?: string; id?: string }[] - huggingFaceModels?: Array<{ - id: string - object: string - created: number - owned_by: string - providers: Array<{ - provider: string - status: "live" | "staging" | "error" - supports_tools?: boolean - supports_structured_output?: boolean - context_length?: number - pricing?: { - input: number - output: number - } - }> - }> - mcpServers?: McpServer[] - commits?: GitCommit[] - listApiConfig?: ProviderSettingsEntry[] - mode?: Mode - customMode?: ModeConfig - slug?: string - success?: boolean - values?: Record - requestId?: string - promptText?: string - results?: - | { path: string; type: "file" | "folder"; label?: string }[] - | { name: string; description?: string; argumentHint?: string; source: "global" | "project" | "built-in" }[] - error?: string - setting?: string - value?: any - hasContent?: boolean // For checkRulesDirectoryResult - items?: MarketplaceItem[] - userInfo?: CloudUserInfo - organizationAllowList?: OrganizationAllowList - tab?: string - marketplaceItems?: MarketplaceItem[] - organizationMcps?: MarketplaceItem[] - marketplaceInstalledMetadata?: MarketplaceInstalledMetadata - errors?: string[] - visibility?: ShareVisibility - rulesFolderPath?: string - settings?: any - messageTs?: number - hasCheckpoint?: boolean - context?: string - commands?: Command[] - queuedMessages?: QueuedMessage[] - list?: string[] // For dismissedUpsells - organizationId?: string | null // For organizationSwitchResult - browserSessionMessages?: ClineMessage[] // For browser session panel updates - isBrowserSessionActive?: boolean // For browser session panel updates - stepIndex?: number // For browserSessionNavigate: the target step index to display - tools?: SerializedCustomToolDefinition[] // For customToolsResult - modes?: { slug: string; name: string }[] // For modes response -} - -export type ExtensionState = Pick< - GlobalSettings, - | "currentApiConfigName" - | "listApiConfigMeta" - | "pinnedApiConfigs" - | "customInstructions" - | "dismissedUpsells" - | "autoApprovalEnabled" - | "alwaysAllowReadOnly" - | "alwaysAllowReadOnlyOutsideWorkspace" - | "alwaysAllowWrite" - | "alwaysAllowWriteOutsideWorkspace" - | "alwaysAllowWriteProtected" - | "alwaysAllowBrowser" - | "alwaysAllowMcp" - | "alwaysAllowModeSwitch" - | "alwaysAllowSubtasks" - | "alwaysAllowFollowupQuestions" - | "alwaysAllowExecute" - | "followupAutoApproveTimeoutMs" - | "allowedCommands" - | "deniedCommands" - | "allowedMaxRequests" - | "allowedMaxCost" - | "browserToolEnabled" - | "browserViewportSize" - | "screenshotQuality" - | "remoteBrowserEnabled" - | "cachedChromeHostUrl" - | "remoteBrowserHost" - | "ttsEnabled" - | "ttsSpeed" - | "soundEnabled" - | "soundVolume" - | "maxConcurrentFileReads" - | "terminalOutputLineLimit" - | "terminalOutputCharacterLimit" - | "terminalShellIntegrationTimeout" - | "terminalShellIntegrationDisabled" - | "terminalCommandDelay" - | "terminalPowershellCounter" - | "terminalZshClearEolMark" - | "terminalZshOhMy" - | "terminalZshP10k" - | "terminalZdotdir" - | "terminalCompressProgressBar" - | "diagnosticsEnabled" - | "diffEnabled" - | "fuzzyMatchThreshold" - | "language" - | "modeApiConfigs" - | "customModePrompts" - | "customSupportPrompts" - | "enhancementApiConfigId" - | "condensingApiConfigId" - | "customCondensingPrompt" - | "codebaseIndexConfig" - | "codebaseIndexModels" - | "profileThresholds" - | "includeDiagnosticMessages" - | "maxDiagnosticMessages" - | "imageGenerationProvider" - | "openRouterImageGenerationSelectedModel" - | "includeTaskHistoryInEnhance" - | "reasoningBlockCollapsed" - | "enterBehavior" - | "includeCurrentTime" - | "includeCurrentCost" - | "maxGitStatusFiles" - | "requestDelaySeconds" -> & { - version: string - clineMessages: ClineMessage[] - currentTaskItem?: HistoryItem - currentTaskTodos?: TodoItem[] // Initial todos for the current task - apiConfiguration: ProviderSettings - uriScheme?: string - shouldShowAnnouncement: boolean - - taskHistory: HistoryItem[] - - writeDelayMs: number - - enableCheckpoints: boolean - checkpointTimeout: number // Timeout for checkpoint initialization in seconds (default: 15) - maxOpenTabsContext: number // Maximum number of VSCode open tabs to include in context (0-500) - maxWorkspaceFiles: number // Maximum number of files to include in current working directory details (0-500) - showRooIgnoredFiles: boolean // Whether to show .rooignore'd files in listings - enableSubfolderRules: boolean // Whether to load rules from subdirectories - maxReadFileLine: number // Maximum number of lines to read from a file before truncating - maxImageFileSize: number // Maximum size of image files to process in MB - maxTotalImageSize: number // Maximum total size for all images in a single read operation in MB - - experiments: Experiments // Map of experiment IDs to their enabled state - - mcpEnabled: boolean - enableMcpServerCreation: boolean - - mode: Mode - customModes: ModeConfig[] - toolRequirements?: Record // Map of tool names to their requirements (e.g. {"apply_diff": true} if diffEnabled) - - cwd?: string // Current working directory - telemetrySetting: TelemetrySetting - telemetryKey?: string - machineId?: string - - renderContext: "sidebar" | "editor" - settingsImportedAt?: number - historyPreviewCollapsed?: boolean - - cloudUserInfo: CloudUserInfo | null - cloudIsAuthenticated: boolean - cloudAuthSkipModel?: boolean // Flag indicating auth completed without model selection (user should pick 3rd-party provider) - cloudApiUrl?: string - cloudOrganizations?: CloudOrganizationMembership[] - sharingEnabled: boolean - publicSharingEnabled: boolean - organizationAllowList: OrganizationAllowList - organizationSettingsVersion?: number - - isBrowserSessionActive: boolean // Actual browser session state - - autoCondenseContext: boolean - autoCondenseContextPercent: number - marketplaceItems?: MarketplaceItem[] - marketplaceInstalledMetadata?: { project: Record; global: Record } - profileThresholds: Record - hasOpenedModeSelector: boolean - openRouterImageApiKey?: string - messageQueue?: QueuedMessage[] - lastShownAnnouncementId?: string - apiModelId?: string - mcpServers?: McpServer[] - hasSystemPromptOverride?: boolean - mdmCompliant?: boolean - remoteControlEnabled: boolean - taskSyncEnabled: boolean - featureRoomoteControlEnabled: boolean - claudeCodeIsAuthenticated?: boolean - debug?: boolean -} - export interface ClineSayTool { tool: | "editedExistingFile" diff --git a/src/shared/api.ts b/src/shared/api.ts index fb7680fbfd..b2ba1e3542 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -39,12 +39,6 @@ export function toRouterName(value?: string): RouterName { throw new Error(`Invalid router name: ${value}`) } -// RouterModels - -export type ModelRecord = Record - -export type RouterModels = Record - // Reasoning export const shouldUseReasoningBudget = ({ diff --git a/src/shared/mcp.ts b/src/shared/mcp.ts deleted file mode 100644 index ef1d51bad3..0000000000 --- a/src/shared/mcp.ts +++ /dev/null @@ -1,83 +0,0 @@ -export type McpErrorEntry = { - message: string - timestamp: number - level: "error" | "warn" | "info" -} - -export type McpServer = { - name: string - config: string - status: "connected" | "connecting" | "disconnected" - error?: string - errorHistory?: McpErrorEntry[] - tools?: McpTool[] - resources?: McpResource[] - resourceTemplates?: McpResourceTemplate[] - disabled?: boolean - timeout?: number - source?: "global" | "project" - projectPath?: string - instructions?: string -} - -export type McpTool = { - name: string - description?: string - inputSchema?: object - alwaysAllow?: boolean - enabledForPrompt?: boolean -} - -export type McpResource = { - uri: string - name: string - mimeType?: string - description?: string -} - -export type McpResourceTemplate = { - uriTemplate: string - name: string - description?: string - mimeType?: string -} - -export type McpResourceResponse = { - _meta?: Record - contents: Array<{ - uri: string - mimeType?: string - text?: string - blob?: string - }> -} - -export type McpToolCallResponse = { - _meta?: Record - content: Array< - | { - type: "text" - text: string - } - | { - type: "image" - data: string - mimeType: string - } - | { - type: "audio" - data: string - mimeType: string - } - | { - type: "resource" - resource: { - uri: string - mimeType?: string - text?: string - blob?: string - } - } - > - isError?: boolean -} diff --git a/src/utils/git.ts b/src/utils/git.ts index ae1310da5b..04c028c3d1 100644 --- a/src/utils/git.ts +++ b/src/utils/git.ts @@ -3,25 +3,15 @@ import * as path from "path" import { promises as fs } from "fs" import { exec } from "child_process" import { promisify } from "util" + +import type { GitRepositoryInfo, GitCommit } from "@roo-code/types" + import { truncateOutput } from "../integrations/misc/extract-text" const execAsync = promisify(exec) + const GIT_OUTPUT_LINE_LIMIT = 500 -export interface GitRepositoryInfo { - repositoryUrl?: string - repositoryName?: string - defaultBranch?: string -} - -export interface GitCommit { - hash: string - shortHash: string - subject: string - author: string - date: string -} - /** * Extracts git repository information from the workspace's .git directory * @param workspaceRoot The root path of the workspace diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index 04d9b76f2c..cccb0422ca 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -2,13 +2,13 @@ import React, { useCallback, useEffect, useRef, useState, useMemo } from "react" import { useEvent } from "react-use" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" -import { ExtensionMessage } from "@roo/ExtensionMessage" +import { type ExtensionMessage, TelemetryEventName } from "@roo-code/types" + import TranslationProvider from "./i18n/TranslationContext" import { MarketplaceViewStateManager } from "./components/marketplace/MarketplaceViewStateManager" import { vscode } from "./utils/vscode" import { telemetryClient } from "./utils/TelemetryClient" -import { TelemetryEventName } from "@roo-code/types" import { initializeSourceMaps, exposeSourceMapsForDebugging } from "./utils/sourceMapInitializer" import { ExtensionStateContextProvider, useExtensionState } from "./context/ExtensionStateContext" import ChatView, { ChatViewRef } from "./components/chat/ChatView" diff --git a/webview-ui/src/__tests__/command-autocomplete.spec.ts b/webview-ui/src/__tests__/command-autocomplete.spec.ts index d239128cce..fb5d2628f6 100644 --- a/webview-ui/src/__tests__/command-autocomplete.spec.ts +++ b/webview-ui/src/__tests__/command-autocomplete.spec.ts @@ -1,4 +1,4 @@ -import type { Command } from "@roo/ExtensionMessage" +import type { Command } from "@roo-code/types" import { getContextMenuOptions, ContextMenuOptionType } from "../utils/context-mentions" diff --git a/webview-ui/src/components/browser-session/BrowserPanelStateProvider.tsx b/webview-ui/src/components/browser-session/BrowserPanelStateProvider.tsx index 50b078c740..8430c772aa 100644 --- a/webview-ui/src/components/browser-session/BrowserPanelStateProvider.tsx +++ b/webview-ui/src/components/browser-session/BrowserPanelStateProvider.tsx @@ -1,5 +1,6 @@ import React, { createContext, useContext, useState, useEffect, useCallback } from "react" -import { ExtensionMessage } from "@roo/ExtensionMessage" + +import { type ExtensionMessage } from "@roo-code/types" interface BrowserPanelState { browserViewportSize: string diff --git a/webview-ui/src/components/browser-session/BrowserSessionPanel.tsx b/webview-ui/src/components/browser-session/BrowserSessionPanel.tsx index fe88106ad2..d9667c56f1 100644 --- a/webview-ui/src/components/browser-session/BrowserSessionPanel.tsx +++ b/webview-ui/src/components/browser-session/BrowserSessionPanel.tsx @@ -1,14 +1,18 @@ import React, { useEffect, useState } from "react" -import { type ClineMessage } from "@roo-code/types" -import BrowserSessionRow from "../chat/BrowserSessionRow" + +import { type ClineMessage, type ExtensionMessage } from "@roo-code/types" + import { TooltipProvider } from "@src/components/ui/tooltip" -import ErrorBoundary from "../ErrorBoundary" import TranslationProvider from "@src/i18n/TranslationContext" -import { ExtensionMessage } from "@roo/ExtensionMessage" -import { BrowserPanelStateProvider, useBrowserPanelState } from "./BrowserPanelStateProvider" import { vscode } from "@src/utils/vscode" + import { ExtensionStateContextProvider } from "@/context/ExtensionStateContext" +import BrowserSessionRow from "../chat/BrowserSessionRow" +import ErrorBoundary from "../ErrorBoundary" + +import { BrowserPanelStateProvider, useBrowserPanelState } from "./BrowserPanelStateProvider" + interface BrowserSessionPanelState { messages: ClineMessage[] } diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index 7fbc658718..dd3d0d4c66 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -3,10 +3,11 @@ import { useEvent } from "react-use" import DynamicTextArea from "react-textarea-autosize" import { VolumeX, Image, WandSparkles, SendHorizontal, MessageSquareX } from "lucide-react" +import type { ExtensionMessage } from "@roo-code/types" + import { mentionRegex, mentionRegexGlobal, commandRegexGlobal, unescapeSpaces } from "@roo/context-mentions" import { WebviewMessage } from "@roo/WebviewMessage" import { Mode, getAllModes } from "@roo/modes" -import { ExtensionMessage } from "@roo/ExtensionMessage" import { vscode } from "@src/utils/vscode" import { useExtensionState } from "@src/context/ExtensionStateContext" diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 6f3ee16ec1..2b784e1fe0 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -11,9 +11,9 @@ import { Trans } from "react-i18next" import { useDebounceEffect } from "@src/utils/useDebounceEffect" import { appendImages } from "@src/utils/imageUtils" -import type { ClineAsk, ClineMessage } from "@roo-code/types" +import type { ClineAsk, ClineMessage, ExtensionMessage } from "@roo-code/types" -import { ClineSayTool, ExtensionMessage } from "@roo/ExtensionMessage" +import { ClineSayTool } from "@roo/ExtensionMessage" import { findLast } from "@roo/array" import { SuggestionItem } from "@roo-code/types" import { combineApiRequests } from "@roo/combineApiRequests" diff --git a/webview-ui/src/components/chat/CommandExecution.tsx b/webview-ui/src/components/chat/CommandExecution.tsx index 0e9d2dd718..716272eaf1 100644 --- a/webview-ui/src/components/chat/CommandExecution.tsx +++ b/webview-ui/src/components/chat/CommandExecution.tsx @@ -3,11 +3,9 @@ import { useEvent } from "react-use" import { t } from "i18next" import { ChevronDown, OctagonX } from "lucide-react" -import { CommandExecutionStatus, commandExecutionStatusSchema } from "@roo-code/types" +import { type ExtensionMessage, type CommandExecutionStatus, commandExecutionStatusSchema } from "@roo-code/types" -import { ExtensionMessage } from "@roo/ExtensionMessage" import { safeJsonParse } from "@roo/safeJsonParse" - import { COMMAND_OUTPUT_STRING } from "@roo/combineCommandSequences" import { parseCommand } from "@roo/parse-command" diff --git a/webview-ui/src/components/chat/ContextMenu.tsx b/webview-ui/src/components/chat/ContextMenu.tsx index cf4b10a981..14f8c22c76 100644 --- a/webview-ui/src/components/chat/ContextMenu.tsx +++ b/webview-ui/src/components/chat/ContextMenu.tsx @@ -1,9 +1,10 @@ import React, { useEffect, useMemo, useRef, useState } from "react" import { getIconForFilePath, getIconUrlByName, getIconForDirectoryPath } from "vscode-material-icons" +import { Trans } from "react-i18next" +import { t } from "i18next" import { Settings } from "lucide-react" -import type { ModeConfig } from "@roo-code/types" -import type { Command } from "@roo/ExtensionMessage" +import type { ModeConfig, Command } from "@roo-code/types" import { ContextMenuOptionType, @@ -13,9 +14,8 @@ import { } from "@src/utils/context-mentions" import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric" import { vscode } from "@src/utils/vscode" + import { buildDocLink } from "@/utils/docLinks" -import { Trans } from "react-i18next" -import { t } from "i18next" interface ContextMenuProps { onSelect: (type: ContextMenuOptionType, value?: string) => void diff --git a/webview-ui/src/components/chat/McpExecution.tsx b/webview-ui/src/components/chat/McpExecution.tsx index a96f368a17..6151e581ba 100644 --- a/webview-ui/src/components/chat/McpExecution.tsx +++ b/webview-ui/src/components/chat/McpExecution.tsx @@ -3,13 +3,16 @@ import { Server, ChevronDown } from "lucide-react" import { useEvent } from "react-use" import { useTranslation } from "react-i18next" -import { McpExecutionStatus, mcpExecutionStatusSchema } from "@roo-code/types" -import { ExtensionMessage, ClineAskUseMcpServer } from "../../../../src/shared/ExtensionMessage" -import { safeJsonParse } from "../../../../src/shared/safeJsonParse" +import { type ExtensionMessage, type McpExecutionStatus, mcpExecutionStatusSchema } from "@roo-code/types" + import { cn } from "@src/lib/utils" import { Button } from "@src/components/ui" + +import { ClineAskUseMcpServer } from "../../../../src/shared/ExtensionMessage" +import { safeJsonParse } from "../../../../src/shared/safeJsonParse" import CodeBlock from "../common/CodeBlock" import McpToolRow from "../mcp/McpToolRow" + import { Markdown } from "./Markdown" interface McpExecutionProps { diff --git a/webview-ui/src/components/chat/SlashCommandItem.tsx b/webview-ui/src/components/chat/SlashCommandItem.tsx index 90d5b39e16..04ade08bbd 100644 --- a/webview-ui/src/components/chat/SlashCommandItem.tsx +++ b/webview-ui/src/components/chat/SlashCommandItem.tsx @@ -1,7 +1,7 @@ import React from "react" import { Edit, Trash2 } from "lucide-react" -import type { Command } from "@roo/ExtensionMessage" +import type { Command } from "@roo-code/types" import { useAppTranslation } from "@/i18n/TranslationContext" import { Button, StandardTooltip } from "@/components/ui" diff --git a/webview-ui/src/components/chat/SlashCommandItemSimple.tsx b/webview-ui/src/components/chat/SlashCommandItemSimple.tsx index 50a12a74f7..d395834abd 100644 --- a/webview-ui/src/components/chat/SlashCommandItemSimple.tsx +++ b/webview-ui/src/components/chat/SlashCommandItemSimple.tsx @@ -1,6 +1,6 @@ import React from "react" -import type { Command } from "@roo/ExtensionMessage" +import type { Command } from "@roo-code/types" interface SlashCommandItemSimpleProps { command: Command diff --git a/webview-ui/src/components/chat/__tests__/SlashCommandItemSimple.spec.tsx b/webview-ui/src/components/chat/__tests__/SlashCommandItemSimple.spec.tsx index 7b2175950d..197b0c60e0 100644 --- a/webview-ui/src/components/chat/__tests__/SlashCommandItemSimple.spec.tsx +++ b/webview-ui/src/components/chat/__tests__/SlashCommandItemSimple.spec.tsx @@ -1,6 +1,6 @@ -import { render, screen, fireEvent } from "@/utils/test-utils" +import type { Command } from "@roo-code/types" -import type { Command } from "@roo/ExtensionMessage" +import { render, screen, fireEvent } from "@/utils/test-utils" import { SlashCommandItemSimple } from "../SlashCommandItemSimple" diff --git a/webview-ui/src/components/cloud/OrganizationSwitcher.tsx b/webview-ui/src/components/cloud/OrganizationSwitcher.tsx index 3c02bfe6ff..94727827ef 100644 --- a/webview-ui/src/components/cloud/OrganizationSwitcher.tsx +++ b/webview-ui/src/components/cloud/OrganizationSwitcher.tsx @@ -1,10 +1,12 @@ import { useState, useEffect } from "react" import { Building2, User, Plus } from "lucide-react" -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SelectSeparator } from "@/components/ui/select" -import { type CloudUserInfo, type CloudOrganizationMembership } from "@roo-code/types" + +import { type CloudUserInfo, type CloudOrganizationMembership, type ExtensionMessage } from "@roo-code/types" + import { useAppTranslation } from "@src/i18n/TranslationContext" import { vscode } from "@src/utils/vscode" -import { type ExtensionMessage } from "@roo/ExtensionMessage" + +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SelectSeparator } from "@/components/ui/select" type OrganizationSwitcherProps = { userInfo: CloudUserInfo diff --git a/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts b/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts index 995e982164..e1056a6d86 100644 --- a/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts +++ b/webview-ui/src/components/marketplace/MarketplaceViewStateManager.ts @@ -11,10 +11,10 @@ * 3. Using minimal state updates to avoid resetting scroll position */ -import { MarketplaceItem } from "@roo-code/types" +import { MarketplaceItem, MarketplaceInstalledMetadata } from "@roo-code/types" + import { vscode } from "../../utils/vscode" import { WebviewMessage } from "../../../../src/shared/WebviewMessage" -import type { MarketplaceInstalledMetadata } from "../../../../src/shared/ExtensionMessage" export interface ViewState { allItems: MarketplaceItem[] diff --git a/webview-ui/src/components/mcp/McpEnabledToggle.tsx b/webview-ui/src/components/mcp/McpEnabledToggle.tsx index e85738ac04..1fd30034b5 100644 --- a/webview-ui/src/components/mcp/McpEnabledToggle.tsx +++ b/webview-ui/src/components/mcp/McpEnabledToggle.tsx @@ -1,5 +1,6 @@ -import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react" import { FormEvent } from "react" +import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react" + import { useExtensionState } from "@src/context/ExtensionStateContext" import { useAppTranslation } from "@src/i18n/TranslationContext" import { vscode } from "@src/utils/vscode" diff --git a/webview-ui/src/components/mcp/McpErrorRow.tsx b/webview-ui/src/components/mcp/McpErrorRow.tsx index cebd8a4d4b..9de2ded184 100644 --- a/webview-ui/src/components/mcp/McpErrorRow.tsx +++ b/webview-ui/src/components/mcp/McpErrorRow.tsx @@ -1,7 +1,7 @@ import { useMemo } from "react" import { formatRelative } from "date-fns" -import type { McpErrorEntry } from "@roo/mcp" +import type { McpErrorEntry } from "@roo-code/types" type McpErrorRowProps = { error: McpErrorEntry diff --git a/webview-ui/src/components/mcp/McpResourceRow.tsx b/webview-ui/src/components/mcp/McpResourceRow.tsx index 651a569a3c..2c48bad723 100644 --- a/webview-ui/src/components/mcp/McpResourceRow.tsx +++ b/webview-ui/src/components/mcp/McpResourceRow.tsx @@ -1,4 +1,4 @@ -import { McpResource, McpResourceTemplate } from "@roo/mcp" +import type { McpResource, McpResourceTemplate } from "@roo-code/types" type McpResourceRowProps = { item: McpResource | McpResourceTemplate diff --git a/webview-ui/src/components/mcp/McpToolRow.tsx b/webview-ui/src/components/mcp/McpToolRow.tsx index aa57b18fd9..5dea579193 100644 --- a/webview-ui/src/components/mcp/McpToolRow.tsx +++ b/webview-ui/src/components/mcp/McpToolRow.tsx @@ -1,6 +1,6 @@ import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react" -import { McpTool } from "@roo/mcp" +import type { McpTool } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { vscode } from "@src/utils/vscode" diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index bacb960c22..a2c6193f58 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -9,7 +9,7 @@ import { } from "@vscode/webview-ui-toolkit/react" import { Webhook } from "lucide-react" -import { McpServer } from "@roo/mcp" +import type { McpServer } from "@roo-code/types" import { vscode } from "@src/utils/vscode" import { useExtensionState } from "@src/context/ExtensionStateContext" diff --git a/webview-ui/src/components/settings/SlashCommandsSettings.tsx b/webview-ui/src/components/settings/SlashCommandsSettings.tsx index ece2806746..58869fe8cc 100644 --- a/webview-ui/src/components/settings/SlashCommandsSettings.tsx +++ b/webview-ui/src/components/settings/SlashCommandsSettings.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react" import { Plus, Globe, Folder, Settings, SquareSlash } from "lucide-react" import { Trans } from "react-i18next" -import type { Command } from "@roo/ExtensionMessage" +import type { Command } from "@roo-code/types" import { useAppTranslation } from "@/i18n/TranslationContext" import { useExtensionState } from "@/context/ExtensionStateContext" diff --git a/webview-ui/src/components/settings/TerminalSettings.tsx b/webview-ui/src/components/settings/TerminalSettings.tsx index c647344c08..d7ae25f0a0 100644 --- a/webview-ui/src/components/settings/TerminalSettings.tsx +++ b/webview-ui/src/components/settings/TerminalSettings.tsx @@ -7,7 +7,7 @@ import { Trans } from "react-i18next" import { buildDocLink } from "@src/utils/docLinks" import { useEvent, useMount } from "react-use" -import { ExtensionMessage } from "@roo/ExtensionMessage" +import { type ExtensionMessage } from "@roo-code/types" import { cn } from "@/lib/utils" import { Slider } from "@/components/ui" diff --git a/webview-ui/src/components/settings/__tests__/SlashCommandsSettings.spec.tsx b/webview-ui/src/components/settings/__tests__/SlashCommandsSettings.spec.tsx index 05ec7b9fd1..cd4ad0b55d 100644 --- a/webview-ui/src/components/settings/__tests__/SlashCommandsSettings.spec.tsx +++ b/webview-ui/src/components/settings/__tests__/SlashCommandsSettings.spec.tsx @@ -1,7 +1,7 @@ import { render, screen, fireEvent, waitFor } from "@/utils/test-utils" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" -import type { Command } from "@roo/ExtensionMessage" +import type { Command } from "@roo-code/types" import { ExtensionStateContextProvider } from "@/context/ExtensionStateContext" import { vscode } from "@/utils/vscode" diff --git a/webview-ui/src/components/settings/providers/Chutes.tsx b/webview-ui/src/components/settings/providers/Chutes.tsx index f061ce49e2..90962e5ccb 100644 --- a/webview-ui/src/components/settings/providers/Chutes.tsx +++ b/webview-ui/src/components/settings/providers/Chutes.tsx @@ -1,14 +1,12 @@ import { useCallback } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import type { ProviderSettings, OrganizationAllowList } from "@roo-code/types" +import type { ProviderSettings, OrganizationAllowList, RouterModels } from "@roo-code/types" import { chutesDefaultModelId } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" -import type { RouterModels } from "@roo/api" - import { ModelPicker } from "../ModelPicker" import { inputEventTransform } from "../transforms" diff --git a/webview-ui/src/components/settings/providers/ClaudeCode.tsx b/webview-ui/src/components/settings/providers/ClaudeCode.tsx index 87072a9b97..9dfcf81c86 100644 --- a/webview-ui/src/components/settings/providers/ClaudeCode.tsx +++ b/webview-ui/src/components/settings/providers/ClaudeCode.tsx @@ -1,8 +1,11 @@ import React from "react" + import { type ProviderSettings, claudeCodeDefaultModelId, claudeCodeModels } from "@roo-code/types" + import { useAppTranslation } from "@src/i18n/TranslationContext" import { Button } from "@src/components/ui" import { vscode } from "@src/utils/vscode" + import { ModelPicker } from "../ModelPicker" import { ClaudeCodeRateLimitDashboard } from "./ClaudeCodeRateLimitDashboard" diff --git a/webview-ui/src/components/settings/providers/DeepInfra.tsx b/webview-ui/src/components/settings/providers/DeepInfra.tsx index 4dca94c39d..fbff11a1d3 100644 --- a/webview-ui/src/components/settings/providers/DeepInfra.tsx +++ b/webview-ui/src/components/settings/providers/DeepInfra.tsx @@ -1,9 +1,12 @@ import { useCallback, useEffect, useState } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { OrganizationAllowList, type ProviderSettings, deepInfraDefaultModelId } from "@roo-code/types" - -import type { RouterModels } from "@roo/api" +import { + type OrganizationAllowList, + type ProviderSettings, + type RouterModels, + deepInfraDefaultModelId, +} from "@roo-code/types" import { vscode } from "@src/utils/vscode" import { useAppTranslation } from "@src/i18n/TranslationContext" diff --git a/webview-ui/src/components/settings/providers/HuggingFace.tsx b/webview-ui/src/components/settings/providers/HuggingFace.tsx index 415ec348f3..2a587df3bd 100644 --- a/webview-ui/src/components/settings/providers/HuggingFace.tsx +++ b/webview-ui/src/components/settings/providers/HuggingFace.tsx @@ -2,9 +2,8 @@ import { useCallback, useState, useEffect, useMemo } from "react" import { useEvent } from "react-use" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import type { ProviderSettings } from "@roo-code/types" +import type { ProviderSettings, ExtensionMessage } from "@roo-code/types" -import { ExtensionMessage } from "@roo/ExtensionMessage" import { vscode } from "@src/utils/vscode" import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" diff --git a/webview-ui/src/components/settings/providers/LMStudio.tsx b/webview-ui/src/components/settings/providers/LMStudio.tsx index 04fb53aa27..8bed1f69b9 100644 --- a/webview-ui/src/components/settings/providers/LMStudio.tsx +++ b/webview-ui/src/components/settings/providers/LMStudio.tsx @@ -4,15 +4,13 @@ import { Trans } from "react-i18next" import { Checkbox } from "vscrui" import { VSCodeLink, VSCodeRadio, VSCodeRadioGroup, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import type { ProviderSettings } from "@roo-code/types" +import type { ProviderSettings, ExtensionMessage, ModelRecord } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" -import { ExtensionMessage } from "@roo/ExtensionMessage" import { useRouterModels } from "@src/components/ui/hooks/useRouterModels" import { vscode } from "@src/utils/vscode" import { inputEventTransform } from "../transforms" -import { ModelRecord } from "@roo/api" type LMStudioProps = { apiConfiguration: ProviderSettings diff --git a/webview-ui/src/components/settings/providers/LiteLLM.tsx b/webview-ui/src/components/settings/providers/LiteLLM.tsx index 0b89b671ce..38ae1f3a96 100644 --- a/webview-ui/src/components/settings/providers/LiteLLM.tsx +++ b/webview-ui/src/components/settings/providers/LiteLLM.tsx @@ -1,10 +1,14 @@ import { useCallback, useState, useEffect, useRef } from "react" import { VSCodeTextField, VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react" -import { type ProviderSettings, type OrganizationAllowList, litellmDefaultModelId } from "@roo-code/types" +import { + type ProviderSettings, + type OrganizationAllowList, + type ExtensionMessage, + litellmDefaultModelId, +} from "@roo-code/types" import { RouterName } from "@roo/api" -import { ExtensionMessage } from "@roo/ExtensionMessage" import { vscode } from "@src/utils/vscode" import { useExtensionState } from "@src/context/ExtensionStateContext" diff --git a/webview-ui/src/components/settings/providers/Mistral.tsx b/webview-ui/src/components/settings/providers/Mistral.tsx index 0c394a3c31..84a0154a63 100644 --- a/webview-ui/src/components/settings/providers/Mistral.tsx +++ b/webview-ui/src/components/settings/providers/Mistral.tsx @@ -1,9 +1,7 @@ import { useCallback } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { type ProviderSettings, mistralDefaultModelId } from "@roo-code/types" - -import type { RouterModels } from "@roo/api" +import { type ProviderSettings, type RouterModels, mistralDefaultModelId } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" diff --git a/webview-ui/src/components/settings/providers/Ollama.tsx b/webview-ui/src/components/settings/providers/Ollama.tsx index 615d3be409..d05c3a6d8e 100644 --- a/webview-ui/src/components/settings/providers/Ollama.tsx +++ b/webview-ui/src/components/settings/providers/Ollama.tsx @@ -2,16 +2,13 @@ import { useState, useCallback, useMemo, useEffect } from "react" import { useEvent } from "react-use" import { VSCodeTextField, VSCodeRadioGroup, VSCodeRadio } from "@vscode/webview-ui-toolkit/react" -import type { ProviderSettings } from "@roo-code/types" - -import { ExtensionMessage } from "@roo/ExtensionMessage" +import type { ProviderSettings, ExtensionMessage, ModelRecord } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { useRouterModels } from "@src/components/ui/hooks/useRouterModels" import { vscode } from "@src/utils/vscode" import { inputEventTransform } from "../transforms" -import { ModelRecord } from "@roo/api" type OllamaProps = { apiConfiguration: ProviderSettings diff --git a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx index ad338d342a..5a663aca98 100644 --- a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx +++ b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx @@ -8,12 +8,11 @@ import { type ModelInfo, type ReasoningEffort, type OrganizationAllowList, + type ExtensionMessage, azureOpenAiDefaultApiVersion, openAiModelInfoSaneDefaults, } from "@roo-code/types" -import { ExtensionMessage } from "@roo/ExtensionMessage" - import { useAppTranslation } from "@src/i18n/TranslationContext" import { Button, StandardTooltip } from "@src/components/ui" diff --git a/webview-ui/src/components/settings/providers/OpenRouter.tsx b/webview-ui/src/components/settings/providers/OpenRouter.tsx index 51c75b3f24..2dba8c8459 100644 --- a/webview-ui/src/components/settings/providers/OpenRouter.tsx +++ b/webview-ui/src/components/settings/providers/OpenRouter.tsx @@ -2,9 +2,12 @@ import { useCallback, useState } from "react" import { Checkbox } from "vscrui" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { type ProviderSettings, type OrganizationAllowList, openRouterDefaultModelId } from "@roo-code/types" - -import type { RouterModels } from "@roo/api" +import { + type ProviderSettings, + type OrganizationAllowList, + type RouterModels, + openRouterDefaultModelId, +} from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { getOpenRouterAuthUrl } from "@src/oauth/urls" diff --git a/webview-ui/src/components/settings/providers/QwenCode.tsx b/webview-ui/src/components/settings/providers/QwenCode.tsx index e3cfe2c96a..a5a4b3d10e 100644 --- a/webview-ui/src/components/settings/providers/QwenCode.tsx +++ b/webview-ui/src/components/settings/providers/QwenCode.tsx @@ -1,5 +1,6 @@ import React from "react" import { VSCodeTextField, VSCodeLink } from "@vscode/webview-ui-toolkit/react" + import { type ProviderSettings } from "@roo-code/types" interface QwenCodeProps { diff --git a/webview-ui/src/components/settings/providers/Requesty.tsx b/webview-ui/src/components/settings/providers/Requesty.tsx index 859d82d03e..ba24a6aafb 100644 --- a/webview-ui/src/components/settings/providers/Requesty.tsx +++ b/webview-ui/src/components/settings/providers/Requesty.tsx @@ -1,9 +1,12 @@ import { useCallback, useEffect, useState } from "react" import { VSCodeCheckbox, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { type ProviderSettings, type OrganizationAllowList, requestyDefaultModelId } from "@roo-code/types" - -import type { RouterModels } from "@roo/api" +import { + type ProviderSettings, + type OrganizationAllowList, + type RouterModels, + requestyDefaultModelId, +} from "@roo-code/types" import { vscode } from "@src/utils/vscode" import { useAppTranslation } from "@src/i18n/TranslationContext" diff --git a/webview-ui/src/components/settings/providers/Roo.tsx b/webview-ui/src/components/settings/providers/Roo.tsx index fec1d2bc1a..48d845998d 100644 --- a/webview-ui/src/components/settings/providers/Roo.tsx +++ b/webview-ui/src/components/settings/providers/Roo.tsx @@ -1,6 +1,9 @@ -import { type ProviderSettings, type OrganizationAllowList, rooDefaultModelId } from "@roo-code/types" - -import type { RouterModels } from "@roo/api" +import { + type ProviderSettings, + type OrganizationAllowList, + type RouterModels, + rooDefaultModelId, +} from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { vscode } from "@src/utils/vscode" diff --git a/webview-ui/src/components/settings/providers/Unbound.tsx b/webview-ui/src/components/settings/providers/Unbound.tsx index e3a064434e..639f1cefab 100644 --- a/webview-ui/src/components/settings/providers/Unbound.tsx +++ b/webview-ui/src/components/settings/providers/Unbound.tsx @@ -2,9 +2,12 @@ import { useCallback, useState, useRef } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" import { useQueryClient } from "@tanstack/react-query" -import { type ProviderSettings, type OrganizationAllowList, unboundDefaultModelId } from "@roo-code/types" - -import type { RouterModels } from "@roo/api" +import { + type ProviderSettings, + type OrganizationAllowList, + type RouterModels, + unboundDefaultModelId, +} from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" diff --git a/webview-ui/src/components/settings/providers/VSCodeLM.tsx b/webview-ui/src/components/settings/providers/VSCodeLM.tsx index a2097badf6..8179668002 100644 --- a/webview-ui/src/components/settings/providers/VSCodeLM.tsx +++ b/webview-ui/src/components/settings/providers/VSCodeLM.tsx @@ -2,9 +2,7 @@ import { useState, useCallback } from "react" import { useEvent } from "react-use" import { LanguageModelChatSelector } from "vscode" -import type { ProviderSettings } from "@roo-code/types" - -import { ExtensionMessage } from "@roo/ExtensionMessage" +import type { ProviderSettings, ExtensionMessage } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui" diff --git a/webview-ui/src/components/settings/providers/VercelAiGateway.tsx b/webview-ui/src/components/settings/providers/VercelAiGateway.tsx index 4578c871bd..1f003ed52b 100644 --- a/webview-ui/src/components/settings/providers/VercelAiGateway.tsx +++ b/webview-ui/src/components/settings/providers/VercelAiGateway.tsx @@ -1,9 +1,12 @@ import { useCallback } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { type ProviderSettings, type OrganizationAllowList, vercelAiGatewayDefaultModelId } from "@roo-code/types" - -import type { RouterModels } from "@roo/api" +import { + type ProviderSettings, + type OrganizationAllowList, + type RouterModels, + vercelAiGatewayDefaultModelId, +} from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" diff --git a/webview-ui/src/components/settings/providers/ZAi.tsx b/webview-ui/src/components/settings/providers/ZAi.tsx index c7f44510c1..3dab8964ff 100644 --- a/webview-ui/src/components/settings/providers/ZAi.tsx +++ b/webview-ui/src/components/settings/providers/ZAi.tsx @@ -1,7 +1,7 @@ import { useCallback } from "react" import { VSCodeTextField, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react" -import { zaiApiLineConfigs, zaiApiLineSchema, type ProviderSettings } from "@roo-code/types" +import { type ProviderSettings, zaiApiLineConfigs, zaiApiLineSchema } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" diff --git a/webview-ui/src/components/ui/hooks/useLmStudioModels.ts b/webview-ui/src/components/ui/hooks/useLmStudioModels.ts index befe6eb238..29f50cb0e8 100644 --- a/webview-ui/src/components/ui/hooks/useLmStudioModels.ts +++ b/webview-ui/src/components/ui/hooks/useLmStudioModels.ts @@ -1,7 +1,6 @@ import { useQuery } from "@tanstack/react-query" -import { ModelRecord } from "@roo/api" -import { ExtensionMessage } from "@roo/ExtensionMessage" +import { type ModelRecord, type ExtensionMessage } from "@roo-code/types" import { vscode } from "@src/utils/vscode" diff --git a/webview-ui/src/components/ui/hooks/useOllamaModels.ts b/webview-ui/src/components/ui/hooks/useOllamaModels.ts index 67a172b0d8..80fc727f71 100644 --- a/webview-ui/src/components/ui/hooks/useOllamaModels.ts +++ b/webview-ui/src/components/ui/hooks/useOllamaModels.ts @@ -1,7 +1,6 @@ import { useQuery } from "@tanstack/react-query" -import { ModelRecord } from "@roo/api" -import { ExtensionMessage } from "@roo/ExtensionMessage" +import { type ModelRecord, type ExtensionMessage } from "@roo-code/types" import { vscode } from "@src/utils/vscode" diff --git a/webview-ui/src/components/ui/hooks/useRooCreditBalance.ts b/webview-ui/src/components/ui/hooks/useRooCreditBalance.ts index 86fe0236c2..19000415cd 100644 --- a/webview-ui/src/components/ui/hooks/useRooCreditBalance.ts +++ b/webview-ui/src/components/ui/hooks/useRooCreditBalance.ts @@ -1,5 +1,7 @@ import { useEffect, useState } from "react" -import type { ExtensionMessage } from "@roo/ExtensionMessage" + +import type { ExtensionMessage } from "@roo-code/types" + import { vscode } from "@src/utils/vscode" /** diff --git a/webview-ui/src/components/ui/hooks/useRouterModels.ts b/webview-ui/src/components/ui/hooks/useRouterModels.ts index 2527168bfd..27e888b7b5 100644 --- a/webview-ui/src/components/ui/hooks/useRouterModels.ts +++ b/webview-ui/src/components/ui/hooks/useRouterModels.ts @@ -1,7 +1,6 @@ import { useQuery } from "@tanstack/react-query" -import { RouterModels } from "@roo/api" -import { ExtensionMessage } from "@roo/ExtensionMessage" +import { type RouterModels, type ExtensionMessage } from "@roo-code/types" import { vscode } from "@src/utils/vscode" diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index 68f54ab0f3..65be3e21fe 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -2,6 +2,8 @@ import { type ProviderName, type ProviderSettings, type ModelInfo, + type ModelRecord, + type RouterModels, anthropicModels, bedrockModels, cerebrasModels, @@ -36,8 +38,6 @@ import { NATIVE_TOOL_DEFAULTS, } from "@roo-code/types" -import type { ModelRecord, RouterModels } from "@roo/api" - import { useRouterModels } from "./useRouterModels" import { useOpenRouterModelProviders } from "./useOpenRouterModelProviders" import { useLmStudioModels } from "./useLmStudioModels" diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 3fe5340bdb..d2ff79a8e0 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -10,18 +10,22 @@ import { type TelemetrySetting, type OrganizationAllowList, type CloudOrganizationMembership, + type ExtensionMessage, + type ExtensionState, + type MarketplaceInstalledMetadata, + type Command, + type McpServer, + RouterModels, ORGANIZATION_ALLOW_ALL, DEFAULT_CHECKPOINT_TIMEOUT_SECONDS, } from "@roo-code/types" -import { ExtensionMessage, ExtensionState, MarketplaceInstalledMetadata, Command } from "@roo/ExtensionMessage" import { findLastIndex } from "@roo/array" -import { McpServer } from "@roo/mcp" + import { checkExistKey } from "@roo/checkExistApiConfig" import { Mode, defaultModeSlug, defaultPrompts } from "@roo/modes" import { CustomSupportPrompts } from "@roo/support-prompt" import { experimentDefault } from "@roo/experiments" -import { RouterModels } from "@roo/api" import { vscode } from "@src/utils/vscode" import { convertTextMateToHljs } from "@src/utils/textMateToHljs" diff --git a/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx b/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx index 84bee7b10d..292a8a7475 100644 --- a/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx +++ b/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx @@ -1,8 +1,11 @@ import { render, screen, act } from "@/utils/test-utils" -import { ProviderSettings, ExperimentId, DEFAULT_CHECKPOINT_TIMEOUT_SECONDS } from "@roo-code/types" - -import { ExtensionState } from "@roo/ExtensionMessage" +import { + type ProviderSettings, + type ExperimentId, + type ExtensionState, + DEFAULT_CHECKPOINT_TIMEOUT_SECONDS, +} from "@roo-code/types" import { ExtensionStateContextProvider, useExtensionState, mergeExtensionState } from "../ExtensionStateContext" diff --git a/webview-ui/src/utils/__tests__/validate.spec.ts b/webview-ui/src/utils/__tests__/validate.spec.ts index 6078d993ad..09239b649c 100644 --- a/webview-ui/src/utils/__tests__/validate.spec.ts +++ b/webview-ui/src/utils/__tests__/validate.spec.ts @@ -1,6 +1,4 @@ -import type { ProviderSettings, OrganizationAllowList } from "@roo-code/types" - -import { RouterModels } from "@roo/api" +import type { ProviderSettings, OrganizationAllowList, RouterModels } from "@roo-code/types" // Mock i18next to return translation keys with interpolated values vi.mock("i18next", () => ({ diff --git a/webview-ui/src/utils/context-mentions.ts b/webview-ui/src/utils/context-mentions.ts index d7aeb0fdd5..22dba8864f 100644 --- a/webview-ui/src/utils/context-mentions.ts +++ b/webview-ui/src/utils/context-mentions.ts @@ -1,7 +1,6 @@ import { Fzf } from "fzf" -import type { ModeConfig } from "@roo-code/types" -import type { Command } from "@roo/ExtensionMessage" +import type { ModeConfig, Command } from "@roo-code/types" import { mentionRegex } from "@roo/context-mentions" diff --git a/webview-ui/src/utils/mcp.ts b/webview-ui/src/utils/mcp.ts index b2a2ca002f..e09c85c9ac 100644 --- a/webview-ui/src/utils/mcp.ts +++ b/webview-ui/src/utils/mcp.ts @@ -1,4 +1,4 @@ -import { McpResource, McpResourceTemplate } from "@roo/mcp" +import type { McpResource, McpResourceTemplate } from "@roo-code/types" /** * Matches a URI against an array of URI templates and returns the matching template diff --git a/webview-ui/src/utils/validate.ts b/webview-ui/src/utils/validate.ts index 06dde70110..df50ca8843 100644 --- a/webview-ui/src/utils/validate.ts +++ b/webview-ui/src/utils/validate.ts @@ -4,6 +4,7 @@ import { type ProviderSettings, type OrganizationAllowList, type ProviderName, + type RouterModels, modelIdKeysByProvider, isProviderName, isDynamicProvider, @@ -11,8 +12,6 @@ import { isCustomProvider, } from "@roo-code/types" -import type { RouterModels } from "@roo/api" - export function validateApiConfiguration( apiConfiguration: ProviderSettings, routerModels?: RouterModels,