mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
Merge branch 'RooVetGit:main' into mistral
This commit is contained in:
commit
e9f6cb4f75
15 changed files with 121 additions and 28 deletions
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
"roo-cline": patch
|
||||
---
|
||||
|
||||
Disable writing in ask mode
|
||||
|
|
@ -1,5 +1,13 @@
|
|||
# Roo Code Changelog
|
||||
|
||||
## [3.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]
|
||||
|
||||
- Fix a bug where aborting in the middle of file writes would not revert the write
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
<a href="https://marketplace.visualstudio.com/items?itemName=RooVeterinaryInc.roo-cline" target="_blank"><img src="https://img.shields.io/badge/Download%20on%20VS%20Marketplace-blue?style=for-the-badge&logo=visualstudiocode&logoColor=white" alt="Download on VS Marketplace"></a>
|
||||
<a href="https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop" target="_blank"><img src="https://img.shields.io/badge/Feature%20Requests-yellow?style=for-the-badge" alt="Feature Requests"></a>
|
||||
<a href="https://marketplace.visualstudio.com/items?itemName=RooVeterinaryInc.roo-cline&ssr=false#review-details" target="_blank"><img src="https://img.shields.io/badge/Rate%20%26%20Review-green?style=for-the-badge" alt="Rate & Review"></a>
|
||||
<a href="https://docs.roocode.com" target="_blank"><img src="https://img.shields.io/badge/Documentation-6B46C1?style=for-the-badge&logo=readthedocs&logoColor=white" alt="Documentation"></a>
|
||||
|
||||
</div>
|
||||
|
||||
**Roo Code** is an AI-powered **autonomous coding agent** that lives in your editor. It can:
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"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",
|
||||
"version": "3.3.20",
|
||||
"icon": "assets/icons/rocket.png",
|
||||
"galleryBanner": {
|
||||
"color": "#617A91",
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
||||
if (!mention) {
|
||||
|
|
@ -29,6 +30,8 @@ export async function openMention(mention?: string): Promise<void> {
|
|||
}
|
||||
} 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<git_commit hash="${mention}">\nError fetching commit info: ${error.message}\n</git_commit>`
|
||||
}
|
||||
} else if (mention === "terminal") {
|
||||
try {
|
||||
const terminalOutput = await getLatestTerminalOutput()
|
||||
parsedText += `\n\n<terminal_output>\n${terminalOutput}\n</terminal_output>`
|
||||
} catch (error) {
|
||||
parsedText += `\n\n<terminal_output>\nError fetching terminal output: ${error.message}\n</terminal_output>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
45
src/integrations/terminal/get-latest-output.ts
Normal file
45
src/integrations/terminal/get-latest-output.ts
Normal file
|
|
@ -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<string> {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
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<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
insertValue = value || ""
|
||||
} else if (type === ContextMenuOptionType.Problems) {
|
||||
insertValue = "problems"
|
||||
} else if (type === ContextMenuOptionType.Terminal) {
|
||||
insertValue = "terminal"
|
||||
} else if (type === ContextMenuOptionType.Git) {
|
||||
insertValue = value || ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
|
|||
)
|
||||
case ContextMenuOptionType.Problems:
|
||||
return <span>Problems</span>
|
||||
case ContextMenuOptionType.Terminal:
|
||||
return <span>Terminal</span>
|
||||
case ContextMenuOptionType.URL:
|
||||
return <span>Paste URL to fetch contents</span>
|
||||
case ContextMenuOptionType.NoResults:
|
||||
|
|
@ -133,6 +135,8 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
|
|||
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<ContextMenuProps> = ({
|
|||
/>
|
||||
)}
|
||||
{(option.type === ContextMenuOptionType.Problems ||
|
||||
option.type === ContextMenuOptionType.Terminal ||
|
||||
((option.type === ContextMenuOptionType.File ||
|
||||
option.type === ContextMenuOptionType.Folder ||
|
||||
option.type === ContextMenuOptionType.OpenedFile ||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,13 @@ const WelcomeView = () => {
|
|||
|
||||
const [errorMessage, setErrorMessage] = useState<string | undefined>(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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue