From 602c6efbd8a8f36f1f209fb3eafdad6fc7eadc09 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Fri, 14 Feb 2025 09:51:50 -0500 Subject: [PATCH 1/9] Add delay on submitting from welcome view --- webview-ui/src/components/welcome/WelcomeView.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/webview-ui/src/components/welcome/WelcomeView.tsx b/webview-ui/src/components/welcome/WelcomeView.tsx index db52612b90..6513e4b10b 100644 --- a/webview-ui/src/components/welcome/WelcomeView.tsx +++ b/webview-ui/src/components/welcome/WelcomeView.tsx @@ -10,7 +10,13 @@ const WelcomeView = () => { const [errorMessage, setErrorMessage] = useState(undefined) - const handleSubmit = () => { + const handleSubmit = async () => { + // Focus the active element's parent to trigger blur + document.activeElement?.parentElement?.focus() + + // Small delay to let blur events complete + await new Promise((resolve) => setTimeout(resolve, 50)) + const error = validateApiConfiguration(apiConfiguration) if (error) { setErrorMessage(error) From 0baf259b1e69820a2a3e436a24bf56494de8c8df Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Fri, 14 Feb 2025 09:58:27 -0500 Subject: [PATCH 2/9] v3.3.20 --- .changeset/fast-weeks-sip.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fast-weeks-sip.md diff --git a/.changeset/fast-weeks-sip.md b/.changeset/fast-weeks-sip.md new file mode 100644 index 0000000000..f924abe599 --- /dev/null +++ b/.changeset/fast-weeks-sip.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +v3.3.20 From 20ffdf36d70f7a70bbc71e577fd8a273387b075a Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Fri, 14 Feb 2025 10:13:49 -0500 Subject: [PATCH 3/9] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8795b54d37..31edb9b368 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "roo-cline", "displayName": "Roo Code (prev. Roo Cline)", - "description": "A VS Code plugin that enhances coding with AI-powered automation, multi-model support, and experimental features.", + "description": "An AI-powered autonomous coding agent that lives in your editor.", "publisher": "RooVeterinaryInc", "version": "3.3.19", "icon": "assets/icons/rocket.png", From 071165ce8d2aea9344f421d5ce1a6052b7bc73b7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 14 Feb 2025 15:16:59 +0000 Subject: [PATCH 4/9] changeset version bump --- .changeset/clever-news-arrive.md | 5 ----- .changeset/fast-weeks-sip.md | 5 ----- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 5 files changed, 10 insertions(+), 13 deletions(-) delete mode 100644 .changeset/clever-news-arrive.md delete mode 100644 .changeset/fast-weeks-sip.md diff --git a/.changeset/clever-news-arrive.md b/.changeset/clever-news-arrive.md deleted file mode 100644 index d490036adb..0000000000 --- a/.changeset/clever-news-arrive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Disable writing in ask mode diff --git a/.changeset/fast-weeks-sip.md b/.changeset/fast-weeks-sip.md deleted file mode 100644 index f924abe599..0000000000 --- a/.changeset/fast-weeks-sip.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -v3.3.20 diff --git a/CHANGELOG.md b/CHANGELOG.md index 39ce0a73f0..611917b21f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Roo Code Changelog +## 3.3.20 + +### Patch Changes + +- Disable writing in ask mode +- v3.3.20 + ## [3.3.19] - Fix a bug where aborting in the middle of file writes would not revert the write diff --git a/package-lock.json b/package-lock.json index 8af12ad90c..f0d36579d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "roo-cline", - "version": "3.3.19", + "version": "3.3.20", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "roo-cline", - "version": "3.3.19", + "version": "3.3.20", "dependencies": { "@anthropic-ai/bedrock-sdk": "^0.10.2", "@anthropic-ai/sdk": "^0.26.0", diff --git a/package.json b/package.json index 31edb9b368..c43cf05b11 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "Roo Code (prev. Roo Cline)", "description": "An AI-powered autonomous coding agent that lives in your editor.", "publisher": "RooVeterinaryInc", - "version": "3.3.19", + "version": "3.3.20", "icon": "assets/icons/rocket.png", "galleryBanner": { "color": "#617A91", From 37e3b08f699b058bee83195408318231776d8f05 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Fri, 14 Feb 2025 10:21:13 -0500 Subject: [PATCH 5/9] Update CHANGELOG.md --- CHANGELOG.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 611917b21f..e0812c66bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,12 @@ # Roo Code Changelog -## 3.3.20 +## [3.3.20] -### Patch Changes - -- Disable writing in ask mode -- v3.3.20 +- Support project-specific custom modes in a .roomodes file +- Add more Mistral models (thanks @d-oit and @bramburn!) +- By popular request, make it so Ask mode can't write to Markdown files and is purely for chatting with +- Add a setting to control the number of open editor tabs to tell the model about (665 is probably too many!) +- Fix race condition bug with entering API key on the welcome screen ## [3.3.19] From e74b807dee5eae7536bd5eb3ef91aa9e1dbc28e1 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Fri, 14 Feb 2025 10:59:45 -0500 Subject: [PATCH 6/9] Add link to docs to readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 62338b4390..53f29e0725 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Download on VS Marketplace Feature Requests Rate & Review +Documentation + **Roo Code** is an AI-powered **autonomous coding agent** that lives in your editor. It can: From 5ffb145c133e2edd8d19f1fc4a0080514f4f9c3a Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Fri, 14 Feb 2025 19:26:04 -0500 Subject: [PATCH 7/9] Enable streaming for o1 --- .../providers/__tests__/openai-native.test.ts | 26 ++++++++++++------- src/api/providers/openai-native.ts | 4 ++- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/api/providers/__tests__/openai-native.test.ts b/src/api/providers/__tests__/openai-native.test.ts index f39d49307f..d6a855849c 100644 --- a/src/api/providers/__tests__/openai-native.test.ts +++ b/src/api/providers/__tests__/openai-native.test.ts @@ -130,11 +130,20 @@ describe("OpenAiNativeHandler", () => { }) mockCreate.mockResolvedValueOnce({ - choices: [{ message: { content: null } }], - usage: { - prompt_tokens: 0, - completion_tokens: 0, - total_tokens: 0, + [Symbol.asyncIterator]: async function* () { + yield { + choices: [ + { + delta: { content: null }, + index: 0, + }, + ], + usage: { + prompt_tokens: 0, + completion_tokens: 0, + total_tokens: 0, + }, + } }, }) @@ -144,10 +153,7 @@ describe("OpenAiNativeHandler", () => { results.push(result) } - expect(results).toEqual([ - { type: "text", text: "" }, - { type: "usage", inputTokens: 0, outputTokens: 0 }, - ]) + expect(results).toEqual([{ type: "usage", inputTokens: 0, outputTokens: 0 }]) // Verify developer role is used for system prompt with o1 model expect(mockCreate).toHaveBeenCalledWith({ @@ -156,6 +162,8 @@ describe("OpenAiNativeHandler", () => { { role: "developer", content: "Formatting re-enabled\n" + systemPrompt }, { role: "user", content: "Hello!" }, ], + stream: true, + stream_options: { include_usage: true }, }) }) diff --git a/src/api/providers/openai-native.ts b/src/api/providers/openai-native.ts index 1a4f9e613a..8feeafdb96 100644 --- a/src/api/providers/openai-native.ts +++ b/src/api/providers/openai-native.ts @@ -56,9 +56,11 @@ export class OpenAiNativeHandler implements ApiHandler, SingleCompletionHandler }, ...convertToOpenAiMessages(messages), ], + stream: true, + stream_options: { include_usage: true }, }) - yield* this.yieldResponseData(response) + yield* this.handleStreamResponse(response) } private async *handleO3FamilyMessage( From 17157c46cbedb833f0215d3cce5cbaf7d962b28e Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Fri, 14 Feb 2025 18:48:47 -0800 Subject: [PATCH 8/9] Add terminal context mention --- src/core/mentions/index.ts | 12 +++++ .../terminal/get-latest-output.ts | 45 +++++++++++++++++++ src/shared/context-mentions.ts | 10 ++--- .../src/components/chat/ChatTextArea.tsx | 3 ++ .../src/components/chat/ContextMenu.tsx | 5 +++ webview-ui/src/utils/context-mentions.ts | 10 ++++- 6 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 src/integrations/terminal/get-latest-output.ts diff --git a/src/core/mentions/index.ts b/src/core/mentions/index.ts index cf5bdeaae0..5853a4abdd 100644 --- a/src/core/mentions/index.ts +++ b/src/core/mentions/index.ts @@ -8,6 +8,7 @@ import { extractTextFromFile } from "../../integrations/misc/extract-text" import { isBinaryFile } from "isbinaryfile" import { diagnosticsToProblemsString } from "../../integrations/diagnostics" import { getCommitInfo, getWorkingState } from "../../utils/git" +import { getLatestTerminalOutput } from "../../integrations/terminal/get-latest-output" export async function openMention(mention?: string): Promise { if (!mention) { @@ -29,6 +30,8 @@ export async function openMention(mention?: string): Promise { } } else if (mention === "problems") { vscode.commands.executeCommand("workbench.actions.view.problems") + } else if (mention === "terminal") { + vscode.commands.executeCommand("workbench.action.terminal.focus") } else if (mention.startsWith("http")) { vscode.env.openExternal(vscode.Uri.parse(mention)) } @@ -51,6 +54,8 @@ export async function parseMentions(text: string, cwd: string, urlContentFetcher return `Working directory changes (see below for details)` } else if (/^[a-f0-9]{7,40}$/.test(mention)) { return `Git commit '${mention}' (see below for commit info)` + } else if (mention === "terminal") { + return `Terminal Output (see below for output)` } return match }) @@ -118,6 +123,13 @@ export async function parseMentions(text: string, cwd: string, urlContentFetcher } catch (error) { parsedText += `\n\n\nError fetching commit info: ${error.message}\n` } + } else if (mention === "terminal") { + try { + const terminalOutput = await getLatestTerminalOutput() + parsedText += `\n\n\n${terminalOutput}\n` + } catch (error) { + parsedText += `\n\n\nError fetching terminal output: ${error.message}\n` + } } } diff --git a/src/integrations/terminal/get-latest-output.ts b/src/integrations/terminal/get-latest-output.ts new file mode 100644 index 0000000000..0c869e7fad --- /dev/null +++ b/src/integrations/terminal/get-latest-output.ts @@ -0,0 +1,45 @@ +import * as vscode from "vscode" + +/** + * Gets the contents of the active terminal + * @returns The terminal contents as a string + */ +export async function getLatestTerminalOutput(): Promise { + // Store original clipboard content to restore later + const originalClipboard = await vscode.env.clipboard.readText() + + try { + // Select terminal content + await vscode.commands.executeCommand("workbench.action.terminal.selectAll") + + // Copy selection to clipboard + await vscode.commands.executeCommand("workbench.action.terminal.copySelection") + + // Clear the selection + await vscode.commands.executeCommand("workbench.action.terminal.clearSelection") + + // Get terminal contents from clipboard + let terminalContents = (await vscode.env.clipboard.readText()).trim() + + // Check if there's actually a terminal open + if (terminalContents === originalClipboard) { + return "" + } + + // Clean up command separation + const lines = terminalContents.split("\n") + const lastLine = lines.pop()?.trim() + if (lastLine) { + let i = lines.length - 1 + while (i >= 0 && !lines[i].trim().startsWith(lastLine)) { + i-- + } + terminalContents = lines.slice(Math.max(i, 0)).join("\n") + } + + return terminalContents + } finally { + // Restore original clipboard content + await vscode.env.clipboard.writeText(originalClipboard) + } +} diff --git a/src/shared/context-mentions.ts b/src/shared/context-mentions.ts index 10375ff23e..915114ab93 100644 --- a/src/shared/context-mentions.ts +++ b/src/shared/context-mentions.ts @@ -26,10 +26,9 @@ Mention regex: - **Exact Word ('problems')**: Matches the exact word 'problems'. - **Word Boundary (`\b`)**: Ensures that 'problems' is matched as a whole word and not as part of another word (e.g., 'problematic'). - `|`: Logical OR. - - `problems\b`: - - **Exact Word ('git-changes')**: Matches the exact word 'git-changes'. - - **Word Boundary (`\b`)**: Ensures that 'git-changes' is matched as a whole word and not as part of another word. - + - `terminal\b`: + - **Exact Word ('terminal')**: Matches the exact word 'terminal'. + - **Word Boundary (`\b`)**: Ensures that 'terminal' is matched as a whole word and not as part of another word (e.g., 'terminals'). - `(?=[.,;:!?]?(?=[\s\r\n]|$))`: - **Positive Lookahead (`(?=...)`)**: Ensures that the match is followed by specific patterns without including them in the match. - `[.,;:!?]?`: @@ -43,6 +42,7 @@ Mention regex: - URLs that start with a protocol (like 'http://') followed by any non-whitespace characters (including query parameters). - The exact word 'problems'. - The exact word 'git-changes'. + - The exact word 'terminal'. - It ensures that any trailing punctuation marks (such as ',', '.', '!', etc.) are not included in the matched mention, allowing the punctuation to follow the mention naturally in the text. - **Global Regex**: @@ -50,7 +50,7 @@ Mention regex: */ export const mentionRegex = - /@((?:\/|\w+:\/\/)[^\s]+?|[a-f0-9]{7,40}\b|problems\b|git-changes\b)(?=[.,;:!?]?(?=[\s\r\n]|$))/ + /@((?:\/|\w+:\/\/)[^\s]+?|[a-f0-9]{7,40}\b|problems\b|git-changes\b|terminal\b)(?=[.,;:!?]?(?=[\s\r\n]|$))/ export const mentionRegexGlobal = new RegExp(mentionRegex.source, "g") export interface MentionSuggestion { diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index 98d57965b7..d317efb96e 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -137,6 +137,7 @@ const ChatTextArea = forwardRef( const queryItems = useMemo(() => { return [ { type: ContextMenuOptionType.Problems, value: "problems" }, + { type: ContextMenuOptionType.Terminal, value: "terminal" }, ...gitCommits, ...openedTabs .filter((tab) => tab.path) @@ -214,6 +215,8 @@ const ChatTextArea = forwardRef( insertValue = value || "" } else if (type === ContextMenuOptionType.Problems) { insertValue = "problems" + } else if (type === ContextMenuOptionType.Terminal) { + insertValue = "terminal" } else if (type === ContextMenuOptionType.Git) { insertValue = value || "" } diff --git a/webview-ui/src/components/chat/ContextMenu.tsx b/webview-ui/src/components/chat/ContextMenu.tsx index bd631a98d5..2bb7a8ee68 100644 --- a/webview-ui/src/components/chat/ContextMenu.tsx +++ b/webview-ui/src/components/chat/ContextMenu.tsx @@ -70,6 +70,8 @@ const ContextMenu: React.FC = ({ ) case ContextMenuOptionType.Problems: return Problems + case ContextMenuOptionType.Terminal: + return Terminal case ContextMenuOptionType.URL: return Paste URL to fetch contents case ContextMenuOptionType.NoResults: @@ -133,6 +135,8 @@ const ContextMenu: React.FC = ({ return "folder" case ContextMenuOptionType.Problems: return "warning" + case ContextMenuOptionType.Terminal: + return "terminal" case ContextMenuOptionType.URL: return "link" case ContextMenuOptionType.Git: @@ -221,6 +225,7 @@ const ContextMenu: React.FC = ({ /> )} {(option.type === ContextMenuOptionType.Problems || + option.type === ContextMenuOptionType.Terminal || ((option.type === ContextMenuOptionType.File || option.type === ContextMenuOptionType.Folder || option.type === ContextMenuOptionType.OpenedFile || diff --git a/webview-ui/src/utils/context-mentions.ts b/webview-ui/src/utils/context-mentions.ts index 0fb57071ab..600a2f760e 100644 --- a/webview-ui/src/utils/context-mentions.ts +++ b/webview-ui/src/utils/context-mentions.ts @@ -61,6 +61,7 @@ export enum ContextMenuOptionType { File = "file", Folder = "folder", Problems = "problems", + Terminal = "terminal", URL = "url", Git = "git", NoResults = "noResults", @@ -151,6 +152,7 @@ export function getContextMenuOptions( return [ { type: ContextMenuOptionType.Problems }, + { type: ContextMenuOptionType.Terminal }, { type: ContextMenuOptionType.URL }, { type: ContextMenuOptionType.Folder }, { type: ContextMenuOptionType.File }, @@ -175,6 +177,9 @@ export function getContextMenuOptions( if ("problems".startsWith(lowerQuery)) { suggestions.push({ type: ContextMenuOptionType.Problems }) } + if ("terminal".startsWith(lowerQuery)) { + suggestions.push({ type: ContextMenuOptionType.Terminal }) + } if (query.startsWith("http")) { suggestions.push({ type: ContextMenuOptionType.URL, value: query }) } @@ -266,8 +271,9 @@ export function shouldShowContextMenu(text: string, position: number): boolean { // Don't show the menu if it's a URL if (textAfterAt.toLowerCase().startsWith("http")) return false - // Don't show the menu if it's a problems - if (textAfterAt.toLowerCase().startsWith("problems")) return false + // Don't show the menu if it's a problems or terminal + if (textAfterAt.toLowerCase().startsWith("problems") || textAfterAt.toLowerCase().startsWith("terminal")) + return false // NOTE: it's okay that menu shows when there's trailing punctuation since user could be inputting a path with marks From fde9d9b6603192aaef67fa788b7b9228f0e0c643 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Fri, 14 Feb 2025 22:28:35 -0500 Subject: [PATCH 9/9] Update test --- webview-ui/src/utils/__tests__/context-mentions.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webview-ui/src/utils/__tests__/context-mentions.test.ts b/webview-ui/src/utils/__tests__/context-mentions.test.ts index d1cf54181f..ee40a9f724 100644 --- a/webview-ui/src/utils/__tests__/context-mentions.test.ts +++ b/webview-ui/src/utils/__tests__/context-mentions.test.ts @@ -73,9 +73,10 @@ describe("getContextMenuOptions", () => { it("should return all option types for empty query", () => { const result = getContextMenuOptions("", null, []) - expect(result).toHaveLength(5) + expect(result).toHaveLength(6) expect(result.map((item) => item.type)).toEqual([ ContextMenuOptionType.Problems, + ContextMenuOptionType.Terminal, ContextMenuOptionType.URL, ContextMenuOptionType.Folder, ContextMenuOptionType.File,